opentitanlib/io/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 clap::ValueEnum;
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9use thiserror::Error;
10
11use crate::impl_serializable_error;
12use crate::transport::TransportError;
13
14/// Errors related to the GPIO interface.
15#[derive(Debug, Error, Serialize, Deserialize)]
16pub enum GpioError {
17 #[error("Invalid pin name {0}")]
18 InvalidPinName(String),
19 #[error("Invalid pin number {0}")]
20 InvalidPinNumber(u8),
21 /// The current mode of the pin (input) does not support the requested operation (set
22 /// level).
23 #[error("Invalid mode for pin {0}")]
24 InvalidPinMode(u8),
25 /// The hardware does not support the requested mode (open drain, pull down input, etc.)
26 #[error("Unsupported mode {0} requested")]
27 UnsupportedPinMode(PinMode),
28 /// The hardware does not support the requested mode (open drain, pull down input, etc.)
29 #[error("Unsupported pull mode {0} requested")]
30 UnsupportedPullMode(PullMode),
31 #[error("Conflicting pin configurations for pin {0}: host:{1}, target:{2}")]
32 PinModeConflict(String, String, String),
33 #[error("Conflicting pin logic values for pin {0}: host:{1}, target:{2}")]
34 PinValueConflict(String, String, String),
35 #[error("Undefined pin logic value for pin {0}")]
36 PinValueUndefined(String),
37 #[error("Unsupported voltage {0}V requested")]
38 UnsupportedPinVoltage(f32),
39 #[error("Unsupported number of pins: {0}")]
40 UnsupportedNumberOfPins(usize),
41 #[error("Mismatched bitbang data length: {0} != {1}")]
42 MismatchedDataLength(usize, usize),
43 #[error("Bitbang data beyond the {0} least significant bits")]
44 InvalidBitbangData(usize),
45 #[error(
46 "Bitbang delay of zero, immediately preceding `await()`, or at end of sequence, not permitted"
47 )]
48 InvalidBitbangDelay,
49 #[error("Dac-bang samples not a multiple of number of pins")]
50 InvalidDacBangData,
51 #[error(
52 "Dac-bang delay of zero or immediately adjacent to linear(), or at end of sequence, not permitted"
53 )]
54 InvalidDacBangDelay,
55 #[error("Generic error: {0}")]
56 Generic(String),
57}
58impl_serializable_error!(GpioError);
59
60/// Mode of I/O pins.
61#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, ValueEnum)]
62#[value(rename_all = "verbatim")]
63pub enum PinMode {
64 Input,
65 PushPull,
66 OpenDrain,
67 AnalogInput,
68 AnalogOutput,
69 Alternate, // Pin used for UART/SPI/I2C or something else
70}
71
72impl std::fmt::Display for PinMode {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 std::fmt::Debug::fmt(self, f)
75 }
76}
77
78/// Mode of weak pull (relevant in Input and OpenDrain modes).
79#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, ValueEnum)]
80#[value(rename_all = "verbatim")]
81pub enum PullMode {
82 None,
83 PullUp,
84 PullDown,
85}
86
87impl std::fmt::Display for PullMode {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 std::fmt::Debug::fmt(self, f)
90 }
91}
92
93/// A trait which represents a single GPIO pin.
94pub trait GpioPin {
95 /// Reads the value of the GPIO pin.
96 fn read(&self) -> Result<bool>;
97
98 /// Sets the value of the GPIO pin to `value`.
99 fn write(&self, value: bool) -> Result<()>;
100
101 /// Sets the mode of the GPIO pin as input, output, or open drain I/O.
102 fn set_mode(&self, mode: PinMode) -> Result<()>;
103
104 /// Sets the weak pull resistors of the GPIO pin.
105 fn set_pull_mode(&self, mode: PullMode) -> Result<()>;
106
107 /// Reads the analog value of the GPIO pin in Volts. `AnalogInput` mode disables digital
108 /// circuitry for better results, but this method may also work in other modes.
109 fn analog_read(&self) -> Result<f32> {
110 Err(TransportError::UnsupportedOperation.into())
111 }
112
113 /// Sets the analog value of the GPIO pin to `value` Volts, must be in `AnalogOutput` mode.
114 fn analog_write(&self, _volts: f32) -> Result<()> {
115 Err(TransportError::UnsupportedOperation.into())
116 }
117
118 /// Simultaneously sets mode, value, and weak pull, some transports may guarantee atomicity.
119 fn set(
120 &self,
121 mode: Option<PinMode>,
122 value: Option<bool>,
123 pull: Option<PullMode>,
124 analog_value: Option<f32>,
125 ) -> Result<()> {
126 // Transports must override this function for truly atomic behavior. Default
127 // implementation below applies each setting separately.
128 if let Some(mode) = mode {
129 self.set_mode(mode)?;
130 }
131 if let Some(pull) = pull {
132 self.set_pull_mode(pull)?;
133 }
134 if let Some(value) = value {
135 self.write(value)?;
136 }
137 if let Some(analog_value) = analog_value {
138 self.analog_write(analog_value)?;
139 }
140 Ok(())
141 }
142
143 /// Not meant for API clients, this method returns the pin name as it is known to the
144 /// transport (which may have been through one or more alias mappings from the name provided
145 /// by the API client.) This method is used by implementations of `GpioMonitoring`.
146 fn get_internal_pin_name(&self) -> Option<&str> {
147 None
148 }
149}
150
151#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
152pub enum Edge {
153 Rising,
154 Falling,
155}
156
157#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
158pub enum ClockNature {
159 /// Unix time can be computed as (t + offset) / resolution, where t is a 64-bit timestamp
160 /// value from `MonitoringEvent`.
161 Wallclock {
162 /// If resolution is microseconds, `resolution` will be 1_000_000.
163 resolution: u64,
164 /// Offset relative to Unix epoch, measured according to above resolution.
165 offset: Option<u64>,
166 },
167 /// The 64-bit timestamp values could be emulator clock counts, or some other measure that
168 /// increases monotonically, but not necessarily uniformly in relation to wall clock time.
169 Unspecified,
170}
171
172/// Represents an edge detected on the GPIO pin.
173#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
174pub struct MonitoringEvent {
175 /// Identification of the signal that had an event, in the form of an index into the array
176 /// originally passed to `monitoring_read()`.
177 pub signal_index: u8,
178 /// Rising or falling edge
179 pub edge: Edge,
180 /// Timestamp of the edge, resolution and epoch is transport-specific, more information in
181 /// `ClockNature`.
182 pub timestamp: u64,
183}
184
185#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
186pub struct MonitoringStartResponse {
187 /// Transport timestamp at the time monitoring started.
188 pub timestamp: u64,
189 /// Initial logic level for each of the given pins.
190 pub initial_levels: Vec<bool>,
191}
192
193#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
194pub struct MonitoringReadResponse {
195 /// List of events having occurred since the start or the last read.
196 pub events: Vec<MonitoringEvent>,
197 /// All events at or before this timestamp are guaranteed to be included.
198 pub timestamp: u64,
199}
200
201/// A trait implemented by transports which support advanced edge-detection on GPIO pins. This
202/// trait allows monitoring a set of pins, and getting a stream of "events" (rising and falling
203/// edges with timestamps) for any change among the set.
204pub trait GpioMonitoring {
205 fn get_clock_nature(&self) -> Result<ClockNature>;
206
207 /// Set up edge trigger detection on the given set of pins, transport will buffer the list
208 /// internally, return the initial level of each of the given pins.
209 fn monitoring_start(&self, pins: &[&dyn GpioPin]) -> Result<MonitoringStartResponse>;
210
211 /// Retrieve list of events detected thus far, optionally stopping the possibly expensive edge
212 /// detection. Buffer overrun will be reported as an `Err`, and result in the stopping of the
213 /// edge detection irrespective of the parameter value.
214 fn monitoring_read(
215 &self,
216 pins: &[&dyn GpioPin],
217 continue_monitoring: bool,
218 ) -> Result<MonitoringReadResponse>;
219}
220
221/// Represents one entry in the specification of a bitbanging waveform. Pins must have been
222/// configured as either `PushPull`, `OpenDrain` or `Input` prior to requesting a bitbang
223/// operation.
224///
225/// `Write` and `Both` are somewhat equivalent to their namesakes in `spi::Transfer`, that is
226/// `Write` requests using the given data for generating waveforms on any of the pins configured
227/// as output, while disregarding the actual levels of the pins. `Both` requests generating a
228/// waveform, and also sampling every pin at each clock tick.
229pub enum BitbangEntry<'rd, 'wr> {
230 /// Represents a sequence of pin values. Bit 0 in each byte controls the first `GpioPin` in
231 /// the slice given to `GpioBitbanging::run()`, bit 1 controls the second `GpioPin`, and so
232 /// on. Each byte is applied to the pins in sequence, with a particular delay between each
233 /// given by the `clock_tick` argument to `run()`.
234 Write(&'wr [u8]),
235 /// Same as `Write`, but this `BitbangEntry` owns the data.
236 WriteOwned(Box<[u8]>),
237 /// Represents a sequence of pin values as above, but additionally captures the value of any
238 /// pins in `Input` or `OpenDrain` mode. At each clock tick, the input levels are sampled
239 /// just before the given output levels are applied, meaning that the data coming back will be
240 /// offset by one sample.
241 Both(&'wr [u8], &'rd mut [u8]),
242 /// Same as `Both`, but this `BitbangEntry` owns the data, which will be overwritten.
243 BothOwned(Box<[u8]>),
244 /// Represents a delay of the given number of clock ticks in which the output levels are held
245 /// as indicated by the last byte of the preceding `Write`/`Both` entry.
246 ///
247 /// A delay of zero is invalid. A delay of one tick is equivalent to not specifying any
248 /// `Delay` between two `Write` blocks, which is also equivalent to concatenating the two into
249 /// a single `Write` block.
250 Delay(u32),
251 /// Similar to `Delay`, but waits until `(pin_values ^ pattern) & mask` equals zero, that is,
252 /// until the set of pins indicated by ones in `mask` all have the the value indicated in
253 /// `pattern`.
254 Await { mask: u8, pattern: u8 },
255}
256
257pub enum DacBangEntry<'wr> {
258 /// Represents a sequence of volt values. If `N` pin is being driven, then samples are
259 /// interleaved such that at first, the first `N` values are applied, one to each of the pins,
260 /// at next tick the next `N` values are applied, and so on, with a particular delay between
261 /// each tick given by the `clock_tick` argument to `run()`.
262 Write(&'wr [f32]),
263 /// Same as `Write`, but this `DacBangEntry` owns the data.
264 WriteOwned(Box<[f32]>),
265 /// Represents a delay of the given number of clock ticks in which the output levels are held
266 /// as indicated by the last values of the preceding `Write` entry.
267 ///
268 /// A delay of zero is invalid. A delay of one tick is equivalent to not specifying any
269 /// `Delay` between two `Write` blocks, which is also equivalent to concatenating the two into
270 /// a single `Write` block.
271 Delay(u32),
272 /// Represents a time span of the given number of clock ticks during which the voltage
273 /// linearly transitions from the previous to the subsequent value.
274 ///
275 /// A value of zero is invalid. A value of one tick is equivalent to not specifying any delay
276 /// between two `Write` blocks. A value of two will result in a single intermediate sample
277 /// halfway between the two voltages. In general, a value of N will result in N-1
278 /// intermediate samples being inserted at this point.
279 Linear(u32),
280}
281
282/// A trait implemented by transports which support synchronous bit-banging on GPIO pins, similar
283/// to FTDI devices. This trait allows generation of arbitrary waveforms on a set of pins, and
284/// optionally getting back samples from same or other pins, taken at precise times.
285pub trait GpioBitbanging {
286 /// Apply the given sequence of values to the given set of GPIO pins, by each tick of a clock
287 /// with the given period. This function does not change the mode of any pins, they must
288 /// already be put into `PushPull`, `OpenDrain` or `Input` mode as appropriate. (In the
289 /// latter case, the specified waveform data does not matter, as the pin is not driving, and
290 /// would be included in the set of pins only in order to have it sampled at each clock tick.)
291 /// Returns a `GpioBitbangOperation` which must be continuously polled, to know when the
292 /// waveform is complete.
293 fn start<'a>(
294 &self,
295 pins: &[&dyn GpioPin],
296 clock_tick: Duration,
297 waveform: Box<[BitbangEntry<'a, 'a>]>,
298 ) -> Result<Box<dyn GpioBitbangOperation<'a, 'a> + 'a>>;
299
300 /// Convenience method which starts the bitbanging operation, and blocks until it is complete.
301 fn run<'a>(
302 &self,
303 pins: &[&dyn GpioPin],
304 clock_tick: Duration,
305 waveform: Box<[BitbangEntry<'a, 'a>]>,
306 ) -> Result<Box<[BitbangEntry<'a, 'a>]>> {
307 let mut bitbang_op = self.start(pins, clock_tick, waveform)?;
308 while !bitbang_op.query()? {}
309 bitbang_op.get_result()
310 }
311
312 /// Apply given sequence of voltage values to the given set of pins assumed to already be in
313 /// AnalogOutput mode.
314 fn dac_start(
315 &self,
316 pins: &[&dyn GpioPin],
317 clock_tick: Duration,
318 waveform: Box<[DacBangEntry]>,
319 ) -> Result<Box<dyn GpioDacBangOperation>>;
320
321 /// Convenience method which starts the DAC-banging operation, and blocks until it is
322 /// complete.
323 fn dac_run(
324 &self,
325 pins: &[&dyn GpioPin],
326 clock_tick: Duration,
327 waveform: Box<[DacBangEntry]>,
328 ) -> Result<()> {
329 let mut dacbang_op = self.dac_start(pins, clock_tick, waveform)?;
330 while !dacbang_op.query()? {}
331 Ok(())
332 }
333}
334
335/// Object representing an ongoing operation of generating a prescribed waveform on a number of
336/// pins. Must be frequently polled via the `query()` function, in order to guerantee completion
337/// of the operation.
338pub trait GpioBitbangOperation<'rd, 'wr> {
339 /// Returns `true` when done, `false` means to call it again.
340 fn query(&mut self) -> Result<bool>;
341
342 /// After `query()` has returned `true`, this method can be called to get back the array of
343 /// `BitbangEntry` originally given, this is particularly useful if there are `WriteOwned` or
344 /// `BothOwned` among the entries of the array.
345 fn get_result(self: Box<Self>) -> Result<Box<[BitbangEntry<'rd, 'wr>]>>;
346}
347
348pub trait GpioDacBangOperation {
349 /// Returns `true` when done, `false` means to call it again.
350 fn query(&mut self) -> Result<bool>;
351}