blob: e669ac474a2355e5e380e06325588484288079cc [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 std::rc::Rc;
use std::str::FromStr;
use structopt::StructOpt;
use thiserror::Error;
use super::eeprom;
use crate::app::TransportWrapper;
use crate::impl_serializable_error;
use crate::util::voltage::Voltage;
#[derive(Clone, Debug, StructOpt, Serialize, Deserialize)]
pub struct SpiParams {
#[structopt(long, help = "SPI instance")]
pub bus: Option<String>,
#[structopt(long, help = "SPI bus speed")]
pub speed: Option<u32>,
#[structopt(long, help = "SPI bus voltage", parse(try_from_str = Voltage::from_str))]
pub voltage: Option<Voltage>,
#[structopt(long, help = "SPI polarity/phase mode", parse(try_from_str = TransferMode::from_str))]
pub mode: Option<TransferMode>,
}
impl SpiParams {
pub fn create(
&self,
transport: &TransportWrapper,
default_instance: &str,
) -> Result<Rc<dyn Target>> {
let spi = transport.spi(self.bus.as_deref().unwrap_or(default_instance))?;
if let Some(speed) = self.speed {
spi.set_max_speed(speed)?;
}
if let Some(voltage) = self.voltage {
spi.set_voltage(voltage)?;
}
if let Some(mode) = self.mode {
spi.set_transfer_mode(mode)?;
}
Ok(spi)
}
}
/// Errors related to the SPI interface and SPI transactions.
#[derive(Error, Debug, Serialize, Deserialize)]
pub enum SpiError {
#[error("Invalid option: {0}")]
InvalidOption(String),
#[error("Invalid word size: {0}")]
InvalidWordSize(u32),
#[error("Invalid speed: {0}")]
InvalidSpeed(u32),
#[error("Invalid data length: {0}")]
InvalidDataLength(usize),
#[error("Mismatched data length: {0} != {1}")]
MismatchedDataLength(usize, usize),
#[error("Invalid transfer mode: {0}")]
InvalidTransferMode(String),
}
impl_serializable_error!(SpiError);
/// Represents the SPI transfer mode.
/// See https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#Clock_polarity_and_phase
/// for details about SPI transfer modes.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum TransferMode {
/// `Mode0` is CPOL=0, CPHA=0.
Mode0,
/// `Mode1` is CPOL=0, CPHA=1.
Mode1,
/// `Mode2` is CPOL=1, CPHA=0.
Mode2,
/// `Mode3` is CPOL=1, CPHA=1.
Mode3,
}
impl FromStr for TransferMode {
type Err = SpiError;
fn from_str(s: &str) -> std::result::Result<TransferMode, Self::Err> {
match s {
"Mode0" | "mode0" | "0" => Ok(TransferMode::Mode0),
"Mode1" | "mode1" | "1" => Ok(TransferMode::Mode1),
"Mode2" | "mode2" | "2" => Ok(TransferMode::Mode2),
"Mode3" | "mode3" | "3" => Ok(TransferMode::Mode3),
_ => Err(SpiError::InvalidTransferMode(s.to_string())),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ClockPhase {
SampleLeading,
SampleTrailing,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ClockPolarity {
IdleLow,
IdleHigh,
}
impl TransferMode {
pub fn phase(&self) -> ClockPhase {
match self {
TransferMode::Mode0 | TransferMode::Mode2 => ClockPhase::SampleLeading,
TransferMode::Mode1 | TransferMode::Mode3 => ClockPhase::SampleTrailing,
}
}
pub fn polarity(&self) -> ClockPolarity {
match self {
TransferMode::Mode0 | TransferMode::Mode1 => ClockPolarity::IdleLow,
TransferMode::Mode2 | TransferMode::Mode3 => ClockPolarity::IdleHigh,
}
}
}
/// Represents a SPI transfer.
pub enum Transfer<'rd, 'wr> {
Read(&'rd mut [u8]),
Write(&'wr [u8]),
Both(&'wr [u8], &'rd mut [u8]),
}
/// A trait which represents a SPI Target.
pub trait Target {
/// Gets the current SPI transfer mode.
fn get_transfer_mode(&self) -> Result<TransferMode>;
/// Sets the current SPI transfer mode.
fn set_transfer_mode(&self, mode: TransferMode) -> Result<()>;
/// Gets the current number of bits per word.
fn get_bits_per_word(&self) -> Result<u32>;
/// Sets the current number of bits per word.
fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()>;
/// Gets the maximum allowed speed of the SPI bus.
fn get_max_speed(&self) -> Result<u32>;
/// Sets the maximum allowed speed of the SPI bus.
fn set_max_speed(&self, max_speed: u32) -> Result<()>;
/// Returns the maximum number of transfers allowed in a single transaction.
fn get_max_transfer_count(&self) -> Result<usize>;
/// Maximum chunksize handled by this SPI device.
fn max_chunk_size(&self) -> Result<usize>;
fn set_voltage(&self, _voltage: Voltage) -> Result<()> {
Err(SpiError::InvalidOption("This target does not support set_voltage".to_string()).into())
}
/// Runs a SPI transaction composed from the slice of [`Transfer`] objects. Will assert the
/// CS for the duration of the entire transactions.
fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()>;
/// Runs a number of EEPROM/FLASH protocol SPI transactions. Will assert and deassert CS for
/// each transaction.
fn run_eeprom_transactions(&self, transactions: &mut [eeprom::Transaction]) -> Result<()> {
// Default implementation translates into generic SPI read/write, which works as long as
// the transport supports generic SPI transfers of sufficint length, and that the mode is
// single-data-wire.
for transfer in transactions {
match transfer {
eeprom::Transaction::Command(cmd) => {
self.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?)])?
}
eeprom::Transaction::Read(cmd, rbuf) => self.run_transaction(&mut [
Transfer::Write(cmd.to_bytes()?),
Transfer::Read(rbuf),
])?,
eeprom::Transaction::Write(cmd, wbuf) => self.run_transaction(&mut [
Transfer::Write(cmd.to_bytes()?),
Transfer::Write(wbuf),
])?,
eeprom::Transaction::WaitForBusyClear => {
while {
let mut buf = [0u8; 1];
self.run_transaction(&mut [
Transfer::Write(&[eeprom::READ_STATUS]),
Transfer::Read(&mut buf),
])?;
buf[0]
} & eeprom::STATUS_WIP
!= 0
{}
}
}
}
Ok(())
}
/// Assert the CS signal. Uses reference counting, will be deasserted when each and every
/// returned `AssertChipSelect` object have gone out of scope.
fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect>;
}
/// Object that keeps the CS asserted, deasserting when it goes out of scope, (unless another
/// instance keeps CS asserted longer.)
pub struct AssertChipSelect {
target: Rc<dyn TargetChipDeassert>,
}
impl AssertChipSelect {
// Needs to be public in order for implementation of `Target` to be able to call it. Never
// called by users of `Target`.
pub fn new(target: Rc<dyn TargetChipDeassert>) -> Self {
Self { target }
}
}
impl Drop for AssertChipSelect {
fn drop(&mut self) {
self.target.deassert_cs()
}
}
// Needs to be public in order for implementation of `Target` to be able to implement it. Never
// called by users of `Target`.
pub trait TargetChipDeassert {
fn deassert_cs(&self);
}