opentitanlib/transport/dediprog/
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::{Context, Result, bail, ensure};
6use std::cell::RefCell;
7use std::rc::Rc;
8
9use crate::io::eeprom;
10use crate::io::eeprom::Transaction::{Read, WaitForBusyClear, Write};
11use crate::io::spi::{
12    AssertChipSelect, ClockPolarity, MaxSizes, SpiError, Target, TargetChipDeassert, Transfer,
13    TransferMode,
14};
15use crate::spiflash::flash::SpiFlash;
16use crate::transport::dediprog::{ClockSpeed, Command, Inner};
17use crate::util::voltage::Voltage;
18
19pub struct DediprogSpi {
20    inner: Rc<RefCell<Inner>>,
21    max_chunk_size: usize,
22}
23
24#[repr(u8)]
25enum ReadMode {
26    ReadStandard = 1,
27    ReadFast = 2,
28    ReadAtmel45 = 3,
29    Read4bAddrFast = 4,
30    Read4bAddrFast0x0c = 5,
31}
32
33#[repr(u8)]
34enum WriteMode {
35    WritePageProgram = 1,
36    WritePageWrite = 2,
37    Write1bAai = 3,
38    Write2bAai = 4,
39    Write128bPage = 5,
40    WritePageAt26df041 = 6,
41    WriteSiliconBlueFpga = 7,
42    Write64bPageNumonyx = 8,
43    Write4bAddr256bPagePgm = 9,
44    Write32bPageMxic512k = 10,
45    Write4bAdr256bPagePgm0x12 = 11,
46    Write4bAdr256bPagePgmFlags = 12,
47}
48
49impl DediprogSpi {
50    const MAX_GENERIC_DATA_LEN: usize = 16;
51
52    const READ_CHUNK_SIZE: usize = 512;
53    const WRITE_CHUNK_SIZE: usize = 256;
54
55    pub fn open(inner: Rc<RefCell<Inner>>) -> Result<Self> {
56        let this = Self {
57            inner,
58            max_chunk_size: 65535 * 256,
59        };
60        this.set_spi_clock()?;
61        Ok(this)
62    }
63
64    fn set_spi_clock(&self) -> Result<()> {
65        self.inner.borrow().device.write_control(
66            rusb::request_type(
67                rusb::Direction::Out,
68                rusb::RequestType::Vendor,
69                rusb::Recipient::Endpoint,
70            ),
71            Command::SetSpiClk as u8,
72            self.inner.borrow().spi_clock as u16,
73            0,
74            &[],
75        )?;
76        Ok(())
77    }
78
79    fn read_sfdp(&self, mut address: u32, mut rbuf: &mut [u8]) -> Result<()> {
80        // Read 16 bytes at a time, returning a single result.
81        while !rbuf.is_empty() {
82            let chunk_size = std::cmp::min(rbuf.len(), Self::MAX_GENERIC_DATA_LEN);
83            let addr_bytes = address.to_be_bytes();
84            let sub_cmd: [u8; 5] = [
85                SpiFlash::READ_SFDP,
86                addr_bytes[1],
87                addr_bytes[2],
88                addr_bytes[3],
89                0,
90            ];
91            self.transmit(&sub_cmd, chunk_size)?;
92            self.receive(&mut rbuf[..chunk_size])?;
93            rbuf = &mut rbuf[chunk_size..];
94            address += chunk_size as u32;
95        }
96        Ok(())
97    }
98
99    fn transmit(&self, wbuf: &[u8], rbuf_len: usize) -> Result<()> {
100        self.inner.borrow().device.write_control(
101            rusb::request_type(
102                rusb::Direction::Out,
103                rusb::RequestType::Vendor,
104                rusb::Recipient::Endpoint,
105            ),
106            Command::Transceive as u8,
107            rbuf_len as u16,
108            0,
109            wbuf,
110        )?;
111        Ok(())
112    }
113
114    /// Receive data for a single SPI operation, using one or more USB packets.
115    fn receive(&self, rbuf: &mut [u8]) -> Result<()> {
116        self.inner.borrow().device.read_control(
117            rusb::request_type(
118                rusb::Direction::In,
119                rusb::RequestType::Vendor,
120                rusb::Recipient::Endpoint,
121            ),
122            Command::Transceive as u8,
123            0,
124            0,
125            rbuf,
126        )?;
127        Ok(())
128    }
129
130    fn eeprom_read_transaction(&self, cmd: &eeprom::Cmd, rbuf: &mut [u8]) -> Result<()> {
131        let mut usbcmd = [0u8; 10];
132        match (
133            cmd.get_opcode(),
134            cmd.get_address_len(),
135            cmd.get_dummy_cycles(),
136        ) {
137            ([SpiFlash::READ_SFDP], 3, 8) => {
138                return self.read_sfdp(cmd.get_address(), rbuf);
139            }
140            ([SpiFlash::READ], 3, 0) => {
141                // Standard read, 3-byte address
142                usbcmd[3] = ReadMode::ReadStandard as u8;
143                usbcmd[4] = SpiFlash::READ;
144            }
145            ([SpiFlash::READ], 4, 0) => {
146                // Standard read, 4-byte address.  Dediprog may not support 4 byte address on
147                // standard read, do 4b fast read instead.
148                usbcmd[3] = ReadMode::Read4bAddrFast as u8;
149                usbcmd[4] = SpiFlash::FAST_READ;
150            }
151            _ => bail!(SpiError::InvalidTransferMode(
152                "Command not supported".to_string()
153            )),
154        }
155
156        ensure!(
157            rbuf.len().is_multiple_of(Self::READ_CHUNK_SIZE),
158            SpiError::InvalidTransferMode(format!(
159                "Read length {} not multiple of {}",
160                rbuf.len(),
161                Self::READ_CHUNK_SIZE
162            ))
163        );
164
165        let chunks = (rbuf.len() / Self::READ_CHUNK_SIZE) as u16;
166
167        usbcmd[0..2].clone_from_slice(&chunks.to_le_bytes());
168        usbcmd[6..10].clone_from_slice(&cmd.get_address().to_le_bytes());
169        self.inner.borrow().device.write_control(
170            rusb::request_type(
171                rusb::Direction::Out,
172                rusb::RequestType::Vendor,
173                rusb::Recipient::Endpoint,
174            ),
175            Command::Read as u8,
176            0,
177            0,
178            &usbcmd,
179        )?;
180        for chunk in rbuf.chunks_exact_mut(Self::READ_CHUNK_SIZE) {
181            self.inner
182                .borrow()
183                .device
184                .read_bulk(self.inner.borrow().in_endpoint, chunk)?;
185        }
186        Ok(())
187    }
188
189    fn eeprom_write_transaction(
190        &self,
191        cmd: &eeprom::Cmd,
192        wbuf: &[u8],
193        _wait_for_busy_clear: bool,
194    ) -> Result<()> {
195        ensure!(
196            wbuf.len().is_multiple_of(Self::WRITE_CHUNK_SIZE),
197            SpiError::InvalidTransferMode(format!(
198                "Write length {} not multiple of {}",
199                wbuf.len(),
200                Self::WRITE_CHUNK_SIZE
201            ))
202        );
203
204        let chunks = (wbuf.len() / Self::WRITE_CHUNK_SIZE) as u16;
205
206        let mut usbcmd = [0u8; 10];
207        usbcmd[0..2].clone_from_slice(&chunks.to_le_bytes());
208
209        match (
210            cmd.get_opcode(),
211            cmd.get_address_len(),
212            cmd.get_dummy_cycles(),
213        ) {
214            ([SpiFlash::PAGE_PROGRAM], 3, 0) => {
215                usbcmd[3] = WriteMode::WritePageProgram as u8;
216                usbcmd[4] = SpiFlash::PAGE_PROGRAM;
217            }
218            ([SpiFlash::PAGE_PROGRAM], 4, 0) => {
219                usbcmd[3] = WriteMode::Write4bAddr256bPagePgm as u8;
220                usbcmd[4] = SpiFlash::PAGE_PROGRAM;
221            }
222            _ => bail!(SpiError::InvalidTransferMode(
223                "Command not supported".to_string()
224            )),
225        }
226        usbcmd[6..10].clone_from_slice(&cmd.get_address().to_le_bytes());
227        self.inner.borrow().device.write_control(
228            rusb::request_type(
229                rusb::Direction::Out,
230                rusb::RequestType::Vendor,
231                rusb::Recipient::Endpoint,
232            ),
233            Command::Write as u8,
234            0,
235            0,
236            &usbcmd,
237        )?;
238        for chunk in wbuf.chunks_exact(Self::WRITE_CHUNK_SIZE) {
239            let mut buf = [0xFFu8; 512];
240            buf[0..Self::WRITE_CHUNK_SIZE].clone_from_slice(chunk);
241            self.inner
242                .borrow()
243                .device
244                .write_bulk(self.inner.borrow().out_endpoint, &buf)?;
245        }
246        Ok(())
247    }
248
249    fn do_run_eeprom_transactions(
250        &self,
251        mut transactions: &mut [eeprom::Transaction],
252    ) -> Result<()> {
253        loop {
254            match transactions {
255                [
256                    eeprom::Transaction::Command(pre_cmd),
257                    Write(cmd, wbuf),
258                    WaitForBusyClear,
259                    rest @ ..,
260                ] => {
261                    transactions = rest;
262                    if pre_cmd.get_opcode() == [SpiFlash::WRITE_ENABLE] {
263                        // Write enable is done by eeprom_write_transaction()
264                    } else {
265                        self.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?)])?
266                    }
267                    self.eeprom_write_transaction(cmd, wbuf, true)?;
268                }
269                [eeprom::Transaction::Command(cmd), rest @ ..] => {
270                    transactions = rest;
271                    self.run_transaction(&mut [Transfer::Write(cmd.to_bytes()?)])?
272                }
273                [Read(cmd, rbuf), rest @ ..] => {
274                    transactions = rest;
275                    self.eeprom_read_transaction(cmd, rbuf)?;
276                }
277                [Write(cmd, wbuf), WaitForBusyClear, rest @ ..] => {
278                    transactions = rest;
279                    self.eeprom_write_transaction(cmd, wbuf, true)?;
280                }
281                [Write(cmd, wbuf), rest @ ..] => {
282                    transactions = rest;
283                    self.eeprom_write_transaction(cmd, wbuf, false)?;
284                }
285                [WaitForBusyClear, rest @ ..] => {
286                    transactions = rest;
287                    let mut status = eeprom::STATUS_WIP;
288                    while status & eeprom::STATUS_WIP != 0 {
289                        self.run_transaction(&mut [
290                            Transfer::Write(&[eeprom::READ_STATUS]),
291                            Transfer::Read(std::slice::from_mut(&mut status)),
292                        ])?;
293                    }
294                }
295                [] => return Ok(()),
296            }
297        }
298    }
299}
300
301impl Target for DediprogSpi {
302    fn get_transfer_mode(&self) -> Result<TransferMode> {
303        unimplemented!();
304    }
305    fn set_transfer_mode(&self, _mode: TransferMode) -> Result<()> {
306        unimplemented!();
307    }
308
309    fn get_bits_per_word(&self) -> Result<u32> {
310        Ok(8)
311    }
312    fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()> {
313        match bits_per_word {
314            8 => Ok(()),
315            _ => Err(SpiError::InvalidWordSize(bits_per_word).into()),
316        }
317    }
318
319    fn get_max_speed(&self) -> Result<u32> {
320        Ok(match self.inner.borrow().spi_clock {
321            ClockSpeed::Clk24Mhz => 24_000_000,
322            ClockSpeed::Clk12Mhz => 12_000_000,
323            ClockSpeed::Clk8Mhz => 8_000_000,
324            ClockSpeed::Clk3Mhz => 3_000_000,
325            ClockSpeed::Clk2p18Mhz => 2_180_000,
326            ClockSpeed::Clk1p5Mhz => 1_500_000,
327            ClockSpeed::Clk750Khz => 750_000,
328            ClockSpeed::Clk375Khz => 375_000,
329        })
330    }
331    fn set_max_speed(&self, frequency: u32) -> Result<()> {
332        self.inner.borrow_mut().spi_clock = if frequency >= 24_000_000 {
333            ClockSpeed::Clk24Mhz
334        } else if frequency >= 12_000_000 {
335            ClockSpeed::Clk12Mhz
336        } else if frequency >= 8_000_000 {
337            ClockSpeed::Clk8Mhz
338        } else if frequency >= 3_000_000 {
339            ClockSpeed::Clk3Mhz
340        } else if frequency >= 2_180_000 {
341            ClockSpeed::Clk2p18Mhz
342        } else if frequency >= 1_500_000 {
343            ClockSpeed::Clk1p5Mhz
344        } else if frequency >= 750_000 {
345            ClockSpeed::Clk750Khz
346        } else {
347            ClockSpeed::Clk375Khz
348        };
349        self.set_spi_clock()
350    }
351
352    fn supports_bidirectional_transfer(&self) -> Result<bool> {
353        Ok(false)
354    }
355
356    fn supports_tpm_poll(&self) -> Result<bool> {
357        Ok(false)
358    }
359
360    fn get_max_transfer_count(&self) -> Result<usize> {
361        // Arbitrary value: number of `Transfers` that can be in a single transaction.
362        Ok(42)
363    }
364
365    /// Maximum `Read` and `Write` data size for `run_transaction()`.
366    fn get_max_transfer_sizes(&self) -> Result<MaxSizes> {
367        Ok(MaxSizes {
368            read: Self::MAX_GENERIC_DATA_LEN,
369            write: Self::MAX_GENERIC_DATA_LEN,
370        })
371    }
372
373    /// Sets the Dediprog voltage to `value` Volts.
374    fn set_voltage(&self, voltage: Voltage) -> Result<()> {
375        let mut inner = self.inner.borrow_mut();
376        inner.voltage = if voltage.as_volts() <= 0.3 {
377            super::Voltage::V0
378        } else if voltage.as_volts() >= 1.6 && voltage.as_volts() <= 2.0 {
379            super::Voltage::V1p8
380        } else if voltage.as_volts() >= 2.3 && voltage.as_volts() <= 2.7 {
381            super::Voltage::V2p5
382        } else if voltage.as_volts() >= 3.0 && voltage.as_volts() <= 3.6 {
383            super::Voltage::V3p5
384        } else {
385            bail!(SpiError::InvalidVoltage(voltage))
386        };
387        inner.set_voltage()
388    }
389
390    fn get_flashrom_programmer(&self) -> Result<String> {
391        let inner = self.inner.borrow();
392        let voltage = match inner.voltage {
393            super::Voltage::V0 => "0V",
394            super::Voltage::V1p8 => "1.8V",
395            super::Voltage::V2p5 => "2.5V",
396            super::Voltage::V3p5 => "3.5V",
397        };
398        let spispeed = match inner.spi_clock {
399            ClockSpeed::Clk24Mhz => "24M",
400            ClockSpeed::Clk12Mhz => "12M",
401            ClockSpeed::Clk8Mhz => "8M",
402            ClockSpeed::Clk3Mhz => "3M",
403            ClockSpeed::Clk2p18Mhz => "2.18M",
404            ClockSpeed::Clk1p5Mhz => "1.5M",
405            ClockSpeed::Clk750Khz => "750k",
406            ClockSpeed::Clk375Khz => "375k",
407        };
408        Ok(format!("dediprog:voltage={voltage},spispeed={spispeed}"))
409    }
410
411    /// Dediprog has limited support for "plain" SPI transactions.  It can only hold the CS
412    /// asserted across a write then optional read, both of at most 16 bytes.
413    fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()> {
414        match transaction {
415            [] => (),
416            [Transfer::Write(wbuf), Transfer::Read(rbuf)] => {
417                // Dediprog can do a short SPI write followed by a short SPI read as a single
418                // USB request/reply.
419                ensure!(
420                    wbuf.len() <= Self::MAX_GENERIC_DATA_LEN,
421                    SpiError::InvalidDataLength(wbuf.len())
422                );
423                ensure!(
424                    rbuf.len() <= Self::MAX_GENERIC_DATA_LEN,
425                    SpiError::InvalidDataLength(rbuf.len())
426                );
427                self.transmit(wbuf, rbuf.len())?;
428                self.receive(rbuf)?;
429            }
430            [Transfer::Write(wbuf)] => {
431                ensure!(
432                    wbuf.len() <= Self::MAX_GENERIC_DATA_LEN,
433                    SpiError::InvalidDataLength(wbuf.len())
434                );
435                self.transmit(wbuf, 0)?;
436            }
437            _ => bail!(SpiError::InvalidTransferMode(
438                "Unsupported combination".to_string()
439            )),
440        }
441        Ok(())
442    }
443
444    /// Maximum payload size of `Read` and `Write` elements for `run_eeprom_transactions()`.
445    fn get_eeprom_max_transfer_sizes(&self) -> Result<MaxSizes> {
446        Ok(MaxSizes {
447            read: self.max_chunk_size,
448            write: self.max_chunk_size,
449        })
450    }
451
452    fn run_eeprom_transactions(&self, transactions: &mut [eeprom::Transaction]) -> Result<()> {
453        self.do_run_eeprom_transactions(transactions)
454    }
455
456    fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect> {
457        unimplemented!();
458    }
459}