use crate::ral::{self, ocotp};
use core::task::{self, Poll};
const WRITE_UNLOCK: u32 = 0x3E77;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
LockedRegionAccess,
DedRead,
Programming,
}
pub struct Ocotp {
pub(crate) ocotp: ocotp::OCOTP,
}
impl Ocotp {
pub fn new(ocotp: ocotp::OCOTP) -> Self {
Self { ocotp }
}
fn check_status(&mut self) -> Poll<Result<(), Error>> {
let (busy, error) = ral::read_reg!(ocotp, self.ocotp, CTRL, BUSY, ERROR);
if error != 0 {
ral::write_reg!(ocotp, self.ocotp, CTRL_SET, ERROR: 1);
return Poll::Ready(Err(Error::LockedRegionAccess));
}
if busy != 0 {
return Poll::Pending;
}
Poll::Ready(Ok(()))
}
fn start_fuse_read(&mut self, fuse_address: FuseAddress) -> Poll<Result<(), Error>> {
task::ready!(self.check_status())?;
ral::modify_reg!(ocotp, self.ocotp, CTRL, ADDR: u32::from(fuse_address.0));
ral::write_reg!(ocotp, self.ocotp, READ_CTRL, READ_FUSE: 1);
Poll::Ready(Ok(()))
}
fn start_fuse_write(
&mut self,
fuse_address: FuseAddress,
fuse_value: u32,
) -> Poll<Result<(), Error>> {
task::ready!(self.check_status())?;
ral::modify_reg!(ocotp, self.ocotp, CTRL, ADDR: u32::from(fuse_address.0), WR_UNLOCK: WRITE_UNLOCK);
ral::write_reg!(ocotp, self.ocotp, DATA, fuse_value);
Poll::Ready(Ok(()))
}
fn poll_fuse_read(&mut self) -> Poll<Result<u32, Error>> {
task::ready!(self.check_status())?;
let fuse_data = self.read_fuse_data();
self.check_end_fuse_read()?;
Poll::Ready(Ok(fuse_data))
}
fn poll_fuse_write(&mut self) -> Poll<Result<(), Error>> {
task::ready!(self.check_status())?;
self.check_end_fuse_write()?;
Poll::Ready(Ok(()))
}
pub fn blocking_fuse_read(&mut self, fuse_address: FuseAddress) -> Result<u32, Error> {
crate::spin_on(self.spin_fuse_read(fuse_address))
}
pub fn blocking_fuse_write(
&mut self,
fuse_address: FuseAddress,
fuse_value: u32,
) -> Result<(), Error> {
crate::spin_on(self.spin_fuse_write(fuse_address, fuse_value))
}
pub async fn spin_fuse_read(&mut self, fuse_address: FuseAddress) -> Result<u32, Error> {
core::future::poll_fn(|_| self.start_fuse_read(fuse_address)).await?;
let fuse_data = core::future::poll_fn(|_| self.poll_fuse_read()).await?;
Ok(fuse_data)
}
pub async fn spin_fuse_write(
&mut self,
fuse_address: FuseAddress,
fuse_value: u32,
) -> Result<(), Error> {
core::future::poll_fn(|_| self.start_fuse_write(fuse_address, fuse_value)).await?;
core::future::poll_fn(|_| self.poll_fuse_write()).await?;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct FuseAddress(u16);
impl FuseAddress {
pub const fn new(addr: u16) -> Option<Self> {
let Some(addr) = addr.checked_sub(Ocotp::FUSE_ADDRESS_OFFSET) else {
return None;
};
if addr % 16 != 0 {
return None;
}
Some(Self(addr / 16))
}
pub const fn raw(&self) -> u16 {
self.0
}
}