blob: 92edacf1757350dc8f03e049285dc405f3740a90 [file] [log] [blame]
use core::cell::Cell;
use core::ops::{Index, IndexMut};
use kernel::common::cells::{OptionalCell, TakeCell};
use kernel::hil;
pub static mut TXBUFFER: [u8; PAGE_SIZE + 5] = [0; PAGE_SIZE + 5];
pub static mut RXBUFFER: [u8; PAGE_SIZE + 5] = [0; PAGE_SIZE + 5];
const PAGE_SIZE: usize = 512;
pub struct NexusSpiflashPage(pub [u8; PAGE_SIZE]);
impl Default for NexusSpiflashPage {
fn default() -> Self {
Self { 0: [0; PAGE_SIZE] }
}
}
impl Index<usize> for NexusSpiflashPage {
type Output = u8;
fn index(&self, idx: usize) -> &u8 {
&self.0[idx]
}
}
impl IndexMut<usize> for NexusSpiflashPage {
fn index_mut(&mut self, idx: usize) -> &mut u8 {
&mut self.0[idx]
}
}
impl AsMut<[u8]> for NexusSpiflashPage {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
enum Opcodes {
Read = 0x03,
}
#[derive(Clone, Copy, PartialEq)]
enum State {
Idle,
WriteOpcode,
ReadPage { offset: usize },
}
pub struct NexusSpiflash<'a, S: hil::spi::SpiMasterDevice + 'a> {
spi: &'a S,
state: Cell<State>,
txbuffer: TakeCell<'static, [u8]>,
rxbuffer: TakeCell<'static, [u8]>,
client: OptionalCell<&'a dyn hil::flash::Client<NexusSpiflash<'a, S>>>,
client_page: TakeCell<'static, NexusSpiflashPage>,
}
impl<'a, S: hil::spi::SpiMasterDevice + 'a> NexusSpiflash<'a, S> {
pub fn new(
spi: &'a S,
txbuffer: &'static mut [u8],
rxbuffer: &'static mut [u8],
) -> NexusSpiflash<'a, S> {
NexusSpiflash {
spi: spi,
state: Cell::new(State::Idle),
txbuffer: TakeCell::new(txbuffer),
rxbuffer: TakeCell::new(rxbuffer),
client: OptionalCell::empty(),
client_page: TakeCell::empty(),
}
}
}
impl<'a, S: hil::spi::SpiMasterDevice + 'a, C: hil::flash::Client<Self>>
hil::flash::HasClient<'a, C> for NexusSpiflash<'a, S>
{
fn set_client(&'a self, client: &'a C) {
self.client.set(client);
}
}
impl<'a, S: hil::spi::SpiMasterDevice + 'a> hil::flash::Flash for NexusSpiflash<'a, S> {
type Page = NexusSpiflashPage;
fn read_page(
&self,
page: usize,
read_buffer: &'static mut <Self as kernel::hil::flash::Flash>::Page,
) -> core::result::Result<
(),
(
kernel::ReturnCode,
&'static mut <Self as kernel::hil::flash::Flash>::Page,
),
> {
self.txbuffer.take().map(|txbuffer| {
self.rxbuffer.take().map(move |rxbuffer| {
txbuffer[0] = Opcodes::Read as u8;
// TODO(atv): See if we can make 4-byte addressing work. Otherwise limited to 2**24 bytes.
let byte = page * PAGE_SIZE;
txbuffer[1] = ((byte >> 16) & 0xFF) as u8;
txbuffer[2] = ((byte >> 08) & 0xFF) as u8;
txbuffer[3] = ((byte >> 00) & 0xFF) as u8;
self.state.set(State::WriteOpcode);
self.spi.hold_low();
self.spi
.read_write_bytes(txbuffer, Some(rxbuffer), 4 as usize);
});
});
self.client_page.replace(read_buffer);
Ok(())
}
fn write_page(
&self,
_: usize,
_: &'static mut <Self as kernel::hil::flash::Flash>::Page,
) -> core::result::Result<
(),
(
kernel::ReturnCode,
&'static mut <Self as kernel::hil::flash::Flash>::Page,
),
> {
todo!()
}
fn erase_page(&self, _: usize) -> kernel::ReturnCode {
todo!()
}
}
impl<'a, S: hil::spi::SpiMasterDevice + 'a> hil::time::AlarmClient for NexusSpiflash<'a, S> {
fn alarm(&self) {
todo!()
}
}
impl<'a, S: hil::spi::SpiMasterDevice + 'a> hil::spi::SpiMasterClient for NexusSpiflash<'a, S> {
fn read_write_done(
&self,
write_buffer: &'static mut [u8],
read_buffer: core::option::Option<&'static mut [u8]>,
_len: usize,
) {
match self.state.get() {
State::WriteOpcode => {
read_buffer.map(move |read_buffer| {
self.state.set(State::ReadPage { offset: 0 });
self.spi
.read_write_bytes(write_buffer, Some(read_buffer), PAGE_SIZE / 2);
});
}
State::ReadPage { offset } => {
read_buffer.map(move |read_buffer| {
self.client_page.map(|page| {
for i in 0..(PAGE_SIZE / 2) {
page[i + offset] = read_buffer[i];
}
});
let next_offset = offset + (PAGE_SIZE / 2);
if next_offset == PAGE_SIZE {
self.state.set(State::Idle);
self.client_page.take().map(|page| {
self.client.map(move |client| {
self.txbuffer.replace(read_buffer);
self.rxbuffer.replace(write_buffer);
client.read_complete(page, hil::flash::Error::CommandComplete);
});
});
} else {
self.state.set(State::ReadPage {
offset: next_offset,
});
// release_low will cause our SPI host to release CS after the read/write.
if next_offset + (PAGE_SIZE / 2) == PAGE_SIZE {
self.spi.release_low();
}
self.spi
.read_write_bytes(write_buffer, Some(read_buffer), PAGE_SIZE / 2);
}
});
}
State::Idle => {
panic!();
}
}
}
}