opentitanlib/transport/ftdi/
gpio.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::Result;
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::rc::Rc;
9
10use embedded_hal::digital::{InputPin, OutputPin};
11use ftdi_embedded_hal as ftdi_hal;
12
13use crate::io::gpio::{GpioError, GpioPin, PinMode, PullMode};
14use crate::transport::ftdi::Chip;
15
16#[derive(Default)]
17enum PinType {
18    Output(ftdi_hal::OutputPin<ftdi::Device>),
19    Input(ftdi_hal::InputPin<ftdi::Device>),
20    #[default]
21    None,
22}
23
24pub struct Pin {
25    pin: RefCell<PinType>,
26    ftdi: Rc<HashMap<ftdi::Interface, ftdi_hal::FtHal<ftdi::Device>>>,
27    pinname: String,
28}
29
30impl Pin {
31    pub fn open<C: Chip>(
32        ftdi: &Rc<HashMap<ftdi::Interface, ftdi_hal::FtHal<ftdi::Device>>>,
33        pinname: String,
34    ) -> Result<Self> {
35        Ok(Self {
36            pin: RefCell::new(PinType::None),
37            ftdi: Rc::clone(ftdi),
38            pinname,
39        })
40    }
41
42    fn map_outpin(&self) -> Result<ftdi_hal::OutputPin<ftdi::Device>> {
43        let pinname = self.pinname.to_lowercase();
44        let interface = match &pinname[0..1] {
45            "a" => ftdi::Interface::A,
46            "b" => ftdi::Interface::B,
47            "c" => ftdi::Interface::C,
48            "d" => ftdi::Interface::D,
49            &_ => return Err(GpioError::InvalidPinName(pinname).into()),
50        };
51        let hal = self
52            .ftdi
53            .get(&interface)
54            .ok_or_else(|| GpioError::InvalidPinName(pinname.clone()))?;
55
56        let pin = match &pinname[1..] {
57            "dbus0" => hal.ad0(),
58            "dbus1" => hal.ad1(),
59            "dbus2" => hal.ad2(),
60            "dbus3" => hal.ad3(),
61            "dbus4" => hal.ad4(),
62            "dbus5" => hal.ad5(),
63            "dbus6" => hal.ad6(),
64            "dbus7" => hal.ad7(),
65            _ => return Err(GpioError::InvalidPinName(self.pinname.to_owned()).into()),
66        }?;
67        Ok(pin)
68    }
69
70    fn map_inpin(&self) -> Result<ftdi_hal::InputPin<ftdi::Device>> {
71        let pinname = self.pinname.to_lowercase();
72        let interface = match &pinname[0..1] {
73            "a" => ftdi::Interface::A,
74            "b" => ftdi::Interface::B,
75            "c" => ftdi::Interface::C,
76            "d" => ftdi::Interface::D,
77            &_ => return Err(GpioError::InvalidPinName(pinname).into()),
78        };
79        let hal = self
80            .ftdi
81            .get(&interface)
82            .ok_or_else(|| GpioError::InvalidPinName(pinname.clone()))?;
83
84        let pin = match &pinname[1..] {
85            "dbus0" => hal.adi0(),
86            "dbus1" => hal.adi1(),
87            "dbus2" => hal.adi2(),
88            "dbus3" => hal.adi3(),
89            "dbus4" => hal.adi4(),
90            "dbus5" => hal.adi5(),
91            "dbus6" => hal.adi6(),
92            "dbus7" => hal.adi7(),
93            _ => return Err(GpioError::InvalidPinName(self.pinname.to_owned()).into()),
94        }?;
95        Ok(pin)
96    }
97}
98
99impl GpioPin for Pin {
100    fn read(&self) -> Result<bool> {
101        let mut pin = self.pin.borrow_mut();
102        match *pin {
103            PinType::Input(ref mut pin) => Ok(pin.is_high()?),
104            _ => Err(GpioError::InvalidPinMode(0).into()),
105        }
106    }
107
108    fn write(&self, value: bool) -> Result<()> {
109        let mut pin = self.pin.borrow_mut();
110        if let PinType::Output(ref mut pin) = *pin {
111            if value {
112                pin.set_high()?;
113            } else {
114                pin.set_low()?;
115            }
116            return Ok(());
117        };
118        Err(GpioError::InvalidPinMode(0).into())
119    }
120
121    fn set_mode(&self, mode: PinMode) -> Result<()> {
122        let mut pin = self.pin.borrow_mut();
123        *pin = match (&*pin, mode) {
124            (PinType::None, PinMode::Input) => PinType::Input(self.map_inpin()?),
125            (PinType::None, PinMode::PushPull | PinMode::OpenDrain) => {
126                PinType::Output(self.map_outpin()?)
127            }
128            _ => return Ok(()),
129        };
130        Ok(())
131    }
132
133    fn set_pull_mode(&self, _mode: PullMode) -> Result<()> {
134        Ok(())
135    }
136
137    fn get_internal_pin_name(&self) -> Option<&str> {
138        Some(&self.pinname)
139    }
140}