blob: 2179bbfc3aaaeac55f1ca4c5a7fcea0fd4a9cb0a [file] [log] [blame]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! # Design Considerations
//!
//! This module defines traits and error types for abstracting system-level
//! clock and reset control operations. The design emphasizes **flexibility**,
//! **composability**, and **extensibility** to support a wide range of hardware
//! platforms and use cases.
//!
//! ## Flexibility
//!
//! - **Platform Independence**: The traits abstract hardware-specific operations,
//! allowing implementations to target different platforms without modifying
//! consumer code.
//! - **Custom Identifiers and Configurations**: Associated types like `ClockId`,
//! `ClockConfig`, and `ResetId` allow implementers to define identifiers and
//! configurations that match their hardware model.
//! - **Error Abstraction**: The `Error` trait and `ErrorKind` enum decouple
//! error handling from specific implementations, enabling generic code to
//! respond to common failure modes while supporting detailed diagnostics.
//!
//! ## Composability
//!
//! - **Unified Error Handling**: The `ErrorType` supertrait provides a consistent
//! interface for accessing error types, enabling integration with other traits
//! and systems.
//! - **Non-Exhaustive ErrorKind**: The `#[non_exhaustive]` attribute on `ErrorKind`
//! allows future expansion without breaking existing code, supporting long-term
//! composability.
//!
//! ## Extensibility
//!
//! - **Custom Error Types**: Implementers can define their own error types and
//! map them to `ErrorKind`, enabling rich, context-specific error reporting
//! while maintaining compatibility with generic consumers.
//! - **Vendor-Specific Configuration**: The `ClockConfig` type supports complex
//! configuration structures such as PLL settings, dividers, or source selectors.
//! - **Partial Implementations**: While all trait methods are required, implementers
//! can provide no-op or stub implementations for unsupported features, enabling
//! partial functionality where appropriate.
//!
//! ## Summary
//!
//! This design provides a robust foundation for building portable, maintainable,
//! and scalable hardware abstraction layers. By leveraging Rust’s trait system,
//! it enables:
//!
//! - Reuse of generic drivers across platforms.
//! - Simplified testing and mocking.
//! - Clear contracts between hardware abstraction and higher-level logic.
use core::time::Duration;
/// Represents common system control operation errors.
///
/// This enumeration defines a standard set of error conditions that can occur
/// during clock and reset control operations. Implementations are free to define
/// more specific or additional error types. However, by providing a mapping to
/// these common errors, generic code can still react to them appropriately.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
/// The specified clock identifier was not found or is invalid.
ClockNotFound,
/// Attempted to enable a clock that is already enabled.
ClockAlreadyEnabled,
/// Attempted to disable a clock that is already disabled.
ClockAlreadyDisabled,
/// The specified clock frequency is invalid or not supported.
InvalidClockFrequency,
/// Clock configuration operation failed due to invalid parameters or hardware constraints.
ClockConfigurationFailed,
/// The specified reset identifier was not found or is invalid.
InvalidResetId,
/// A hardware-level failure occurred during the operation.
HardwareFailure,
/// The operation was denied due to insufficient permissions or security restrictions.
PermissionDenied,
/// The operation timed out before completion.
Timeout,
}
/// Trait for system control error types.
///
/// This trait provides a standard interface for all error types used in
/// system control operations. It requires implementors to provide a mapping
/// to the common `ErrorKind` enumeration, enabling generic error handling
/// while preserving implementation-specific error details.
pub trait Error: core::fmt::Debug {
/// Convert error to a generic error kind.
///
/// By using this method, errors freely defined by system control implementations
/// can be converted to a set of generic errors upon which generic
/// code can act.
fn kind(&self) -> ErrorKind;
}
impl Error for core::convert::Infallible {
/// Convert error to a generic error kind.
///
/// Since `core::convert::Infallible` represents an error that can never occur,
/// this implementation uses pattern matching on the uninhabited type to
/// ensure this method can never actually be called.
fn kind(&self) -> ErrorKind {
match *self {}
}
}
/// Trait providing access to the associated error type.
///
/// This trait serves as a foundation for other traits that need to define
/// error handling. By separating error type definition from specific operations,
/// it enables composition and reuse across different trait implementations.
pub trait ErrorType {
/// The error type used by this implementation.
///
/// This associated type must implement the `Error` trait to ensure
/// it can be converted to generic error kinds for interoperability.
type Error: Error;
}
/// Trait for clock control operations.
/// Abstracts enabling, disabling, and configuring clocks for peripherals or system components.
pub trait ClockControl: ErrorType {
/// Type for identifying a clock (e.g., peripheral ID, clock name, or register offset).
type ClockId: Clone + PartialEq;
/// Type for configuring a clock.
type ClockConfig: PartialEq;
/// Enables a clock for the specified clock ID.
///
/// # Arguments
///
/// * `clock_id` - A reference to the identifier of the clock to enable.
///
/// # Returns
///
/// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`.
fn enable(&mut self, clock_id: &Self::ClockId) -> Result<(), Self::Error>;
/// Disables a clock for the specified clock ID.
///
/// # Arguments
///
/// * `clock_id` - A reference to the identifier of the clock to disable.
///
/// # Returns
///
/// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`.
fn disable(&mut self, clock_id: &Self::ClockId) -> Result<(), Self::Error>;
/// Sets the frequency of a clock (in Hz).
///
/// # Arguments
///
/// * `clock_id` - A reference to the identifier of the clock to set the frequency for.
/// * `frequency_hz` - The frequency to set, in Hertz.
///
/// # Returns
///
/// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`.
fn set_frequency(
&mut self,
clock_id: &Self::ClockId,
frequency_hz: u64,
) -> Result<(), Self::Error>;
/// Gets the current frequency of a clock (in Hz).
///
/// # Arguments
///
/// * `clock_id` - A reference to the identifier of the clock to get the frequency for.
///
/// # Returns
///
/// * `Result<u64, Self::Error>` - Ok with the current frequency in Hertz, or an error of type `Self::Error`.
fn get_frequency(&self, clock_id: &Self::ClockId) -> Result<u64, Self::Error>;
/// Configures clock-specific parameters (e.g., divider, source).
/// Vendor-specific parameters can be passed via `ClockConfig`.
///
/// # Arguments
///
/// * `clock_id` - A reference to the identifier of the clock to configure.
/// * `config` - The configuration parameters for the clock.
///
/// # Returns
///
/// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`.
fn configure(
&mut self,
clock_id: &Self::ClockId,
config: Self::ClockConfig,
) -> Result<(), Self::Error>;
/// Retrieves the current configuration of a clock.
///
/// # Arguments
///
/// * `clock_id` - A reference to the identifier of the clock to get the configuration for.
///
/// # Returns
///
/// * `Result<Self::ClockConfig, Self::Error>` - Ok with the current configuration, or an error of type `Self::Error`.
fn get_config(&self, clock_id: &Self::ClockId) -> Result<Self::ClockConfig, Self::Error>;
}
/// Trait for reset control operations.
/// Abstracts asserting and deasserting reset signals for peripherals or system components.
pub trait ResetControl: ErrorType {
/// Type for identifying a reset line (e.g., peripheral ID, reset name, or register offset).
type ResetId: Clone + PartialEq;
/// Asserts the reset signal for the specified reset ID (holds the component in reset).
///
/// # Arguments
///
/// * `reset_id` - A reference to the identifier of the reset line to assert.
///
/// # Returns
///
/// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`.
fn reset_assert(&mut self, reset_id: &Self::ResetId) -> Result<(), Self::Error>;
/// Deasserts the reset signal for the specified reset ID (releases the component from reset).
///
/// # Arguments
///
/// * `reset_id` - A reference to the identifier of the reset line to deassert.
///
/// # Returns
///
/// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`.
fn reset_deassert(&mut self, reset_id: &Self::ResetId) -> Result<(), Self::Error>;
/// Performs a pulse reset (asserts then deasserts) with a specified duration (in microseconds).
///
/// # Arguments
///
/// * `reset_id` - A reference to the identifier of the reset line to pulse.
/// * `duration_us` - The duration of the pulse in microseconds.
///
/// # Returns
///
/// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`.
fn reset_pulse(
&mut self,
reset_id: &Self::ResetId,
duration: Duration,
) -> Result<(), Self::Error>;
/// Checks if the reset signal is currently asserted for the specified reset ID.
///
/// # Arguments
///
/// * `reset_id` - A reference to the identifier of the reset line to check.
///
/// # Returns
///
/// * `Result<bool, Self::Error>` - Ok with a boolean indicating if the reset is asserted, or an error of type `Self::Error`.
fn reset_is_asserted(&self, reset_id: &Self::ResetId) -> Result<bool, Self::Error>;
}
/// Blanket trait that combines clock and reset control functionality.
///
/// This trait provides a unified interface for system control operations,
/// combining both clock management and reset control capabilities. It is
/// automatically implemented for any type that implements both `ClockControl`
/// and `ResetControl` with the same error type.
///
/// # Design
///
/// The blanket implementation pattern allows consumers to use a single trait
/// bound for system control operations while maintaining the flexibility of
/// separate, composable traits for clock and reset functionality.
///
/// # Example
///
/// ```rust,ignore
/// fn configure_system<T: SystemControl>(controller: &mut T)
/// where
/// T::ClockId: From<u32>,
/// T::ResetId: From<u32>,
/// T::ClockConfig: Default,
/// {
/// // Enable peripheral clock
/// controller.enable(&T::ClockId::from(42)).unwrap();
///
/// // Release from reset
/// controller.reset_deassert(&T::ResetId::from(42)).unwrap();
/// }
/// ```
pub trait SystemControl: ClockControl + ResetControl {}
/// Blanket implementation of SystemControl for types implementing both traits.
///
/// This implementation is automatically provided for any type that implements
/// both `ClockControl` and `ResetControl` with compatible error types.
impl<T> SystemControl for T where T: ClockControl + ResetControl {}