1use std::iter;
6use std::time::Duration;
7
8use anyhow::{Context, Result, bail};
9use serde::{Deserialize, Serialize};
10use thiserror::Error;
11
12use ot_hal::dif::lc_ctrl::{
13 DifLcCtrlState, LcCtrlReg, LcCtrlStatus, LcCtrlTransitionCmd, LcCtrlTransitionCtrl,
14};
15use ot_hal::top::earlgrey as top_earlgrey;
16use ot_hal::util::multibits::MultiBitBool8;
17
18use crate::app::{TransportWrapper, UartRx};
19use crate::impl_serializable_error;
20use crate::io::jtag::{Jtag, JtagParams, JtagTap};
21use crate::test_utils::poll;
22
23#[derive(Error, Debug, Deserialize, Serialize)]
25pub enum LcTransitionError {
26 #[error("LC controller not ready to perform an LC transition (status: 0x{0:x}).")]
27 LcCtrlNotReady(LcCtrlStatus),
28 #[error("LC transition mutex was already claimed.")]
29 MutexAlreadyClaimed,
30 #[error("Failed to claim LC transition mutex.")]
31 FailedToClaimMutex,
32 #[error("Volatile raw unlock is not supported on this chip.")]
33 VolatileRawUnlockNotSupported,
34 #[error("Volatile raw unlock is unexpectedly supported on this chip.")]
35 VolatileRawUnlockSupported,
36 #[error("LC transition target programming failed (target state: 0x{0:x}).")]
37 TargetProgrammingFailed(u32),
38 #[error("LC transition failed (status: 0x{0:x}).")]
39 TransitionFailed(LcCtrlStatus),
40 #[error("Bad post transition LC state: 0x{0:x}.")]
41 BadPostTransitionState(u32),
42 #[error("Invalid LC state: {0:x}")]
43 InvalidState(u32),
44 #[error("Generic error {0}")]
45 Generic(String),
46}
47impl_serializable_error!(LcTransitionError);
48
49pub fn claim_lc_mutex(jtag: &mut dyn Jtag, claim: bool) -> Result<()> {
50 if claim {
51 if jtag.read_lc_ctrl_reg(&LcCtrlReg::ClaimTransitionIf)?
53 == u8::from(MultiBitBool8::True) as u32
54 {
55 return Err(LcTransitionError::MutexAlreadyClaimed.into());
56 }
57
58 jtag.write_lc_ctrl_reg(
60 &LcCtrlReg::ClaimTransitionIf,
61 u8::from(MultiBitBool8::True) as u32,
62 )?;
63
64 if jtag.read_lc_ctrl_reg(&LcCtrlReg::ClaimTransitionIf)?
66 != u8::from(MultiBitBool8::True) as u32
67 {
68 return Err(LcTransitionError::FailedToClaimMutex.into());
69 }
70 } else {
71 jtag.write_lc_ctrl_reg(
73 &LcCtrlReg::ClaimTransitionIf,
74 u8::from(MultiBitBool8::False) as u32,
75 )?;
76 }
77 Ok(())
78}
79
80fn setup_lc_transition(
81 jtag: &mut dyn Jtag,
82 target_lc_state: DifLcCtrlState,
83 token: Option<[u32; 4]>,
84) -> Result<()> {
85 let status = jtag.read_lc_ctrl_reg(&LcCtrlReg::Status)?;
87 let status = LcCtrlStatus::from_bits(status).ok_or(LcTransitionError::InvalidState(status))?;
88 if status != LcCtrlStatus::INITIALIZED | LcCtrlStatus::READY {
89 return Err(LcTransitionError::LcCtrlNotReady(status).into());
90 }
91
92 claim_lc_mutex(jtag, true)?;
93
94 jtag.write_lc_ctrl_reg(
96 &LcCtrlReg::TransitionTarget,
97 target_lc_state.redundant_encoding(),
98 )?;
99
100 let target_lc_state_programmed = DifLcCtrlState::from_redundant_encoding(
102 jtag.read_lc_ctrl_reg(&LcCtrlReg::TransitionTarget)?,
103 )?;
104 if target_lc_state_programmed != target_lc_state {
105 return Err(
106 LcTransitionError::TargetProgrammingFailed(target_lc_state_programmed.into()).into(),
107 );
108 }
109
110 if let Some(token_words) = token {
112 let token_regs = [
113 &LcCtrlReg::TransitionToken0,
114 &LcCtrlReg::TransitionToken1,
115 &LcCtrlReg::TransitionToken2,
116 &LcCtrlReg::TransitionToken3,
117 ];
118
119 for (reg, value) in iter::zip(token_regs, token_words) {
120 jtag.write_lc_ctrl_reg(reg, value)?;
121 }
122 }
123
124 Ok(())
125}
126
127pub fn trigger_lc_transition(
177 transport: &TransportWrapper,
178 mut jtag: Box<dyn Jtag + '_>,
179 target_lc_state: DifLcCtrlState,
180 token: Option<[u32; 4]>,
181 use_external_clk: bool,
182 reset_tap_straps: Option<JtagTap>,
183) -> Result<()> {
184 setup_lc_transition(&mut *jtag, target_lc_state, token)?;
187
188 if use_external_clk {
190 jtag.write_lc_ctrl_reg(
191 &LcCtrlReg::TransitionCtrl,
192 LcCtrlTransitionCtrl::EXT_CLOCK_EN.bits(),
193 )?;
194 } else {
195 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl, 0)?;
196 }
197
198 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCmd, LcCtrlTransitionCmd::START.bits())?;
200
201 wait_for_status(
202 &mut *jtag,
203 Duration::from_secs(3),
204 LcCtrlStatus::TRANSITION_SUCCESSFUL,
205 )
206 .context("failed waiting for TRANSITION_SUCCESSFUL status.")?;
207
208 let post_transition_lc_state = jtag.read_lc_ctrl_reg(&LcCtrlReg::LcState)?;
210 if post_transition_lc_state != DifLcCtrlState::PostTransition.redundant_encoding() {
211 return Err(LcTransitionError::BadPostTransitionState(post_transition_lc_state).into());
212 }
213
214 jtag.disconnect()?;
216 if let Some(tap) = reset_tap_straps {
217 transport.pin_strapping("PINMUX_TAP_LC")?.remove()?;
218 match tap {
219 JtagTap::LcTap => transport.pin_strapping("PINMUX_TAP_LC")?.apply()?,
220 JtagTap::RiscvTap => transport.pin_strapping("PINMUX_TAP_RISCV")?.apply()?,
221 _ => bail!("Unsupported tap {tap:?}"),
222 }
223 }
224 transport.reset(UartRx::Clear)?;
225
226 Ok(())
227}
228
229#[allow(clippy::too_many_arguments)]
241pub fn trigger_volatile_raw_unlock<'t>(
242 transport: &'t TransportWrapper,
243 mut jtag: Box<dyn Jtag + 't>,
244 target_lc_state: DifLcCtrlState,
245 hashed_token: Option<[u32; 4]>,
246 use_external_clk: bool,
247 post_transition_tap: JtagTap,
248 jtag_params: &JtagParams,
249 expect_raw_unlock_supported: bool,
250) -> Result<Box<dyn Jtag + 't>> {
251 setup_lc_transition(&mut *jtag, target_lc_state, hashed_token)?;
254
255 let mut ctrl = LcCtrlTransitionCtrl::VOLATILE_RAW_UNLOCK;
257 if use_external_clk {
258 ctrl |= LcCtrlTransitionCtrl::EXT_CLOCK_EN;
259 }
260 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl, ctrl.bits())?;
261
262 let read = jtag.read_lc_ctrl_reg(&LcCtrlReg::TransitionCtrl)?;
264 if read < 2u32 && expect_raw_unlock_supported {
265 return Err(LcTransitionError::VolatileRawUnlockNotSupported.into());
266 } else if read >= 2u32 && !expect_raw_unlock_supported {
267 return Err(LcTransitionError::VolatileRawUnlockSupported.into());
268 }
269
270 if post_transition_tap == JtagTap::RiscvTap {
272 transport.pin_strapping("PINMUX_TAP_LC")?.remove()?;
273 transport.pin_strapping("PINMUX_TAP_RISCV")?.apply()?;
274 }
275
276 jtag.write_lc_ctrl_reg(&LcCtrlReg::TransitionCmd, LcCtrlTransitionCmd::START.bits())?;
278
279 if post_transition_tap == JtagTap::RiscvTap {
283 jtag.disconnect()?;
284 jtag = transport.jtag(jtag_params)?.connect(JtagTap::RiscvTap)?;
285 }
286
287 if expect_raw_unlock_supported {
288 wait_for_status(
289 &mut *jtag,
290 Duration::from_secs(3),
291 LcCtrlStatus::TRANSITION_SUCCESSFUL,
292 )
293 .context("failed waiting for TRANSITION_SUCCESSFUL status.")?;
294 } else {
295 let mut status = LcCtrlStatus::INITIALIZED | LcCtrlStatus::TOKEN_ERROR;
296 if use_external_clk {
297 status |= LcCtrlStatus::EXT_CLOCK_SWITCHED;
298 }
299 wait_for_status(&mut *jtag, Duration::from_secs(3), status)
300 .context("failed waiting for TOKEN_ERROR status.")?;
301 }
302 Ok(jtag)
303}
304
305pub fn wait_for_status(jtag: &mut dyn Jtag, timeout: Duration, status: LcCtrlStatus) -> Result<()> {
306 let jtag_tap = jtag.tap();
307
308 poll::poll_until(timeout, Duration::from_millis(1), || {
310 let polled_status = match jtag_tap {
311 JtagTap::LcTap => jtag.read_lc_ctrl_reg(&LcCtrlReg::Status).unwrap(),
312 JtagTap::RiscvTap => {
313 let mut status = [0u32];
314 jtag.read_memory32(
315 top_earlgrey::LC_CTRL_REGS_BASE_ADDR as u32 + LcCtrlReg::Status as u32,
316 &mut status,
317 )?;
318 status[0]
319 }
320 _ => bail!("Unsupported tap {jtag_tap:?}"),
321 };
322
323 let polled_status =
324 LcCtrlStatus::from_bits(polled_status).context("status has invalid bits set")?;
325
326 if polled_status.intersects(LcCtrlStatus::ERRORS & !status) {
330 bail!("status {polled_status:#b} has error bits set");
331 }
332
333 Ok(polled_status.contains(status))
334 })
335}