1 // SPDX-License-Identifier: GPL-2.0 2 3 //! GSP Sequencer implementation for Pre-hopper GSP boot sequence. 4 5 use core::array; 6 7 use kernel::{ 8 device, 9 dma::Coherent, 10 io::{ 11 poll::read_poll_timeout, 12 Io, // 13 }, 14 prelude::*, 15 time::{ 16 delay::fsleep, 17 Delta, // 18 }, 19 transmute::FromBytes, // 20 }; 21 22 use crate::{ 23 driver::Bar0, 24 falcon::{ 25 gsp::Gsp, 26 sec2::Sec2, 27 Falcon, // 28 }, 29 gsp::{ 30 cmdq::{ 31 Cmdq, 32 MessageFromGsp, // 33 }, 34 fw, 35 GspBootContext, 36 LibosMemoryRegionInitArgument, // 37 }, 38 num::FromSafeCast, 39 sbuffer::SBufferIter, 40 }; 41 42 /// GSP Sequencer information containing the command sequence and data. 43 struct GspSequence { 44 /// Current command index for error reporting. 45 cmd_index: u32, 46 /// Command data buffer containing the sequence of commands. 47 cmd_data: KVec<u8>, 48 } 49 50 impl MessageFromGsp for GspSequence { 51 const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer; 52 type InitError = Error; 53 type Message = fw::RunCpuSequencer; 54 55 fn read( 56 msg: &Self::Message, 57 sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>, 58 ) -> Result<Self, Self::InitError> { 59 let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?; 60 Ok(GspSequence { 61 cmd_index: msg.cmd_index(), 62 cmd_data, 63 }) 64 } 65 } 66 67 const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>(); 68 69 /// GSP Sequencer Command types with payload data. 70 /// Commands have an opcode and an opcode-dependent struct. 71 #[allow(clippy::enum_variant_names)] 72 #[derive(Debug)] 73 pub(crate) enum GspSeqCmd { 74 RegWrite(fw::RegWritePayload), 75 RegModify(fw::RegModifyPayload), 76 RegPoll(fw::RegPollPayload), 77 DelayUs(fw::DelayUsPayload), 78 RegStore(fw::RegStorePayload), 79 CoreReset, 80 CoreStart, 81 CoreWaitForHalt, 82 CoreResume, 83 } 84 85 impl GspSeqCmd { 86 /// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes. 87 pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> { 88 let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?; 89 let opcode_size = core::mem::size_of::<u32>(); 90 91 let (cmd, size) = match fw_cmd.opcode()? { 92 fw::SeqBufOpcode::RegWrite => { 93 let payload = fw_cmd.reg_write_payload()?; 94 let size = opcode_size + size_of_val(&payload); 95 (GspSeqCmd::RegWrite(payload), size) 96 } 97 fw::SeqBufOpcode::RegModify => { 98 let payload = fw_cmd.reg_modify_payload()?; 99 let size = opcode_size + size_of_val(&payload); 100 (GspSeqCmd::RegModify(payload), size) 101 } 102 fw::SeqBufOpcode::RegPoll => { 103 let payload = fw_cmd.reg_poll_payload()?; 104 let size = opcode_size + size_of_val(&payload); 105 (GspSeqCmd::RegPoll(payload), size) 106 } 107 fw::SeqBufOpcode::DelayUs => { 108 let payload = fw_cmd.delay_us_payload()?; 109 let size = opcode_size + size_of_val(&payload); 110 (GspSeqCmd::DelayUs(payload), size) 111 } 112 fw::SeqBufOpcode::RegStore => { 113 let payload = fw_cmd.reg_store_payload()?; 114 let size = opcode_size + size_of_val(&payload); 115 (GspSeqCmd::RegStore(payload), size) 116 } 117 fw::SeqBufOpcode::CoreReset => (GspSeqCmd::CoreReset, opcode_size), 118 fw::SeqBufOpcode::CoreStart => (GspSeqCmd::CoreStart, opcode_size), 119 fw::SeqBufOpcode::CoreWaitForHalt => (GspSeqCmd::CoreWaitForHalt, opcode_size), 120 fw::SeqBufOpcode::CoreResume => (GspSeqCmd::CoreResume, opcode_size), 121 }; 122 123 if data.len() < size { 124 dev_err!(dev, "Data is not enough for command\n"); 125 return Err(EINVAL); 126 } 127 128 Ok((cmd, size)) 129 } 130 } 131 132 /// GSP Sequencer for executing firmware commands during boot. 133 pub(crate) struct GspSequencer<'a> { 134 /// `Bar0` for register access. 135 bar: Bar0<'a>, 136 /// SEC2 falcon for core operations. 137 sec2_falcon: &'a Falcon<'a, Sec2>, 138 /// GSP falcon for core operations. 139 gsp_falcon: &'a Falcon<'a, Gsp>, 140 /// LibOS memory region init arguments. 141 libos: &'a Coherent<[LibosMemoryRegionInitArgument]>, 142 /// Bootloader application version. 143 bootloader_app_version: u32, 144 /// Device for logging. 145 dev: &'a device::Device, 146 } 147 148 impl fw::RegWritePayload { 149 fn run(&self, sequencer: &GspSequencer<'_>) -> Result { 150 let addr = usize::from_safe_cast(self.addr()); 151 152 sequencer.bar.try_write32(self.val(), addr) 153 } 154 } 155 156 impl fw::RegModifyPayload { 157 fn run(&self, sequencer: &GspSequencer<'_>) -> Result { 158 let addr = usize::from_safe_cast(self.addr()); 159 160 sequencer.bar.try_read32(addr).and_then(|val| { 161 sequencer 162 .bar 163 .try_write32((val & !self.mask()) | self.val(), addr) 164 }) 165 } 166 } 167 168 impl fw::RegPollPayload { 169 fn run(&self, sequencer: &GspSequencer<'_>) -> Result { 170 let addr = usize::from_safe_cast(self.addr()); 171 172 // Default timeout to 4 seconds. 173 let timeout_us = if self.timeout() == 0 { 174 4_000_000 175 } else { 176 i64::from(self.timeout()) 177 }; 178 179 // First read. 180 sequencer.bar.try_read32(addr)?; 181 182 // Poll the requested register with requested timeout. 183 read_poll_timeout( 184 || sequencer.bar.try_read32(addr), 185 |current| (current & self.mask()) == self.val(), 186 Delta::ZERO, 187 Delta::from_micros(timeout_us), 188 ) 189 .map(|_| ()) 190 } 191 } 192 193 impl fw::DelayUsPayload { 194 fn run(&self, _sequencer: &GspSequencer<'_>) -> Result { 195 fsleep(Delta::from_micros(i64::from(self.val()))); 196 Ok(()) 197 } 198 } 199 200 impl fw::RegStorePayload { 201 fn run(&self, sequencer: &GspSequencer<'_>) -> Result { 202 let addr = usize::from_safe_cast(self.addr()); 203 204 sequencer.bar.try_read32(addr).map(|_| ()) 205 } 206 } 207 208 impl GspSeqCmd { 209 fn run(&self, seq: &GspSequencer<'_>) -> Result { 210 match self { 211 GspSeqCmd::RegWrite(cmd) => cmd.run(seq), 212 GspSeqCmd::RegModify(cmd) => cmd.run(seq), 213 GspSeqCmd::RegPoll(cmd) => cmd.run(seq), 214 GspSeqCmd::DelayUs(cmd) => cmd.run(seq), 215 GspSeqCmd::RegStore(cmd) => cmd.run(seq), 216 GspSeqCmd::CoreReset => { 217 seq.gsp_falcon.reset()?; 218 seq.gsp_falcon.dma_reset(); 219 Ok(()) 220 } 221 GspSeqCmd::CoreStart => { 222 seq.gsp_falcon.start()?; 223 Ok(()) 224 } 225 GspSeqCmd::CoreWaitForHalt => { 226 seq.gsp_falcon.wait_till_halted()?; 227 Ok(()) 228 } 229 GspSeqCmd::CoreResume => { 230 // At this point, 'SEC2-RTOS' has been loaded into SEC2 by the sequencer 231 // but neither SEC2-RTOS nor GSP-RM is running yet. This part of the 232 // sequencer will start both. 233 234 // Reset the GSP to prepare it for resuming. 235 seq.gsp_falcon.reset()?; 236 237 let libos_dma_address = seq.libos.dma_address(); 238 239 // Write the libOS DMA address to GSP mailboxes. 240 seq.gsp_falcon.write_mailboxes( 241 Some(libos_dma_address as u32), 242 Some((libos_dma_address >> 32) as u32), 243 ); 244 245 // Start the SEC2 falcon which will trigger GSP-RM to resume on the GSP. 246 seq.sec2_falcon.start()?; 247 248 // Poll until GSP-RM reload/resume has completed (up to 2 seconds). 249 seq.gsp_falcon.check_reload_completed(Delta::from_secs(2))?; 250 251 // Verify SEC2 completed successfully by checking its mailbox for errors. 252 let mbox0 = seq.sec2_falcon.read_mailbox0(); 253 if mbox0 != 0 { 254 dev_err!(seq.dev, "Sequencer: sec2 errors: {:?}\n", mbox0); 255 return Err(EIO); 256 } 257 258 // Configure GSP with the bootloader version. 259 seq.gsp_falcon.write_os_version(seq.bootloader_app_version); 260 261 // Verify the GSP's RISC-V core is active indicating successful GSP boot. 262 if !seq.gsp_falcon.is_riscv_active() { 263 dev_err!(seq.dev, "Sequencer: RISC-V core is not active\n"); 264 return Err(EIO); 265 } 266 Ok(()) 267 } 268 } 269 } 270 } 271 272 /// Iterator over GSP sequencer commands. 273 struct GspSeqIter<'a> { 274 /// Command data buffer. 275 cmd_data: &'a [u8], 276 /// Current position in the buffer. 277 current_offset: usize, 278 /// Total number of commands to process. 279 total_cmds: u32, 280 /// Number of commands processed so far. 281 cmds_processed: u32, 282 /// Device for logging. 283 dev: &'a device::Device, 284 } 285 286 impl<'a> GspSeqIter<'a> { 287 fn new(seq: &'a GspSequence, dev: &'a device::Device) -> Self { 288 Self { 289 cmd_data: &seq.cmd_data, 290 current_offset: 0, 291 total_cmds: seq.cmd_index, 292 cmds_processed: 0, 293 dev, 294 } 295 } 296 } 297 298 impl<'a> Iterator for GspSeqIter<'a> { 299 type Item = Result<GspSeqCmd>; 300 301 fn next(&mut self) -> Option<Self::Item> { 302 // Stop if we've processed all commands or reached the end of data. 303 if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() { 304 return None; 305 } 306 307 // Check if we have enough data for opcode. 308 if self.current_offset + core::mem::size_of::<u32>() > self.cmd_data.len() { 309 return Some(Err(EIO)); 310 } 311 312 let offset = self.current_offset; 313 314 // Handle command creation based on available data, 315 // zero-pad if necessary (since last command may not be full size). 316 let mut buffer = [0u8; CMD_SIZE]; 317 let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() { 318 CMD_SIZE 319 } else { 320 self.cmd_data.len() - offset 321 }; 322 buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]); 323 let cmd_result = GspSeqCmd::new(&buffer, self.dev); 324 325 cmd_result.map_or_else( 326 |_err| { 327 dev_err!(self.dev, "Error parsing command at offset {}\n", offset); 328 None 329 }, 330 |(cmd, size)| { 331 self.current_offset += size; 332 self.cmds_processed += 1; 333 Some(Ok(cmd)) 334 }, 335 ) 336 } 337 } 338 339 impl<'a> GspSequencer<'a> { 340 pub(crate) fn run( 341 cmdq: &Cmdq, 342 ctx: &'a GspBootContext<'_, '_>, 343 libos: &'a Coherent<[LibosMemoryRegionInitArgument]>, 344 bootloader_app_version: u32, 345 ) -> Result { 346 let seq_info = loop { 347 match cmdq.receive_msg::<GspSequence>(Cmdq::RECEIVE_TIMEOUT) { 348 Ok(seq_info) => break seq_info, 349 Err(ERANGE) => continue, 350 Err(e) => return Err(e), 351 } 352 }; 353 354 let sequencer = GspSequencer { 355 bar: ctx.bar, 356 sec2_falcon: ctx.sec2_falcon, 357 gsp_falcon: ctx.gsp_falcon, 358 libos, 359 bootloader_app_version, 360 dev: ctx.dev(), 361 }; 362 363 dev_dbg!(sequencer.dev, "Running CPU Sequencer commands\n"); 364 365 for cmd_result in GspSeqIter::new(&seq_info, sequencer.dev) { 366 match cmd_result { 367 Ok(cmd) => cmd.run(&sequencer)?, 368 Err(e) => { 369 dev_err!( 370 sequencer.dev, 371 "Error running command at index {}\n", 372 seq_info.cmd_index 373 ); 374 return Err(e); 375 } 376 } 377 } 378 379 dev_dbg!( 380 sequencer.dev, 381 "CPU Sequencer commands completed successfully\n" 382 ); 383 Ok(()) 384 } 385 } 386