xref: /linux/drivers/gpu/nova-core/falcon.rs (revision 3606620b316c29e3de8ff87b40828c722086a9c9)
169f5cd67SAlexandre Courbot // SPDX-License-Identifier: GPL-2.0
269f5cd67SAlexandre Courbot 
369f5cd67SAlexandre Courbot //! Falcon microprocessor base support
469f5cd67SAlexandre Courbot 
569f5cd67SAlexandre Courbot use core::ops::Deref;
669f5cd67SAlexandre Courbot use core::time::Duration;
769f5cd67SAlexandre Courbot use hal::FalconHal;
869f5cd67SAlexandre Courbot use kernel::bindings;
969f5cd67SAlexandre Courbot use kernel::device;
1069f5cd67SAlexandre Courbot use kernel::prelude::*;
1169f5cd67SAlexandre Courbot use kernel::types::ARef;
1269f5cd67SAlexandre Courbot 
1369f5cd67SAlexandre Courbot use crate::dma::DmaObject;
1469f5cd67SAlexandre Courbot use crate::driver::Bar0;
1569f5cd67SAlexandre Courbot use crate::gpu::Chipset;
1669f5cd67SAlexandre Courbot use crate::regs;
1769f5cd67SAlexandre Courbot use crate::util;
1869f5cd67SAlexandre Courbot 
1969f5cd67SAlexandre Courbot pub(crate) mod gsp;
2069f5cd67SAlexandre Courbot mod hal;
2169f5cd67SAlexandre Courbot pub(crate) mod sec2;
2269f5cd67SAlexandre Courbot 
2369f5cd67SAlexandre Courbot /// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
2469f5cd67SAlexandre Courbot /// register.
2569f5cd67SAlexandre Courbot #[repr(u8)]
2669f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
2769f5cd67SAlexandre Courbot pub(crate) enum FalconCoreRev {
2869f5cd67SAlexandre Courbot     #[default]
2969f5cd67SAlexandre Courbot     Rev1 = 1,
3069f5cd67SAlexandre Courbot     Rev2 = 2,
3169f5cd67SAlexandre Courbot     Rev3 = 3,
3269f5cd67SAlexandre Courbot     Rev4 = 4,
3369f5cd67SAlexandre Courbot     Rev5 = 5,
3469f5cd67SAlexandre Courbot     Rev6 = 6,
3569f5cd67SAlexandre Courbot     Rev7 = 7,
3669f5cd67SAlexandre Courbot }
3769f5cd67SAlexandre Courbot 
38*3606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
3969f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconCoreRev {
4069f5cd67SAlexandre Courbot     type Error = Error;
4169f5cd67SAlexandre Courbot 
4269f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
4369f5cd67SAlexandre Courbot         use FalconCoreRev::*;
4469f5cd67SAlexandre Courbot 
4569f5cd67SAlexandre Courbot         let rev = match value {
4669f5cd67SAlexandre Courbot             1 => Rev1,
4769f5cd67SAlexandre Courbot             2 => Rev2,
4869f5cd67SAlexandre Courbot             3 => Rev3,
4969f5cd67SAlexandre Courbot             4 => Rev4,
5069f5cd67SAlexandre Courbot             5 => Rev5,
5169f5cd67SAlexandre Courbot             6 => Rev6,
5269f5cd67SAlexandre Courbot             7 => Rev7,
5369f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
5469f5cd67SAlexandre Courbot         };
5569f5cd67SAlexandre Courbot 
5669f5cd67SAlexandre Courbot         Ok(rev)
5769f5cd67SAlexandre Courbot     }
5869f5cd67SAlexandre Courbot }
5969f5cd67SAlexandre Courbot 
6069f5cd67SAlexandre Courbot /// Revision subversion number of a falcon core, used in the
6169f5cd67SAlexandre Courbot /// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register.
6269f5cd67SAlexandre Courbot #[repr(u8)]
6369f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
6469f5cd67SAlexandre Courbot pub(crate) enum FalconCoreRevSubversion {
6569f5cd67SAlexandre Courbot     #[default]
6669f5cd67SAlexandre Courbot     Subversion0 = 0,
6769f5cd67SAlexandre Courbot     Subversion1 = 1,
6869f5cd67SAlexandre Courbot     Subversion2 = 2,
6969f5cd67SAlexandre Courbot     Subversion3 = 3,
7069f5cd67SAlexandre Courbot }
7169f5cd67SAlexandre Courbot 
72*3606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
7369f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconCoreRevSubversion {
7469f5cd67SAlexandre Courbot     type Error = Error;
7569f5cd67SAlexandre Courbot 
7669f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
7769f5cd67SAlexandre Courbot         use FalconCoreRevSubversion::*;
7869f5cd67SAlexandre Courbot 
7969f5cd67SAlexandre Courbot         let sub_version = match value & 0b11 {
8069f5cd67SAlexandre Courbot             0 => Subversion0,
8169f5cd67SAlexandre Courbot             1 => Subversion1,
8269f5cd67SAlexandre Courbot             2 => Subversion2,
8369f5cd67SAlexandre Courbot             3 => Subversion3,
8469f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
8569f5cd67SAlexandre Courbot         };
8669f5cd67SAlexandre Courbot 
8769f5cd67SAlexandre Courbot         Ok(sub_version)
8869f5cd67SAlexandre Courbot     }
8969f5cd67SAlexandre Courbot }
9069f5cd67SAlexandre Courbot 
9169f5cd67SAlexandre Courbot /// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
9269f5cd67SAlexandre Courbot /// register.
9369f5cd67SAlexandre Courbot #[repr(u8)]
9469f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone)]
9569f5cd67SAlexandre Courbot pub(crate) enum FalconSecurityModel {
9669f5cd67SAlexandre Courbot     /// Non-Secure: runs unsigned code without privileges.
9769f5cd67SAlexandre Courbot     #[default]
9869f5cd67SAlexandre Courbot     None = 0,
9969f5cd67SAlexandre Courbot     /// Low-Secure: runs code with some privileges. Can only be entered from `Heavy` mode, which
10069f5cd67SAlexandre Courbot     /// will typically validate the LS code through some signature.
10169f5cd67SAlexandre Courbot     Light = 2,
10269f5cd67SAlexandre Courbot     /// High-Secure: runs signed code with full privileges. Signature is validated by boot ROM.
10369f5cd67SAlexandre Courbot     Heavy = 3,
10469f5cd67SAlexandre Courbot }
10569f5cd67SAlexandre Courbot 
106*3606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
10769f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconSecurityModel {
10869f5cd67SAlexandre Courbot     type Error = Error;
10969f5cd67SAlexandre Courbot 
11069f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
11169f5cd67SAlexandre Courbot         use FalconSecurityModel::*;
11269f5cd67SAlexandre Courbot 
11369f5cd67SAlexandre Courbot         let sec_model = match value {
11469f5cd67SAlexandre Courbot             0 => None,
11569f5cd67SAlexandre Courbot             2 => Light,
11669f5cd67SAlexandre Courbot             3 => Heavy,
11769f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
11869f5cd67SAlexandre Courbot         };
11969f5cd67SAlexandre Courbot 
12069f5cd67SAlexandre Courbot         Ok(sec_model)
12169f5cd67SAlexandre Courbot     }
12269f5cd67SAlexandre Courbot }
12369f5cd67SAlexandre Courbot 
12469f5cd67SAlexandre Courbot /// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`]
12569f5cd67SAlexandre Courbot /// register.
12669f5cd67SAlexandre Courbot #[repr(u8)]
12769f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
12869f5cd67SAlexandre Courbot pub(crate) enum FalconModSelAlgo {
12969f5cd67SAlexandre Courbot     /// RSA3K.
13069f5cd67SAlexandre Courbot     #[default]
13169f5cd67SAlexandre Courbot     Rsa3k = 1,
13269f5cd67SAlexandre Courbot }
13369f5cd67SAlexandre Courbot 
134*3606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
13569f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconModSelAlgo {
13669f5cd67SAlexandre Courbot     type Error = Error;
13769f5cd67SAlexandre Courbot 
13869f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
13969f5cd67SAlexandre Courbot         match value {
14069f5cd67SAlexandre Courbot             1 => Ok(FalconModSelAlgo::Rsa3k),
14169f5cd67SAlexandre Courbot             _ => Err(EINVAL),
14269f5cd67SAlexandre Courbot         }
14369f5cd67SAlexandre Courbot     }
14469f5cd67SAlexandre Courbot }
14569f5cd67SAlexandre Courbot 
14669f5cd67SAlexandre Courbot /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register.
14769f5cd67SAlexandre Courbot #[repr(u8)]
14869f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
14969f5cd67SAlexandre Courbot pub(crate) enum DmaTrfCmdSize {
15069f5cd67SAlexandre Courbot     /// 256 bytes transfer.
15169f5cd67SAlexandre Courbot     #[default]
15269f5cd67SAlexandre Courbot     Size256B = 0x6,
15369f5cd67SAlexandre Courbot }
15469f5cd67SAlexandre Courbot 
155*3606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
15669f5cd67SAlexandre Courbot impl TryFrom<u8> for DmaTrfCmdSize {
15769f5cd67SAlexandre Courbot     type Error = Error;
15869f5cd67SAlexandre Courbot 
15969f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
16069f5cd67SAlexandre Courbot         match value {
16169f5cd67SAlexandre Courbot             0x6 => Ok(Self::Size256B),
16269f5cd67SAlexandre Courbot             _ => Err(EINVAL),
16369f5cd67SAlexandre Courbot         }
16469f5cd67SAlexandre Courbot     }
16569f5cd67SAlexandre Courbot }
16669f5cd67SAlexandre Courbot 
16769f5cd67SAlexandre Courbot /// Currently active core on a dual falcon/riscv (Peregrine) controller.
16869f5cd67SAlexandre Courbot #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16969f5cd67SAlexandre Courbot pub(crate) enum PeregrineCoreSelect {
17069f5cd67SAlexandre Courbot     /// Falcon core is active.
17169f5cd67SAlexandre Courbot     #[default]
17269f5cd67SAlexandre Courbot     Falcon = 0,
17369f5cd67SAlexandre Courbot     /// RISC-V core is active.
17469f5cd67SAlexandre Courbot     Riscv = 1,
17569f5cd67SAlexandre Courbot }
17669f5cd67SAlexandre Courbot 
17769f5cd67SAlexandre Courbot impl From<bool> for PeregrineCoreSelect {
17869f5cd67SAlexandre Courbot     fn from(value: bool) -> Self {
17969f5cd67SAlexandre Courbot         match value {
18069f5cd67SAlexandre Courbot             false => PeregrineCoreSelect::Falcon,
18169f5cd67SAlexandre Courbot             true => PeregrineCoreSelect::Riscv,
18269f5cd67SAlexandre Courbot         }
18369f5cd67SAlexandre Courbot     }
18469f5cd67SAlexandre Courbot }
18569f5cd67SAlexandre Courbot 
18669f5cd67SAlexandre Courbot /// Different types of memory present in a falcon core.
18769f5cd67SAlexandre Courbot #[derive(Debug, Clone, Copy, PartialEq, Eq)]
18869f5cd67SAlexandre Courbot pub(crate) enum FalconMem {
18969f5cd67SAlexandre Courbot     /// Instruction Memory.
19069f5cd67SAlexandre Courbot     Imem,
19169f5cd67SAlexandre Courbot     /// Data Memory.
19269f5cd67SAlexandre Courbot     Dmem,
19369f5cd67SAlexandre Courbot }
19469f5cd67SAlexandre Courbot 
19569f5cd67SAlexandre Courbot /// Target/source of a DMA transfer to/from falcon memory.
19669f5cd67SAlexandre Courbot #[derive(Debug, Clone, Default)]
19769f5cd67SAlexandre Courbot pub(crate) enum FalconFbifTarget {
19869f5cd67SAlexandre Courbot     /// VRAM.
19969f5cd67SAlexandre Courbot     #[default]
20069f5cd67SAlexandre Courbot     LocalFb = 0,
20169f5cd67SAlexandre Courbot     /// Coherent system memory.
20269f5cd67SAlexandre Courbot     CoherentSysmem = 1,
20369f5cd67SAlexandre Courbot     /// Non-coherent system memory.
20469f5cd67SAlexandre Courbot     NoncoherentSysmem = 2,
20569f5cd67SAlexandre Courbot }
20669f5cd67SAlexandre Courbot 
207*3606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
20869f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconFbifTarget {
20969f5cd67SAlexandre Courbot     type Error = Error;
21069f5cd67SAlexandre Courbot 
21169f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
21269f5cd67SAlexandre Courbot         let res = match value {
21369f5cd67SAlexandre Courbot             0 => Self::LocalFb,
21469f5cd67SAlexandre Courbot             1 => Self::CoherentSysmem,
21569f5cd67SAlexandre Courbot             2 => Self::NoncoherentSysmem,
21669f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
21769f5cd67SAlexandre Courbot         };
21869f5cd67SAlexandre Courbot 
21969f5cd67SAlexandre Courbot         Ok(res)
22069f5cd67SAlexandre Courbot     }
22169f5cd67SAlexandre Courbot }
22269f5cd67SAlexandre Courbot 
22369f5cd67SAlexandre Courbot /// Type of memory addresses to use.
22469f5cd67SAlexandre Courbot #[derive(Debug, Clone, Default)]
22569f5cd67SAlexandre Courbot pub(crate) enum FalconFbifMemType {
22669f5cd67SAlexandre Courbot     /// Virtual memory addresses.
22769f5cd67SAlexandre Courbot     #[default]
22869f5cd67SAlexandre Courbot     Virtual = 0,
22969f5cd67SAlexandre Courbot     /// Physical memory addresses.
23069f5cd67SAlexandre Courbot     Physical = 1,
23169f5cd67SAlexandre Courbot }
23269f5cd67SAlexandre Courbot 
23369f5cd67SAlexandre Courbot /// Conversion from a single-bit register field.
23469f5cd67SAlexandre Courbot impl From<bool> for FalconFbifMemType {
23569f5cd67SAlexandre Courbot     fn from(value: bool) -> Self {
23669f5cd67SAlexandre Courbot         match value {
23769f5cd67SAlexandre Courbot             false => Self::Virtual,
23869f5cd67SAlexandre Courbot             true => Self::Physical,
23969f5cd67SAlexandre Courbot         }
24069f5cd67SAlexandre Courbot     }
24169f5cd67SAlexandre Courbot }
24269f5cd67SAlexandre Courbot 
24369f5cd67SAlexandre Courbot /// Trait defining the parameters of a given Falcon instance.
24469f5cd67SAlexandre Courbot pub(crate) trait FalconEngine: Sync {
24569f5cd67SAlexandre Courbot     /// Base I/O address for the falcon, relative from which its registers are accessed.
24669f5cd67SAlexandre Courbot     const BASE: usize;
24769f5cd67SAlexandre Courbot }
24869f5cd67SAlexandre Courbot 
24969f5cd67SAlexandre Courbot /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
25069f5cd67SAlexandre Courbot #[derive(Debug)]
25169f5cd67SAlexandre Courbot pub(crate) struct FalconLoadTarget {
25269f5cd67SAlexandre Courbot     /// Offset from the start of the source object to copy from.
25369f5cd67SAlexandre Courbot     pub(crate) src_start: u32,
25469f5cd67SAlexandre Courbot     /// Offset from the start of the destination memory to copy into.
25569f5cd67SAlexandre Courbot     pub(crate) dst_start: u32,
25669f5cd67SAlexandre Courbot     /// Number of bytes to copy.
25769f5cd67SAlexandre Courbot     pub(crate) len: u32,
25869f5cd67SAlexandre Courbot }
25969f5cd67SAlexandre Courbot 
26069f5cd67SAlexandre Courbot /// Parameters for the falcon boot ROM.
26169f5cd67SAlexandre Courbot #[derive(Debug)]
26269f5cd67SAlexandre Courbot pub(crate) struct FalconBromParams {
26369f5cd67SAlexandre Courbot     /// Offset in `DMEM`` of the firmware's signature.
26469f5cd67SAlexandre Courbot     pub(crate) pkc_data_offset: u32,
26569f5cd67SAlexandre Courbot     /// Mask of engines valid for this firmware.
26669f5cd67SAlexandre Courbot     pub(crate) engine_id_mask: u16,
26769f5cd67SAlexandre Courbot     /// ID of the ucode used to infer a fuse register to validate the signature.
26869f5cd67SAlexandre Courbot     pub(crate) ucode_id: u8,
26969f5cd67SAlexandre Courbot }
27069f5cd67SAlexandre Courbot 
27169f5cd67SAlexandre Courbot /// Trait for providing load parameters of falcon firmwares.
27269f5cd67SAlexandre Courbot pub(crate) trait FalconLoadParams {
27369f5cd67SAlexandre Courbot     /// Returns the load parameters for `IMEM`.
27469f5cd67SAlexandre Courbot     fn imem_load_params(&self) -> FalconLoadTarget;
27569f5cd67SAlexandre Courbot 
27669f5cd67SAlexandre Courbot     /// Returns the load parameters for `DMEM`.
27769f5cd67SAlexandre Courbot     fn dmem_load_params(&self) -> FalconLoadTarget;
27869f5cd67SAlexandre Courbot 
27969f5cd67SAlexandre Courbot     /// Returns the parameters to write into the BROM registers.
28069f5cd67SAlexandre Courbot     fn brom_params(&self) -> FalconBromParams;
28169f5cd67SAlexandre Courbot 
28269f5cd67SAlexandre Courbot     /// Returns the start address of the firmware.
28369f5cd67SAlexandre Courbot     fn boot_addr(&self) -> u32;
28469f5cd67SAlexandre Courbot }
28569f5cd67SAlexandre Courbot 
28669f5cd67SAlexandre Courbot /// Trait for a falcon firmware.
28769f5cd67SAlexandre Courbot ///
28869f5cd67SAlexandre Courbot /// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
28969f5cd67SAlexandre Courbot /// object.
29069f5cd67SAlexandre Courbot pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
29169f5cd67SAlexandre Courbot     /// Engine on which this firmware is to be loaded.
29269f5cd67SAlexandre Courbot     type Target: FalconEngine;
29369f5cd67SAlexandre Courbot }
29469f5cd67SAlexandre Courbot 
29569f5cd67SAlexandre Courbot /// Contains the base parameters common to all Falcon instances.
29669f5cd67SAlexandre Courbot pub(crate) struct Falcon<E: FalconEngine> {
29769f5cd67SAlexandre Courbot     hal: KBox<dyn FalconHal<E>>,
29869f5cd67SAlexandre Courbot     dev: ARef<device::Device>,
29969f5cd67SAlexandre Courbot }
30069f5cd67SAlexandre Courbot 
30169f5cd67SAlexandre Courbot impl<E: FalconEngine + 'static> Falcon<E> {
30269f5cd67SAlexandre Courbot     /// Create a new falcon instance.
30369f5cd67SAlexandre Courbot     ///
30469f5cd67SAlexandre Courbot     /// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv
30569f5cd67SAlexandre Courbot     /// controller.
30669f5cd67SAlexandre Courbot     pub(crate) fn new(
30769f5cd67SAlexandre Courbot         dev: &device::Device,
30869f5cd67SAlexandre Courbot         chipset: Chipset,
30969f5cd67SAlexandre Courbot         bar: &Bar0,
31069f5cd67SAlexandre Courbot         need_riscv: bool,
31169f5cd67SAlexandre Courbot     ) -> Result<Self> {
31269f5cd67SAlexandre Courbot         let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, E::BASE);
31369f5cd67SAlexandre Courbot         // Check that the revision and security model contain valid values.
31469f5cd67SAlexandre Courbot         let _ = hwcfg1.core_rev()?;
31569f5cd67SAlexandre Courbot         let _ = hwcfg1.security_model()?;
31669f5cd67SAlexandre Courbot 
31769f5cd67SAlexandre Courbot         if need_riscv {
31869f5cd67SAlexandre Courbot             let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
31969f5cd67SAlexandre Courbot             if !hwcfg2.riscv() {
32069f5cd67SAlexandre Courbot                 dev_err!(
32169f5cd67SAlexandre Courbot                     dev,
32269f5cd67SAlexandre Courbot                     "riscv support requested on a controller that does not support it\n"
32369f5cd67SAlexandre Courbot                 );
32469f5cd67SAlexandre Courbot                 return Err(EINVAL);
32569f5cd67SAlexandre Courbot             }
32669f5cd67SAlexandre Courbot         }
32769f5cd67SAlexandre Courbot 
32869f5cd67SAlexandre Courbot         Ok(Self {
32969f5cd67SAlexandre Courbot             hal: hal::falcon_hal(chipset)?,
33069f5cd67SAlexandre Courbot             dev: dev.into(),
33169f5cd67SAlexandre Courbot         })
33269f5cd67SAlexandre Courbot     }
33369f5cd67SAlexandre Courbot 
33469f5cd67SAlexandre Courbot     /// Wait for memory scrubbing to complete.
33569f5cd67SAlexandre Courbot     fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
33669f5cd67SAlexandre Courbot         // TIMEOUT: memory scrubbing should complete in less than 20ms.
33769f5cd67SAlexandre Courbot         util::wait_on(Duration::from_millis(20), || {
33869f5cd67SAlexandre Courbot             if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE).mem_scrubbing_done() {
33969f5cd67SAlexandre Courbot                 Some(())
34069f5cd67SAlexandre Courbot             } else {
34169f5cd67SAlexandre Courbot                 None
34269f5cd67SAlexandre Courbot             }
34369f5cd67SAlexandre Courbot         })
34469f5cd67SAlexandre Courbot     }
34569f5cd67SAlexandre Courbot 
34669f5cd67SAlexandre Courbot     /// Reset the falcon engine.
34769f5cd67SAlexandre Courbot     fn reset_eng(&self, bar: &Bar0) -> Result {
34869f5cd67SAlexandre Courbot         let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
34969f5cd67SAlexandre Courbot 
35069f5cd67SAlexandre Courbot         // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
35169f5cd67SAlexandre Courbot         // RESET_READY so a non-failing timeout is used.
35269f5cd67SAlexandre Courbot         let _ = util::wait_on(Duration::from_micros(150), || {
35369f5cd67SAlexandre Courbot             let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
35469f5cd67SAlexandre Courbot             if r.reset_ready() {
35569f5cd67SAlexandre Courbot                 Some(())
35669f5cd67SAlexandre Courbot             } else {
35769f5cd67SAlexandre Courbot                 None
35869f5cd67SAlexandre Courbot             }
35969f5cd67SAlexandre Courbot         });
36069f5cd67SAlexandre Courbot 
36169f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(true));
36269f5cd67SAlexandre Courbot 
363*3606620bSAlexandre Courbot         // TODO[DLAY]: replace with udelay() or equivalent once available.
36469f5cd67SAlexandre Courbot         // TIMEOUT: falcon engine should not take more than 10us to reset.
36569f5cd67SAlexandre Courbot         let _: Result = util::wait_on(Duration::from_micros(10), || None);
36669f5cd67SAlexandre Courbot 
36769f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(false));
36869f5cd67SAlexandre Courbot 
36969f5cd67SAlexandre Courbot         self.reset_wait_mem_scrubbing(bar)?;
37069f5cd67SAlexandre Courbot 
37169f5cd67SAlexandre Courbot         Ok(())
37269f5cd67SAlexandre Courbot     }
37369f5cd67SAlexandre Courbot 
37469f5cd67SAlexandre Courbot     /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
37569f5cd67SAlexandre Courbot     pub(crate) fn reset(&self, bar: &Bar0) -> Result {
37669f5cd67SAlexandre Courbot         self.reset_eng(bar)?;
37769f5cd67SAlexandre Courbot         self.hal.select_core(self, bar)?;
37869f5cd67SAlexandre Courbot         self.reset_wait_mem_scrubbing(bar)?;
37969f5cd67SAlexandre Courbot 
38069f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_RM::default()
38169f5cd67SAlexandre Courbot             .set_value(regs::NV_PMC_BOOT_0::read(bar).into())
38269f5cd67SAlexandre Courbot             .write(bar, E::BASE);
38369f5cd67SAlexandre Courbot 
38469f5cd67SAlexandre Courbot         Ok(())
38569f5cd67SAlexandre Courbot     }
38669f5cd67SAlexandre Courbot 
38769f5cd67SAlexandre Courbot     /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
38869f5cd67SAlexandre Courbot     /// `target_mem`.
38969f5cd67SAlexandre Courbot     ///
39069f5cd67SAlexandre Courbot     /// `sec` is set if the loaded firmware is expected to run in secure mode.
39169f5cd67SAlexandre Courbot     fn dma_wr<F: FalconFirmware<Target = E>>(
39269f5cd67SAlexandre Courbot         &self,
39369f5cd67SAlexandre Courbot         bar: &Bar0,
39469f5cd67SAlexandre Courbot         fw: &F,
39569f5cd67SAlexandre Courbot         target_mem: FalconMem,
39669f5cd67SAlexandre Courbot         load_offsets: FalconLoadTarget,
39769f5cd67SAlexandre Courbot         sec: bool,
39869f5cd67SAlexandre Courbot     ) -> Result {
39969f5cd67SAlexandre Courbot         const DMA_LEN: u32 = 256;
40069f5cd67SAlexandre Courbot 
40169f5cd67SAlexandre Courbot         // For IMEM, we want to use the start offset as a virtual address tag for each page, since
40269f5cd67SAlexandre Courbot         // code addresses in the firmware (and the boot vector) are virtual.
40369f5cd67SAlexandre Courbot         //
40469f5cd67SAlexandre Courbot         // For DMEM we can fold the start offset into the DMA handle.
40569f5cd67SAlexandre Courbot         let (src_start, dma_start) = match target_mem {
40669f5cd67SAlexandre Courbot             FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()),
40769f5cd67SAlexandre Courbot             FalconMem::Dmem => (
40869f5cd67SAlexandre Courbot                 0,
40969f5cd67SAlexandre Courbot                 fw.dma_handle_with_offset(load_offsets.src_start as usize)?,
41069f5cd67SAlexandre Courbot             ),
41169f5cd67SAlexandre Courbot         };
41269f5cd67SAlexandre Courbot         if dma_start % DMA_LEN as bindings::dma_addr_t > 0 {
41369f5cd67SAlexandre Courbot             dev_err!(
41469f5cd67SAlexandre Courbot                 self.dev,
41569f5cd67SAlexandre Courbot                 "DMA transfer start addresses must be a multiple of {}",
41669f5cd67SAlexandre Courbot                 DMA_LEN
41769f5cd67SAlexandre Courbot             );
41869f5cd67SAlexandre Courbot             return Err(EINVAL);
41969f5cd67SAlexandre Courbot         }
42069f5cd67SAlexandre Courbot         if load_offsets.len % DMA_LEN > 0 {
42169f5cd67SAlexandre Courbot             dev_err!(
42269f5cd67SAlexandre Courbot                 self.dev,
42369f5cd67SAlexandre Courbot                 "DMA transfer length must be a multiple of {}",
42469f5cd67SAlexandre Courbot                 DMA_LEN
42569f5cd67SAlexandre Courbot             );
42669f5cd67SAlexandre Courbot             return Err(EINVAL);
42769f5cd67SAlexandre Courbot         }
42869f5cd67SAlexandre Courbot 
42969f5cd67SAlexandre Courbot         // Set up the base source DMA address.
43069f5cd67SAlexandre Courbot 
43169f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMATRFBASE::default()
43269f5cd67SAlexandre Courbot             .set_base((dma_start >> 8) as u32)
43369f5cd67SAlexandre Courbot             .write(bar, E::BASE);
43469f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
43569f5cd67SAlexandre Courbot             .set_base((dma_start >> 40) as u16)
43669f5cd67SAlexandre Courbot             .write(bar, E::BASE);
43769f5cd67SAlexandre Courbot 
43869f5cd67SAlexandre Courbot         let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
43969f5cd67SAlexandre Courbot             .set_size(DmaTrfCmdSize::Size256B)
44069f5cd67SAlexandre Courbot             .set_imem(target_mem == FalconMem::Imem)
44169f5cd67SAlexandre Courbot             .set_sec(if sec { 1 } else { 0 });
44269f5cd67SAlexandre Courbot 
44369f5cd67SAlexandre Courbot         for pos in (0..load_offsets.len).step_by(DMA_LEN as usize) {
44469f5cd67SAlexandre Courbot             // Perform a transfer of size `DMA_LEN`.
44569f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
44669f5cd67SAlexandre Courbot                 .set_offs(load_offsets.dst_start + pos)
44769f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
44869f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
44969f5cd67SAlexandre Courbot                 .set_offs(src_start + pos)
45069f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
45169f5cd67SAlexandre Courbot             cmd.write(bar, E::BASE);
45269f5cd67SAlexandre Courbot 
45369f5cd67SAlexandre Courbot             // Wait for the transfer to complete.
45469f5cd67SAlexandre Courbot             // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
45569f5cd67SAlexandre Courbot             // should ever take that long.
45669f5cd67SAlexandre Courbot             util::wait_on(Duration::from_secs(2), || {
45769f5cd67SAlexandre Courbot                 let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, E::BASE);
45869f5cd67SAlexandre Courbot                 if r.idle() {
45969f5cd67SAlexandre Courbot                     Some(())
46069f5cd67SAlexandre Courbot                 } else {
46169f5cd67SAlexandre Courbot                     None
46269f5cd67SAlexandre Courbot                 }
46369f5cd67SAlexandre Courbot             })?;
46469f5cd67SAlexandre Courbot         }
46569f5cd67SAlexandre Courbot 
46669f5cd67SAlexandre Courbot         Ok(())
46769f5cd67SAlexandre Courbot     }
46869f5cd67SAlexandre Courbot 
46969f5cd67SAlexandre Courbot     /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
47069f5cd67SAlexandre Courbot     pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
47169f5cd67SAlexandre Courbot         regs::NV_PFALCON_FBIF_CTL::alter(bar, E::BASE, |v| v.set_allow_phys_no_ctx(true));
47269f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, E::BASE);
47369f5cd67SAlexandre Courbot         regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, E::BASE, |v| {
47469f5cd67SAlexandre Courbot             v.set_target(FalconFbifTarget::CoherentSysmem)
47569f5cd67SAlexandre Courbot                 .set_mem_type(FalconFbifMemType::Physical)
47669f5cd67SAlexandre Courbot         });
47769f5cd67SAlexandre Courbot 
47869f5cd67SAlexandre Courbot         self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?;
47969f5cd67SAlexandre Courbot         self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?;
48069f5cd67SAlexandre Courbot 
48169f5cd67SAlexandre Courbot         self.hal.program_brom(self, bar, &fw.brom_params())?;
48269f5cd67SAlexandre Courbot 
48369f5cd67SAlexandre Courbot         // Set `BootVec` to start of non-secure code.
48469f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_BOOTVEC::default()
48569f5cd67SAlexandre Courbot             .set_value(fw.boot_addr())
48669f5cd67SAlexandre Courbot             .write(bar, E::BASE);
48769f5cd67SAlexandre Courbot 
48869f5cd67SAlexandre Courbot         Ok(())
48969f5cd67SAlexandre Courbot     }
49069f5cd67SAlexandre Courbot 
49169f5cd67SAlexandre Courbot     /// Runs the loaded firmware and waits for its completion.
49269f5cd67SAlexandre Courbot     ///
49369f5cd67SAlexandre Courbot     /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
49469f5cd67SAlexandre Courbot     /// prior to running.
49569f5cd67SAlexandre Courbot     ///
49669f5cd67SAlexandre Courbot     /// Wait up to two seconds for the firmware to complete, and return its exit status read from
49769f5cd67SAlexandre Courbot     /// the `MBOX0` and `MBOX1` registers.
49869f5cd67SAlexandre Courbot     pub(crate) fn boot(
49969f5cd67SAlexandre Courbot         &self,
50069f5cd67SAlexandre Courbot         bar: &Bar0,
50169f5cd67SAlexandre Courbot         mbox0: Option<u32>,
50269f5cd67SAlexandre Courbot         mbox1: Option<u32>,
50369f5cd67SAlexandre Courbot     ) -> Result<(u32, u32)> {
50469f5cd67SAlexandre Courbot         if let Some(mbox0) = mbox0 {
50569f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX0::default()
50669f5cd67SAlexandre Courbot                 .set_value(mbox0)
50769f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
50869f5cd67SAlexandre Courbot         }
50969f5cd67SAlexandre Courbot 
51069f5cd67SAlexandre Courbot         if let Some(mbox1) = mbox1 {
51169f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX1::default()
51269f5cd67SAlexandre Courbot                 .set_value(mbox1)
51369f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
51469f5cd67SAlexandre Courbot         }
51569f5cd67SAlexandre Courbot 
51669f5cd67SAlexandre Courbot         match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE).alias_en() {
51769f5cd67SAlexandre Courbot             true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
51869f5cd67SAlexandre Courbot                 .set_startcpu(true)
51969f5cd67SAlexandre Courbot                 .write(bar, E::BASE),
52069f5cd67SAlexandre Courbot             false => regs::NV_PFALCON_FALCON_CPUCTL::default()
52169f5cd67SAlexandre Courbot                 .set_startcpu(true)
52269f5cd67SAlexandre Courbot                 .write(bar, E::BASE),
52369f5cd67SAlexandre Courbot         }
52469f5cd67SAlexandre Courbot 
52569f5cd67SAlexandre Courbot         // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
52669f5cd67SAlexandre Courbot         util::wait_on(Duration::from_secs(2), || {
52769f5cd67SAlexandre Courbot             let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE);
52869f5cd67SAlexandre Courbot             if r.halted() {
52969f5cd67SAlexandre Courbot                 Some(())
53069f5cd67SAlexandre Courbot             } else {
53169f5cd67SAlexandre Courbot                 None
53269f5cd67SAlexandre Courbot             }
53369f5cd67SAlexandre Courbot         })?;
53469f5cd67SAlexandre Courbot 
53569f5cd67SAlexandre Courbot         let (mbox0, mbox1) = (
53669f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, E::BASE).value(),
53769f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, E::BASE).value(),
53869f5cd67SAlexandre Courbot         );
53969f5cd67SAlexandre Courbot 
54069f5cd67SAlexandre Courbot         Ok((mbox0, mbox1))
54169f5cd67SAlexandre Courbot     }
54269f5cd67SAlexandre Courbot 
54369f5cd67SAlexandre Courbot     /// Returns the fused version of the signature to use in order to run a HS firmware on this
54469f5cd67SAlexandre Courbot     /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
54569f5cd67SAlexandre Courbot     pub(crate) fn signature_reg_fuse_version(
54669f5cd67SAlexandre Courbot         &self,
54769f5cd67SAlexandre Courbot         bar: &Bar0,
54869f5cd67SAlexandre Courbot         engine_id_mask: u16,
54969f5cd67SAlexandre Courbot         ucode_id: u8,
55069f5cd67SAlexandre Courbot     ) -> Result<u32> {
55169f5cd67SAlexandre Courbot         self.hal
55269f5cd67SAlexandre Courbot             .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
55369f5cd67SAlexandre Courbot     }
55469f5cd67SAlexandre Courbot }
555