blob: 96c8522a91f773425369b8221e9b839aeeafcbef [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::{Context, Result};
use safe_ftdi as ftdi;
use std::cell::RefCell;
use std::rc::Rc;
use crate::io::spi::{
AssertChipSelect, ClockPolarity, SpiError, Target, TargetChipDeassert, Transfer, TransferMode,
};
use crate::transport::nexus::Nexus;
use crate::transport::ultradebug::mpsse;
struct Inner {
mode: TransferMode,
cs_asserted_count: u32,
}
/// Represents the Nexus SPI device.
pub struct NexusSpi {
pub device: Rc<RefCell<mpsse::Context>>,
inner: RefCell<Inner>,
}
impl NexusSpi {
pub const PIN_CLOCK: u8 = 0;
pub const PIN_MOSI: u8 = 1;
pub const PIN_MISO: u8 = 2;
pub const PIN_CHIP_SELECT: u8 = 3;
pub const MASK_CHIP_SELECT: u8 = 1u8 << Self::PIN_CHIP_SELECT;
pub const PIN_SPI_ZB: u8 = 4;
pub fn open(ultradebug: &Nexus) -> Result<Self> {
// let mpsse = ultradebug.mpsse(ftdi::Interface::B)?;
let mpsse = ultradebug.mpsse(ftdi::Interface::A)?;
// Note: platforms ultradebugs tristate their SPI lines
// unless SPI_ZB is driven low. Non-platforms ultradebugs
// don't use SPI_ZB, so this is safe for both types of devices.
// log::debug!("Setting SPI_ZB");
// mpsse
// .borrow_mut()
// .gpio_set(NexusSpi::PIN_SPI_ZB, false)
// .context("FTDI error f")?;
Ok(NexusSpi {
device: mpsse,
inner: RefCell::new(Inner {
mode: TransferMode::Mode0,
cs_asserted_count: 0,
}),
})
}
fn do_assert_cs(&self, assert: bool) -> Result<()> {
let device = self.device.borrow();
// Assert or deassert CS#
device
.execute(&mut [mpsse::Command::SetLowGpio(
device.gpio_direction,
if assert {
device.gpio_value & !Self::MASK_CHIP_SELECT
} else {
device.gpio_value | Self::MASK_CHIP_SELECT
},
)])
.context("FTDI error g")?;
Ok(())
}
}
impl Target for NexusSpi {
fn get_transfer_mode(&self) -> Result<TransferMode> {
Ok(self.inner.borrow().mode)
}
fn set_transfer_mode(&self, mode: TransferMode) -> Result<()> {
self.inner.borrow_mut().mode = mode;
Ok(())
}
fn get_bits_per_word(&self) -> Result<u32> {
Ok(8)
}
fn set_bits_per_word(&self, bits_per_word: u32) -> Result<()> {
match bits_per_word {
8 => Ok(()),
_ => Err(SpiError::InvalidWordSize(bits_per_word).into()),
}
}
fn get_max_speed(&self) -> Result<u32> {
Ok(self.device.borrow().max_clock_frequency)
}
fn set_max_speed(&self, frequency: u32) -> Result<()> {
let mut device = self.device.borrow_mut();
device
.set_clock_frequency(frequency)
.context("FTDI errorh ")?;
Ok(())
}
fn get_max_transfer_count(&self) -> Result<usize> {
// Arbitrary value: number of `Transfers` that can be in a single transaction.
Ok(42)
}
fn max_chunk_size(&self) -> Result<usize> {
// Size of the FTDI read buffer. We can't perform a read larger than this;
// the FTDI device simply won't read any more.
Ok(65536)
}
fn run_transaction(&self, transaction: &mut [Transfer]) -> Result<()> {
let (rdedge, wredge) = match self.inner.borrow().mode.polarity() {
ClockPolarity::IdleLow => (mpsse::ClockEdge::Rising, mpsse::ClockEdge::Falling),
ClockPolarity::IdleHigh => (mpsse::ClockEdge::Falling, mpsse::ClockEdge::Rising),
};
let mut command = Vec::new();
let device = self.device.borrow();
let cs_not_already_asserted = self.inner.borrow().cs_asserted_count == 0;
if cs_not_already_asserted {
// Assert CS# (drive low).
command.push(mpsse::Command::SetLowGpio(
device.gpio_direction,
device.gpio_value & !Self::MASK_CHIP_SELECT,
));
}
// Translate SPI Read/Write Transactions into MPSSE Commands.
for transfer in transaction.iter_mut() {
command.push(match transfer {
Transfer::Read(buf) => mpsse::Command::ReadData(
mpsse::DataShiftOptions {
read_clock_edge: rdedge,
read_data: true,
..Default::default()
},
buf,
),
Transfer::Write(buf) => mpsse::Command::WriteData(
mpsse::DataShiftOptions {
write_clock_edge: wredge,
write_data: true,
..Default::default()
},
buf,
),
Transfer::Both(wbuf, rbuf) => mpsse::Command::TransactData(
mpsse::DataShiftOptions {
write_clock_edge: wredge,
write_data: true,
..Default::default()
},
wbuf,
mpsse::DataShiftOptions {
read_clock_edge: rdedge,
read_data: true,
..Default::default()
},
rbuf,
),
});
}
if cs_not_already_asserted {
// Release CS# (allow to float high).
command.push(mpsse::Command::SetLowGpio(
device.gpio_direction,
device.gpio_value | Self::MASK_CHIP_SELECT,
));
}
device.execute(&mut command).context("FTDI error i")?;
Ok(())
}
fn assert_cs(self: Rc<Self>) -> Result<AssertChipSelect> {
{
let mut inner = self.inner.borrow_mut();
if inner.cs_asserted_count == 0 {
self.do_assert_cs(true)?;
}
inner.cs_asserted_count += 1;
}
Ok(AssertChipSelect::new(self))
}
}
impl TargetChipDeassert for NexusSpi {
fn deassert_cs(&self) {
let mut inner = self.inner.borrow_mut();
inner.cs_asserted_count -= 1;
if inner.cs_asserted_count == 0 {
// We cannot propagate errors through `Drop::drop()`, so panic on any error. (Logging
// would be another option.)
self.do_assert_cs(false)
.expect("Error while deasserting CS");
}
}
}