diff options
Diffstat (limited to 'drivers/gpu/nova-core/falcon.rs')
| -rw-r--r-- | drivers/gpu/nova-core/falcon.rs | 107 |
1 files changed, 50 insertions, 57 deletions
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs index 82c661aef594..37bfee1d0949 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -8,12 +8,14 @@ use hal::FalconHal; use kernel::{ device, - dma::DmaAddress, + dma::{ + DmaAddress, + DmaMask, // + }, io::poll::read_poll_timeout, prelude::*, sync::aref::ARef, time::{ - delay::fsleep, Delta, // }, }; @@ -21,6 +23,7 @@ use kernel::{ use crate::{ dma::DmaObject, driver::Bar0, + falcon::hal::LoadMethod, gpu::Chipset, num::{ FromSafeCast, @@ -237,8 +240,11 @@ impl From<PeregrineCoreSelect> for bool { /// Different types of memory present in a falcon core. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum FalconMem { - /// Instruction Memory. - Imem, + /// Secure Instruction Memory. + ImemSecure, + /// Non-Secure Instruction Memory. + #[expect(unused)] + ImemNonSecure, /// Data Memory. Dmem, } @@ -345,8 +351,12 @@ pub(crate) struct FalconBromParams { /// Trait for providing load parameters of falcon firmwares. pub(crate) trait FalconLoadParams { - /// Returns the load parameters for `IMEM`. - fn imem_load_params(&self) -> FalconLoadTarget; + /// Returns the load parameters for Secure `IMEM`. + fn imem_sec_load_params(&self) -> FalconLoadTarget; + + /// Returns the load parameters for Non-Secure `IMEM`, + /// used only on Turing and GA100. + fn imem_ns_load_params(&self) -> Option<FalconLoadTarget>; /// Returns the load parameters for `DMEM`. fn dmem_load_params(&self) -> FalconLoadTarget; @@ -388,48 +398,11 @@ impl<E: FalconEngine + 'static> Falcon<E> { regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID); } - /// Wait for memory scrubbing to complete. - fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result { - // TIMEOUT: memory scrubbing should complete in less than 20ms. - read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), - |r| r.mem_scrubbing_done(), - Delta::ZERO, - Delta::from_millis(20), - ) - .map(|_| ()) - } - - /// Reset the falcon engine. - fn reset_eng(&self, bar: &Bar0) -> Result { - let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID); - - // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set - // RESET_READY so a non-failing timeout is used. - let _ = read_poll_timeout( - || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)), - |r| r.reset_ready(), - Delta::ZERO, - Delta::from_micros(150), - ); - - regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true)); - - // TIMEOUT: falcon engine should not take more than 10us to reset. - fsleep(Delta::from_micros(10)); - - regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false)); - - self.reset_wait_mem_scrubbing(bar)?; - - Ok(()) - } - /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete. pub(crate) fn reset(&self, bar: &Bar0) -> Result { - self.reset_eng(bar)?; + self.hal.reset_eng(bar)?; self.hal.select_core(self, bar)?; - self.reset_wait_mem_scrubbing(bar)?; + self.hal.reset_wait_mem_scrubbing(bar)?; regs::NV_PFALCON_FALCON_RM::default() .set_value(regs::NV_PMC_BOOT_0::read(bar).into()) @@ -448,7 +421,6 @@ impl<E: FalconEngine + 'static> Falcon<E> { fw: &F, target_mem: FalconMem, load_offsets: FalconLoadTarget, - sec: bool, ) -> Result { const DMA_LEN: u32 = 256; @@ -457,7 +429,9 @@ impl<E: FalconEngine + 'static> Falcon<E> { // // For DMEM we can fold the start offset into the DMA handle. let (src_start, dma_start) = match target_mem { - FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()), + FalconMem::ImemSecure | FalconMem::ImemNonSecure => { + (load_offsets.src_start, fw.dma_handle()) + } FalconMem::Dmem => ( 0, fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?, @@ -466,12 +440,18 @@ impl<E: FalconEngine + 'static> Falcon<E> { if dma_start % DmaAddress::from(DMA_LEN) > 0 { dev_err!( self.dev, - "DMA transfer start addresses must be a multiple of {}", + "DMA transfer start addresses must be a multiple of {}\n", DMA_LEN ); return Err(EINVAL); } + // The DMATRFBASE/1 register pair only supports a 49-bit address. + if dma_start > DmaMask::new::<49>().value() { + dev_err!(self.dev, "DMA address {:#x} exceeds 49 bits\n", dma_start); + return Err(ERANGE); + } + // DMA transfers can only be done in units of 256 bytes. Compute how many such transfers we // need to perform. let num_transfers = load_offsets.len.div_ceil(DMA_LEN); @@ -483,11 +463,11 @@ impl<E: FalconEngine + 'static> Falcon<E> { .and_then(|size| size.checked_add(load_offsets.src_start)) { None => { - dev_err!(self.dev, "DMA transfer length overflow"); + dev_err!(self.dev, "DMA transfer length overflow\n"); return Err(EOVERFLOW); } Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => { - dev_err!(self.dev, "DMA transfer goes beyond range of DMA object"); + dev_err!(self.dev, "DMA transfer goes beyond range of DMA object\n"); return Err(EINVAL); } Some(_) => (), @@ -508,8 +488,7 @@ impl<E: FalconEngine + 'static> Falcon<E> { let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default() .set_size(DmaTrfCmdSize::Size256B) - .set_imem(target_mem == FalconMem::Imem) - .set_sec(if sec { 1 } else { 0 }); + .with_falcon_mem(target_mem); for pos in (0..num_transfers).map(|i| i * DMA_LEN) { // Perform a transfer of size `DMA_LEN`. @@ -536,15 +515,22 @@ impl<E: FalconEngine + 'static> Falcon<E> { } /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it. - pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { + fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { + // The Non-Secure section only exists on firmware used by Turing and GA100, and + // those platforms do not use DMA. + if fw.imem_ns_load_params().is_some() { + debug_assert!(false); + return Err(EINVAL); + } + self.dma_reset(bar); regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| { v.set_target(FalconFbifTarget::CoherentSysmem) .set_mem_type(FalconFbifMemType::Physical) }); - self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?; - self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?; + self.dma_wr(bar, fw, FalconMem::ImemSecure, fw.imem_sec_load_params())?; + self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params())?; self.hal.program_brom(self, bar, &fw.brom_params())?; @@ -651,8 +637,15 @@ impl<E: FalconEngine + 'static> Falcon<E> { /// /// Returns `true` if the RISC-V core is active, `false` otherwise. pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool { - let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID); - cpuctl.active_stat() + self.hal.is_riscv_active(bar) + } + + // Load a firmware image into Falcon memory + pub(crate) fn load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result { + match self.hal.load_method() { + LoadMethod::Dma => self.dma_load(bar, fw), + LoadMethod::Pio => Err(ENOTSUPP), + } } /// Write the application version to the OS register. |
