From afcdffd11a9f77939cd7f57988e4f8a337773df2 Mon Sep 17 00:00:00 2001 From: Douglas Reis Date: Fri, 13 Dec 2024 15:30:26 +0000 Subject: [PATCH] [opentitanlib] Add gpio implementation for ftdi transport Signed-off-by: Douglas Reis (cherry picked from commit dcd643ba13ec84c32e84ec58a528fff0952cfbe6) --- sw/host/opentitanlib/BUILD | 1 + .../opentitanlib/src/transport/ftdi/gpio.rs | 140 ++++++++++++++++++ .../opentitanlib/src/transport/ftdi/mod.rs | 13 +- 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 sw/host/opentitanlib/src/transport/ftdi/gpio.rs diff --git a/sw/host/opentitanlib/BUILD b/sw/host/opentitanlib/BUILD index 6fdb5d268563c..5c75e006f8f41 100644 --- a/sw/host/opentitanlib/BUILD +++ b/sw/host/opentitanlib/BUILD @@ -185,6 +185,7 @@ rust_library( "src/transport/dediprog/spi.rs", "src/transport/errors.rs", "src/transport/ftdi/chip.rs", + "src/transport/ftdi/gpio.rs", "src/transport/ftdi/mod.rs", "src/transport/hyperdebug/c2d2.rs", "src/transport/hyperdebug/dfu.rs", diff --git a/sw/host/opentitanlib/src/transport/ftdi/gpio.rs b/sw/host/opentitanlib/src/transport/ftdi/gpio.rs new file mode 100644 index 0000000000000..d3b7b5546b278 --- /dev/null +++ b/sw/host/opentitanlib/src/transport/ftdi/gpio.rs @@ -0,0 +1,140 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +use embedded_hal::digital::{InputPin, OutputPin}; +use ftdi_embedded_hal as ftdi_hal; + +use crate::io::gpio::{GpioError, GpioPin, PinMode, PullMode}; +use crate::transport::ftdi::Chip; + +#[derive(Default)] +enum PinType { + Output(ftdi_hal::OutputPin), + Input(ftdi_hal::InputPin), + #[default] + None, +} + +pub struct Pin { + pin: RefCell, + ftdi: Rc>>, + pinname: String, +} + +impl Pin { + pub fn open( + ftdi: &Rc>>, + pinname: String, + ) -> Result { + Ok(Self { + pin: RefCell::new(PinType::None), + ftdi: Rc::clone(ftdi), + pinname, + }) + } + + fn map_outpin(&self) -> Result> { + let pinname = self.pinname.to_lowercase(); + let interface = match &pinname[0..1] { + "a" => ftdi::Interface::A, + "b" => ftdi::Interface::B, + "c" => ftdi::Interface::C, + "d" => ftdi::Interface::D, + &_ => return Err(GpioError::InvalidPinName(pinname).into()), + }; + let hal = self + .ftdi + .get(&interface) + .ok_or_else(|| GpioError::InvalidPinName(pinname.clone()))?; + + let pin = match &pinname[1..] { + "dbus0" => hal.ad0(), + "dbus1" => hal.ad1(), + "dbus2" => hal.ad2(), + "dbus3" => hal.ad3(), + "dbus4" => hal.ad4(), + "dbus5" => hal.ad5(), + "dbus6" => hal.ad6(), + "dbus7" => hal.ad7(), + _ => return Err(GpioError::InvalidPinName(self.pinname.to_owned()).into()), + }?; + Ok(pin) + } + + fn map_inpin(&self) -> Result> { + let pinname = self.pinname.to_lowercase(); + let interface = match &pinname[0..1] { + "a" => ftdi::Interface::A, + "b" => ftdi::Interface::B, + "c" => ftdi::Interface::C, + "d" => ftdi::Interface::D, + &_ => return Err(GpioError::InvalidPinName(pinname).into()), + }; + let hal = self + .ftdi + .get(&interface) + .ok_or_else(|| GpioError::InvalidPinName(pinname.clone()))?; + + let pin = match &pinname[1..] { + "dbus0" => hal.adi0(), + "dbus1" => hal.adi1(), + "dbus2" => hal.adi2(), + "dbus3" => hal.adi3(), + "dbus4" => hal.adi4(), + "dbus5" => hal.adi5(), + "dbus6" => hal.adi6(), + "dbus7" => hal.adi7(), + _ => return Err(GpioError::InvalidPinName(self.pinname.to_owned()).into()), + }?; + Ok(pin) + } +} + +impl GpioPin for Pin { + fn read(&self) -> Result { + let mut pin = self.pin.borrow_mut(); + match *pin { + PinType::Input(ref mut pin) => Ok(pin.is_high()?), + _ => Err(GpioError::InvalidPinMode(0).into()), + } + } + + fn write(&self, value: bool) -> Result<()> { + let mut pin = self.pin.borrow_mut(); + if let PinType::Output(ref mut pin) = *pin { + if value { + pin.set_high()?; + } else { + pin.set_low()?; + } + return Ok(()); + }; + Err(GpioError::InvalidPinMode(0).into()) + } + + fn set_mode(&self, mode: PinMode) -> Result<()> { + let mut pin = self.pin.borrow_mut(); + *pin = match (&*pin, mode) { + (PinType::None, PinMode::Input) => PinType::Input(self.map_inpin()?), + (PinType::None, PinMode::PushPull | PinMode::OpenDrain) => { + PinType::Output(self.map_outpin()?) + } + _ => return Ok(()), + }; + Ok(()) + } + + fn set_pull_mode(&self, _mode: PullMode) -> Result<()> { + Ok(()) + } + + fn get_internal_pin_name(&self) -> Option<&str> { + Some(&self.pinname) + } +} diff --git a/sw/host/opentitanlib/src/transport/ftdi/mod.rs b/sw/host/opentitanlib/src/transport/ftdi/mod.rs index 5097d34ce2626..3b881f80a7cba 100644 --- a/sw/host/opentitanlib/src/transport/ftdi/mod.rs +++ b/sw/host/opentitanlib/src/transport/ftdi/mod.rs @@ -25,6 +25,7 @@ use chip::Chip; use ftdi_embedded_hal as ftdi_hal; pub mod chip; +pub mod gpio; #[derive(Default)] struct Inner { @@ -101,7 +102,17 @@ impl Transport for Ftdi { } fn gpio_pin(&self, pinname: &str) -> Result> { - Err(TransportError::UnsupportedOperation.into()) + let mut inner = self.inner.borrow_mut(); + Ok(match inner.gpio.entry(pinname.to_string()) { + Entry::Vacant(v) => { + let u = v.insert(Rc::new(gpio::Pin::open::( + &self.ftdi_interfaces, + pinname.to_string(), + )?)); + Rc::clone(u) + } + Entry::Occupied(o) => Rc::clone(o.get()), + }) } fn spi(&self, _instance: &str) -> Result> {