opentitanlib/transport/hyperdebug/
c2d2.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, 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
12/// The C2D2 (Case Closed Debugging Debugger) is used to bring up OT and EC chips sitting
13/// inside a computing device, such that those OT chips can provide Case Closed Debugging
14/// support to allow bringup of the rest of the computing device.  C2D2 devices happen to speak
15/// almost exactly the same USB protocol as HyperDebug.  This `Flavor` implementation defines
16/// the few deviations: USB PID value, and the handling of OT reset signal.
17pub struct C2d2Flavor {}
18
19impl C2d2Flavor {
20    const PID_C2D2: u16 = 0x5041;
21}
22
23impl Flavor for C2d2Flavor {
24    fn gpio_pin(inner: &Rc<Inner>, pinname: &str) -> Result<Rc<dyn GpioPin>> {
25        if pinname == "SPIVREF_RSVD_H1VREF_H1_RST_ODL" {
26            return Ok(Rc::new(C2d2ResetPin::open(inner)?));
27        }
28        StandardFlavor::gpio_pin(inner, pinname)
29    }
30
31    fn spi_index(_inner: &Rc<Inner>, instance: &str) -> Result<(u8, u8)> {
32        if instance == "0" {
33            return Ok((super::spi::USB_SPI_REQ_ENABLE, 0));
34        }
35        bail!(TransportError::InvalidInstance(
36            TransportInterfaceType::Spi,
37            instance.to_string()
38        ))
39    }
40
41    fn get_default_usb_vid() -> u16 {
42        VID_GOOGLE
43    }
44
45    fn get_default_usb_pid() -> u16 {
46        Self::PID_C2D2
47    }
48    fn perform_initial_fw_check() -> bool {
49        // The c2d2 firmware and hyperdebug firmware are different, so do not check.
50        false
51    }
52}
53
54pub struct C2d2ResetPin {
55    inner: Rc<Inner>,
56}
57
58impl C2d2ResetPin {
59    pub fn open(inner: &Rc<Inner>) -> Result<Self> {
60        Ok(Self {
61            inner: Rc::clone(inner),
62        })
63    }
64}
65
66impl GpioPin for C2d2ResetPin {
67    /// Reads the value of the reset pin.
68    fn read(&self) -> Result<bool> {
69        let line = self
70            .inner
71            .cmd_one_line_output("gpioget SPIVREF_RSVD_H1VREF_H1_RST_ODL")
72            .map_err(|_| {
73                TransportError::CommunicationError("No output from gpioget".to_string())
74            })?;
75        Ok(line.trim_start().starts_with('1'))
76    }
77
78    /// Sets the value of the GPIO reset pin by means of the special h1_reset command.
79    fn write(&self, value: bool) -> Result<()> {
80        self.inner
81            .cmd_one_line_output(&format!("h1_reset {}", if value { 0 } else { 1 }))?;
82        Ok(())
83    }
84
85    /// Sets the mode of the GPIO pin as input, output, or open drain I/O.
86    fn set_mode(&self, _mode: PinMode) -> Result<()> {
87        bail!(TransportError::UnsupportedOperation)
88    }
89
90    /// Sets the weak pull resistors of the GPIO pin.
91    fn set_pull_mode(&self, _mode: PullMode) -> Result<()> {
92        bail!(TransportError::UnsupportedOperation)
93    }
94}