Introducing the sgpiom peripheral in ast10x0
diff --git a/hal/blocking/src/gpio_port.rs b/hal/blocking/src/gpio_port.rs
index 606fa56..2ed422e 100644
--- a/hal/blocking/src/gpio_port.rs
+++ b/hal/blocking/src/gpio_port.rs
@@ -28,6 +28,8 @@
     /// The pin is not configured for the requested operation
     /// (e.g., reading output value from input pin)
     InvalidMode,
+    /// The pin is reserved and cannot be configured or driven
+    ReservedPin,
 }
 
 /// Trait for GPIO errors
@@ -46,6 +48,64 @@
     }
 }
 
+/// Active polarity for a GPIO pin
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum ActivePolarity {
+    /// Pin is active when driven high
+    ActiveHigh,
+    /// Pin is active when driven low
+    ActiveLow,
+}
+
+/// Direction for a GPIO pin
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum PinDirection {
+    /// Pin is configured as an input
+    Input,
+    /// Pin is configured as an output
+    Output,
+}
+
+/// Standard pin configuration combining direction, polarity, and optional initial output level
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct PinConfig {
+    /// Pin direction (input or output)
+    pub direction: PinDirection,
+    /// Active polarity declaration
+    pub polarity: ActivePolarity,
+    /// Initial driven value when direction is Output; ignored for inputs
+    pub initial_output: Option<bool>,
+}
+
+impl PinConfig {
+    /// Convenience constructor for a standard active-high output with a defined initial level
+    pub const fn output_active_high(initial: bool) -> Self {
+        Self {
+            direction: PinDirection::Output,
+            polarity: ActivePolarity::ActiveHigh,
+            initial_output: Some(initial),
+        }
+    }
+
+    /// Convenience constructor for a standard active-low output with a defined initial level
+    pub const fn output_active_low(initial: bool) -> Self {
+        Self {
+            direction: PinDirection::Output,
+            polarity: ActivePolarity::ActiveLow,
+            initial_output: Some(initial),
+        }
+    }
+
+    /// Convenience constructor for an input pin
+    pub const fn input(polarity: ActivePolarity) -> Self {
+        Self {
+            direction: PinDirection::Input,
+            polarity,
+            initial_output: None,
+        }
+    }
+}
+
 /// Edge sensitivity for interrupt configuration
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 pub enum EdgeSensitivity {
@@ -172,3 +232,15 @@
 
 /// Automatically implement GpioController for any type implementing both required traits
 impl<T: GpioPort + GpioInterrupt> GpioController for T {}
+
+/// Trait for SGPIO passthrough: mirrors sampled inputs to output latch under a mask (FR-05)
+pub trait GpioBankPassthrough: GpioPort {
+    /// Set the mask of pins subject to passthrough.
+    /// When active, sampled input state for masked pins is forwarded to the output latch.
+    fn set_passthrough_mask(&mut self, mask: Self::Mask) -> Result<(), Self::Error>;
+
+    /// Disable passthrough for all pins.
+    fn clear_passthrough(&mut self) -> Result<(), Self::Error>;
+}
+
+
diff --git a/target/ast10x0/peripherals/BUILD.bazel b/target/ast10x0/peripherals/BUILD.bazel
index 450531b..554c537 100644
--- a/target/ast10x0/peripherals/BUILD.bazel
+++ b/target/ast10x0/peripherals/BUILD.bazel
@@ -33,6 +33,11 @@
         "scu/routing.rs",
         "scu/status.rs",
         "scu/types.rs",
+        "sgpiom/controller.rs",
+        "sgpiom/hal_impl.rs",
+        "sgpiom/mod.rs",
+        "sgpiom/register_block.rs",
+        "sgpiom/types.rs",
         "smc/controller.rs",
         "smc/device/block_device.rs",
         "smc/device/flash.rs",
diff --git a/target/ast10x0/peripherals/lib.rs b/target/ast10x0/peripherals/lib.rs
index be02f13..e34dea0 100644
--- a/target/ast10x0/peripherals/lib.rs
+++ b/target/ast10x0/peripherals/lib.rs
@@ -5,6 +5,7 @@
 
 pub mod i2c;
 pub mod scu;
+pub mod sgpiom;
 pub mod smc;
 pub mod spimonitor;
 pub mod uart;
diff --git a/target/ast10x0/peripherals/sgpiom/controller.rs b/target/ast10x0/peripherals/sgpiom/controller.rs
new file mode 100644
index 0000000..5dc39f7
--- /dev/null
+++ b/target/ast10x0/peripherals/sgpiom/controller.rs
@@ -0,0 +1,44 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+use openprot_hal_blocking::gpio_port::GpioErrorKind;
+
+use super::hal_impl::SgpiomBankPort;
+
+/// Concrete SGPIOM controller owning up to `N` child bank ports (US-01, US-05).
+///
+/// Each slot is `Option<SgpiomBankPort>` so that absent banks (disabled in
+/// Devicetree) are represented safely without crashing callers (US-07).
+/// Operations on one bank do not affect any other bank.
+pub struct SgpiomController<const N: usize> {
+    banks: [Option<SgpiomBankPort>; N],
+}
+
+impl<const N: usize> SgpiomController<N> {
+    /// Construct from an array of optional bank instances.
+    pub fn new(banks: [Option<SgpiomBankPort>; N]) -> Self {
+        Self { banks }
+    }
+
+    /// Return the total number of bank slots (present or absent).
+    pub fn num_banks(&self) -> usize {
+        N
+    }
+
+    /// Obtain a mutable reference to the bank at `index`.
+    ///
+    /// Returns `Err(GpioErrorKind::InvalidPort)` when the index is out of range
+    /// or the bank slot is absent.
+    pub fn bank(&mut self, index: usize) -> Result<&mut SgpiomBankPort, GpioErrorKind> {
+        self.banks
+            .get_mut(index)
+            .and_then(Option::as_mut)
+            .ok_or(GpioErrorKind::InvalidPort)
+    }
+
+    /// Return `None` when the bank is absent; never errors.
+    /// Prefer this for optional-bank patterns (US-07).
+    pub fn bank_opt(&mut self, index: usize) -> Option<&mut SgpiomBankPort> {
+        self.banks.get_mut(index)?.as_mut()
+    }
+}
diff --git a/target/ast10x0/peripherals/sgpiom/hal_impl.rs b/target/ast10x0/peripherals/sgpiom/hal_impl.rs
new file mode 100644
index 0000000..9bba5e0
--- /dev/null
+++ b/target/ast10x0/peripherals/sgpiom/hal_impl.rs
@@ -0,0 +1,148 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+use openprot_hal_blocking::gpio_port::{
+    ActivePolarity, GpioBankPassthrough, GpioError, GpioErrorKind, GpioErrorType, GpioPort,
+    PinConfig, PinDirection, PinMask,
+};
+
+use super::register_block::Sgpiom;
+use super::types::{BankDevice, Error};
+
+/// 32-bit pin mask for a single SGPIOM bank.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct SgpiomMask(pub u32);
+
+impl PinMask for SgpiomMask {
+    fn empty() -> Self {
+        Self(0)
+    }
+
+    fn all() -> Self {
+        Self(0xFFFF_FFFF)
+    }
+
+    fn is_empty(&self) -> bool {
+        self.0 == 0
+    }
+
+    fn contains(&self, other: Self) -> bool {
+        (self.0 & other.0) == other.0
+    }
+
+    fn union(&self, other: Self) -> Self {
+        Self(self.0 | other.0)
+    }
+
+    fn intersection(&self, other: Self) -> Self {
+        Self(self.0 & other.0)
+    }
+
+    fn toggle(&self) -> Self {
+        Self(!self.0)
+    }
+}
+
+impl GpioError for Error {
+    fn kind(&self) -> GpioErrorKind {
+        match self {
+            Error::InvalidPin => GpioErrorKind::InvalidPin,
+            Error::InvalidNgpios => GpioErrorKind::UnsupportedConfiguration,
+            Error::UnsupportedFlags => GpioErrorKind::UnsupportedConfiguration,
+        }
+    }
+}
+
+/// A single SGPIOM bank exposed as a HAL [`GpioPort`].
+///
+/// Combines the shared [`Sgpiom`] register block with a [`BankDevice`] descriptor.
+/// Use [`SgpiomBankPort::new`] to construct; the same safety contract as [`Sgpiom::new`]
+/// applies.
+pub struct SgpiomBankPort {
+    pub(super) sgpiom: Sgpiom,
+    pub(super) dev: BankDevice,
+}
+
+impl SgpiomBankPort {
+    /// Create a bank port from an existing `Sgpiom` instance and a `BankDevice` descriptor.
+    ///
+    /// # Safety
+    ///
+    /// Same contract as [`Sgpiom::new`]: the register block pointer must be valid, non-null,
+    /// and access must be externally coordinated for the lifetime of this value.
+    pub const unsafe fn new(sgpiom: Sgpiom, dev: BankDevice) -> Self {
+        Self { sgpiom, dev }
+    }
+}
+
+impl GpioErrorType for SgpiomBankPort {
+    type Error = Error;
+}
+
+impl GpioPort for SgpiomBankPort {
+    type Config = PinConfig;
+    type Mask = SgpiomMask;
+
+    fn configure(&mut self, pins: Self::Mask, config: Self::Config) -> Result<(), Self::Error> {
+        // Reject pins outside this bank's ngpios window.
+        let valid_mask: u32 = if self.dev.ngpios >= 32 {
+            u32::MAX
+        } else {
+            (1u32 << self.dev.ngpios) - 1
+        };
+        if (pins.0 & !valid_mask) != 0 {
+            return Err(Error::InvalidPin);
+        }
+
+        if config.direction == PinDirection::Output {
+            if let Some(logical_high) = config.initial_output {
+                // Map logical level through active polarity to physical drive level.
+                let drive_high = match config.polarity {
+                    ActivePolarity::ActiveHigh => logical_high,
+                    ActivePolarity::ActiveLow => !logical_high,
+                };
+                if drive_high {
+                    self.sgpiom.port_set_bits_raw(self.dev.bank, pins.0);
+                } else {
+                    self.sgpiom.port_clear_bits_raw(self.dev.bank, pins.0);
+                }
+            }
+        }
+
+        // SGPIOM direction is hardware-managed; no direction register write is required.
+        Ok(())
+    }
+
+    fn set_reset(
+        &mut self,
+        set_mask: Self::Mask,
+        reset_mask: Self::Mask,
+    ) -> Result<(), Self::Error> {
+        // Atomically apply both masks: set wins over reset for overlapping bits.
+        self.sgpiom
+            .port_set_masked_raw(self.dev.bank, set_mask.0 | reset_mask.0, set_mask.0);
+        Ok(())
+    }
+
+    fn read_input(&self) -> Result<Self::Mask, Self::Error> {
+        Ok(SgpiomMask(self.sgpiom.port_get_raw(self.dev.bank)))
+    }
+
+    fn toggle(&mut self, pins: Self::Mask) -> Result<(), Self::Error> {
+        self.sgpiom.port_toggle_bits(self.dev.bank, pins.0);
+        Ok(())
+    }
+}
+
+impl GpioBankPassthrough for SgpiomBankPort {
+    /// Sample current inputs for `mask` pins and write them to the output latch (one-shot).
+    fn set_passthrough_mask(&mut self, mask: Self::Mask) -> Result<(), Self::Error> {
+        self.sgpiom.passthrough_masked(self.dev.bank, mask.0);
+        Ok(())
+    }
+
+    /// No persistent passthrough hardware state exists; this is a no-op.
+    fn clear_passthrough(&mut self) -> Result<(), Self::Error> {
+        Ok(())
+    }
+}
diff --git a/target/ast10x0/peripherals/sgpiom/mod.rs b/target/ast10x0/peripherals/sgpiom/mod.rs
new file mode 100644
index 0000000..e2e3ab4
--- /dev/null
+++ b/target/ast10x0/peripherals/sgpiom/mod.rs
@@ -0,0 +1,18 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+//! AST10x0 Serial GPIO Matrix (SGPIOM) peripheral driver.
+
+mod controller;
+mod hal_impl;
+mod register_block;
+mod types;
+
+pub use controller::SgpiomController;
+pub use hal_impl::{SgpiomBankPort, SgpiomMask};
+pub use register_block::Sgpiom;
+pub use types::{
+    Bank, BankDevice, Direction, Error, InitialLevel, InterruptMode, InterruptTrigger,
+    SgpiomPinConfig,
+};
+
diff --git a/target/ast10x0/peripherals/sgpiom/register_block.rs b/target/ast10x0/peripherals/sgpiom/register_block.rs
new file mode 100644
index 0000000..4d45e61
--- /dev/null
+++ b/target/ast10x0/peripherals/sgpiom/register_block.rs
@@ -0,0 +1,272 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+use ast1060_pac as device;
+
+use super::types::{
+    Bank, BankDevice, Direction, Error, InitialLevel, InterruptMode, InterruptTrigger,
+    SgpiomPinConfig,
+};
+
+pub struct Sgpiom {
+    sgpiom: *const device::sgpiom::RegisterBlock,
+}
+
+impl Sgpiom {
+    /// Create an SGPIOM instance from a raw register-block pointer.
+    ///
+    /// # Safety
+    ///
+    /// - `sgpiom` must be a valid, non-null pointer to the AST1060 SGPIOM register block.
+    /// - The pointed register block must remain valid for the lifetime of this `Sgpiom`.
+    /// - Caller must enforce global ownership so concurrent mutable access does not occur.
+    pub const unsafe fn new(sgpiom: *const device::sgpiom::RegisterBlock) -> Self {
+        Self { sgpiom }
+    }
+
+    /// Create an instance pointing to the global AST1060 SGPIOM register block.
+    ///
+    /// # Safety
+    ///
+    /// Caller must ensure access to the singleton SGPIOM is coordinated.
+    pub unsafe fn new_global() -> Self {
+        // SAFETY: Caller upholds the singleton access contract.
+        unsafe { Self::new(device::Sgpiom::ptr()) }
+    }
+
+    #[inline]
+    fn regs(&self) -> &device::sgpiom::RegisterBlock {
+        // SAFETY: `Sgpiom` construction is `unsafe`, so caller upholds pointer validity,
+        // non-nullness, and aliasing/ownership requirements.
+        unsafe { &*self.sgpiom }
+    }
+
+    /// Configures SGPIOM global settings.
+    ///
+    /// `ngpios` is total SGPIO count across banks.
+    pub fn configure_global(&self, ngpios: u16, clock_div: u16) -> Result<(), Error> {
+        if ngpios == 0 {
+            return Err(Error::InvalidNgpios);
+        }
+
+        let numbers = ((ngpios as u32 + 7) / 8) & 0x1f;
+        let mut value = self.regs().gpio554().read().bits();
+
+        value |= 1; // enable
+        value &= !(0x1f << 6);
+        value |= numbers << 6;
+        value &= !(0xffff << 16);
+        value |= (clock_div as u32) << 16;
+
+        self.regs().gpio554().write(|w| unsafe { w.bits(value) });
+        Ok(())
+    }
+
+    /// Read the raw 32-bit output register for a bank.
+    #[must_use]
+    pub fn port_get_raw(&self, bank: Bank) -> u32 {
+        match bank {
+            Bank::Ad => self.regs().gpio500().read().bits(),
+            Bank::Eh => self.regs().gpio51c().read().bits(),
+            Bank::Il => self.regs().gpio538().read().bits(),
+            Bank::Mp => self.regs().gpio590().read().bits(),
+        }
+    }
+
+    pub fn port_set_masked_raw(&self, bank: Bank, mask: u32, value: u32) {
+        let current = self.port_get_raw(bank);
+        let next = (current & !mask) | (value & mask);
+        self.port_write_raw(bank, next);
+    }
+
+    pub fn port_set_bits_raw(&self, bank: Bank, mask: u32) {
+        self.port_set_masked_raw(bank, mask, mask);
+    }
+
+    pub fn port_clear_bits_raw(&self, bank: Bank, mask: u32) {
+        self.port_set_masked_raw(bank, mask, 0);
+    }
+
+    pub fn port_toggle_bits(&self, bank: Bank, mask: u32) {
+        let current = self.port_get_raw(bank);
+        self.port_write_raw(bank, current ^ mask);
+    }
+
+    pub fn pin_set_raw(&self, dev: &BankDevice, pin: u8, high: bool) -> Result<(), Error> {
+        dev.validate_pin(pin)?;
+        let bit = 1u32 << pin;
+        if high {
+            self.port_set_bits_raw(dev.bank, bit);
+        } else {
+            self.port_clear_bits_raw(dev.bank, bit);
+        }
+        Ok(())
+    }
+
+    pub fn configure_pin(
+        &self,
+        dev: &BankDevice,
+        pin: u8,
+        cfg: SgpiomPinConfig,
+    ) -> Result<(), Error> {
+        dev.validate_pin(pin)?;
+
+        if cfg.pull_up || cfg.pull_down {
+            return Err(Error::UnsupportedFlags);
+        }
+
+        if cfg.direction == Direction::Output {
+            if let Some(initial) = cfg.initial {
+                self.pin_set_raw(dev, pin, initial == InitialLevel::High)?;
+            }
+        }
+
+        // SGPIOM direction is hardware managed in this design; no extra register write needed.
+        Ok(())
+    }
+
+    pub fn configure_interrupt(
+        &self,
+        dev: &BankDevice,
+        pin: u8,
+        mode: InterruptMode,
+        trig: InterruptTrigger,
+    ) -> Result<(), Error> {
+        dev.validate_pin(pin)?;
+
+        let bit = 1u32 << pin;
+        let int_type = match mode {
+            InterruptMode::Disabled => 0u8,
+            InterruptMode::Level => match trig {
+                InterruptTrigger::Low => 2,
+                InterruptTrigger::High => 3,
+                InterruptTrigger::Both => return Err(Error::UnsupportedFlags),
+            },
+            InterruptMode::Edge => match trig {
+                InterruptTrigger::Low => 0,
+                InterruptTrigger::High => 1,
+                InterruptTrigger::Both => 4,
+            },
+        };
+
+        match mode {
+            InterruptMode::Disabled => {
+                let en = self.int_en_read(dev.bank) & !bit;
+                self.int_en_write(dev.bank, en);
+            }
+            _ => {
+                let en = self.int_en_read(dev.bank) | bit;
+                self.int_en_write(dev.bank, en);
+
+                let mut s0 = self.int_sens_read(dev.bank, 0) & !bit;
+                let mut s1 = self.int_sens_read(dev.bank, 1) & !bit;
+                let mut s2 = self.int_sens_read(dev.bank, 2) & !bit;
+
+                if (int_type & 0x1) != 0 {
+                    s0 |= bit;
+                }
+                if (int_type & 0x2) != 0 {
+                    s1 |= bit;
+                }
+                if (int_type & 0x4) != 0 {
+                    s2 |= bit;
+                }
+
+                self.int_sens_write(dev.bank, 0, s0);
+                self.int_sens_write(dev.bank, 1, s1);
+                self.int_sens_write(dev.bank, 2, s2);
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Read the latched interrupt status register for a bank.
+    #[must_use]
+    pub fn interrupt_status(&self, bank: Bank) -> u32 {
+        match bank {
+            Bank::Ad => self.regs().gpio514().read().bits(),
+            Bank::Eh => self.regs().gpio530().read().bits(),
+            Bank::Il => self.regs().gpio54c().read().bits(),
+            Bank::Mp => self.regs().gpio5a4().read().bits(),
+        }
+    }
+
+    /// Acknowledge (clear) interrupt status bits for a bank.
+    pub fn clear_interrupt_status(&self, bank: Bank, mask: u32) {
+        match bank {
+            Bank::Ad => self.regs().gpio514().write(|w| unsafe { w.bits(mask) }),
+            Bank::Eh => self.regs().gpio530().write(|w| unsafe { w.bits(mask) }),
+            Bank::Il => self.regs().gpio54c().write(|w| unsafe { w.bits(mask) }),
+            Bank::Mp => self.regs().gpio5a4().write(|w| unsafe { w.bits(mask) }),
+        };
+    }
+
+    pub fn passthrough_masked(&self, bank: Bank, mask: u32) {
+        let sampled = self.port_get_raw(bank);
+        self.port_set_masked_raw(bank, mask, sampled);
+    }
+
+    fn port_write_raw(&self, bank: Bank, value: u32) {
+        match bank {
+            Bank::Ad => self.regs().gpio500().write(|w| unsafe { w.bits(value) }),
+            Bank::Eh => self.regs().gpio51c().write(|w| unsafe { w.bits(value) }),
+            Bank::Il => self.regs().gpio538().write(|w| unsafe { w.bits(value) }),
+            Bank::Mp => self.regs().gpio590().write(|w| unsafe { w.bits(value) }),
+        };
+    }
+
+    fn int_en_read(&self, bank: Bank) -> u32 {
+        match bank {
+            Bank::Ad => self.regs().gpio504().read().bits(),
+            Bank::Eh => self.regs().gpio520().read().bits(),
+            Bank::Il => self.regs().gpio53c().read().bits(),
+            Bank::Mp => self.regs().gpio594().read().bits(),
+        }
+    }
+
+    fn int_en_write(&self, bank: Bank, value: u32) {
+        match bank {
+            Bank::Ad => self.regs().gpio504().write(|w| unsafe { w.bits(value) }),
+            Bank::Eh => self.regs().gpio520().write(|w| unsafe { w.bits(value) }),
+            Bank::Il => self.regs().gpio53c().write(|w| unsafe { w.bits(value) }),
+            Bank::Mp => self.regs().gpio594().write(|w| unsafe { w.bits(value) }),
+        };
+    }
+
+    fn int_sens_read(&self, bank: Bank, index: u8) -> u32 {
+        match (bank, index) {
+            (Bank::Ad, 0) => self.regs().gpio508().read().bits(),
+            (Bank::Ad, 1) => self.regs().gpio50c().read().bits(),
+            (Bank::Ad, 2) => self.regs().gpio510().read().bits(),
+            (Bank::Eh, 0) => self.regs().gpio524().read().bits(),
+            (Bank::Eh, 1) => self.regs().gpio528().read().bits(),
+            (Bank::Eh, 2) => self.regs().gpio52c().read().bits(),
+            (Bank::Il, 0) => self.regs().gpio540().read().bits(),
+            (Bank::Il, 1) => self.regs().gpio544().read().bits(),
+            (Bank::Il, 2) => self.regs().gpio548().read().bits(),
+            (Bank::Mp, 0) => self.regs().gpio598().read().bits(),
+            (Bank::Mp, 1) => self.regs().gpio59c().read().bits(),
+            (Bank::Mp, 2) => self.regs().gpio5a0().read().bits(),
+            _ => 0,
+        }
+    }
+
+    fn int_sens_write(&self, bank: Bank, index: u8, value: u32) {
+        match (bank, index) {
+            (Bank::Ad, 0) => self.regs().gpio508().write(|w| unsafe { w.bits(value) }),
+            (Bank::Ad, 1) => self.regs().gpio50c().write(|w| unsafe { w.bits(value) }),
+            (Bank::Ad, 2) => self.regs().gpio510().write(|w| unsafe { w.bits(value) }),
+            (Bank::Eh, 0) => self.regs().gpio524().write(|w| unsafe { w.bits(value) }),
+            (Bank::Eh, 1) => self.regs().gpio528().write(|w| unsafe { w.bits(value) }),
+            (Bank::Eh, 2) => self.regs().gpio52c().write(|w| unsafe { w.bits(value) }),
+            (Bank::Il, 0) => self.regs().gpio540().write(|w| unsafe { w.bits(value) }),
+            (Bank::Il, 1) => self.regs().gpio544().write(|w| unsafe { w.bits(value) }),
+            (Bank::Il, 2) => self.regs().gpio548().write(|w| unsafe { w.bits(value) }),
+            (Bank::Mp, 0) => self.regs().gpio598().write(|w| unsafe { w.bits(value) }),
+            (Bank::Mp, 1) => self.regs().gpio59c().write(|w| unsafe { w.bits(value) }),
+            (Bank::Mp, 2) => self.regs().gpio5a0().write(|w| unsafe { w.bits(value) }),
+            _ => {}
+        };
+    }
+}
diff --git a/target/ast10x0/peripherals/sgpiom/types.rs b/target/ast10x0/peripherals/sgpiom/types.rs
new file mode 100644
index 0000000..6e4a45a
--- /dev/null
+++ b/target/ast10x0/peripherals/sgpiom/types.rs
@@ -0,0 +1,93 @@
+// Licensed under the Apache-2.0 license
+// SPDX-License-Identifier: Apache-2.0
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Error {
+    InvalidPin,
+    InvalidNgpios,
+    UnsupportedFlags,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Direction {
+    Input,
+    Output,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum InitialLevel {
+    Low,
+    High,
+}
+
+/// Low-level per-pin configuration used by [`crate::sgpiom::register_block::Sgpiom::configure_pin`].
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct SgpiomPinConfig {
+    pub direction: Direction,
+    pub initial: Option<InitialLevel>,
+    pub pull_up: bool,
+    pub pull_down: bool,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum InterruptMode {
+    Disabled,
+    Level,
+    Edge,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum InterruptTrigger {
+    Low,
+    High,
+    Both,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+pub enum Bank {
+    Ad = 0,
+    Eh = 1,
+    Il = 2,
+    Mp = 3,
+}
+
+impl Bank {
+    #[inline]
+    pub const fn from_pin_offset(pin_offset: u8) -> Option<Self> {
+        match pin_offset >> 5 {
+            0 => Some(Self::Ad),
+            1 => Some(Self::Eh),
+            2 => Some(Self::Il),
+            3 => Some(Self::Mp),
+            _ => None,
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct BankDevice {
+    pub bank: Bank,
+    pub pin_offset: u8,
+    pub ngpios: u8,
+}
+
+impl BankDevice {
+    #[inline]
+    pub const fn new(bank: Bank, pin_offset: u8, ngpios: u8) -> Self {
+        Self {
+            bank,
+            pin_offset,
+            ngpios,
+        }
+    }
+
+    #[inline]
+    pub fn validate_pin(&self, pin: u8) -> Result<(), Error> {
+        if pin < self.ngpios && pin < 32 {
+            Ok(())
+        } else {
+            Err(Error::InvalidPin)
+        }
+    }
+}