opentitanlib/io/
spi.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::Result;
6use clap::Args;
7use serde::{Deserialize, Serialize};
8use std::rc::Rc;
9use std::str::FromStr;
10use thiserror::Error;
11
12use super::{eeprom, gpio};
13use crate::app::TransportWrapper;
14use crate::impl_serializable_error;
15use crate::transport::TransportError;
16use crate::util::voltage::Voltage;
17
18#[derive(Clone, Debug, Args, Serialize, Deserialize)]
19pub struct SpiParams {
20    /// SPI instance.
21    #[arg(long)]
22    pub bus: Option<String>,
23
24    /// SPI bus speed.
25    #[arg(long)]
26    pub speed: Option<u32>,
27
28    /// SPI chip select pin.
29    #[arg(long)]
30    pub chip_select: Option<String>,
31
32    /// SPI bus voltage.
33    #[arg(long)]
34    pub voltage: Option<Voltage>,
35
36    /// SPI polarity/phase mode.
37    #[arg(long)]
38    pub mode: Option<TransferMode>,
39}
40
41impl SpiParams {
42    pub fn create(
43        &self,
44        transport: &TransportWrapper,
45        default_instance: &str,
46    ) -> Result<Rc<dyn Target>> {
47        let spi = transport.spi(self.bus.as_deref().unwrap_or(default_instance))?;
48        if let Some(ref cs) = self.chip_select {
49            spi.set_pins(
50                None,
51                None,
52                None,
53                Some(&transport.gpio_pin(cs.as_str())?),
54                None,
55            )?;
56        }
57        if let Some(speed) = self.speed {
58            spi.set_max_speed(speed)?;
59        }
60        if let Some(voltage) = self.voltage {
61            spi.set_voltage(voltage)?;
62        }
63        if let Some(mode) = self.mode {
64            spi.set_transfer_mode(mode)?;
65        }
66        Ok(spi)
67    }
68}
69
70/// Errors related to the SPI interface and SPI transactions.
71#[derive(Error, Debug, Serialize, Deserialize)]
72pub enum SpiError {
73    #[error("Invalid option: {0}")]
74    InvalidOption(String),
75    #[error("Invalid word size: {0}")]
76    InvalidWordSize(u32),
77    #[error("Invalid speed: {0}")]
78    InvalidSpeed(u32),
79    #[error("Invalid data length: {0}")]
80    InvalidDataLength(usize),
81    #[error("Invalid data width: {0:?}")]
82    InvalidDataWidth(eeprom::DataWidth),
83    #[error("Double transfer rate not supported")]
84    InvalidDoubleTransferRate(),
85    #[error("Invalid number of dummy cycles: {0}")]
86    InvalidDummyCycles(u8),
87    #[error("Mismatched data length: {0} != {1}")]
88    MismatchedDataLength(usize, usize),
89    #[error("Invalid transfer mode: {0}")]
90    InvalidTransferMode(String),
91    #[error("Invalid SPI voltage: {0}")]
92    InvalidVoltage(Voltage),
93    #[error("Given pin does not support requested SPI function")]
94    InvalidPin,
95}
96impl_serializable_error!(SpiError);
97
98/// Represents the SPI transfer mode.
99/// See <https://en.wikipedia.org/wiki/Serial_Peripheral_Interface#Clock_polarity_and_phase>
100/// for details about SPI transfer modes.
101#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
102pub enum TransferMode {
103    /// `Mode0` is CPOL=0, CPHA=0.
104    Mode0,
105    /// `Mode1` is CPOL=0, CPHA=1.
106    Mode1,
107    /// `Mode2` is CPOL=1, CPHA=0.
108    Mode2,
109    /// `Mode3` is CPOL=1, CPHA=1.
110    Mode3,
111}
112
113impl FromStr for TransferMode {
114    type Err = SpiError;
115    fn from_str(s: &str) -> std::result::Result<TransferMode, Self::Err> {
116        match s {
117            "Mode0" | "mode0" | "0" => Ok(TransferMode::Mode0),
118            "Mode1" | "mode1" | "1" => Ok(TransferMode::Mode1),
119            "Mode2" | "mode2" | "2" => Ok(TransferMode::Mode2),
120            "Mode3" | "mode3" | "3" => Ok(TransferMode::Mode3),
121            _ => Err(SpiError::InvalidTransferMode(s.to_string())),
122        }
123    }
124}
125
126#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
127pub enum ClockPhase {
128    SampleLeading,
129    SampleTrailing,
130}
131
132#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
133pub enum ClockPolarity {
134    IdleLow,
135    IdleHigh,
136}
137
138impl TransferMode {
139    pub fn phase(&self) -> ClockPhase {
140        match self {
141            TransferMode::Mode0 | TransferMode::Mode2 => ClockPhase::SampleLeading,
142            TransferMode::Mode1 | TransferMode::Mode3 => ClockPhase::SampleTrailing,
143        }
144    }
145    pub fn polarity(&self) -> ClockPolarity {
146        match self {
147            TransferMode::Mode0 | TransferMode::Mode1 => ClockPolarity::IdleLow,
148            TransferMode::Mode2 | TransferMode::Mode3 => ClockPolarity::IdleHigh,
149        }
150    }
151}
152
153/// Represents maximum allowed read or write operation in bytes.
154#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
155pub struct MaxSizes {
156    pub read: usize,
157    pub write: usize,
158}
159
160/// Represents a SPI transfer.
161pub enum Transfer<'rd, 'wr> {
162    Read(&'rd mut [u8]),
163    Write(&'wr [u8]),
164    // Check supports_bidirectional_transfer before using this.
165    Both(&'wr [u8], &'rd mut [u8]),
166    // Repeatedly poll until device response with 0x01
167    TpmPoll,
168    // Wait for pulse on non-standard Google signal
169    GscReady,
170}
171
172/// A trait which represents a SPI Target.
173pub trait Target {
174    /// Gets the current SPI transfer mode.
175    fn get_transfer_mode(&self) -> Result<TransferMode>;
176    /// Sets the current SPI transfer mode.
177    fn set_transfer_mode(&self, mode: TransferMode) -> Result<()>;
178
179    /// Gets the current number of bits per word.
180    fn get_bits_per_word(&self) -> Result<u32>;
181    /// Sets the current number of bits per word.
182    fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()>;
183
184    /// Gets the maximum allowed speed of the SPI bus.
185    fn get_max_speed(&self) -> Result<u32>;
186    /// Sets the maximum allowed speed of the SPI bus.
187    fn set_max_speed(&self, max_speed: u32) -> Result<()>;
188
189    /// Indicates whether `Transfer::Both()` is supported.
190    fn supports_bidirectional_transfer(&self) -> Result<bool>;
191
192    /// Indicates whether `Transfer::TpmPoll` is supported.
193    fn supports_tpm_poll(&self) -> Result<bool>;
194
195    /// Sets which pins should be used for SPI communication.  `None` value means use the same pin
196    /// as previously, or the implementation default if never before specified.  This call is not
197    /// supported by most backend transports, and ones that do support it may still have
198    /// restrictions on which set of pins can be used in which roles.
199    fn set_pins(
200        &self,
201        _serial_clock: Option<&Rc<dyn gpio::GpioPin>>,
202        _host_out_device_in: Option<&Rc<dyn gpio::GpioPin>>,
203        _host_in_device_out: Option<&Rc<dyn gpio::GpioPin>>,
204        _chip_select: Option<&Rc<dyn gpio::GpioPin>>,
205        _gsc_ready: Option<&Rc<dyn gpio::GpioPin>>,
206    ) -> Result<()> {
207        Err(SpiError::InvalidPin.into())
208    }
209
210    /// Returns the maximum number of transfers allowed in a single transaction.
211    fn get_max_transfer_count(&self) -> Result<usize>;
212
213    /// Maximum `Read` and `Write` data size for `run_transaction()`.
214    fn get_max_transfer_sizes(&self) -> Result<MaxSizes>;
215
216    fn set_voltage(&self, _voltage: Voltage) -> Result<()> {
217        Err(SpiError::InvalidOption("This target does not support set_voltage".to_string()).into())
218    }
219
220    /// Returns `"raiden_debug_spi:serial=XXX"` or similar string usable for passing via `-p`
221    /// argument to `flashrom`, in order for it to connect to this SPI port instance.
222    fn get_flashrom_programmer(&self) -> Result<String> {
223        Err(TransportError::UnsupportedOperation.into())
224    }
225
226    /// Runs a SPI transaction composed from the slice of [`Transfer`] objects.  Will assert the
227    /// CS for the duration of the entire transactions.
228    fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()>;
229
230    /// Maximum payload size of `Read` and `Write` elements for `run_eeprom_transactions()`.
231    fn get_eeprom_max_transfer_sizes(&self) -> Result<MaxSizes> {
232        // By default, go by the low-level SPI limits, allowing for 6 bytes of opcode+address+dummy
233        let spi_max = self.get_max_transfer_sizes()?;
234        Ok(MaxSizes {
235            read: spi_max.read,
236            write: spi_max.write - 6,
237        })
238    }
239
240    /// Runs a number of EEPROM/FLASH protocol SPI transactions.  Will assert and deassert CS for
241    /// each transaction.
242    fn run_eeprom_transactions(&self, transactions: &mut [eeprom::Transaction]) -> Result<()> {
243        eeprom::default_run_eeprom_transactions(self, transactions)
244    }
245
246    /// Assert the CS signal.  Uses reference counting, will be deasserted when each and every
247    /// returned `AssertChipSelect` object have gone out of scope.
248    fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect>;
249}
250
251/// Object that keeps the CS asserted, deasserting when it goes out of scope, (unless another
252/// instance keeps CS asserted longer.)
253pub struct AssertChipSelect {
254    target: Rc<dyn TargetChipDeassert>,
255}
256
257impl AssertChipSelect {
258    // Needs to be public in order for implementation of `Target` to be able to call it.  Never
259    // called by users of `Target`.
260    pub fn new(target: Rc<dyn TargetChipDeassert>) -> Self {
261        Self { target }
262    }
263}
264
265impl Drop for AssertChipSelect {
266    fn drop(&mut self) {
267        self.target.deassert_cs()
268    }
269}
270
271// Needs to be public in order for implementation of `Target` to be able to implement it.  Never
272// called by users of `Target`.
273pub trait TargetChipDeassert {
274    fn deassert_cs(&self);
275}