blob: 378152bf06f249ae97cee6ca90cc4ade82de20b9 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! Low-level flash driver interface.
#![no_std]
use core::num::NonZero;
use util_types::PowerOf2Usize;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
/// Low-level flash driver interface.
///
/// This trait defines the interface for interacting with flash hardware at a low level.
/// It supports an asynchronous-style execution model using a start-poll-complete pattern:
/// 1. Start the operation (`start_erase`, `start_program`).
/// 2. Poll status (`is_busy`) or wait for interrupt.
/// 3. Finalize and check errors (`complete_op`).
///
/// Read operations (`read`) are assumed to be synchronous and blocking for simplicity.
pub trait FlashDriver {
/// The error type returned by driver operations.
type Error;
/// The default page size in bytes.
const PAGE_SIZE: usize;
/// The maximum size of a single program operation (write window).
/// Program operations cannot span across boundaries aligned to this size.
const PROGRAM_WINDOW_SIZE: usize;
/// The maximum size of a single read operation.
const MAX_READ_SIZE: usize;
/// The alignment required for read operations (addresses and lengths).
const READ_ALIGNMENT: usize;
/// The alignment required for program operations (addresses and lengths).
const PROGRAM_ALIGNMENT: usize;
/// Returns the total size of the flash in bytes.
fn size(&self) -> NonZero<usize>;
/// Returns a bitmap of supported erase block sizes.
///
/// Each bit `i` represents a supported erase block size of `2^i` bytes.
/// For example, if bit 11 is set, then 2048-byte erases are supported.
fn erasable_sizes_bitmap(&mut self) -> Result<u32, Self::Error>;
/// Reads data from flash synchronously.
///
/// # Arguments
/// * `start_addr`: The address to start reading from. Must be aligned to `READ_ALIGNMENT`.
/// * `buf`: The buffer to read data into. Must have size <= `MAX_READ_SIZE` and be aligned to `READ_ALIGNMENT`.
fn read(&mut self, start_addr: FlashAddress, buf: &mut [u8]) -> Result<(), Self::Error>;
/// Starts an erase operation asynchronously.
///
/// The operation is not guaranteed to be complete until `complete_op` is called
/// and returns `Ok`.
///
/// # Arguments
/// * `start_addr`: The start address of the block to erase. Must be aligned to the block size.
/// * `size`: The size of the block to erase. Must have its corresponding bit set in `ERASABLE_SIZES_BITMAP`.
fn start_erase(
&mut self,
start_addr: FlashAddress,
size: PowerOf2Usize,
) -> Result<(), Self::Error>;
/// Starts a program operation asynchronously.
///
/// The operation is not guaranteed to be complete until `complete_op` is called
/// and returns `Ok`.
///
/// The programmed region must not cross a `PROGRAM_WINDOW_SIZE` boundary.
///
/// # Arguments
/// * `start_address`: The address to start programming at. Must be aligned to `PROGRAM_ALIGNMENT`.
/// * `data`: The data to program. Must have size <= `PROGRAM_WINDOW_SIZE` and be aligned to `PROGRAM_ALIGNMENT`.
fn start_program(
&mut self,
start_address: FlashAddress,
data: &[u8],
) -> Result<(), Self::Error>;
/// Returns whether the driver is currently busy with an operation.
fn is_busy(&mut self) -> bool;
/// Completes a pending erase or program operation and returns the result.
///
/// This should be called after `is_busy` returns false to check for errors and
/// reset the driver state.
fn complete_op(&mut self) -> Result<(), Self::Error>;
}
/// Represents an address in flash memory.
///
/// A flash address consists of an offset within the flash address space.
#[derive(Default, Clone, Copy, PartialEq, Eq, IntoBytes, Immutable, FromBytes, KnownLayout)]
#[repr(transparent)]
pub struct FlashAddress {
offset: u32,
}
impl core::fmt::Display for FlashAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "0x{:08x}", self.offset)
}
}
impl core::fmt::Debug for FlashAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(self, f)
}
}
impl FlashAddress {
/// Creates a new `FlashAddress`.
pub const fn new(offset: u32) -> Self {
Self { offset }
}
/// Returns the offset.
pub fn offset(&self) -> u32 {
self.offset
}
}
impl core::ops::Add<usize> for FlashAddress {
type Output = Self;
fn add(self, other: usize) -> Self {
Self {
offset: self.offset + other as u32,
}
}
}
impl core::ops::AddAssign<usize> for FlashAddress {
fn add_assign(&mut self, other: usize) {
self.offset += other as u32;
}
}
impl core::ops::BitAnd<usize> for FlashAddress {
type Output = Self;
fn bitand(self, other: usize) -> Self {
Self {
offset: self.offset & other as u32,
}
}
}
impl core::ops::BitAndAssign<usize> for FlashAddress {
fn bitand_assign(&mut self, other: usize) {
self.offset &= other as u32;
}
}