diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs
index 53b94b9e2..278c93ff4 100644
--- a/embassy-stm32/src/can/bxcan/mod.rs
+++ b/embassy-stm32/src/can/bxcan/mod.rs
@@ -18,7 +18,7 @@ pub use super::common::{BufferedCanReceiver, BufferedCanSender};
 use super::frame::{Envelope, Frame};
 use super::util;
 use crate::can::enums::{BusError, TryReadError};
-use crate::gpio::AFType;
+use crate::gpio::{AfType, OutputType, Pull, Speed};
 use crate::interrupt::typelevel::Interrupt;
 use crate::rcc::{self, RccPeripheral};
 use crate::{interrupt, peripherals, Peripheral};
@@ -188,8 +188,8 @@ impl<'d> Can<'d> {
         let info = T::info();
         let regs = &T::info().regs;
 
-        rx.set_as_af(rx.af_num(), AFType::Input);
-        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
+        rx.set_as_af(rx.af_num(), AfType::input(Pull::None));
+        tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 
         rcc::enable_and_reset::<T>();
 
@@ -223,8 +223,8 @@ impl<'d> Can<'d> {
             info.sce_interrupt.enable();
         }
 
-        rx.set_as_af(rx.af_num(), AFType::Input);
-        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
+        rx.set_as_af(rx.af_num(), AfType::input(Pull::None));
+        tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 
         Registers(T::regs()).leave_init_mode();
 
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs
index f3ad718ae..c549313f3 100644
--- a/embassy-stm32/src/can/fdcan.rs
+++ b/embassy-stm32/src/can/fdcan.rs
@@ -10,7 +10,7 @@ use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender};
 use embassy_sync::waitqueue::AtomicWaker;
 
 use crate::can::fd::peripheral::Registers;
-use crate::gpio::AFType;
+use crate::gpio::{AfType, OutputType, Pull, Speed};
 use crate::interrupt::typelevel::Interrupt;
 use crate::rcc::{self, RccPeripheral};
 use crate::{interrupt, peripherals, Peripheral};
@@ -184,8 +184,8 @@ impl<'d> CanConfigurator<'d> {
     ) -> CanConfigurator<'d> {
         into_ref!(_peri, rx, tx);
 
-        rx.set_as_af(rx.af_num(), AFType::Input);
-        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
+        rx.set_as_af(rx.af_num(), AfType::input(Pull::None));
+        tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 
         rcc::enable_and_reset::<T>();
 
@@ -193,8 +193,8 @@ impl<'d> CanConfigurator<'d> {
         config.timestamp_source = TimestampSource::Prescaler(TimestampPrescaler::_1);
         T::registers().into_config_mode(config);
 
-        rx.set_as_af(rx.af_num(), AFType::Input);
-        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
+        rx.set_as_af(rx.af_num(), AfType::input(Pull::None));
+        tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 
         unsafe {
             T::IT0Interrupt::unpend(); // Not unsafe
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs
index 858ae49ca..4ba4e824e 100644
--- a/embassy-stm32/src/dcmi.rs
+++ b/embassy-stm32/src/dcmi.rs
@@ -7,7 +7,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
 use embassy_sync::waitqueue::AtomicWaker;
 
 use crate::dma::Transfer;
-use crate::gpio::{AFType, Speed};
+use crate::gpio::{AfType, Pull};
 use crate::interrupt::typelevel::Interrupt;
 use crate::{interrupt, rcc, Peripheral};
 
@@ -109,8 +109,7 @@ macro_rules! config_pins {
         into_ref!($($pin),*);
         critical_section::with(|_| {
             $(
-                $pin.set_as_af($pin.af_num(), AFType::Input);
-                $pin.set_speed(Speed::VeryHigh);
+                $pin.set_as_af($pin.af_num(), AfType::input(Pull::None));
             )*
         })
     };
diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs
index c59ee3f2d..51f124542 100644
--- a/embassy-stm32/src/dsihost.rs
+++ b/embassy-stm32/src/dsihost.rs
@@ -5,7 +5,7 @@ use core::marker::PhantomData;
 use embassy_hal_internal::{into_ref, PeripheralRef};
 
 //use crate::gpio::{AnyPin, SealedPin};
-use crate::gpio::{AFType, AnyPin, Pull, Speed};
+use crate::gpio::{AfType, AnyPin, OutputType, Speed};
 use crate::rcc::{self, RccPeripheral};
 use crate::{peripherals, Peripheral};
 
@@ -80,8 +80,7 @@ impl<'d, T: Instance> DsiHost<'d, T> {
         rcc::enable_and_reset::<T>();
 
         // Set Tearing Enable pin according to CubeMx example
-        te.set_as_af_pull(te.af_num(), AFType::OutputPushPull, Pull::None);
-        te.set_speed(Speed::Low);
+        te.set_as_af(te.af_num(), AfType::output(OutputType::PushPull, Speed::Low));
         /*
                 T::regs().wcr().modify(|w| {
                     w.set_dsien(true);
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs
index 6f0174def..cce75ece7 100644
--- a/embassy-stm32/src/eth/v1/mod.rs
+++ b/embassy-stm32/src/eth/v1/mod.rs
@@ -12,7 +12,9 @@ use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress
 pub(crate) use self::rx_desc::{RDes, RDesRing};
 pub(crate) use self::tx_desc::{TDes, TDesRing};
 use super::*;
-use crate::gpio::{AFType, AnyPin, SealedPin};
+#[cfg(eth_v1a)]
+use crate::gpio::Pull;
+use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
 use crate::interrupt::InterruptExt;
 #[cfg(eth_v1a)]
 use crate::pac::AFIO;
@@ -61,7 +63,7 @@ macro_rules! config_in_pins {
         critical_section::with(|_| {
             $(
                 // TODO properly create a set_as_input function
-                $pin.set_as_af($pin.af_num(), AFType::Input);
+                $pin.set_as_af($pin.af_num(), AfType::input(Pull::None));
             )*
         })
     }
@@ -72,8 +74,7 @@ macro_rules! config_af_pins {
     ($($pin:ident),*) => {
         critical_section::with(|_| {
             $(
-                // We are lucky here, this configures to max speed (50MHz)
-                $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
+                $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
             )*
         })
     };
@@ -84,8 +85,7 @@ macro_rules! config_pins {
     ($($pin:ident),*) => {
         critical_section::with(|_| {
             $(
-                $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
-                $pin.set_speed(crate::gpio::Speed::VeryHigh);
+                $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
             )*
         })
     };
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
index a19aa42cf..b26f08cd9 100644
--- a/embassy-stm32/src/eth/v2/mod.rs
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -8,7 +8,7 @@ use stm32_metapac::syscfg::vals::EthSelPhy;
 
 pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing};
 use super::*;
-use crate::gpio::{AFType, AnyPin, SealedPin as _, Speed};
+use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed};
 use crate::interrupt::InterruptExt;
 use crate::pac::ETH;
 use crate::rcc::SealedRccPeripheral;
@@ -56,8 +56,8 @@ macro_rules! config_pins {
     ($($pin:ident),*) => {
         critical_section::with(|_| {
             $(
-                $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
-                $pin.set_speed(Speed::VeryHigh);
+                // TODO: shouldn't some pins be configured as inputs?
+                $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
             )*
         })
     };
diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs
index 82d8089f4..7aea466e8 100644
--- a/embassy-stm32/src/fmc.rs
+++ b/embassy-stm32/src/fmc.rs
@@ -3,7 +3,7 @@ use core::marker::PhantomData;
 
 use embassy_hal_internal::into_ref;
 
-use crate::gpio::{AFType, Pull, Speed};
+use crate::gpio::{AfType, OutputType, Pull, Speed};
 use crate::{rcc, Peripheral};
 
 /// FMC driver
@@ -76,8 +76,7 @@ macro_rules! config_pins {
     ($($pin:ident),*) => {
         into_ref!($($pin),*);
         $(
-            $pin.set_as_af_pull($pin.af_num(), AFType::OutputPushPull, Pull::Up);
-            $pin.set_speed(Speed::VeryHigh);
+            $pin.set_as_af($pin.af_num(), AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up));
         )*
     };
 }
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs
index de08127cf..86bc226a9 100644
--- a/embassy-stm32/src/gpio.rs
+++ b/embassy-stm32/src/gpio.rs
@@ -32,7 +32,9 @@ impl<'d> Flex<'d> {
     }
 
     /// Put the pin into input mode.
-    #[inline]
+    ///
+    /// The internal weak pull-up and pull-down resistors will be enabled according to `pull`.
+    #[inline(never)]
     pub fn set_as_input(&mut self, pull: Pull) {
         critical_section::with(|_| {
             let r = self.pin.block();
@@ -51,35 +53,35 @@ impl<'d> Flex<'d> {
                     Pull::None => vals::CnfIn::FLOATING,
                 };
 
-                let crlh = if n < 8 { 0 } else { 1 };
-                r.cr(crlh).modify(|w| {
+                r.cr(n / 8).modify(|w| {
                     w.set_mode(n % 8, vals::Mode::INPUT);
                     w.set_cnf_in(n % 8, cnf);
                 });
             }
             #[cfg(gpio_v2)]
             {
-                r.pupdr().modify(|w| w.set_pupdr(n, pull.into()));
+                r.pupdr().modify(|w| w.set_pupdr(n, pull.to_pupdr()));
                 r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSHPULL));
                 r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT));
             }
         });
     }
 
-    /// Put the pin into output mode.
+    /// Put the pin into push-pull output mode.
     ///
     /// The pin level will be whatever was set before (or low by default). If you want it to begin
     /// at a specific level, call `set_high`/`set_low` on the pin first.
-    #[inline]
+    ///
+    /// The internal weak pull-up and pull-down resistors will be disabled.
+    #[inline(never)]
     pub fn set_as_output(&mut self, speed: Speed) {
         critical_section::with(|_| {
             let r = self.pin.block();
             let n = self.pin.pin() as usize;
             #[cfg(gpio_v1)]
             {
-                let crlh = if n < 8 { 0 } else { 1 };
-                r.cr(crlh).modify(|w| {
-                    w.set_mode(n % 8, speed.into());
+                r.cr(n / 8).modify(|w| {
+                    w.set_mode(n % 8, speed.to_mode());
                     w.set_cnf_out(n % 8, vals::CnfOut::PUSHPULL);
                 });
             }
@@ -87,44 +89,50 @@ impl<'d> Flex<'d> {
             {
                 r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING));
                 r.otyper().modify(|w| w.set_ot(n, vals::Ot::PUSHPULL));
-                self.pin.set_speed(speed);
+                r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr()));
                 r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT));
             }
         });
     }
 
-    /// Put the pin into input + output mode.
+    /// Put the pin into input + open-drain output mode.
     ///
-    /// This is commonly used for "open drain" mode.
-    /// the hardware will drive the line low if you set it to low, and will leave it floating if you set
+    /// The hardware will drive the line low if you set it to low, and will leave it floating if you set
     /// it to high, in which case you can read the input to figure out whether another device
     /// is driving the line low.
     ///
     /// The pin level will be whatever was set before (or low by default). If you want it to begin
     /// at a specific level, call `set_high`/`set_low` on the pin first.
-    #[inline]
-    pub fn set_as_input_output(&mut self, speed: Speed, pull: Pull) {
+    ///
+    /// The internal weak pull-up and pull-down resistors will be disabled.
+    #[inline(never)]
+    pub fn set_as_input_output(&mut self, speed: Speed) {
+        #[cfg(gpio_v1)]
         critical_section::with(|_| {
             let r = self.pin.block();
             let n = self.pin.pin() as usize;
-            #[cfg(gpio_v1)]
-            {
-                let crlh = if n < 8 { 0 } else { 1 };
-                match pull {
-                    Pull::Up => r.bsrr().write(|w| w.set_bs(n, true)),
-                    Pull::Down => r.bsrr().write(|w| w.set_br(n, true)),
-                    Pull::None => {}
-                }
-                r.cr(crlh).modify(|w| w.set_mode(n % 8, speed.into()));
-                r.cr(crlh).modify(|w| w.set_cnf_out(n % 8, vals::CnfOut::OPENDRAIN));
-            }
-            #[cfg(gpio_v2)]
-            {
-                r.pupdr().modify(|w| w.set_pupdr(n, pull.into()));
-                r.otyper().modify(|w| w.set_ot(n, vals::Ot::OPENDRAIN));
-                self.pin.set_speed(speed);
-                r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT));
-            }
+            r.cr(n / 8).modify(|w| w.set_mode(n % 8, speed.to_mode()));
+            r.cr(n / 8).modify(|w| w.set_cnf_out(n % 8, vals::CnfOut::OPENDRAIN));
+        });
+
+        #[cfg(gpio_v2)]
+        self.set_as_input_output_pull(speed, Pull::None);
+    }
+
+    /// Put the pin into input + open-drain output mode with internal pullup or pulldown.
+    ///
+    /// This works like [`Self::set_as_input_output()`], but it also allows to enable the internal
+    /// weak pull-up or pull-down resistors.
+    #[inline(never)]
+    #[cfg(gpio_v2)]
+    pub fn set_as_input_output_pull(&mut self, speed: Speed, pull: Pull) {
+        critical_section::with(|_| {
+            let r = self.pin.block();
+            let n = self.pin.pin() as usize;
+            r.pupdr().modify(|w| w.set_pupdr(n, pull.to_pupdr()));
+            r.otyper().modify(|w| w.set_ot(n, vals::Ot::OPENDRAIN));
+            r.ospeedr().modify(|w| w.set_ospeedr(n, speed.to_ospeedr()));
+            r.moder().modify(|w| w.set_moder(n, vals::Moder::OUTPUT));
         });
     }
 
@@ -134,18 +142,18 @@ impl<'d> Flex<'d> {
     /// as the mode change is handled by the driver.
     #[inline]
     pub fn set_as_analog(&mut self) {
+        // TODO: does this also need a critical section, like other methods?
         self.pin.set_as_analog();
     }
 
     /// Put the pin into AF mode, unchecked.
     ///
-    /// This puts the pin into the AF mode, with the requested number, pull and speed. This is
+    /// This puts the pin into the AF mode, with the requested number and AF type. This is
     /// completely unchecked, it can attach the pin to literally any peripheral, so use with care.
     #[inline]
-    pub fn set_as_af_unchecked(&mut self, af_num: u8, af_type: AFType, pull: Pull, speed: Speed) {
+    pub fn set_as_af_unchecked(&mut self, af_num: u8, af_type: AfType) {
         critical_section::with(|_| {
-            self.pin.set_as_af_pull(af_num, af_type, pull);
-            self.pin.set_speed(speed);
+            self.pin.set_as_af(af_num, af_type);
         });
     }
 
@@ -223,21 +231,7 @@ impl<'d> Drop for Flex<'d> {
     #[inline]
     fn drop(&mut self) {
         critical_section::with(|_| {
-            let r = self.pin.block();
-            let n = self.pin.pin() as usize;
-            #[cfg(gpio_v1)]
-            {
-                let crlh = if n < 8 { 0 } else { 1 };
-                r.cr(crlh).modify(|w| {
-                    w.set_mode(n % 8, vals::Mode::INPUT);
-                    w.set_cnf_in(n % 8, vals::CnfIn::FLOATING);
-                });
-            }
-            #[cfg(gpio_v2)]
-            {
-                r.pupdr().modify(|w| w.set_pupdr(n, vals::Pupdr::FLOATING));
-                r.moder().modify(|w| w.set_moder(n, vals::Moder::INPUT));
-            }
+            self.pin.set_as_disconnected();
         });
     }
 }
@@ -254,57 +248,56 @@ pub enum Pull {
     Down,
 }
 
-#[cfg(gpio_v2)]
-impl From<Pull> for vals::Pupdr {
-    fn from(pull: Pull) -> Self {
-        use Pull::*;
-
-        match pull {
-            None => vals::Pupdr::FLOATING,
-            Up => vals::Pupdr::PULLUP,
-            Down => vals::Pupdr::PULLDOWN,
+impl Pull {
+    #[cfg(gpio_v2)]
+    const fn to_pupdr(self) -> vals::Pupdr {
+        match self {
+            Pull::None => vals::Pupdr::FLOATING,
+            Pull::Up => vals::Pupdr::PULLUP,
+            Pull::Down => vals::Pupdr::PULLDOWN,
         }
     }
 }
 
-/// Speed settings
+/// Speed setting for an output.
 ///
-/// These vary depending on the chip, check the reference manual or datasheet for details.
-#[allow(missing_docs)]
+/// These vary depending on the chip, check the reference manual and datasheet ("I/O port
+/// characteristics") for details.
 #[derive(Debug, Copy, Clone)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Speed {
+    #[cfg_attr(gpio_v1, doc = "Output speed OUTPUT2MHZ")]
+    #[cfg_attr(gpio_v2, doc = "Output speed 00")]
     Low,
+    #[cfg_attr(gpio_v1, doc = "Output speed OUTPUT10MHZ")]
+    #[cfg_attr(gpio_v2, doc = "Output speed 01")]
     Medium,
-    #[cfg(not(any(syscfg_f0, gpio_v1)))]
+    #[cfg_attr(gpio_v2, doc = "Output speed 10")]
+    #[cfg(not(any(gpio_v1, syscfg_f0)))]
     High,
+    #[cfg_attr(gpio_v1, doc = "Output speed OUTPUT50MHZ")]
+    #[cfg_attr(gpio_v2, doc = "Output speed 10")]
     VeryHigh,
 }
 
-#[cfg(gpio_v1)]
-impl From<Speed> for vals::Mode {
-    fn from(speed: Speed) -> Self {
-        use Speed::*;
-
-        match speed {
-            Low => vals::Mode::OUTPUT2MHZ,
-            Medium => vals::Mode::OUTPUT10MHZ,
-            VeryHigh => vals::Mode::OUTPUT50MHZ,
+impl Speed {
+    #[cfg(gpio_v1)]
+    const fn to_mode(self) -> vals::Mode {
+        match self {
+            Speed::Low => vals::Mode::OUTPUT2MHZ,
+            Speed::Medium => vals::Mode::OUTPUT10MHZ,
+            Speed::VeryHigh => vals::Mode::OUTPUT50MHZ,
         }
     }
-}
 
-#[cfg(gpio_v2)]
-impl From<Speed> for vals::Ospeedr {
-    fn from(speed: Speed) -> Self {
-        use Speed::*;
-
-        match speed {
-            Low => vals::Ospeedr::LOWSPEED,
-            Medium => vals::Ospeedr::MEDIUMSPEED,
+    #[cfg(gpio_v2)]
+    const fn to_ospeedr(self: Speed) -> vals::Ospeedr {
+        match self {
+            Speed::Low => vals::Ospeedr::LOWSPEED,
+            Speed::Medium => vals::Ospeedr::MEDIUMSPEED,
             #[cfg(not(syscfg_f0))]
-            High => vals::Ospeedr::HIGHSPEED,
-            VeryHigh => vals::Ospeedr::VERYHIGHSPEED,
+            Speed::High => vals::Ospeedr::HIGHSPEED,
+            Speed::VeryHigh => vals::Ospeedr::VERYHIGHSPEED,
         }
     }
 }
@@ -445,17 +438,29 @@ pub struct OutputOpenDrain<'d> {
 }
 
 impl<'d> OutputOpenDrain<'d> {
-    /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level] and [Speed], [Pull] configuration.
+    /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level] and [Speed].
     #[inline]
-    pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level, speed: Speed, pull: Pull) -> Self {
+    pub fn new(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level, speed: Speed) -> Self {
         let mut pin = Flex::new(pin);
-
         match initial_output {
             Level::High => pin.set_high(),
             Level::Low => pin.set_low(),
         }
+        pin.set_as_input_output(speed);
+        Self { pin }
+    }
 
-        pin.set_as_input_output(speed, pull);
+    /// Create a new GPIO open drain output driver for a [Pin] with the provided [Level], [Speed]
+    /// and [Pull].
+    #[inline]
+    #[cfg(gpio_v2)]
+    pub fn new_pull(pin: impl Peripheral<P = impl Pin> + 'd, initial_output: Level, speed: Speed, pull: Pull) -> Self {
+        let mut pin = Flex::new(pin);
+        match initial_output {
+            Level::High => pin.set_high(),
+            Level::Low => pin.set_low(),
+        }
+        pin.set_as_input_output_pull(speed, pull);
         Self { pin }
     }
 
@@ -521,6 +526,8 @@ impl<'d> OutputOpenDrain<'d> {
 }
 
 /// GPIO output type
+#[derive(Debug, Copy, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum OutputType {
     /// Drive the pin both high or low.
     PushPull,
@@ -528,25 +535,170 @@ pub enum OutputType {
     OpenDrain,
 }
 
-impl From<OutputType> for AFType {
-    fn from(value: OutputType) -> Self {
-        match value {
-            OutputType::OpenDrain => AFType::OutputOpenDrain,
-            OutputType::PushPull => AFType::OutputPushPull,
+impl OutputType {
+    #[cfg(gpio_v1)]
+    const fn to_cnf_out(self) -> vals::CnfOut {
+        match self {
+            OutputType::PushPull => vals::CnfOut::ALTPUSHPULL,
+            OutputType::OpenDrain => vals::CnfOut::ALTOPENDRAIN,
+        }
+    }
+
+    #[cfg(gpio_v2)]
+    const fn to_ot(self) -> vals::Ot {
+        match self {
+            OutputType::PushPull => vals::Ot::PUSHPULL,
+            OutputType::OpenDrain => vals::Ot::OPENDRAIN,
         }
     }
 }
 
-/// Alternate function type settings
-#[derive(Debug, Copy, Clone)]
-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
-pub enum AFType {
-    /// Input
-    Input,
-    /// Output, drive the pin both high or low.
-    OutputPushPull,
-    /// Output, drive the pin low, or don't drive it at all if the output level is high.
-    OutputOpenDrain,
+/// Alternate function type settings.
+#[derive(Copy, Clone)]
+#[cfg(gpio_v1)]
+pub struct AfType {
+    mode: vals::Mode,
+    cnf: u8,
+    pull: Pull,
+}
+
+#[cfg(gpio_v1)]
+impl AfType {
+    /// Input with optional pullup or pulldown.
+    pub const fn input(pull: Pull) -> Self {
+        let cnf_in = match pull {
+            Pull::Up | Pull::Down => vals::CnfIn::PULL,
+            Pull::None => vals::CnfIn::FLOATING,
+        };
+        Self {
+            mode: vals::Mode::INPUT,
+            cnf: cnf_in.to_bits(),
+            pull,
+        }
+    }
+
+    /// Output with output type and speed and no pull-up or pull-down.
+    pub const fn output(output_type: OutputType, speed: Speed) -> Self {
+        Self {
+            mode: speed.to_mode(),
+            cnf: output_type.to_cnf_out().to_bits(),
+            pull: Pull::None,
+        }
+    }
+}
+
+#[inline(never)]
+#[cfg(gpio_v1)]
+fn set_as_af(pin_port: u8, _af_num: u8, af_type: AfType) {
+    let pin = unsafe { AnyPin::steal(pin_port) };
+    let r = pin.block();
+    let n = pin._pin() as usize;
+
+    r.cr(n / 8).modify(|w| {
+        w.set_mode(n % 8, af_type.mode);
+        // note that we are writing the CNF field, which is exposed as both `cnf_in` and `cnf_out`
+        // in the PAC. the choice of `cnf_in` instead of `cnf_out` in this code is arbitrary and
+        // does not affect the result.
+        w.set_cnf_in(n % 8, vals::CnfIn::from_bits(af_type.cnf));
+    });
+
+    match af_type.pull {
+        Pull::Up => r.bsrr().write(|w| w.set_bs(n, true)),
+        Pull::Down => r.bsrr().write(|w| w.set_br(n, true)),
+        Pull::None => {}
+    }
+}
+
+/// Alternate function type settings.
+#[derive(Copy, Clone)]
+#[cfg(gpio_v2)]
+pub struct AfType {
+    pupdr: vals::Pupdr,
+    ot: vals::Ot,
+    ospeedr: vals::Ospeedr,
+}
+
+#[cfg(gpio_v2)]
+impl AfType {
+    /// Input with optional pullup or pulldown.
+    pub const fn input(pull: Pull) -> Self {
+        Self {
+            pupdr: pull.to_pupdr(),
+            ot: vals::Ot::PUSHPULL,
+            ospeedr: vals::Ospeedr::LOWSPEED,
+        }
+    }
+
+    /// Output with output type and speed and no pull-up or pull-down.
+    pub const fn output(output_type: OutputType, speed: Speed) -> Self {
+        Self::output_pull(output_type, speed, Pull::None)
+    }
+
+    /// Output with output type, speed and pull-up or pull-down;
+    pub const fn output_pull(output_type: OutputType, speed: Speed, pull: Pull) -> Self {
+        Self {
+            pupdr: pull.to_pupdr(),
+            ot: output_type.to_ot(),
+            ospeedr: speed.to_ospeedr(),
+        }
+    }
+}
+
+#[inline(never)]
+#[cfg(gpio_v2)]
+fn set_as_af(pin_port: u8, af_num: u8, af_type: AfType) {
+    let pin = unsafe { AnyPin::steal(pin_port) };
+    let r = pin.block();
+    let n = pin._pin() as usize;
+
+    r.afr(n / 8).modify(|w| w.set_afr(n % 8, af_num));
+    r.pupdr().modify(|w| w.set_pupdr(n, af_type.pupdr));
+    r.otyper().modify(|w| w.set_ot(n, af_type.ot));
+    r.ospeedr().modify(|w| w.set_ospeedr(n, af_type.ospeedr));
+    r.moder().modify(|w| w.set_moder(n, vals::Moder::ALTERNATE));
+}
+
+#[inline(never)]
+fn set_as_analog(pin_port: u8) {
+    let pin = unsafe { AnyPin::steal(pin_port) };
+    let r = pin.block();
+    let n = pin._pin() as usize;
+
+    #[cfg(gpio_v1)]
+    r.cr(n / 8).modify(|w| {
+        w.set_mode(n % 8, vals::Mode::INPUT);
+        w.set_cnf_in(n % 8, vals::CnfIn::ANALOG);
+    });
+
+    #[cfg(gpio_v2)]
+    r.moder().modify(|w| w.set_moder(n, vals::Moder::ANALOG));
+}
+
+#[inline(never)]
+fn get_pull(pin_port: u8) -> Pull {
+    let pin = unsafe { AnyPin::steal(pin_port) };
+    let r = pin.block();
+    let n = pin._pin() as usize;
+
+    #[cfg(gpio_v1)]
+    return match r.cr(n / 8).read().mode(n % 8) {
+        vals::Mode::INPUT => match r.cr(n / 8).read().cnf_in(n % 8) {
+            vals::CnfIn::PULL => match r.odr().read().odr(n) {
+                vals::Odr::LOW => Pull::Down,
+                vals::Odr::HIGH => Pull::Up,
+            },
+            _ => Pull::None,
+        },
+        _ => Pull::None,
+    };
+
+    #[cfg(gpio_v2)]
+    return match r.pupdr().read().pupdr(n) {
+        vals::Pupdr::FLOATING => Pull::None,
+        vals::Pupdr::PULLDOWN => Pull::Down,
+        vals::Pupdr::PULLUP => Pull::Up,
+        vals::Pupdr::_RESERVED_3 => Pull::None,
+    };
 }
 
 pub(crate) trait SealedPin {
@@ -556,6 +708,7 @@ pub(crate) trait SealedPin {
     fn _pin(&self) -> u8 {
         self.pin_port() % 16
     }
+
     #[inline]
     fn _port(&self) -> u8 {
         self.pin_port() / 16
@@ -581,146 +734,31 @@ pub(crate) trait SealedPin {
     }
 
     #[inline]
-    fn set_as_af(&self, af_num: u8, af_type: AFType) {
-        self.set_as_af_pull(af_num, af_type, Pull::None);
-    }
-
-    #[cfg(gpio_v1)]
-    #[inline]
-    fn set_as_af_pull(&self, _af_num: u8, af_type: AFType, pull: Pull) {
-        // F1 uses the AFIO register for remapping.
-        // For now, this is not implemented, so af_num is ignored
-        // _af_num should be zero here, since it is not set by stm32-data
-        let r = self.block();
-        let n = self._pin() as usize;
-        let crlh = if n < 8 { 0 } else { 1 };
-        match af_type {
-            AFType::Input => {
-                let cnf = match pull {
-                    Pull::Up => {
-                        r.bsrr().write(|w| w.set_bs(n, true));
-                        vals::CnfIn::PULL
-                    }
-                    Pull::Down => {
-                        r.bsrr().write(|w| w.set_br(n, true));
-                        vals::CnfIn::PULL
-                    }
-                    Pull::None => vals::CnfIn::FLOATING,
-                };
-
-                r.cr(crlh).modify(|w| {
-                    w.set_mode(n % 8, vals::Mode::INPUT);
-                    w.set_cnf_in(n % 8, cnf);
-                });
-            }
-            AFType::OutputPushPull => {
-                r.cr(crlh).modify(|w| {
-                    w.set_mode(n % 8, vals::Mode::OUTPUT50MHZ);
-                    w.set_cnf_out(n % 8, vals::CnfOut::ALTPUSHPULL);
-                });
-            }
-            AFType::OutputOpenDrain => {
-                r.cr(crlh).modify(|w| {
-                    w.set_mode(n % 8, vals::Mode::OUTPUT50MHZ);
-                    w.set_cnf_out(n % 8, vals::CnfOut::ALTOPENDRAIN);
-                });
-            }
-        }
-    }
-
-    #[cfg(gpio_v2)]
-    #[inline]
-    fn set_as_af_pull(&self, af_num: u8, af_type: AFType, pull: Pull) {
-        let pin = self._pin() as usize;
-        let block = self.block();
-        block.afr(pin / 8).modify(|w| w.set_afr(pin % 8, af_num));
-        match af_type {
-            AFType::Input => {}
-            AFType::OutputPushPull => block.otyper().modify(|w| w.set_ot(pin, vals::Ot::PUSHPULL)),
-            AFType::OutputOpenDrain => block.otyper().modify(|w| w.set_ot(pin, vals::Ot::OPENDRAIN)),
-        }
-        block.pupdr().modify(|w| w.set_pupdr(pin, pull.into()));
-
-        block.moder().modify(|w| w.set_moder(pin, vals::Moder::ALTERNATE));
+    fn set_as_af(&self, af_num: u8, af_type: AfType) {
+        set_as_af(self.pin_port(), af_num, af_type)
     }
 
     #[inline]
     fn set_as_analog(&self) {
-        let pin = self._pin() as usize;
-        let block = self.block();
-        #[cfg(gpio_v1)]
-        {
-            let crlh = if pin < 8 { 0 } else { 1 };
-            block.cr(crlh).modify(|w| {
-                w.set_mode(pin % 8, vals::Mode::INPUT);
-                w.set_cnf_in(pin % 8, vals::CnfIn::ANALOG);
-            });
-        }
-        #[cfg(gpio_v2)]
-        block.moder().modify(|w| w.set_moder(pin, vals::Moder::ANALOG));
+        set_as_analog(self.pin_port());
     }
 
     /// Set the pin as "disconnected", ie doing nothing and consuming the lowest
     /// amount of power possible.
     ///
-    /// This is currently the same as set_as_analog but is semantically different really.
-    /// Drivers should set_as_disconnected pins when dropped.
+    /// This is currently the same as [`Self::set_as_analog()`] but is semantically different
+    /// really. Drivers should `set_as_disconnected()` pins when dropped.
+    ///
+    /// Note that this also disables the internal weak pull-up and pull-down resistors.
     #[inline]
     fn set_as_disconnected(&self) {
         self.set_as_analog();
     }
 
-    /// Sets the speed of the output pin.
-    ///
-    /// This should never be called for AFType::Input on the STM32F1 series, since MODE and
-    /// CNF bits are not independent. If the CNF bits are altered afterwards as well, this
-    /// will put the pin into output mode.
-    #[inline]
-    fn set_speed(&self, speed: Speed) {
-        let pin = self._pin() as usize;
-
-        #[cfg(gpio_v1)]
-        {
-            let crlh = if pin < 8 { 0 } else { 1 };
-            self.block().cr(crlh).modify(|w| {
-                w.set_mode(pin % 8, speed.into());
-            });
-        }
-
-        #[cfg(gpio_v2)]
-        self.block().ospeedr().modify(|w| w.set_ospeedr(pin, speed.into()));
-    }
-
     /// Get the pull-up configuration.
     #[inline]
     fn pull(&self) -> Pull {
-        critical_section::with(|_| {
-            let r = self.block();
-            let n = self._pin() as usize;
-            #[cfg(gpio_v1)]
-            {
-                let crlh = if n < 8 { 0 } else { 1 };
-                match r.cr(crlh).read().mode(n % 8) {
-                    vals::Mode::INPUT => match r.cr(crlh).read().cnf_in(n % 8) {
-                        vals::CnfIn::PULL => match r.odr().read().odr(n) {
-                            vals::Odr::LOW => Pull::Down,
-                            vals::Odr::HIGH => Pull::Up,
-                        },
-                        _ => Pull::None,
-                    },
-                    _ => Pull::None,
-                }
-            }
-            #[cfg(gpio_v2)]
-            {
-                match r.pupdr().read().pupdr(n) {
-                    vals::Pupdr::FLOATING => Pull::None,
-                    vals::Pupdr::PULLDOWN => Pull::Down,
-                    vals::Pupdr::PULLUP => Pull::Up,
-                    vals::Pupdr::_RESERVED_3 => Pull::None,
-                }
-            }
-        })
+        critical_section::with(|_| get_pull(self.pin_port()))
     }
 }
 
diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs
index c9d5bff17..13343fc2a 100644
--- a/embassy-stm32/src/hrtim/mod.rs
+++ b/embassy-stm32/src/hrtim/mod.rs
@@ -7,7 +7,7 @@ use core::marker::PhantomData;
 use embassy_hal_internal::{into_ref, PeripheralRef};
 pub use traits::Instance;
 
-use crate::gpio::{AFType, AnyPin};
+use crate::gpio::{AfType, AnyPin, OutputType, Speed};
 use crate::time::Hertz;
 use crate::{rcc, Peripheral};
 
@@ -80,9 +80,10 @@ macro_rules! advanced_channel_impl {
                 into_ref!(pin);
                 critical_section::with(|_| {
                     pin.set_low();
-                    pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
-                    #[cfg(gpio_v2)]
-                    pin.set_speed(crate::gpio::Speed::VeryHigh);
+                    pin.set_as_af(
+                        pin.af_num(),
+                        AfType::output(OutputType::PushPull, Speed::VeryHigh),
+                    );
                 });
                 PwmPin {
                     _pin: pin.map_into(),
@@ -97,9 +98,10 @@ macro_rules! advanced_channel_impl {
                 into_ref!(pin);
                 critical_section::with(|_| {
                     pin.set_low();
-                    pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
-                    #[cfg(gpio_v2)]
-                    pin.set_speed(crate::gpio::Speed::VeryHigh);
+                    pin.set_as_af(
+                        pin.af_num(),
+                        AfType::output(OutputType::PushPull, Speed::VeryHigh),
+                    );
                 });
                 ComplementaryPwmPin {
                     _pin: pin.map_into(),
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs
index f43cb4567..739e960b9 100644
--- a/embassy-stm32/src/i2c/mod.rs
+++ b/embassy-stm32/src/i2c/mod.rs
@@ -15,7 +15,9 @@ use embassy_sync::waitqueue::AtomicWaker;
 use embassy_time::{Duration, Instant};
 
 use crate::dma::ChannelAndRequest;
-use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed};
+#[cfg(gpio_v2)]
+use crate::gpio::Pull;
+use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed};
 use crate::interrupt::typelevel::Interrupt;
 use crate::mode::{Async, Blocking, Mode};
 use crate::rcc::{RccInfo, SealedRccPeripheral};
@@ -50,11 +52,13 @@ pub struct Config {
     ///
     /// Using external pullup resistors is recommended for I2C. If you do
     /// have external pullups you should not enable this.
+    #[cfg(gpio_v2)]
     pub sda_pullup: bool,
     /// Enable internal pullup on SCL.
     ///
     /// Using external pullup resistors is recommended for I2C. If you do
     /// have external pullups you should not enable this.
+    #[cfg(gpio_v2)]
     pub scl_pullup: bool,
     /// Timeout.
     #[cfg(feature = "time")]
@@ -64,7 +68,9 @@ pub struct Config {
 impl Default for Config {
     fn default() -> Self {
         Self {
+            #[cfg(gpio_v2)]
             sda_pullup: false,
+            #[cfg(gpio_v2)]
             scl_pullup: false,
             #[cfg(feature = "time")]
             timeout: embassy_time::Duration::from_millis(1000),
@@ -73,18 +79,32 @@ impl Default for Config {
 }
 
 impl Config {
-    fn scl_pull_mode(&self) -> Pull {
-        match self.scl_pullup {
-            true => Pull::Up,
-            false => Pull::Down,
-        }
+    fn scl_af(&self) -> AfType {
+        #[cfg(gpio_v1)]
+        return AfType::output(OutputType::OpenDrain, Speed::Medium);
+        #[cfg(gpio_v2)]
+        return AfType::output_pull(
+            OutputType::OpenDrain,
+            Speed::Medium,
+            match self.scl_pullup {
+                true => Pull::Up,
+                false => Pull::Down,
+            },
+        );
     }
 
-    fn sda_pull_mode(&self) -> Pull {
-        match self.sda_pullup {
-            true => Pull::Up,
-            false => Pull::Down,
-        }
+    fn sda_af(&self) -> AfType {
+        #[cfg(gpio_v1)]
+        return AfType::output(OutputType::OpenDrain, Speed::Medium);
+        #[cfg(gpio_v2)]
+        return AfType::output_pull(
+            OutputType::OpenDrain,
+            Speed::Medium,
+            match self.sda_pullup {
+                true => Pull::Up,
+                false => Pull::Down,
+            },
+        );
     }
 }
 
@@ -118,8 +138,8 @@ impl<'d> I2c<'d, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(scl, AFType::OutputOpenDrain, Speed::Medium, config.scl_pull_mode()),
-            new_pin!(sda, AFType::OutputOpenDrain, Speed::Medium, config.sda_pull_mode()),
+            new_pin!(scl, config.scl_af()),
+            new_pin!(sda, config.sda_af()),
             new_dma!(tx_dma),
             new_dma!(rx_dma),
             freq,
@@ -139,8 +159,8 @@ impl<'d> I2c<'d, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(scl, AFType::OutputOpenDrain, Speed::Medium, config.scl_pull_mode()),
-            new_pin!(sda, AFType::OutputOpenDrain, Speed::Medium, config.sda_pull_mode()),
+            new_pin!(scl, config.scl_af()),
+            new_pin!(sda, config.sda_af()),
             None,
             None,
             freq,
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs
index f893dd235..094de2461 100644
--- a/embassy-stm32/src/i2s.rs
+++ b/embassy-stm32/src/i2s.rs
@@ -3,7 +3,7 @@
 use embassy_hal_internal::into_ref;
 
 use crate::dma::ChannelAndRequest;
-use crate::gpio::{AFType, AnyPin, SealedPin, Speed};
+use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
 use crate::mode::Async;
 use crate::pac::spi::vals;
 use crate::spi::{Config as SpiConfig, *};
@@ -180,7 +180,7 @@ impl<'d> I2S<'d> {
         into_ref!(sd);
         Self::new_inner(
             peri,
-            new_pin!(sd, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             ws,
             ck,
@@ -209,7 +209,7 @@ impl<'d> I2S<'d> {
         Self::new_inner(
             peri,
             None,
-            new_pin!(sd, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             ws,
             ck,
             mck,
@@ -244,8 +244,8 @@ impl<'d> I2S<'d> {
         into_ref!(txsd, rxsd);
         Self::new_inner(
             peri,
-            new_pin!(txsd, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(rxsd, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             ws,
             ck,
             mck,
@@ -292,14 +292,9 @@ impl<'d> I2S<'d> {
     ) -> Self {
         into_ref!(ws, ck, mck);
 
-        ws.set_as_af(ws.af_num(), AFType::OutputPushPull);
-        ws.set_speed(Speed::VeryHigh);
-
-        ck.set_as_af(ck.af_num(), AFType::OutputPushPull);
-        ck.set_speed(Speed::VeryHigh);
-
-        mck.set_as_af(mck.af_num(), AFType::OutputPushPull);
-        mck.set_speed(Speed::VeryHigh);
+        ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
+        ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
+        mck.set_as_af(mck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 
         let mut spi_cfg = SpiConfig::default();
         spi_cfg.frequency = freq;
diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs
index c262e7a0c..481d77843 100644
--- a/embassy-stm32/src/ltdc.rs
+++ b/embassy-stm32/src/ltdc.rs
@@ -62,11 +62,10 @@ impl<'d, T: Instance> Ltdc<'d, T> {
 
         rcc::enable_and_reset::<T>();
 
-        //new_pin!(clk, AFType::OutputPushPull, Speed::VeryHigh,  Pull::None);
+        //new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh));
 
         // Set Tearing Enable pin according to CubeMx example
-        //te.set_as_af_pull(te.af_num(), AFType::OutputPushPull, Pull::None);
-        //te.set_speed(Speed::Low);
+        //te.set_as_af_pull(te.af_num(), AfType::output(OutputType::PushPull, Speed::Low));
         /*
                 T::regs().wcr().modify(|w| {
                     w.set_dsien(true);
diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs
index dcd25cbe9..ae53deb08 100644
--- a/embassy-stm32/src/macros.rs
+++ b/embassy-stm32/src/macros.rs
@@ -97,23 +97,9 @@ macro_rules! new_dma {
 }
 
 macro_rules! new_pin {
-    ($name:ident, $aftype:expr) => {{
-        new_pin!($name, $aftype, crate::gpio::Speed::Medium, crate::gpio::Pull::None)
-    }};
-    ($name:ident, $aftype:expr, $speed:expr) => {
-        new_pin!($name, $aftype, $speed, crate::gpio::Pull::None)
-    };
-    ($name:ident, $aftype:expr, $speed:expr, $pull:expr) => {{
+    ($name:ident, $af_type:expr) => {{
         let pin = $name.into_ref();
-        pin.set_as_af_pull(pin.af_num(), $aftype, $pull);
-        // Do not call set_speed on AFType::Input, as MODE and CNF bits are not independent
-        // for gpio_v1
-        match $aftype {
-            crate::gpio::AFType::Input => {}
-            _ => {
-                pin.set_speed($speed);
-            }
-        };
+        pin.set_as_af(pin.af_num(), $af_type);
         Some(pin.map_into())
     }};
 }
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs
index 882781cce..f6eb0d17c 100644
--- a/embassy-stm32/src/ospi/mod.rs
+++ b/embassy-stm32/src/ospi/mod.rs
@@ -13,7 +13,7 @@ pub use enums::*;
 use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits};
 
 use crate::dma::{word, ChannelAndRequest};
-use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed};
+use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
 use crate::mode::{Async, Blocking, Mode as PeriMode};
 use crate::pac::octospi::{vals, Octospi as Regs};
 use crate::rcc::{self, RccPeripheral};
@@ -548,16 +548,19 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::Input, Speed::VeryHigh),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::input(Pull::None)),
             None,
             None,
             None,
             None,
             None,
             None,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             None,
             config,
@@ -577,16 +580,19 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             None,
             None,
             None,
             None,
             None,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             None,
             config,
@@ -608,16 +614,19 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             None,
             None,
             None,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             None,
             config,
@@ -643,16 +652,19 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d4, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d5, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d6, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d7, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             None,
             config,
@@ -678,16 +690,19 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d4, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d5, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d6, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d7, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             None,
             config,
@@ -710,16 +725,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::Input, Speed::VeryHigh),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::input(Pull::None)),
             None,
             None,
             None,
             None,
             None,
             None,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             new_dma!(dma),
             config,
@@ -740,16 +758,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             None,
             None,
             None,
             None,
             None,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             new_dma!(dma),
             config,
@@ -772,16 +793,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             None,
             None,
             None,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             new_dma!(dma),
             config,
@@ -808,16 +832,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d4, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d5, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d6, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d7, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             new_dma!(dma),
             config,
@@ -844,16 +871,19 @@ impl<'d, T: Instance> Ospi<'d, T, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d4, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d5, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d6, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d7, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             new_dma!(dma),
             config,
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs
index 06c8f4812..308947e99 100644
--- a/embassy-stm32/src/qspi/mod.rs
+++ b/embassy-stm32/src/qspi/mod.rs
@@ -10,7 +10,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
 use enums::*;
 
 use crate::dma::ChannelAndRequest;
-use crate::gpio::{AFType, AnyPin, Pull, Speed};
+use crate::gpio::{AfType, AnyPin, OutputType, Pull, Speed};
 use crate::mode::{Async, Blocking, Mode as PeriMode};
 use crate::pac::quadspi::Quadspi as Regs;
 use crate::rcc::{self, RccPeripheral};
@@ -248,12 +248,15 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             config,
             FlashSelection::Flash1,
@@ -273,12 +276,15 @@ impl<'d, T: Instance> Qspi<'d, T, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             None,
             config,
             FlashSelection::Flash2,
@@ -301,12 +307,15 @@ impl<'d, T: Instance> Qspi<'d, T, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             new_dma!(dma),
             config,
             FlashSelection::Flash1,
@@ -327,12 +336,15 @@ impl<'d, T: Instance> Qspi<'d, T, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(d0, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d1, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d2, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(d3, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(nss, AFType::OutputPushPull, Speed::VeryHigh, Pull::Up),
+            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(
+                nss,
+                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
+            ),
             new_dma!(dma),
             config,
             FlashSelection::Flash2,
diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs
index 40e963466..d1ce14c86 100644
--- a/embassy-stm32/src/rcc/mco.rs
+++ b/embassy-stm32/src/rcc/mco.rs
@@ -2,7 +2,7 @@ use core::marker::PhantomData;
 
 use embassy_hal_internal::into_ref;
 
-use crate::gpio::{AFType, Speed};
+use crate::gpio::{AfType, OutputType, Speed};
 #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))]
 pub use crate::pac::rcc::vals::Mcopre as McoPrescaler;
 #[cfg(not(any(
@@ -101,8 +101,7 @@ impl<'d, T: McoInstance> Mco<'d, T> {
 
         critical_section::with(|_| unsafe {
             T::_apply_clock_settings(source, prescaler);
-            pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
-            pin.set_speed(Speed::VeryHigh);
+            pin.set_as_af(pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
         });
 
         Self { phantom: PhantomData }
diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs
index 87bde12eb..c48d81b5f 100644
--- a/embassy-stm32/src/sai/mod.rs
+++ b/embassy-stm32/src/sai/mod.rs
@@ -9,7 +9,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
 pub use crate::dma::word;
 #[cfg(not(gpdma))]
 use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer};
-use crate::gpio::{AFType, AnyPin, SealedPin as _};
+use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
 use crate::pac::sai::{vals, Sai as Regs};
 use crate::rcc::{self, RccPeripheral};
 use crate::{peripherals, Peripheral};
@@ -656,17 +656,17 @@ fn dr<W: word::Word>(w: crate::pac::sai::Sai, sub_block: WhichSubBlock) -> *mut
 }
 
 // return the type for (sd, sck)
-fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AFType, AFType) {
+fn get_af_types(mode: Mode, tx_rx: TxRx) -> (AfType, AfType) {
     (
         //sd is defined by tx/rx mode
         match tx_rx {
-            TxRx::Transmitter => AFType::OutputPushPull,
-            TxRx::Receiver => AFType::Input,
+            TxRx::Transmitter => AfType::output(OutputType::PushPull, Speed::VeryHigh),
+            TxRx::Receiver => AfType::input(Pull::None),
         },
         //clocks (mclk, sck and fs) are defined by master/slave
         match mode {
-            Mode::Master => AFType::OutputPushPull,
-            Mode::Slave => AFType::Input,
+            Mode::Master => AfType::output(OutputType::PushPull, Speed::VeryHigh),
+            Mode::Slave => AfType::input(Pull::None),
         },
     )
 }
@@ -768,9 +768,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
         into_ref!(mclk);
 
         let (_sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
-
         mclk.set_as_af(mclk.af_num(), ck_af_type);
-        mclk.set_speed(crate::gpio::Speed::VeryHigh);
 
         if config.master_clock_divider == MasterClockDivider::MasterClockDisabled {
             config.master_clock_divider = MasterClockDivider::Div1;
@@ -796,12 +794,8 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
 
         let (sd_af_type, ck_af_type) = get_af_types(config.mode, config.tx_rx);
         sd.set_as_af(sd.af_num(), sd_af_type);
-        sd.set_speed(crate::gpio::Speed::VeryHigh);
-
         sck.set_as_af(sck.af_num(), ck_af_type);
-        sck.set_speed(crate::gpio::Speed::VeryHigh);
         fs.set_as_af(fs.af_num(), ck_af_type);
-        fs.set_speed(crate::gpio::Speed::VeryHigh);
 
         let sub_block = S::WHICH;
         let request = dma.request();
@@ -834,9 +828,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> {
         into_ref!(dma, peri, sd);
 
         let (sd_af_type, _ck_af_type) = get_af_types(config.mode, config.tx_rx);
-
         sd.set_as_af(sd.af_num(), sd_af_type);
-        sd.set_speed(crate::gpio::Speed::VeryHigh);
 
         let sub_block = S::WHICH;
         let request = dma.request();
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 9c14837e1..ee5539518 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -13,7 +13,9 @@ use embassy_sync::waitqueue::AtomicWaker;
 use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR};
 
 use crate::dma::NoDma;
-use crate::gpio::{AFType, AnyPin, Pull, SealedPin, Speed};
+#[cfg(gpio_v2)]
+use crate::gpio::Pull;
+use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed};
 use crate::interrupt::typelevel::Interrupt;
 use crate::pac::sdmmc::Sdmmc as RegBlock;
 use crate::rcc::{self, RccPeripheral};
@@ -292,6 +294,13 @@ pub struct Sdmmc<'d, T: Instance, Dma: SdmmcDma<T> = NoDma> {
     card: Option<Card>,
 }
 
+const CLK_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh);
+#[cfg(gpio_v1)]
+const CMD_AF: AfType = AfType::output(OutputType::PushPull, Speed::VeryHigh);
+#[cfg(gpio_v2)]
+const CMD_AF: AfType = AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up);
+const DATA_AF: AfType = CMD_AF;
+
 #[cfg(sdmmc_v1)]
 impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
     /// Create a new SDMMC driver, with 1 data lane.
@@ -307,13 +316,9 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
         into_ref!(clk, cmd, d0);
 
         critical_section::with(|_| {
-            clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None);
-            cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up);
-            d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up);
-
-            clk.set_speed(Speed::VeryHigh);
-            cmd.set_speed(Speed::VeryHigh);
-            d0.set_speed(Speed::VeryHigh);
+            clk.set_as_af(clk.af_num(), CLK_AF);
+            cmd.set_as_af(cmd.af_num(), CMD_AF);
+            d0.set_as_af(d0.af_num(), DATA_AF);
         });
 
         Self::new_inner(
@@ -345,19 +350,12 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
         into_ref!(clk, cmd, d0, d1, d2, d3);
 
         critical_section::with(|_| {
-            clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None);
-            cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up);
-            d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up);
-            d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::Up);
-            d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::Up);
-            d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::Up);
-
-            clk.set_speed(Speed::VeryHigh);
-            cmd.set_speed(Speed::VeryHigh);
-            d0.set_speed(Speed::VeryHigh);
-            d1.set_speed(Speed::VeryHigh);
-            d2.set_speed(Speed::VeryHigh);
-            d3.set_speed(Speed::VeryHigh);
+            clk.set_as_af(clk.af_num(), CLK_AF);
+            cmd.set_as_af(cmd.af_num(), CMD_AF);
+            d0.set_as_af(d0.af_num(), DATA_AF);
+            d1.set_as_af(d1.af_num(), DATA_AF);
+            d2.set_as_af(d2.af_num(), DATA_AF);
+            d3.set_as_af(d3.af_num(), DATA_AF);
         });
 
         Self::new_inner(
@@ -388,13 +386,9 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
         into_ref!(clk, cmd, d0);
 
         critical_section::with(|_| {
-            clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None);
-            cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up);
-            d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up);
-
-            clk.set_speed(Speed::VeryHigh);
-            cmd.set_speed(Speed::VeryHigh);
-            d0.set_speed(Speed::VeryHigh);
+            clk.set_as_af(clk.af_num(), CLK_AF);
+            cmd.set_as_af(cmd.af_num(), CMD_AF);
+            d0.set_as_af(d0.af_num(), DATA_AF);
         });
 
         Self::new_inner(
@@ -425,19 +419,12 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
         into_ref!(clk, cmd, d0, d1, d2, d3);
 
         critical_section::with(|_| {
-            clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None);
-            cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up);
-            d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up);
-            d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::Up);
-            d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::Up);
-            d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::Up);
-
-            clk.set_speed(Speed::VeryHigh);
-            cmd.set_speed(Speed::VeryHigh);
-            d0.set_speed(Speed::VeryHigh);
-            d1.set_speed(Speed::VeryHigh);
-            d2.set_speed(Speed::VeryHigh);
-            d3.set_speed(Speed::VeryHigh);
+            clk.set_as_af(clk.af_num(), CLK_AF);
+            cmd.set_as_af(cmd.af_num(), CMD_AF);
+            d0.set_as_af(d0.af_num(), DATA_AF);
+            d1.set_as_af(d1.af_num(), DATA_AF);
+            d2.set_as_af(d2.af_num(), DATA_AF);
+            d3.set_as_af(d3.af_num(), DATA_AF);
         });
 
         Self::new_inner(
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 2e5f82dcb..656676d9f 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -10,7 +10,7 @@ use embassy_hal_internal::PeripheralRef;
 pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
 
 use crate::dma::{word, ChannelAndRequest};
-use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _, Speed};
+use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
 use crate::mode::{Async, Blocking, Mode as PeriMode};
 use crate::pac::spi::{regs, vals, Spi as Regs};
 use crate::rcc::{RccInfo, SealedRccPeripheral};
@@ -90,11 +90,21 @@ impl Config {
         }
     }
 
-    fn sck_pull_mode(&self) -> Pull {
-        match self.mode.polarity {
-            Polarity::IdleLow => Pull::Down,
-            Polarity::IdleHigh => Pull::Up,
-        }
+    #[cfg(gpio_v1)]
+    fn sck_af(&self) -> AfType {
+        AfType::output(OutputType::PushPull, Speed::VeryHigh)
+    }
+
+    #[cfg(gpio_v2)]
+    fn sck_af(&self) -> AfType {
+        AfType::output_pull(
+            OutputType::PushPull,
+            Speed::VeryHigh,
+            match self.mode.polarity {
+                Polarity::IdleLow => Pull::Down,
+                Polarity::IdleHigh => Pull::Up,
+            },
+        )
     }
 }
 /// SPI driver.
@@ -453,9 +463,9 @@ impl<'d> Spi<'d, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
-            new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(miso, AFType::Input, Speed::VeryHigh, config.miso_pull),
+            new_pin!(sck, config.sck_af()),
+            new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(miso, AfType::input(config.miso_pull)),
             None,
             None,
             config,
@@ -471,9 +481,9 @@ impl<'d> Spi<'d, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
+            new_pin!(sck, config.sck_af()),
             None,
-            new_pin!(miso, AFType::Input, Speed::VeryHigh, config.miso_pull),
+            new_pin!(miso, AfType::input(config.miso_pull)),
             None,
             None,
             config,
@@ -489,8 +499,8 @@ impl<'d> Spi<'d, Blocking> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
-            new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(sck, config.sck_af()),
+            new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             None,
             None,
@@ -509,7 +519,7 @@ impl<'d> Spi<'d, Blocking> {
         Self::new_inner(
             peri,
             None,
-            new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             None,
             None,
@@ -531,9 +541,9 @@ impl<'d> Spi<'d, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
-            new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
-            new_pin!(miso, AFType::Input, Speed::VeryHigh),
+            new_pin!(sck, config.sck_af()),
+            new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
+            new_pin!(miso, AfType::input(config.miso_pull)),
             new_dma!(tx_dma),
             new_dma!(rx_dma),
             config,
@@ -551,9 +561,9 @@ impl<'d> Spi<'d, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
+            new_pin!(sck, config.sck_af()),
             None,
-            new_pin!(miso, AFType::Input, Speed::VeryHigh),
+            new_pin!(miso, AfType::input(config.miso_pull)),
             #[cfg(any(spi_v1, spi_f1, spi_v2))]
             new_dma!(tx_dma),
             #[cfg(any(spi_v3, spi_v4, spi_v5))]
@@ -573,8 +583,8 @@ impl<'d> Spi<'d, Async> {
     ) -> Self {
         Self::new_inner(
             peri,
-            new_pin!(sck, AFType::OutputPushPull, Speed::VeryHigh, config.sck_pull_mode()),
-            new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(sck, config.sck_af()),
+            new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             new_dma!(tx_dma),
             None,
@@ -594,7 +604,7 @@ impl<'d> Spi<'d, Async> {
         Self::new_inner(
             peri,
             None,
-            new_pin!(mosi, AFType::OutputPushPull, Speed::VeryHigh),
+            new_pin!(mosi, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
             None,
             new_dma!(tx_dma),
             None,
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index a892646cf..46ccbf3df 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -32,9 +32,10 @@ macro_rules! complementary_channel_impl {
                 into_ref!(pin);
                 critical_section::with(|_| {
                     pin.set_low();
-                    pin.set_as_af(pin.af_num(), output_type.into());
-                    #[cfg(gpio_v2)]
-                    pin.set_speed(crate::gpio::Speed::VeryHigh);
+                    pin.set_as_af(
+                        pin.af_num(),
+                        crate::gpio::AfType::output(output_type, crate::gpio::Speed::VeryHigh),
+                    );
                 });
                 ComplementaryPwmPin {
                     _pin: pin.map_into(),
diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs
index 0258d4077..341ac2c04 100644
--- a/embassy-stm32/src/timer/input_capture.rs
+++ b/embassy-stm32/src/timer/input_capture.rs
@@ -12,7 +12,7 @@ use super::{
     CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin,
     GeneralInstance4Channel,
 };
-use crate::gpio::{AFType, AnyPin, Pull};
+use crate::gpio::{AfType, AnyPin, Pull};
 use crate::interrupt::typelevel::{Binding, Interrupt};
 use crate::time::Hertz;
 use crate::Peripheral;
@@ -38,11 +38,9 @@ macro_rules! channel_impl {
     ($new_chx:ident, $channel:ident, $pin_trait:ident) => {
         impl<'d, T: GeneralInstance4Channel> CapturePin<'d, T, $channel> {
             #[doc = concat!("Create a new ", stringify!($channel), " capture pin instance.")]
-            pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull_type: Pull) -> Self {
+            pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<T>> + 'd, pull: Pull) -> Self {
                 into_ref!(pin);
-
-                pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type);
-
+                pin.set_as_af(pin.af_num(), AfType::input(pull));
                 CapturePin {
                     _pin: pin.map_into(),
                     phantom: PhantomData,
diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs
index dcf098a78..e3eb6042a 100644
--- a/embassy-stm32/src/timer/pwm_input.rs
+++ b/embassy-stm32/src/timer/pwm_input.rs
@@ -4,7 +4,7 @@ use embassy_hal_internal::into_ref;
 
 use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource};
 use super::{Channel, Channel1Pin, Channel2Pin, GeneralInstance4Channel};
-use crate::gpio::{AFType, Pull};
+use crate::gpio::{AfType, Pull};
 use crate::time::Hertz;
 use crate::Peripheral;
 
@@ -19,12 +19,12 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
     pub fn new(
         tim: impl Peripheral<P = T> + 'd,
         pin: impl Peripheral<P = impl Channel1Pin<T>> + 'd,
-        pull_type: Pull,
+        pull: Pull,
         freq: Hertz,
     ) -> Self {
         into_ref!(pin);
 
-        pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type);
+        pin.set_as_af(pin.af_num(), AfType::input(pull));
 
         Self::new_inner(tim, freq, Channel::Ch1, Channel::Ch2)
     }
@@ -33,12 +33,12 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> {
     pub fn new_alt(
         tim: impl Peripheral<P = T> + 'd,
         pin: impl Peripheral<P = impl Channel2Pin<T>> + 'd,
-        pull_type: Pull,
+        pull: Pull,
         freq: Hertz,
     ) -> Self {
         into_ref!(pin);
 
-        pin.set_as_af_pull(pin.af_num(), AFType::Input, pull_type);
+        pin.set_as_af(pin.af_num(), AfType::input(pull));
 
         Self::new_inner(tim, freq, Channel::Ch2, Channel::Ch1)
     }
diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs
index ab9879be6..fc5835414 100644
--- a/embassy-stm32/src/timer/qei.rs
+++ b/embassy-stm32/src/timer/qei.rs
@@ -7,7 +7,7 @@ use stm32_metapac::timer::vals;
 
 use super::low_level::Timer;
 use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel};
-use crate::gpio::{AFType, AnyPin};
+use crate::gpio::{AfType, AnyPin, Pull};
 use crate::Peripheral;
 
 /// Counting direction
@@ -37,9 +37,7 @@ macro_rules! channel_impl {
                 into_ref!(pin);
                 critical_section::with(|_| {
                     pin.set_low();
-                    pin.set_as_af(pin.af_num(), AFType::Input);
-                    #[cfg(gpio_v2)]
-                    pin.set_speed(crate::gpio::Speed::VeryHigh);
+                    pin.set_as_af(pin.af_num(), AfType::input(Pull::None));
                 });
                 QeiPin {
                     _pin: pin.map_into(),
diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs
index b54e9a0d6..b7771bd64 100644
--- a/embassy-stm32/src/timer/simple_pwm.rs
+++ b/embassy-stm32/src/timer/simple_pwm.rs
@@ -6,7 +6,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
 
 use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer};
 use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel};
-use crate::gpio::{AnyPin, OutputType};
+use crate::gpio::{AfType, AnyPin, OutputType, Speed};
 use crate::time::Hertz;
 use crate::Peripheral;
 
@@ -35,9 +35,7 @@ macro_rules! channel_impl {
                 into_ref!(pin);
                 critical_section::with(|_| {
                     pin.set_low();
-                    pin.set_as_af(pin.af_num(), output_type.into());
-                    #[cfg(gpio_v2)]
-                    pin.set_speed(crate::gpio::Speed::VeryHigh);
+                    pin.set_as_af(pin.af_num(), AfType::output(output_type, Speed::VeryHigh));
                 });
                 PwmPin {
                     _pin: pin.map_into(),
diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs
index 8cab3a24c..8ba208ce7 100644
--- a/embassy-stm32/src/tsc/mod.rs
+++ b/embassy-stm32/src/tsc/mod.rs
@@ -70,7 +70,7 @@ use core::marker::PhantomData;
 use embassy_hal_internal::{into_ref, PeripheralRef};
 pub use enums::*;
 
-use crate::gpio::{AFType, AnyPin};
+use crate::gpio::{AfType, AnyPin, OutputType, Speed};
 use crate::pac::tsc::Tsc as Regs;
 use crate::rcc::{self, RccPeripheral};
 use crate::{peripherals, Peripheral};
@@ -371,11 +371,14 @@ macro_rules! group_impl {
                     pin.set_low();
                     pin.set_as_af(
                         pin.af_num(),
-                        match role {
-                            PinType::Channel => AFType::OutputPushPull,
-                            PinType::Sample => AFType::OutputOpenDrain,
-                            PinType::Shield => AFType::OutputPushPull,
-                        },
+                        AfType::output(
+                            match role {
+                                PinType::Channel => OutputType::PushPull,
+                                PinType::Sample => OutputType::OpenDrain,
+                                PinType::Shield => OutputType::PushPull,
+                            },
+                            Speed::VeryHigh,
+                        ),
                     );
                     self.d1 = Some(TscPin {
                         _pin: pin.map_into(),
@@ -392,11 +395,14 @@ macro_rules! group_impl {
                     pin.set_low();
                     pin.set_as_af(
                         pin.af_num(),
-                        match role {
-                            PinType::Channel => AFType::OutputPushPull,
-                            PinType::Sample => AFType::OutputOpenDrain,
-                            PinType::Shield => AFType::OutputPushPull,
-                        },
+                        AfType::output(
+                            match role {
+                                PinType::Channel => OutputType::PushPull,
+                                PinType::Sample => OutputType::OpenDrain,
+                                PinType::Shield => OutputType::PushPull,
+                            },
+                            Speed::VeryHigh,
+                        ),
                     );
                     self.d2 = Some(TscPin {
                         _pin: pin.map_into(),
@@ -413,11 +419,14 @@ macro_rules! group_impl {
                     pin.set_low();
                     pin.set_as_af(
                         pin.af_num(),
-                        match role {
-                            PinType::Channel => AFType::OutputPushPull,
-                            PinType::Sample => AFType::OutputOpenDrain,
-                            PinType::Shield => AFType::OutputPushPull,
-                        },
+                        AfType::output(
+                            match role {
+                                PinType::Channel => OutputType::PushPull,
+                                PinType::Sample => OutputType::OpenDrain,
+                                PinType::Shield => OutputType::PushPull,
+                            },
+                            Speed::VeryHigh,
+                        ),
                     );
                     self.d3 = Some(TscPin {
                         _pin: pin.map_into(),
@@ -434,11 +443,14 @@ macro_rules! group_impl {
                     pin.set_low();
                     pin.set_as_af(
                         pin.af_num(),
-                        match role {
-                            PinType::Channel => AFType::OutputPushPull,
-                            PinType::Sample => AFType::OutputOpenDrain,
-                            PinType::Shield => AFType::OutputPushPull,
-                        },
+                        AfType::output(
+                            match role {
+                                PinType::Channel => OutputType::PushPull,
+                                PinType::Sample => OutputType::OpenDrain,
+                                PinType::Shield => OutputType::PushPull,
+                            },
+                            Speed::VeryHigh,
+                        ),
                     );
                     self.d4 = Some(TscPin {
                         _pin: pin.map_into(),
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index fd79e035e..33bc009a8 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -15,7 +15,7 @@ use super::{
     clear_interrupt_flags, configure, rdr, reconfigure, sr, tdr, Config, ConfigError, CtsPin, Error, Info, Instance,
     Regs, RtsPin, RxPin, TxPin,
 };
-use crate::gpio::{AFType, AnyPin, SealedPin as _};
+use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
 use crate::interrupt::{self, InterruptExt};
 use crate::time::Hertz;
 
@@ -210,8 +210,8 @@ impl<'d> BufferedUart<'d> {
     ) -> Result<Self, ConfigError> {
         Self::new_inner(
             peri,
-            new_pin!(rx, AFType::Input),
-            new_pin!(tx, AFType::OutputPushPull),
+            new_pin!(rx, AfType::input(Pull::None)),
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             None,
             None,
@@ -235,10 +235,10 @@ impl<'d> BufferedUart<'d> {
     ) -> Result<Self, ConfigError> {
         Self::new_inner(
             peri,
-            new_pin!(rx, AFType::Input),
-            new_pin!(tx, AFType::OutputPushPull),
-            new_pin!(rts, AFType::OutputPushPull),
-            new_pin!(cts, AFType::Input),
+            new_pin!(rx, AfType::input(Pull::None)),
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
+            new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)),
+            new_pin!(cts, AfType::input(Pull::None)),
             None,
             tx_buffer,
             rx_buffer,
@@ -260,11 +260,11 @@ impl<'d> BufferedUart<'d> {
     ) -> Result<Self, ConfigError> {
         Self::new_inner(
             peri,
-            new_pin!(rx, AFType::Input),
-            new_pin!(tx, AFType::OutputPushPull),
+            new_pin!(rx, AfType::input(Pull::None)),
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             None,
-            new_pin!(de, AFType::OutputPushPull),
+            new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)),
             tx_buffer,
             rx_buffer,
             config,
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index 53321391a..5754f783e 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -14,7 +14,7 @@ use embassy_sync::waitqueue::AtomicWaker;
 use futures_util::future::{select, Either};
 
 use crate::dma::ChannelAndRequest;
-use crate::gpio::{AFType, AnyPin, SealedPin as _};
+use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
 use crate::interrupt::typelevel::Interrupt as _;
 use crate::interrupt::{self, Interrupt, InterruptExt};
 use crate::mode::{Async, Blocking, Mode};
@@ -159,11 +159,11 @@ pub struct Config {
     #[cfg(any(usart_v3, usart_v4))]
     pub swap_rx_tx: bool,
 
-    /// Set this to true to invert TX pin signal values (V<sub>DD</sub> =0/mark, Gnd = 1/idle).
+    /// Set this to true to invert TX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).
     #[cfg(any(usart_v3, usart_v4))]
     pub invert_tx: bool,
 
-    /// Set this to true to invert RX pin signal values (V<sub>DD</sub> =0/mark, Gnd = 1/idle).
+    /// Set this to true to invert RX pin signal values (V<sub>DD</sub> = 0/mark, Gnd = 1/idle).
     #[cfg(any(usart_v3, usart_v4))]
     pub invert_rx: bool,
 
@@ -172,19 +172,20 @@ pub struct Config {
 }
 
 impl Config {
-    fn tx_af(&self) -> AFType {
+    fn tx_af(&self) -> AfType {
         #[cfg(any(usart_v3, usart_v4))]
         if self.swap_rx_tx {
-            return AFType::Input;
+            return AfType::input(Pull::None);
         };
-        AFType::OutputPushPull
+        AfType::output(OutputType::PushPull, Speed::Medium)
     }
-    fn rx_af(&self) -> AFType {
+
+    fn rx_af(&self) -> AfType {
         #[cfg(any(usart_v3, usart_v4))]
         if self.swap_rx_tx {
-            return AFType::OutputPushPull;
+            return AfType::output(OutputType::PushPull, Speed::Medium);
         };
-        AFType::Input
+        AfType::input(Pull::None)
     }
 }
 
@@ -342,7 +343,7 @@ impl<'d> UartTx<'d, Async> {
     ) -> Result<Self, ConfigError> {
         Self::new_inner(
             peri,
-            new_pin!(tx, AFType::OutputPushPull),
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             new_dma!(tx_dma),
             config,
@@ -359,8 +360,8 @@ impl<'d> UartTx<'d, Async> {
     ) -> Result<Self, ConfigError> {
         Self::new_inner(
             peri,
-            new_pin!(tx, AFType::OutputPushPull),
-            new_pin!(cts, AFType::Input),
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
+            new_pin!(cts, AfType::input(Pull::None)),
             new_dma!(tx_dma),
             config,
         )
@@ -401,7 +402,13 @@ impl<'d> UartTx<'d, Blocking> {
         tx: impl Peripheral<P = impl TxPin<T>> + 'd,
         config: Config,
     ) -> Result<Self, ConfigError> {
-        Self::new_inner(peri, new_pin!(tx, AFType::OutputPushPull), None, None, config)
+        Self::new_inner(
+            peri,
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
+            None,
+            None,
+            config,
+        )
     }
 
     /// Create a new blocking tx-only UART with a clear-to-send pin
@@ -413,8 +420,8 @@ impl<'d> UartTx<'d, Blocking> {
     ) -> Result<Self, ConfigError> {
         Self::new_inner(
             peri,
-            new_pin!(tx, AFType::OutputPushPull),
-            new_pin!(cts, AFType::Input),
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
+            new_pin!(cts, AfType::input(Pull::None)),
             None,
             config,
         )
@@ -508,7 +515,13 @@ impl<'d> UartRx<'d, Async> {
         rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd,
         config: Config,
     ) -> Result<Self, ConfigError> {
-        Self::new_inner(peri, new_pin!(rx, AFType::Input), None, new_dma!(rx_dma), config)
+        Self::new_inner(
+            peri,
+            new_pin!(rx, AfType::input(Pull::None)),
+            None,
+            new_dma!(rx_dma),
+            config,
+        )
     }
 
     /// Create a new rx-only UART with a request-to-send pin
@@ -522,8 +535,8 @@ impl<'d> UartRx<'d, Async> {
     ) -> Result<Self, ConfigError> {
         Self::new_inner(
             peri,
-            new_pin!(rx, AFType::Input),
-            new_pin!(rts, AFType::OutputPushPull),
+            new_pin!(rx, AfType::input(Pull::None)),
+            new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)),
             new_dma!(rx_dma),
             config,
         )
@@ -751,7 +764,7 @@ impl<'d> UartRx<'d, Blocking> {
         rx: impl Peripheral<P = impl RxPin<T>> + 'd,
         config: Config,
     ) -> Result<Self, ConfigError> {
-        Self::new_inner(peri, new_pin!(rx, AFType::Input), None, None, config)
+        Self::new_inner(peri, new_pin!(rx, AfType::input(Pull::None)), None, None, config)
     }
 
     /// Create a new rx-only UART with a request-to-send pin
@@ -763,8 +776,8 @@ impl<'d> UartRx<'d, Blocking> {
     ) -> Result<Self, ConfigError> {
         Self::new_inner(
             peri,
-            new_pin!(rx, AFType::Input),
-            new_pin!(rts, AFType::OutputPushPull),
+            new_pin!(rx, AfType::input(Pull::None)),
+            new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             config,
         )
@@ -968,8 +981,8 @@ impl<'d> Uart<'d, Async> {
             peri,
             new_pin!(rx, config.rx_af()),
             new_pin!(tx, config.tx_af()),
-            new_pin!(rts, AFType::OutputPushPull),
-            new_pin!(cts, AFType::Input),
+            new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)),
+            new_pin!(cts, AfType::input(Pull::None)),
             None,
             new_dma!(tx_dma),
             new_dma!(rx_dma),
@@ -995,7 +1008,7 @@ impl<'d> Uart<'d, Async> {
             new_pin!(tx, config.tx_af()),
             None,
             None,
-            new_pin!(de, AFType::OutputPushPull),
+            new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)),
             new_dma!(tx_dma),
             new_dma!(rx_dma),
             config,
@@ -1030,7 +1043,7 @@ impl<'d> Uart<'d, Async> {
         Self::new_inner(
             peri,
             None,
-            new_pin!(tx, AFType::OutputPushPull),
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             None,
             None,
@@ -1066,7 +1079,7 @@ impl<'d> Uart<'d, Async> {
             peri,
             None,
             None,
-            new_pin!(rx, AFType::OutputPushPull),
+            new_pin!(rx, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             None,
             new_dma!(tx_dma),
@@ -1125,8 +1138,8 @@ impl<'d> Uart<'d, Blocking> {
             peri,
             new_pin!(rx, config.rx_af()),
             new_pin!(tx, config.tx_af()),
-            new_pin!(rts, AFType::OutputPushPull),
-            new_pin!(cts, AFType::Input),
+            new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)),
+            new_pin!(cts, AfType::input(Pull::None)),
             None,
             None,
             None,
@@ -1149,7 +1162,7 @@ impl<'d> Uart<'d, Blocking> {
             new_pin!(tx, config.tx_af()),
             None,
             None,
-            new_pin!(de, AFType::OutputPushPull),
+            new_pin!(de, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             None,
             config,
@@ -1181,7 +1194,7 @@ impl<'d> Uart<'d, Blocking> {
         Self::new_inner(
             peri,
             None,
-            new_pin!(tx, AFType::OutputPushPull),
+            new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             None,
             None,
@@ -1214,7 +1227,7 @@ impl<'d> Uart<'d, Blocking> {
             peri,
             None,
             None,
-            new_pin!(rx, AFType::OutputPushPull),
+            new_pin!(rx, AfType::output(OutputType::PushPull, Speed::Medium)),
             None,
             None,
             None,
diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs
index e5131250a..8ee8dcc36 100644
--- a/embassy-stm32/src/usb/otg.rs
+++ b/embassy-stm32/src/usb/otg.rs
@@ -10,7 +10,7 @@ use embassy_usb_synopsys_otg::{
     PhyType, State,
 };
 
-use crate::gpio::AFType;
+use crate::gpio::{AfType, OutputType, Speed};
 use crate::interrupt;
 use crate::interrupt::typelevel::Interrupt;
 use crate::rcc::{self, RccPeripheral};
@@ -39,9 +39,7 @@ macro_rules! config_ulpi_pins {
         into_ref!($($pin),*);
         critical_section::with(|_| {
             $(
-                $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
-                #[cfg(gpio_v2)]
-                $pin.set_speed(crate::gpio::Speed::VeryHigh);
+                $pin.set_as_af($pin.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
             )*
         })
     };
@@ -77,8 +75,8 @@ impl<'d, T: Instance> Driver<'d, T> {
     ) -> Self {
         into_ref!(dp, dm);
 
-        dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
-        dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
+        dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
+        dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
 
         let regs = T::regs();
 
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index b6c88ac9a..1d9d19a73 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -277,8 +277,9 @@ impl<'d, T: Instance> Driver<'d, T> {
 
         #[cfg(not(stm32l1))]
         {
-            dp.set_as_af(dp.af_num(), crate::gpio::AFType::OutputPushPull);
-            dm.set_as_af(dm.af_num(), crate::gpio::AFType::OutputPushPull);
+            use crate::gpio::{AfType, OutputType, Speed};
+            dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
+            dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
         }
         #[cfg(stm32l1)]
         let _ = (dp, dm); // suppress "unused" warnings.
diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs
index a95b44b74..b796996ea 100644
--- a/examples/stm32h7/src/bin/low_level_timer_api.rs
+++ b/examples/stm32h7/src/bin/low_level_timer_api.rs
@@ -3,7 +3,7 @@
 
 use defmt::*;
 use embassy_executor::Spawner;
-use embassy_stm32::gpio::{AFType, Flex, Pull, Speed};
+use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed};
 use embassy_stm32::time::{khz, Hertz};
 use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer};
 use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel};
@@ -83,10 +83,10 @@ impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> {
         let mut ch2 = Flex::new(ch2);
         let mut ch3 = Flex::new(ch3);
         let mut ch4 = Flex::new(ch4);
-        ch1.set_as_af_unchecked(af1, AFType::OutputPushPull, Pull::None, Speed::VeryHigh);
-        ch2.set_as_af_unchecked(af2, AFType::OutputPushPull, Pull::None, Speed::VeryHigh);
-        ch3.set_as_af_unchecked(af3, AFType::OutputPushPull, Pull::None, Speed::VeryHigh);
-        ch4.set_as_af_unchecked(af4, AFType::OutputPushPull, Pull::None, Speed::VeryHigh);
+        ch1.set_as_af_unchecked(af1, AfType::output(OutputType::PushPull, Speed::VeryHigh));
+        ch2.set_as_af_unchecked(af2, AfType::output(OutputType::PushPull, Speed::VeryHigh));
+        ch3.set_as_af_unchecked(af3, AfType::output(OutputType::PushPull, Speed::VeryHigh));
+        ch4.set_as_af_unchecked(af4, AfType::output(OutputType::PushPull, Speed::VeryHigh));
 
         let mut this = Self {
             tim: LLTimer::new(tim),
diff --git a/tests/stm32/src/bin/gpio.rs b/tests/stm32/src/bin/gpio.rs
index dfa299ab5..1d1018c5c 100644
--- a/tests/stm32/src/bin/gpio.rs
+++ b/tests/stm32/src/bin/gpio.rs
@@ -112,7 +112,7 @@ async fn main(_spawner: Spawner) {
         let b = Input::new(&mut b, Pull::Down);
         // no pull, the status is undefined
 
-        let mut a = OutputOpenDrain::new(&mut a, Level::Low, Speed::Low, Pull::None);
+        let mut a = OutputOpenDrain::new(&mut a, Level::Low, Speed::Low);
         delay();
         assert!(b.is_low());
         a.set_high(); // High-Z output
@@ -203,7 +203,7 @@ async fn main(_spawner: Spawner) {
 
         let mut a = Flex::new(&mut a);
         a.set_low();
-        a.set_as_input_output(Speed::Low, Pull::None);
+        a.set_as_input_output(Speed::Low);
         delay();
         assert!(b.is_low());
         a.set_high(); // High-Z output