1use anyhow::Result;
6use serde::{Deserialize, Serialize};
7
8use super::spi::{SpiError, Target, Transfer};
9
10#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
11pub enum Switch {
13 Mode111,
14 Mode11N,
15 Mode1NN,
16 ModeNNN,
17}
18
19#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
20pub enum DataWidth {
21 Single, Dual, Quad,
24 Octo,
25}
26
27#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
28pub struct Cmd {
29 data: [u8; 8],
30 opcode_len: u8,
31 addr_len: u8,
32 mode: Mode,
33}
34
35#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
36pub enum AddressMode {
37 #[default]
38 Mode3b = 3,
39 Mode4b = 4,
40}
41
42#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
43pub struct Mode {
44 pub dummy_cycles: u8,
46 pub switch: Switch,
49 pub width: DataWidth,
51 pub double_transfer_rate: bool,
53}
54
55impl Mode {
56 pub fn cmd(&self, opcode: u8) -> Cmd {
58 let mut result = Cmd {
59 data: [0u8; 8],
60 opcode_len: 1,
61 addr_len: 0,
62 mode: *self,
63 };
64 result.data[0] = opcode;
65 result
66 }
67 pub fn cmd_addr(&self, opcode: u8, addr: u32, addr_mode: AddressMode) -> Cmd {
69 let mut result = Cmd {
70 data: [0u8; 8],
71 opcode_len: 1,
72 addr_len: addr_mode as u8,
73 mode: *self,
74 };
75 result.data[0] = opcode;
76 result.data[1..1 + result.addr_len as usize]
77 .clone_from_slice(&addr.to_be_bytes()[4 - result.addr_len as usize..]);
78 result
79 }
80 pub fn cmd2(&self, opcode1: u8, opcode2: u8) -> Cmd {
82 let mut result = Cmd {
83 data: [0u8; 8],
84 opcode_len: 2,
85 addr_len: 0,
86 mode: *self,
87 };
88 result.data[0] = opcode1;
89 result.data[1] = opcode2;
90 result
91 }
92 pub fn cmd2_addr(&self, opcode1: u8, opcode2: u8, addr: u32, addr_mode: AddressMode) -> Cmd {
94 let mut result = Cmd {
95 data: [0u8; 8],
96 opcode_len: 2,
97 addr_len: addr_mode as u8,
98 mode: *self,
99 };
100 result.data[0] = opcode1;
101 result.data[1] = opcode2;
102 result.data[2..2 + result.addr_len as usize]
103 .clone_from_slice(&addr.to_be_bytes()[4 - result.addr_len as usize..]);
104 result
105 }
106
107 pub fn dummy_cycles(&self, dummy_cycles: u8) -> Mode {
108 Mode {
109 dummy_cycles,
110 switch: self.switch,
111 width: self.width,
112 double_transfer_rate: self.double_transfer_rate,
113 }
114 }
115}
116
117pub const MODE_111: Mode = Mode {
119 dummy_cycles: 0,
120 switch: Switch::Mode111,
121 width: DataWidth::Single,
122 double_transfer_rate: false,
123};
124
125pub const MODE_1S1S1D: Mode = Mode {
127 dummy_cycles: 0,
128 switch: Switch::Mode11N,
129 width: DataWidth::Single,
130 double_transfer_rate: true,
131};
132
133pub const MODE_1S1D1D: Mode = Mode {
135 dummy_cycles: 0,
136 switch: Switch::Mode1NN,
137 width: DataWidth::Single,
138 double_transfer_rate: true,
139};
140
141pub const MODE_112: Mode = Mode {
143 dummy_cycles: 0,
144 switch: Switch::Mode11N,
145 width: DataWidth::Dual,
146 double_transfer_rate: false,
147};
148
149pub const MODE_122: Mode = Mode {
150 dummy_cycles: 0,
151 switch: Switch::Mode1NN,
152 width: DataWidth::Dual,
153 double_transfer_rate: false,
154};
155
156pub const MODE_222: Mode = Mode {
157 dummy_cycles: 0,
158 switch: Switch::ModeNNN,
159 width: DataWidth::Dual,
160 double_transfer_rate: false,
161};
162
163pub const MODE_114: Mode = Mode {
165 dummy_cycles: 0,
166 switch: Switch::Mode11N,
167 width: DataWidth::Quad,
168 double_transfer_rate: false,
169};
170
171pub const MODE_144: Mode = Mode {
172 dummy_cycles: 0,
173 switch: Switch::Mode1NN,
174 width: DataWidth::Quad,
175 double_transfer_rate: false,
176};
177
178pub const MODE_444: Mode = Mode {
179 dummy_cycles: 0,
180 switch: Switch::ModeNNN,
181 width: DataWidth::Quad,
182 double_transfer_rate: false,
183};
184
185impl Cmd {
186 pub fn to_bytes(&self) -> Result<&[u8]> {
190 if self.mode.switch == Switch::Mode111 && self.mode.dummy_cycles.is_multiple_of(8) {
191 Ok(&self.data
192 [0..(self.opcode_len + self.addr_len + self.mode.dummy_cycles / 8) as usize])
193 } else {
194 Err(SpiError::InvalidOption(
195 "This target does not support the requested mode".to_string(),
196 )
197 .into())
198 }
199 }
200
201 pub fn get_opcode_len(&self) -> u8 {
202 self.opcode_len
203 }
204
205 pub fn get_opcode(&self) -> &[u8] {
206 &self.data[..self.opcode_len as usize]
207 }
208
209 pub fn get_address_len(&self) -> u8 {
210 self.addr_len
211 }
212
213 pub fn get_address(&self) -> u32 {
214 let mut addr_bytes = [0u8; 4];
215 addr_bytes[4 - self.addr_len as usize..].clone_from_slice(
216 &self.data[self.opcode_len as usize..(self.opcode_len + self.addr_len) as usize],
217 );
218 u32::from_be_bytes(addr_bytes)
219 }
220
221 pub fn get_dummy_cycles(&self) -> u8 {
222 self.mode.dummy_cycles
223 }
224
225 pub fn get_switch(&self) -> Switch {
226 self.mode.switch
227 }
228
229 pub fn get_width(&self) -> DataWidth {
230 self.mode.width
231 }
232
233 pub fn get_double_transfer_rate(&self) -> bool {
234 self.mode.double_transfer_rate
235 }
236}
237
238pub enum Transaction<'rd, 'wr> {
239 Command(Cmd),
240 Read(Cmd, &'rd mut [u8]),
241 Write(Cmd, &'wr [u8]),
242 WaitForBusyClear,
243}
244
245pub const READ_STATUS: u8 = 0x05;
246pub const STATUS_WIP: u8 = 0x01;
247
248pub fn default_run_eeprom_transactions<T: Target + ?Sized>(
249 spi: &T,
250 transactions: &mut [Transaction],
251) -> Result<()> {
252 for transfer in transactions {
256 match transfer {
257 Transaction::Command(cmd) => {
258 spi.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?)])?
259 }
260 Transaction::Read(cmd, rbuf) => {
261 spi.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?), Transfer::Read(rbuf)])?
262 }
263 Transaction::Write(cmd, wbuf) => {
264 spi.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?), Transfer::Write(wbuf)])?
265 }
266 Transaction::WaitForBusyClear => {
267 let mut status = STATUS_WIP;
268 while status & STATUS_WIP != 0 {
269 spi.run_transaction(&mut [
270 Transfer::Write(&[READ_STATUS]),
271 Transfer::Read(std::slice::from_mut(&mut status)),
272 ])?;
273 }
274 }
275 }
276 }
277 Ok(())
278}