psoc-drivers 0.1.0

Hardware driver implementations for psoc-rs
//! Asynchronous GPIO pin operations.
//!
//! This module provides interrupt-driven async methods for waiting on GPIO pins. Using one of these
//! methods will cause an interrupt handler to be registered for the appropriate port, which will
//! cause a linker error if the application also defines an interrupt handler for the same port.
//!
//!
// Copyright (c) 2026, Infineon Technologies AG or an affiliate of Infineon Technologies AG.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing permissions and
// limitations under the License.

// TODO: implement type-erased async pins

use core::task::{Context, Poll};

use psoc_macros::{optional_interrupt, require_interrupt};
use psoc_utils::waker::*;

use crate::{gpio::*, interrupt};

use embedded_hal_async::digital::Wait;

macro_rules! impl_port {
    ($port:literal) => { paste::paste! {
        #[cfg([< gpio $port >])]
        mod [< port $port >] {
            use super::*;
            impl<const PIN: u8, Mode: InputMode, Sec: Security> Pin<$port, PIN, Mode, Sec> {
                /// Waits for the pin to become high. If the pin is already high,
                /// completes immediately.
                pub async fn wait_for_high(&mut self) {
                    if self.is_high() {
                        return;
                    }
                    let interrupt = Interrupt::new(self, InterruptEdge::Rising, self.security);
                    // If the pin rose before we finished configuring the interrupt, don't wait
                    if interrupt.pin.is_high() {
                        return;
                    }
                    interrupt.await;
                }

                /// Waits for the pin to become low. If the pin is already low,
                /// completes immediately.
                pub async fn wait_for_low(&mut self) {
                    if self.is_low() {
                        return;
                    }
                    let interrupt = Interrupt::new(self, InterruptEdge::Falling, self.security);
                    // If the pin fell before we finished configuring the interrupt, don't wait
                    if interrupt.pin.is_low() {
                        return;
                    }
                    interrupt.await;
                }

                /// Waits for a rising edge on the pin. If the pin is already high, does not
                /// complete until the pin falls and then rises again.
                pub async fn wait_for_rising_edge(&mut self) {
                    Interrupt::new(self, InterruptEdge::Rising, self.security).await;
                }

                /// Waits for a falling edge on the pin. If the pin is already low, does not
                /// complete until the pin rises and then falls again.
                pub async fn wait_for_falling_edge(&mut self) {
                    Interrupt::new(self, InterruptEdge::Falling, self.security).await;
                }

                /// Waits for any edge on the pin.
                pub async fn wait_for_any_edge(&mut self) {
                    Interrupt::new(self, InterruptEdge::RisingFalling, self.security).await;
                }
            }

            impl<const PIN: u8, Mode: InputMode, Sec: Security> Wait for Pin<$port, PIN, Mode, Sec> {
                async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
                    Ok(self.wait_for_high().await)
                }

                async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
                    Ok(self.wait_for_low().await)
                }

                async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
                    Ok(self.wait_for_rising_edge().await)
                }

                async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
                    Ok(self.wait_for_falling_edge().await)
                }

                async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
                    Ok(self.wait_for_any_edge().await)
                }
            }

            /// A Future that resolves once an edge interrupt fires on a pin.
            struct Interrupt<'a, P> {
                pin: &'a mut P,
                waker: WakerRegistration,
            }

            impl<'a, P> Interrupt<'a, P>
            where
                for<'p> &'p mut P: Into<AnyPin<'p>>,
            {
                /// Creates a future that awaits an edge on a pin.
                fn new(pin: &'a mut P, edge: InterruptEdge, security: impl Into<SecurityAttribute>) -> Self {
                    #[cfg(trustzone)]
                    require_interrupt!(handle_secure_interrupt);
                    require_interrupt!(handle_nonsecure_interrupt);

                    // Configure the interrupt edge and clear the interrupt.
                    {
                        let mut pin = pin.into();
                        pin.set_interrupt_edge(edge);
                        pin.clear_interrupt();

                        if security.into().is_secure() {
                            #[cfg(trustzone)]
                            interrupt::unmask(regs::interrupt::[< IOSS_INTERRUPTS_SEC_GPIO_ $port >]);
                        } else {
                            interrupt::unmask(regs::interrupt::[< IOSS_INTERRUPTS_GPIO_ $port >]);
                        }
                    }

                    Self {
                        pin,
                        waker: WakerRegistration::new(),
                    }
                }
            }

            impl<'a, P> Future for Interrupt<'a, P>
            where
                for<'p> &'p mut P: Into<AnyPin<'p>>,
            {
                type Output = ();

                fn poll(mut self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
                    let (mut pin, waker) = unsafe {
                        let this = self.as_mut().get_unchecked_mut();
                        (
                            this.pin.into(),
                            core::pin::Pin::new_unchecked(&mut this.waker),
                        )
                    };

                    // Has the interrupt fired?
                    if pin.interrupt_asserted() {
                        // Yes; clear it and return ready.
                        pin.clear_interrupt();
                        Poll::Ready(())
                    } else {
                        // No; register a wakeup once the interrupt fires, unmask the inerrupt,
                        // and return pending.
                        critical_section::with(|cs| {
                            core::pin::Pin::static_ref(&WAKERS).insert(waker, cx.waker().clone(), cs);
                            pin.set_interrupt_enabled_cs(true, cs);
                        });

                        // If an interrupt triggered between us checking above and unmasking the
                        // interrupt, it will fire here, triggering the waker and causing us to be
                        // polled again.
                        Poll::Pending
                    }
                }
            }

            static WAKERS: WakerRegistry = WakerRegistry::new();

            #[allow(clippy::extra_unused_type_parameters)]
            fn handle_interrupt<T>() {
                unsafe {
                    // Mask all interrupts for this port so they won't fire again.
                    // Wakers will check and clear the interrupt status bits, and unmask any interrupts
                    // they're still interested in.
                    regs::GPIO.prt()[$port].intr_mask().init(|r| r);

                    // When a GPIO interrupt comes in, wake all futures waiting on this port. This
                    // may cause some spurious wakeups when an application is simultaneously waiting on
                    // multiple pins on one port, but spurious wakeups are OK; the interrupt futures will
                    // get polled again, check for completion, and re-register themselves if needed. This
                    // design means we only need one waker queue per port instead of per pin.
                    critical_section::with(|cs| core::pin::Pin::static_ref(&WAKERS).wake(cs));
                }
            }

            #[cfg(trustzone)]
            #[optional_interrupt([< IOSS_INTERRUPTS_SEC_GPIO_ $port >])]
            fn handle_secure_interrupt() {
                handle_interrupt::<()>();
            }

            #[optional_interrupt([< IOSS_INTERRUPTS_GPIO_ $port >])]
            fn handle_nonsecure_interrupt() {
                handle_interrupt::<()>();
            }
        }
    }}
}

impl_port!(0);
impl_port!(1);
impl_port!(2);
impl_port!(3);
impl_port!(4);
impl_port!(5);
impl_port!(6);
impl_port!(7);
impl_port!(8);
impl_port!(9);
impl_port!(10);
impl_port!(11);
impl_port!(12);
impl_port!(13);
impl_port!(14);
impl_port!(15);