1use thiserror::Error;
5
6use crate::{
7 chip::{AxiError, HlComms},
8 error::PlatformError,
9};
10
11#[derive(Debug, Clone, Copy)]
12pub enum PowerState {
13 Busy,
14 ShortIdle,
15 LongIdle,
16}
17
18#[derive(Debug)]
19pub enum ArcState {
20 A0,
21 A1,
22 A3,
23 A5,
24}
25
26#[derive(Debug)]
27pub enum FwType {
28 ArcL2,
29 FwBundle,
30 FwBundleSPI,
31}
32
33#[derive(Debug)]
34pub enum TypedArcMsg {
35 Nop,
36 Test { arg: u32 },
37 ArcGoToSleep,
38
39 SetPowerState(PowerState),
40
41 FwVersion(FwType),
42 GetSmbusTelemetryAddr,
43
44 SetArcState { state: ArcState },
45
46 ResetSafeClks { arg: u32 },
47 ToggleTensixReset { arg: u32 },
48 DeassertRiscVReset,
49 GetAiclk,
50 TriggerReset,
51 GetHarvesting,
52 TriggerSpiCopyLtoR,
53 GetSpiDumpAddr,
54 SpiRead { addr: u32 },
55 SpiWrite,
56}
57
58impl From<TypedArcMsg> for ArcMsg {
59 fn from(val: TypedArcMsg) -> Self {
60 ArcMsg::Typed(val)
61 }
62}
63
64impl TypedArcMsg {
65 pub fn msg_code(&self) -> u16 {
66 match self {
67 TypedArcMsg::Nop => 0x11,
68 TypedArcMsg::ArcGoToSleep => 0x55,
69 TypedArcMsg::Test { .. } => 0x90,
70 TypedArcMsg::GetSmbusTelemetryAddr => 0x2C,
71 TypedArcMsg::TriggerSpiCopyLtoR => 0x50,
72 TypedArcMsg::SetPowerState(state) => match state {
73 PowerState::Busy => 0x52,
74 PowerState::ShortIdle => 0x53,
75 PowerState::LongIdle => 0x54,
76 },
77 TypedArcMsg::TriggerReset => 0x56,
78 TypedArcMsg::GetHarvesting => 0x57,
79 TypedArcMsg::DeassertRiscVReset => 0xba,
80 TypedArcMsg::ResetSafeClks { .. } => 0xbb,
81 TypedArcMsg::ToggleTensixReset { .. } => 0xaf,
82 TypedArcMsg::GetAiclk => 0x34,
83 TypedArcMsg::SetArcState { state } => match state {
84 ArcState::A0 => 0xA0,
85 ArcState::A1 => 0xA1,
86 ArcState::A3 => 0xA3,
87 ArcState::A5 => 0xA5,
88 },
89 TypedArcMsg::FwVersion(_) => 0xb9,
90 TypedArcMsg::GetSpiDumpAddr => 0x29,
91 TypedArcMsg::SpiRead { .. } => 0x2A,
92 TypedArcMsg::SpiWrite => 0x2B,
93 }
94 }
95}
96
97#[derive(Debug)]
98pub enum ArcMsg {
99 Typed(TypedArcMsg),
100 Raw { msg: u16, arg0: u16, arg1: u16 },
101 Buf([u32; 8]),
102}
103
104impl ArcMsg {
105 pub fn msg_code(&self) -> u16 {
106 let code = match self {
107 ArcMsg::Raw { msg, .. } => *msg,
108 ArcMsg::Typed(msg) => msg.msg_code(),
109 ArcMsg::Buf([msg, ..]) => u16::from(msg.to_le_bytes()[0]),
110 };
111
112 0xaa00 | code
113 }
114
115 pub fn args(&self) -> (u16, u16) {
116 match self {
117 ArcMsg::Raw { arg0, arg1, .. } => (*arg0, *arg1),
118 ArcMsg::Typed(msg) => match &msg {
119 TypedArcMsg::Test { arg }
120 | TypedArcMsg::ResetSafeClks { arg }
121 | TypedArcMsg::ToggleTensixReset { arg }
122 | TypedArcMsg::SpiRead { addr: arg } => {
123 ((arg & 0xFFFF) as u16, ((arg >> 16) & 0xFFFF) as u16)
124 }
125 TypedArcMsg::SpiWrite => (0xFFFF, 0xFFFF),
126 TypedArcMsg::Nop
127 | TypedArcMsg::ArcGoToSleep
128 | TypedArcMsg::GetSmbusTelemetryAddr
129 | TypedArcMsg::SetPowerState(_)
130 | TypedArcMsg::DeassertRiscVReset
131 | TypedArcMsg::GetAiclk
132 | TypedArcMsg::TriggerReset
133 | TypedArcMsg::GetHarvesting
134 | TypedArcMsg::GetSpiDumpAddr
135 | TypedArcMsg::TriggerSpiCopyLtoR
136 | TypedArcMsg::SetArcState { .. } => (0, 0),
137 TypedArcMsg::FwVersion(ty) => match ty {
138 FwType::ArcL2 => (0, 0),
139 FwType::FwBundle => (1, 0),
140 FwType::FwBundleSPI => (2, 0),
141 },
142 },
143 ArcMsg::Buf(_) => unimplemented!(),
144 }
145 }
146
147 pub fn from_values(msg: u32, arg0: u16, arg1: u16) -> Self {
148 let arg = ((arg1 as u32) << 16) | arg0 as u32;
149 let msg = 0xFF & msg;
150 let msg = match msg {
151 0x11 => TypedArcMsg::Nop,
152 0x34 => TypedArcMsg::GetAiclk,
153 0x56 => TypedArcMsg::TriggerReset,
154 0xbb => TypedArcMsg::ResetSafeClks { arg },
155 0xaf => TypedArcMsg::ToggleTensixReset { arg },
156 0xba => TypedArcMsg::DeassertRiscVReset,
157 0x50 => TypedArcMsg::TriggerSpiCopyLtoR,
158 0x52 => TypedArcMsg::SetPowerState(PowerState::Busy),
159 0x53 => TypedArcMsg::SetPowerState(PowerState::ShortIdle),
160 0x54 => TypedArcMsg::SetPowerState(PowerState::LongIdle),
161 0x57 => TypedArcMsg::GetHarvesting,
162 0x90 => TypedArcMsg::Test { arg },
163 0xA0 => TypedArcMsg::SetArcState {
164 state: ArcState::A0,
165 },
166 0xA1 => TypedArcMsg::SetArcState {
167 state: ArcState::A1,
168 },
169 0xA3 => TypedArcMsg::SetArcState {
170 state: ArcState::A3,
171 },
172 0xA5 => TypedArcMsg::SetArcState {
173 state: ArcState::A5,
174 },
175 0xB9 => TypedArcMsg::FwVersion(match arg {
176 0 => FwType::ArcL2,
177 1 => FwType::FwBundle,
178 2 => FwType::FwBundleSPI,
179 _ => panic!("Unknown FW type {arg}"),
180 }),
181 value => {
182 unimplemented!("Unknown ARC message {:#x}", value)
183 }
184 };
185
186 ArcMsg::Typed(msg)
187 }
188}
189
190#[derive(Error, Debug)]
191pub enum ArcMsgProtocolError {
192 #[error("Message {0} not recognized")]
193 MsgNotRecognized(u16),
194 #[error("Timed out while waiting {0:?} for ARC to respond")]
195 Timeout(std::time::Duration),
196 #[error("ARC is asleep")]
197 ArcAsleep,
198 #[error("Failed to trigger FW interrupt")]
199 FwIntFailed,
200 #[error("Mailbox {0} is invalid")]
201 InvalidMailbox(usize),
202 #[error("Unknown error code {0}")]
203 UnknownErrorCode(u8),
204}
205
206impl ArcMsgProtocolError {
207 #[inline(always)]
208 pub fn into_error(self) -> ArcMsgError {
209 ArcMsgError::ProtocolError {
210 source: self,
211 backtrace: crate::error::BtWrapper(std::backtrace::Backtrace::capture()),
212 }
213 }
214}
215
216#[derive(Error, Debug)]
217pub enum ArcMsgError {
218 #[error("{source}\n{backtrace}")]
219 ProtocolError {
220 source: ArcMsgProtocolError,
221 backtrace: crate::error::BtWrapper,
222 },
223
224 #[error(transparent)]
225 AxiError(#[from] AxiError),
226}
227
228#[derive(Debug)]
229pub enum ArcMsgOk {
230 Ok { rc: u32, arg: u32 },
231 OkBuf([u32; 8]),
232 OkNoWait,
233}
234
235fn trigger_fw_int<T: HlComms>(comms: &T, addrs: &ArcMsgAddr) -> Result<bool, PlatformError> {
240 let misc = comms.axi_read32(addrs.arc_misc_cntl)?;
241
242 if misc & (1 << 16) != 0 {
243 return Ok(false);
244 }
245
246 let misc_bit16_set = misc | (1 << 16);
247 comms.axi_write32(addrs.arc_misc_cntl, misc_bit16_set)?;
248
249 Ok(true)
250}
251
252#[derive(Clone, Debug)]
253pub struct ArcMsgAddr {
254 pub scratch_base: u64,
255 pub arc_misc_cntl: u64,
256}
257
258pub fn arc_msg<T: HlComms>(
259 comms: &T,
260 msg: &ArcMsg,
261 wait_for_done: bool,
262 timeout: std::time::Duration,
263 msg_reg: u64,
264 return_reg: u64,
265 addrs: &ArcMsgAddr,
266) -> Result<ArcMsgOk, PlatformError> {
267 const MSG_ERROR_REPLY: u32 = 0xffffffff;
268
269 let (arg0, arg1) = msg.args();
270
271 let code = msg.msg_code();
272
273 let current_code = comms.axi_read32(addrs.scratch_base + (msg_reg * 4))?;
274 if (current_code & 0xFFFF) as u16 == TypedArcMsg::ArcGoToSleep.msg_code() {
275 Err(ArcMsgProtocolError::ArcAsleep.into_error())?;
276 }
277
278 comms.axi_write32(
279 addrs.scratch_base + (return_reg * 4),
280 arg0 as u32 | ((arg1 as u32) << 16),
281 )?;
282
283 comms.axi_write32(addrs.scratch_base + (msg_reg * 4), code as u32)?;
284
285 if !trigger_fw_int(comms, addrs)? {
286 Err(ArcMsgProtocolError::FwIntFailed.into_error())?;
287 }
288
289 if wait_for_done {
290 let start = std::time::Instant::now();
291 loop {
292 let status = comms.axi_read32(addrs.scratch_base + (msg_reg * 4))?;
293 if (status & 0xFFFF) as u16 == code & 0xFF {
294 let exit_code = (status >> 16) & 0xFFFF;
295 let arg = comms.axi_read32(addrs.scratch_base + (return_reg * 4))?;
296
297 return Ok(ArcMsgOk::Ok { rc: exit_code, arg });
298 } else if status == MSG_ERROR_REPLY {
299 Err(ArcMsgProtocolError::MsgNotRecognized(code).into_error())?;
300 }
301
302 std::thread::sleep(std::time::Duration::from_millis(1));
303 if start.elapsed() > timeout {
304 Err(ArcMsgProtocolError::Timeout(timeout).into_error())?;
305 }
306 }
307 }
308
309 Ok(ArcMsgOk::OkNoWait)
310}