opentitanlib/transport/hyperdebug/
servo_micro.rs1use anyhow::{Result, bail};
6use std::rc::Rc;
7
8use crate::io::gpio::{GpioPin, PinMode, PullMode};
9use crate::transport::hyperdebug::{Flavor, Inner, StandardFlavor, VID_GOOGLE};
10use crate::transport::{TransportError, TransportInterfaceType};
11
12pub struct ServoMicroFlavor;
18
19impl ServoMicroFlavor {
20 const PID_SERVO_MICRO: u16 = 0x501A;
21}
22
23impl Flavor for ServoMicroFlavor {
24 fn gpio_pin(inner: &Rc<Inner>, pinname: &str) -> Result<Rc<dyn GpioPin>> {
25 if pinname == "IO_EXP_16" {
26 return Ok(Rc::new(ServoMicroResetPin::open(inner)?));
27 }
28 StandardFlavor::gpio_pin(inner, pinname)
29 }
30
31 fn spi_index(_inner: &Rc<Inner>, instance: &str) -> Result<(u8, u8)> {
32 bail!(TransportError::InvalidInstance(
33 TransportInterfaceType::Spi,
34 instance.to_string()
35 ))
36 }
37
38 fn get_default_usb_vid() -> u16 {
39 VID_GOOGLE
40 }
41
42 fn get_default_usb_pid() -> u16 {
43 Self::PID_SERVO_MICRO
44 }
45 fn perform_initial_fw_check() -> bool {
46 false
48 }
49}
50
51#[derive(Copy, Clone)]
53#[repr(u8)]
54enum IoExpanderRegister {
55 Input1 = 1,
57 Output1 = 3,
60 FloatPin1 = 7,
64}
65
66pub struct ServoMicroResetPin {
68 inner: Rc<Inner>,
69}
70
71impl ServoMicroResetPin {
72 const RESET_PIN_MASK: u8 = 1 << 6;
74
75 pub fn open(inner: &Rc<Inner>) -> Result<Self> {
76 Ok(Self {
77 inner: Rc::clone(inner),
78 })
79 }
80
81 fn read_reg(&self, reg: IoExpanderRegister) -> Result<u8> {
83 let cmd = format!("i2cxfer r 0 0x20 {}", reg as u8);
84 let line = self
85 .inner
86 .cmd_one_line_output(&cmd)
87 .map_err(|_| TransportError::CommunicationError(format!("No output from {cmd}")))?;
88 let Some(Ok(val)) = line
89 .split_ascii_whitespace()
90 .last()
91 .map(|s| s.trim_matches(|c| !char::is_ascii_digit(&c)).parse::<u8>())
92 else {
93 bail!(TransportError::CommunicationError(format!(
94 "Bad i2cxfer output: '{line}'"
95 )))
96 };
97 Ok(val)
98 }
99
100 fn write_reg(&self, reg: IoExpanderRegister, val: u8) -> Result<()> {
102 let cmd = format!("i2cxfer w 0 0x20 {} {val}", reg as u8);
103 self.inner.cmd_no_output(&cmd)
104 }
105
106 fn read_pin(&self, reg: IoExpanderRegister) -> Result<bool> {
108 self.read_reg(reg)
109 .map(|val| val & Self::RESET_PIN_MASK != 0)
110 }
111
112 fn write_pin(&self, reg: IoExpanderRegister, val: bool) -> Result<()> {
115 let read_val = self.read_reg(reg)?;
116 let write_val = if val {
117 read_val | Self::RESET_PIN_MASK
118 } else {
119 read_val & !Self::RESET_PIN_MASK
120 };
121 if read_val != write_val {
123 self.write_reg(reg, write_val)
124 } else {
125 Ok(())
126 }
127 }
128}
129
130impl GpioPin for ServoMicroResetPin {
131 fn read(&self) -> Result<bool> {
133 self.read_pin(IoExpanderRegister::Input1)
134 }
135
136 fn write(&self, value: bool) -> Result<()> {
138 if !value {
140 self.write_pin(IoExpanderRegister::Output1, false)?;
141 }
142 self.write_pin(IoExpanderRegister::FloatPin1, value)
143 }
144
145 fn set_mode(&self, _mode: PinMode) -> Result<()> {
146 bail!(TransportError::UnsupportedOperation)
147 }
148
149 fn set_pull_mode(&self, _mode: PullMode) -> Result<()> {
150 bail!(TransportError::UnsupportedOperation)
151 }
152}