blob: 91350f81d894eea5f1fbe03568bed69f95d8fc8a [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::{ensure, Context, Result};
use lazy_static::lazy_static;
use safe_ftdi as ftdi;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use crate::collection;
use crate::io::gpio::{GpioError, GpioPin, PinMode, PullMode};
use crate::transport::nexus::Nexus;
use crate::transport::ultradebug::mpsse;
use crate::util::parse_int::ParseInt;
/// Represents the Nexus GPIO pins.
pub struct NexusGpio {
pub device: Rc<RefCell<mpsse::Context>>,
}
impl NexusGpio {
pub const PIN_SW_STRAP0: u8 = 4;
pub const PIN_SW_STRAP1: u8 = 5;
pub const PIN_SW_STRAP2: u8 = 6;
pub const PIN_RESET_B: u8 = 7;
const LAST_PIN_NUM: u8 = 7;
pub fn open(nexus: &Nexus) -> Result<Self> {
Ok(NexusGpio {
device: nexus.mpsse(ftdi::Interface::A)?,
})
}
pub fn pin(&self, pinname: &str) -> Result<NexusGpioPin> {
Ok(NexusGpioPin {
device: self.device.clone(),
pin_id: self.pin_name_to_number(pinname)?,
})
}
/// Given an nexus pin name, return its pin number.
pub fn pin_name_to_number(&self, pinname: &str) -> Result<u8> {
// If the pinname is an integer, use it; otherwise try to see if it
// is a symbolic name of a pin.
if let Ok(pinnum) = u8::from_str(pinname) {
ensure!(
pinnum <= NexusGpio::LAST_PIN_NUM,
GpioError::InvalidPinNumber(pinnum)
);
return Ok(pinnum);
}
let pinname = pinname.to_uppercase();
let pn = pinname.as_str();
PIN_NAMES
.get(pn)
.copied()
.ok_or_else(|| GpioError::InvalidPinName(pinname).into())
}
}
pub struct NexusGpioPin {
device: Rc<RefCell<mpsse::Context>>,
pin_id: u8,
}
impl GpioPin for NexusGpioPin {
/// Reads the value of the the GPIO pin `id`.
fn read(&self) -> Result<bool> {
let bits = self.device.borrow_mut().gpio_get().context("FTDI error")?;
Ok(bits & (1 << self.pin_id) != 0)
}
/// Sets the value of the GPIO pin `id` to `value`.
fn write(&self, value: bool) -> Result<()> {
self.device
.borrow_mut()
.gpio_set(self.pin_id, value)
.context("FTDI error")?;
Ok(())
}
/// Sets the `direction` of GPIO `id` as input or output.
fn set_mode(&self, mode: PinMode) -> Result<()> {
let direction = match mode {
PinMode::Input => false,
PinMode::PushPull => true,
PinMode::OpenDrain => return Err(GpioError::UnsupportedPinMode(mode).into()),
PinMode::AnalogInput |
PinMode::AnalogOutput |
PinMode::Alternate => todo!(),
};
self.device
.borrow_mut()
.gpio_set_direction(self.pin_id, direction)
.context("FTDI error")?;
Ok(())
}
fn set_pull_mode(&self, _mode: PullMode) -> Result<()> {
Ok(())
}
}
lazy_static! {
static ref PIN_NAMES: HashMap<&'static str, u8> = collection! {
"SW_STRAP0" => 4,
"SW_STRAP1" => 5,
"SW_STRAP2" => 6,
"RESET_B" => 7,
};
}