//! Device tree-based driver definitions

use alloc::sync::Arc;
use device_api::gpio::GpioPinLevel;
use yggdrasil_abi::error::Error;

mod controller;
mod macros;
mod registry;
mod syscon;
mod traits;
mod tree;

pub mod util;

pub use controller::{map_interrupt, map_interrupt_at};
pub use macros::device_tree_driver;
pub use registry::{lookup_phandle, register_driver};
pub use syscon::DeviceTreeSyscon;
pub use traits::{
    DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreePinController,
    DeviceTreeResetController, Driver, ProbeContext,
};
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};

/// Contextual information about a GPIO pin being configured
pub struct DeviceTreeGpioPins {
    /// Whether to enable input on the pin
    pub input: bool,
    /// Whether to enable output on the pin
    pub output: bool,
    /// Initial level to configure the GPIO with
    pub initial_level: GpioPinLevel,
}

/// Specifies initialization sequence requirement for a driver
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum InitSequence {
    /// Driver can be initialized during early boot
    Early = 1,
    /// Driver must be initialized when sleep function is available
    Late = 2,
}

/// Performs a walk of the device tree and initializes nodes which haven't already been
/// initialized/probed.
///
/// Reports successful initializations via `success_handler` and
/// failures via `error_handler`.
pub fn lazy_init<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
    success_handler: S,
    error_handler: F,
    sequence: InitSequence,
) {
    walk_device_tree::<(), _>(|node| {
        match node.clone().lazy_init(sequence) {
            Some(Ok(())) => success_handler(node),
            Some(Err(error)) => error_handler(node, error),
            None => (),
        }
        None
    });
}

/// Performs a walk of the device and initializes IRQs for nodes. Additionally performs lazy
/// initialization (if need to enable IRQs, but a device is not initialized).
///
/// Reports successful initializations via `success_handler` and
/// failures via `error_handler`.
pub fn init_irqs<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
    success_handler: S,
    error_handler: F,
    sequence: InitSequence,
) {
    walk_device_tree::<(), _>(|node| {
        match node.clone().init_irqs(sequence) {
            Some(Ok(())) => success_handler(node),
            Some(Err(error)) => error_handler(node, error),
            None => (),
        }

        None
    });
}
