opentitanlib/io/
eeprom.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 serde::{Deserialize, Serialize};
7
8use super::spi::{SpiError, Target, Transfer};
9use crate::spiflash::SpiFlash;
10
11#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
12/// Declarations of if and when to switch from single-lane SPI to a faster mode.
13pub enum Switch {
14    Mode111,
15    Mode11N,
16    Mode1NN,
17    ModeNNN,
18}
19
20#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
21pub enum DataWidth {
22    Single, // Standard SPI
23    Dual,   // Use both COPI and CIPO for data
24    Quad,
25    Octo,
26}
27
28#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
29pub struct Cmd {
30    data: [u8; 8],
31    opcode_len: u8,
32    addr_len: u8,
33    mode: Mode,
34}
35
36#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
37pub enum AddressMode {
38    #[default]
39    Mode3b = 3,
40    Mode4b = 4,
41}
42
43#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
44pub struct Mode {
45    /// The number of no-operation clock cycles between address and data phases.
46    pub dummy_cycles: u8,
47    /// Declarations of if and when to switch from single-lane SPI to a faster mode as declared by
48    /// `width` and `double_transfer_rate`.
49    pub switch: Switch,
50    /// How many lanes to use after the switch (1, 2, 4, or 8).
51    pub width: DataWidth,
52    /// Whether to shift data on both rising and falling clock edges after the switch.
53    pub double_transfer_rate: bool,
54}
55
56impl Mode {
57    /// One-byte opcode, without address
58    pub fn cmd(&self, opcode: u8) -> Cmd {
59        let mut result = Cmd {
60            data: [0u8; 8],
61            opcode_len: 1,
62            addr_len: 0,
63            mode: *self,
64        };
65        result.data[0] = opcode;
66        result
67    }
68    /// One-byte opcode, with address
69    pub fn cmd_addr(&self, opcode: u8, addr: u32, addr_mode: AddressMode) -> Cmd {
70        let mut result = Cmd {
71            data: [0u8; 8],
72            opcode_len: 1,
73            addr_len: addr_mode as u8,
74            mode: *self,
75        };
76        result.data[0] = opcode;
77        result.data[1..1 + result.addr_len as usize]
78            .clone_from_slice(&addr.to_be_bytes()[4 - result.addr_len as usize..]);
79        result
80    }
81    /// Two-byte opcode, without address
82    pub fn cmd2(&self, opcode1: u8, opcode2: u8) -> Cmd {
83        let mut result = Cmd {
84            data: [0u8; 8],
85            opcode_len: 2,
86            addr_len: 0,
87            mode: *self,
88        };
89        result.data[0] = opcode1;
90        result.data[1] = opcode2;
91        result
92    }
93    /// Two-byte opcode, with address
94    pub fn cmd2_addr(&self, opcode1: u8, opcode2: u8, addr: u32, addr_mode: AddressMode) -> Cmd {
95        let mut result = Cmd {
96            data: [0u8; 8],
97            opcode_len: 2,
98            addr_len: addr_mode as u8,
99            mode: *self,
100        };
101        result.data[0] = opcode1;
102        result.data[1] = opcode2;
103        result.data[2..2 + result.addr_len as usize]
104            .clone_from_slice(&addr.to_be_bytes()[4 - result.addr_len as usize..]);
105        result
106    }
107
108    pub fn dummy_cycles(&self, dummy_cycles: u8) -> Mode {
109        Mode {
110            dummy_cycles,
111            switch: self.switch,
112            width: self.width,
113            double_transfer_rate: self.double_transfer_rate,
114        }
115    }
116}
117
118/// Single-wire
119pub const MODE_111: Mode = Mode {
120    dummy_cycles: 0,
121    switch: Switch::Mode111,
122    width: DataWidth::Single,
123    double_transfer_rate: false,
124};
125
126/// Double transfer rate on data phase
127pub const MODE_1S1S1D: Mode = Mode {
128    dummy_cycles: 0,
129    switch: Switch::Mode11N,
130    width: DataWidth::Single,
131    double_transfer_rate: true,
132};
133
134/// Double transfer rate on address and data phase
135pub const MODE_1S1D1D: Mode = Mode {
136    dummy_cycles: 0,
137    switch: Switch::Mode1NN,
138    width: DataWidth::Single,
139    double_transfer_rate: true,
140};
141
142/// Single-wire address, dual-wire data
143pub const MODE_112: Mode = Mode {
144    dummy_cycles: 0,
145    switch: Switch::Mode11N,
146    width: DataWidth::Dual,
147    double_transfer_rate: false,
148};
149
150pub const MODE_122: Mode = Mode {
151    dummy_cycles: 0,
152    switch: Switch::Mode1NN,
153    width: DataWidth::Dual,
154    double_transfer_rate: false,
155};
156
157pub const MODE_222: Mode = Mode {
158    dummy_cycles: 0,
159    switch: Switch::ModeNNN,
160    width: DataWidth::Dual,
161    double_transfer_rate: false,
162};
163
164/// Single-wire address, quad-wire data
165pub const MODE_114: Mode = Mode {
166    dummy_cycles: 0,
167    switch: Switch::Mode11N,
168    width: DataWidth::Quad,
169    double_transfer_rate: false,
170};
171
172pub const MODE_144: Mode = Mode {
173    dummy_cycles: 0,
174    switch: Switch::Mode1NN,
175    width: DataWidth::Quad,
176    double_transfer_rate: false,
177};
178
179pub const MODE_444: Mode = Mode {
180    dummy_cycles: 0,
181    switch: Switch::ModeNNN,
182    width: DataWidth::Quad,
183    double_transfer_rate: false,
184};
185
186impl Cmd {
187    /// Method use to get binary representation of the command for use on "plain" SPI.  Will be
188    /// used in cases where the transport backend does not have specialied EEPROM/Flash
189    /// communication primitives.
190    pub fn to_bytes(&self) -> Result<&[u8]> {
191        if self.mode.switch == Switch::Mode111 && self.mode.dummy_cycles.is_multiple_of(8) {
192            Ok(&self.data
193                [0..(self.opcode_len + self.addr_len + self.mode.dummy_cycles / 8) as usize])
194        } else {
195            Err(SpiError::InvalidOption(
196                "This target does not support the requested mode".to_string(),
197            )
198            .into())
199        }
200    }
201
202    pub fn get_opcode_len(&self) -> u8 {
203        self.opcode_len
204    }
205
206    pub fn get_opcode(&self) -> &[u8] {
207        &self.data[..self.opcode_len as usize]
208    }
209
210    pub fn get_address_len(&self) -> u8 {
211        self.addr_len
212    }
213
214    pub fn get_address(&self) -> u32 {
215        let mut addr_bytes = [0u8; 4];
216        addr_bytes[4 - self.addr_len as usize..].clone_from_slice(
217            &self.data[self.opcode_len as usize..(self.opcode_len + self.addr_len) as usize],
218        );
219        u32::from_be_bytes(addr_bytes)
220    }
221
222    pub fn get_dummy_cycles(&self) -> u8 {
223        self.mode.dummy_cycles
224    }
225
226    pub fn get_switch(&self) -> Switch {
227        self.mode.switch
228    }
229
230    pub fn get_width(&self) -> DataWidth {
231        self.mode.width
232    }
233
234    pub fn get_double_transfer_rate(&self) -> bool {
235        self.mode.double_transfer_rate
236    }
237}
238
239pub enum Transaction<'rd, 'wr> {
240    Command(Cmd),
241    Read(Cmd, &'rd mut [u8]),
242    Write(Cmd, &'wr [u8]),
243    WaitForBusyClear,
244}
245
246pub fn default_run_eeprom_transactions<T: Target + ?Sized>(
247    spi: &T,
248    transactions: &mut [Transaction],
249) -> Result<()> {
250    // Default implementation translates into generic SPI read/write, which works as long as
251    // the transport supports generic SPI transfers of sufficient length, and that the mode is
252    // single-data-wire.
253    for transfer in transactions {
254        match transfer {
255            Transaction::Command(cmd) => {
256                spi.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?)])?
257            }
258            Transaction::Read(cmd, rbuf) => {
259                spi.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?), Transfer::Read(rbuf)])?
260            }
261            Transaction::Write(cmd, wbuf) => {
262                spi.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?), Transfer::Write(wbuf)])?
263            }
264            Transaction::WaitForBusyClear => {
265                let mut status = SpiFlash::STATUS_WIP;
266                while status & SpiFlash::STATUS_WIP != 0 {
267                    spi.run_transaction(&mut [
268                        Transfer::Write(&[SpiFlash::READ_STATUS]),
269                        Transfer::Read(std::slice::from_mut(&mut status)),
270                    ])?;
271                }
272            }
273        }
274    }
275    Ok(())
276}