| // 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, |
| }; |
| } |