add active low/high for gpio open-drain mode
diff --git a/target/ast10x0/peripherals/gpio/mod.rs b/target/ast10x0/peripherals/gpio/mod.rs
index e0d7e3d..a0d31ac 100644
--- a/target/ast10x0/peripherals/gpio/mod.rs
+++ b/target/ast10x0/peripherals/gpio/mod.rs
@@ -12,8 +12,8 @@
 
 pub use registers::GpioRegisters;
 pub use types::{
-    Floating, GpioError, GpioExt, Input, InputMode, InterruptMode, OpenDrain, OpenDrainMode,
-    Output, OutputMode, PullDown, PullUp, PushPull, Tristate,
+    ActiveHigh, ActiveLow, Floating, GpioError, GpioExt, Input, InputMode, InterruptMode,
+    OpenDrain, OpenDrainMode, Output, OutputMode, PullDown, PullUp, PushPull, Tristate,
 };
 
 macro_rules! gpio_macro {
@@ -137,8 +137,14 @@
                     {
                         let p = unsafe { &*device::Gpio::ptr() };
                         p.$data_val_reg().modify(|r, w| unsafe {
-                            w.bits(r.bits() & !(1u32 << ($pos + $i)))
+                            let mask = 1u32 << ($pos + $i);
+                            w.bits(if ODM::ACTIVE_HIGH {
+                                r.bits() | mask
+                            } else {
+                                r.bits() & !mask
+                            })
                         });
+                        // Start released in the inactive state.
                         p.$dir_reg().modify(|r, w| unsafe {
                             w.bits(r.bits() & !(1u32 << ($pos + $i)))
                         });
@@ -197,7 +203,9 @@
                 {
                     fn is_set_high(&mut self) -> Result<bool, Self::Error> {
                         let p = unsafe { &*device::Gpio::ptr() };
-                        Ok((p.$dir_reg().read().bits() & (1u32 << ($pos + $i))) == 0)
+                        let is_output =
+                            (p.$dir_reg().read().bits() & (1u32 << ($pos + $i))) != 0;
+                        Ok(is_output == ODM::ACTIVE_HIGH)
                     }
 
                     fn is_set_low(&mut self) -> Result<bool, Self::Error> {
@@ -212,7 +220,12 @@
                     fn set_high(&mut self) -> Result<(), Self::Error> {
                         let p = unsafe { &*device::Gpio::ptr() };
                         p.$dir_reg().modify(|r, w| unsafe {
-                            w.bits(r.bits() & !(1u32 << ($pos + $i)))
+                            let mask = 1u32 << ($pos + $i);
+                            w.bits(if ODM::ACTIVE_HIGH {
+                                r.bits() | mask
+                            } else {
+                                r.bits() & !mask
+                            })
                         });
                         Ok(())
                     }
@@ -220,7 +233,12 @@
                     fn set_low(&mut self) -> Result<(), Self::Error> {
                         let p = unsafe { &*device::Gpio::ptr() };
                         p.$dir_reg().modify(|r, w| unsafe {
-                            w.bits(r.bits() | (1u32 << ($pos + $i)))
+                            let mask = 1u32 << ($pos + $i);
+                            w.bits(if ODM::ACTIVE_HIGH {
+                                r.bits() & !mask
+                            } else {
+                                r.bits() | mask
+                            })
                         });
                         Ok(())
                     }
diff --git a/target/ast10x0/peripherals/gpio/types.rs b/target/ast10x0/peripherals/gpio/types.rs
index bd054bf..50e90b5 100644
--- a/target/ast10x0/peripherals/gpio/types.rs
+++ b/target/ast10x0/peripherals/gpio/types.rs
@@ -13,8 +13,20 @@
 
 /// `OpenDrain` modes implement this trait.
 pub trait OpenDrainMode {
-    /// Returns whether pull-up is enabled.
-    fn pup() -> bool;
+    /// Whether the actively driven level is high.
+    const ACTIVE_HIGH: bool;
+}
+
+/// Active-low open-drain mode: output drives low and input releases high.
+pub struct ActiveLow;
+impl OpenDrainMode for ActiveLow {
+    const ACTIVE_HIGH: bool = false;
+}
+
+/// Active-high open-drain mode: output drives high and input releases low.
+pub struct ActiveHigh;
+impl OpenDrainMode for ActiveHigh {
+    const ACTIVE_HIGH: bool = true;
 }
 
 /// Input mode (type state).
@@ -28,11 +40,6 @@
 /// Sub-mode of Input: Floating input (type state).
 pub struct Floating;
 impl InputMode for Floating {}
-impl OpenDrainMode for Floating {
-    fn pup() -> bool {
-        false
-    }
-}
 
 /// Sub-mode of Input: Pulled down input (type state).
 pub struct PullDown;
@@ -41,11 +48,6 @@
 /// Sub-mode of Input: Pulled up input (type state).
 pub struct PullUp;
 impl InputMode for PullUp {}
-impl OpenDrainMode for PullUp {
-    fn pup() -> bool {
-        true
-    }
-}
 
 /// Tri-state mode (type state).
 pub struct Tristate;
@@ -69,7 +71,7 @@
 where
     ODM: OpenDrainMode,
 {
-    pub(super) _pull: PhantomData<ODM>,
+    pub(super) _mode: PhantomData<ODM>,
 }
 impl<ODM> OutputMode for OpenDrain<ODM> where ODM: OpenDrainMode {}
 
diff --git a/target/ast10x0/tests/peripherals/gpio/gpio_smoke/target.rs b/target/ast10x0/tests/peripherals/gpio/gpio_smoke/target.rs
index 75270c7..b69ed08 100644
--- a/target/ast10x0/tests/peripherals/gpio/gpio_smoke/target.rs
+++ b/target/ast10x0/tests/peripherals/gpio/gpio_smoke/target.rs
@@ -6,7 +6,7 @@
 #![no_std]
 #![no_main]
 
-use ast10x0_peripherals::gpio::{gpioa, Floating, GpioExt};
+use ast10x0_peripherals::gpio::{gpioa, ActiveLow, GpioExt};
 use ast10x0_peripherals::scu::{pinctrl, ScuRegisters};
 use console_backend::console_backend_write_all;
 use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin};
@@ -41,7 +41,7 @@
     }
     pw_log::info!("GPIOA1 pull-up input read high");
 
-    let mut pa3 = gpioa.pa3.into_open_drain_output::<Floating>();
+    let mut pa3 = gpioa.pa3.into_open_drain_output::<ActiveLow>();
     if pa3.set_low().is_err() || !pa3.is_set_low().unwrap_or(false) {
         pw_log::error!("GPIOA3 open-drain output did not latch low");
         return false;