blob: 39c926352aad88414e75b520217f4e47440ff1f4 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use anyhow::Result;
use serde::{Deserialize, Serialize};
use super::spi::SpiError;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum DataWidth {
Single, // Standard SPI
SingleDtr, // Data on both rising and falling edges
Dual, // Use both COPI and CIPO for data
DualDtr, // Both COPI and CIPO, both clock edges
Quad,
QuadDtr,
Octo,
OctoDtr,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Cmd {
data: [u8; 8],
opcode_len: u8,
opcode_width: DataWidth,
addr_len: u8,
addr_width: DataWidth,
dummy_cycles: u8,
data_width: DataWidth,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddressMode {
Mode3b = 3,
Mode4b = 4,
}
impl Default for AddressMode {
fn default() -> Self {
AddressMode::Mode3b
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Mode {
pub opcode_width: DataWidth,
pub addr_width: DataWidth,
pub dummy_cycles: u8,
pub data_width: DataWidth,
}
impl Mode {
/// One-byte opcode, without address
pub fn cmd(&self, opcode: u8) -> Cmd {
let mut result = Cmd {
data: [0u8; 8],
opcode_len: 1,
opcode_width: self.opcode_width,
addr_len: 0,
addr_width: self.addr_width,
dummy_cycles: self.dummy_cycles,
data_width: self.data_width,
};
result.data[0] = opcode;
result
}
/// One-byte opcode, with address
pub fn cmd_addr(&self, opcode: u8, addr: u32, addr_mode: AddressMode) -> Cmd {
let mut result = Cmd {
data: [0u8; 8],
opcode_len: 1,
opcode_width: self.opcode_width,
addr_len: addr_mode as u8,
addr_width: self.addr_width,
dummy_cycles: self.dummy_cycles,
data_width: self.data_width,
};
result.data[0] = opcode;
result.data[1..1 + result.addr_len as usize]
.clone_from_slice(&addr.to_be_bytes()[4 - result.addr_len as usize..]);
result
}
/// Two-byte opcode, without address
pub fn cmd2(&self, opcode1: u8, opcode2: u8) -> Cmd {
let mut result = Cmd {
data: [0u8; 8],
opcode_len: 2,
opcode_width: self.opcode_width,
addr_len: 0,
addr_width: self.addr_width,
dummy_cycles: self.dummy_cycles,
data_width: self.data_width,
};
result.data[0] = opcode1;
result.data[1] = opcode2;
result
}
/// Two-byte opcode, with address
pub fn cmd2_addr(&self, opcode1: u8, opcode2: u8, addr: u32, addr_mode: AddressMode) -> Cmd {
let mut result = Cmd {
data: [0u8; 8],
opcode_len: 2,
opcode_width: self.opcode_width,
addr_len: addr_mode as u8,
addr_width: self.addr_width,
dummy_cycles: self.dummy_cycles,
data_width: self.data_width,
};
result.data[0] = opcode1;
result.data[1] = opcode2;
result.data[2..2 + result.addr_len as usize]
.clone_from_slice(&addr.to_be_bytes()[4 - result.addr_len as usize..]);
result
}
pub fn dummy_cycles(&self, dummy_cycles: u8) -> Mode {
Mode {
opcode_width: self.opcode_width,
addr_width: self.addr_width,
dummy_cycles,
data_width: self.data_width,
}
}
}
/// Single-wire
pub const MODE_111: Mode = Mode {
opcode_width: DataWidth::Single,
addr_width: DataWidth::Single,
dummy_cycles: 0,
data_width: DataWidth::Single,
};
/// Single-wire address, dual-wire data
pub const MODE_112: Mode = Mode {
opcode_width: DataWidth::Single,
addr_width: DataWidth::Single,
dummy_cycles: 0,
data_width: DataWidth::Dual,
};
pub const MODE_122: Mode = Mode {
opcode_width: DataWidth::Single,
addr_width: DataWidth::Dual,
dummy_cycles: 0,
data_width: DataWidth::Dual,
};
pub const MODE_222: Mode = Mode {
opcode_width: DataWidth::Dual,
addr_width: DataWidth::Dual,
dummy_cycles: 0,
data_width: DataWidth::Dual,
};
/// Single-wire address, quad-wire data
pub const MODE_114: Mode = Mode {
opcode_width: DataWidth::Single,
addr_width: DataWidth::Single,
dummy_cycles: 0,
data_width: DataWidth::Quad,
};
pub const MODE_144: Mode = Mode {
opcode_width: DataWidth::Single,
addr_width: DataWidth::Quad,
dummy_cycles: 0,
data_width: DataWidth::Quad,
};
pub const MODE_444: Mode = Mode {
opcode_width: DataWidth::Quad,
addr_width: DataWidth::Quad,
dummy_cycles: 0,
data_width: DataWidth::Quad,
};
impl Cmd {
/// Method use to get binary representation of the command for use on "plain" SPI. Will be
/// used in cases where the transport backend does not have specialied EEPROM/Flash
/// communication primitives.
pub fn to_bytes(&self) -> Result<&[u8]> {
if self.opcode_width == DataWidth::Single
&& self.addr_width == DataWidth::Single
&& self.dummy_cycles % 8 == 0
&& self.data_width == DataWidth::Single
{
Ok(&self.data[0..(self.opcode_len + self.addr_len + self.dummy_cycles / 8) as usize])
} else {
Err(SpiError::InvalidOption(
"This target does not support the requested mode".to_string(),
)
.into())
}
}
pub fn get_opcode_len(&self) -> u8 {
self.opcode_len
}
pub fn get_opcode_width(&self) -> DataWidth {
self.opcode_width
}
pub fn get_opcode(&self) -> &[u8] {
&self.data[..self.opcode_len as usize]
}
pub fn get_address_len(&self) -> u8 {
self.addr_len
}
pub fn get_address_width(&self) -> DataWidth {
self.addr_width
}
pub fn get_address(&self) -> u32 {
let mut addr_bytes = [0u8; 4];
addr_bytes[4 - self.addr_len as usize..].clone_from_slice(
&self.data[self.opcode_len as usize..(self.opcode_len + self.addr_len) as usize],
);
u32::from_be_bytes(addr_bytes)
}
pub fn get_dummy_cycles(&self) -> u8 {
self.dummy_cycles
}
pub fn get_data_width(&self) -> DataWidth {
self.data_width
}
}
pub enum Transaction<'rd, 'wr> {
Command(Cmd),
Read(Cmd, &'rd mut [u8]),
Write(Cmd, &'wr [u8]),
WaitForBusyClear,
}
pub const READ_STATUS: u8 = 0x05;
pub const STATUS_WIP: u8 = 0x01;