Skip to content

Commit

Permalink
[opentitanlib] Add gpio implementation for ftdi transport
Browse files Browse the repository at this point in the history
Signed-off-by: Douglas Reis <[email protected]>
  • Loading branch information
engdoreis committed Jan 7, 2025
1 parent ae4ad64 commit ad61976
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 1 deletion.
1 change: 1 addition & 0 deletions sw/host/opentitanlib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,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",
Expand Down
140 changes: 140 additions & 0 deletions sw/host/opentitanlib/src/transport/ftdi/gpio.rs
Original file line number Diff line number Diff line change
@@ -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<ftdi::Device>),
Input(ftdi_hal::InputPin<ftdi::Device>),
#[default]
None,
}

pub struct Pin {
pin: RefCell<PinType>,
ftdi: Rc<HashMap<ftdi::Interface, ftdi_hal::FtHal<ftdi::Device>>>,
pinname: String,
}

impl Pin {
pub fn open<C: Chip>(
ftdi: &Rc<HashMap<ftdi::Interface, ftdi_hal::FtHal<ftdi::Device>>>,
pinname: String,
) -> Result<Self> {
Ok(Self {
pin: RefCell::new(PinType::None),
ftdi: Rc::clone(ftdi),
pinname,
})
}

fn map_outpin(&self) -> Result<ftdi_hal::OutputPin<ftdi::Device>> {
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<ftdi_hal::InputPin<ftdi::Device>> {
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<bool> {
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)
}
}
13 changes: 12 additions & 1 deletion sw/host/opentitanlib/src/transport/ftdi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use chip::Chip;
use ftdi_embedded_hal as ftdi_hal;

pub mod chip;
pub mod gpio;

#[derive(Default)]
struct Inner {
Expand Down Expand Up @@ -101,7 +102,17 @@ impl<C: Chip> Transport for Ftdi<C> {
}

fn gpio_pin(&self, pinname: &str) -> Result<Rc<dyn GpioPin>> {
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::<C>(
&self.ftdi_interfaces,
pinname.to_string(),
)?));
Rc::clone(u)
}
Entry::Occupied(o) => Rc::clone(o.get()),
})
}

fn spi(&self, _instance: &str) -> Result<Rc<dyn Target>> {
Expand Down

0 comments on commit ad61976

Please sign in to comment.