xref: /linux/drivers/gpu/nova-core/falcon.rs (revision 220994d61cebfc04f071d69049127657c7e8191b)
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 hal::FalconHal;
769f5cd67SAlexandre Courbot use kernel::bindings;
869f5cd67SAlexandre Courbot use kernel::device;
969f5cd67SAlexandre Courbot use kernel::prelude::*;
104092e1b4SAlexandre Courbot use kernel::time::Delta;
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 
231b8233bbSDanilo Krummrich // TODO[FPRI]: Replace with `ToPrimitive`.
241b8233bbSDanilo Krummrich macro_rules! impl_from_enum_to_u32 {
251b8233bbSDanilo Krummrich     ($enum_type:ty) => {
261b8233bbSDanilo Krummrich         impl From<$enum_type> for u32 {
271b8233bbSDanilo Krummrich             fn from(value: $enum_type) -> Self {
281b8233bbSDanilo Krummrich                 value as u32
291b8233bbSDanilo Krummrich             }
301b8233bbSDanilo Krummrich         }
311b8233bbSDanilo Krummrich     };
321b8233bbSDanilo Krummrich }
331b8233bbSDanilo Krummrich 
3469f5cd67SAlexandre Courbot /// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
3569f5cd67SAlexandre Courbot /// register.
3669f5cd67SAlexandre Courbot #[repr(u8)]
3769f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
3869f5cd67SAlexandre Courbot pub(crate) enum FalconCoreRev {
3969f5cd67SAlexandre Courbot     #[default]
4069f5cd67SAlexandre Courbot     Rev1 = 1,
4169f5cd67SAlexandre Courbot     Rev2 = 2,
4269f5cd67SAlexandre Courbot     Rev3 = 3,
4369f5cd67SAlexandre Courbot     Rev4 = 4,
4469f5cd67SAlexandre Courbot     Rev5 = 5,
4569f5cd67SAlexandre Courbot     Rev6 = 6,
4669f5cd67SAlexandre Courbot     Rev7 = 7,
4769f5cd67SAlexandre Courbot }
481b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconCoreRev);
4969f5cd67SAlexandre Courbot 
503606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
5169f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconCoreRev {
5269f5cd67SAlexandre Courbot     type Error = Error;
5369f5cd67SAlexandre Courbot 
try_from(value: u8) -> Result<Self>5469f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
5569f5cd67SAlexandre Courbot         use FalconCoreRev::*;
5669f5cd67SAlexandre Courbot 
5769f5cd67SAlexandre Courbot         let rev = match value {
5869f5cd67SAlexandre Courbot             1 => Rev1,
5969f5cd67SAlexandre Courbot             2 => Rev2,
6069f5cd67SAlexandre Courbot             3 => Rev3,
6169f5cd67SAlexandre Courbot             4 => Rev4,
6269f5cd67SAlexandre Courbot             5 => Rev5,
6369f5cd67SAlexandre Courbot             6 => Rev6,
6469f5cd67SAlexandre Courbot             7 => Rev7,
6569f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
6669f5cd67SAlexandre Courbot         };
6769f5cd67SAlexandre Courbot 
6869f5cd67SAlexandre Courbot         Ok(rev)
6969f5cd67SAlexandre Courbot     }
7069f5cd67SAlexandre Courbot }
7169f5cd67SAlexandre Courbot 
7269f5cd67SAlexandre Courbot /// Revision subversion number of a falcon core, used in the
7369f5cd67SAlexandre Courbot /// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register.
7469f5cd67SAlexandre Courbot #[repr(u8)]
7569f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
7669f5cd67SAlexandre Courbot pub(crate) enum FalconCoreRevSubversion {
7769f5cd67SAlexandre Courbot     #[default]
7869f5cd67SAlexandre Courbot     Subversion0 = 0,
7969f5cd67SAlexandre Courbot     Subversion1 = 1,
8069f5cd67SAlexandre Courbot     Subversion2 = 2,
8169f5cd67SAlexandre Courbot     Subversion3 = 3,
8269f5cd67SAlexandre Courbot }
831b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconCoreRevSubversion);
8469f5cd67SAlexandre Courbot 
853606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
8669f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconCoreRevSubversion {
8769f5cd67SAlexandre Courbot     type Error = Error;
8869f5cd67SAlexandre Courbot 
try_from(value: u8) -> Result<Self>8969f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
9069f5cd67SAlexandre Courbot         use FalconCoreRevSubversion::*;
9169f5cd67SAlexandre Courbot 
9269f5cd67SAlexandre Courbot         let sub_version = match value & 0b11 {
9369f5cd67SAlexandre Courbot             0 => Subversion0,
9469f5cd67SAlexandre Courbot             1 => Subversion1,
9569f5cd67SAlexandre Courbot             2 => Subversion2,
9669f5cd67SAlexandre Courbot             3 => Subversion3,
9769f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
9869f5cd67SAlexandre Courbot         };
9969f5cd67SAlexandre Courbot 
10069f5cd67SAlexandre Courbot         Ok(sub_version)
10169f5cd67SAlexandre Courbot     }
10269f5cd67SAlexandre Courbot }
10369f5cd67SAlexandre Courbot 
10469f5cd67SAlexandre Courbot /// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
10569f5cd67SAlexandre Courbot /// register.
10669f5cd67SAlexandre Courbot #[repr(u8)]
10769f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone)]
108*0b980688SJoel Fernandes /// Security mode of the Falcon microprocessor.
109*0b980688SJoel Fernandes ///
110*0b980688SJoel Fernandes /// See `falcon.rst` for more details.
11169f5cd67SAlexandre Courbot pub(crate) enum FalconSecurityModel {
11269f5cd67SAlexandre Courbot     /// Non-Secure: runs unsigned code without privileges.
11369f5cd67SAlexandre Courbot     #[default]
11469f5cd67SAlexandre Courbot     None = 0,
115*0b980688SJoel Fernandes     /// Light-Secured (LS): Runs signed code with some privileges.
116*0b980688SJoel Fernandes     /// Entry into this mode is only possible from 'Heavy-secure' mode, which verifies the code's
117*0b980688SJoel Fernandes     /// signature.
118*0b980688SJoel Fernandes     ///
119*0b980688SJoel Fernandes     /// Also known as Low-Secure, Privilege Level 2 or PL2.
12069f5cd67SAlexandre Courbot     Light = 2,
121*0b980688SJoel Fernandes     /// Heavy-Secured (HS): Runs signed code with full privileges.
122*0b980688SJoel Fernandes     /// The code's signature is verified by the Falcon Boot ROM (BROM).
123*0b980688SJoel Fernandes     ///
124*0b980688SJoel Fernandes     /// Also known as High-Secure, Privilege Level 3 or PL3.
12569f5cd67SAlexandre Courbot     Heavy = 3,
12669f5cd67SAlexandre Courbot }
1271b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconSecurityModel);
12869f5cd67SAlexandre Courbot 
1293606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
13069f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconSecurityModel {
13169f5cd67SAlexandre Courbot     type Error = Error;
13269f5cd67SAlexandre Courbot 
try_from(value: u8) -> Result<Self>13369f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
13469f5cd67SAlexandre Courbot         use FalconSecurityModel::*;
13569f5cd67SAlexandre Courbot 
13669f5cd67SAlexandre Courbot         let sec_model = match value {
13769f5cd67SAlexandre Courbot             0 => None,
13869f5cd67SAlexandre Courbot             2 => Light,
13969f5cd67SAlexandre Courbot             3 => Heavy,
14069f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
14169f5cd67SAlexandre Courbot         };
14269f5cd67SAlexandre Courbot 
14369f5cd67SAlexandre Courbot         Ok(sec_model)
14469f5cd67SAlexandre Courbot     }
14569f5cd67SAlexandre Courbot }
14669f5cd67SAlexandre Courbot 
14769f5cd67SAlexandre Courbot /// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`]
148*0b980688SJoel Fernandes /// register. It is passed to the Falcon Boot ROM (BROM) as a parameter.
14969f5cd67SAlexandre Courbot #[repr(u8)]
15069f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
15169f5cd67SAlexandre Courbot pub(crate) enum FalconModSelAlgo {
152*0b980688SJoel Fernandes     /// AES.
153*0b980688SJoel Fernandes     #[expect(dead_code)]
154*0b980688SJoel Fernandes     Aes = 0,
15569f5cd67SAlexandre Courbot     /// RSA3K.
15669f5cd67SAlexandre Courbot     #[default]
15769f5cd67SAlexandre Courbot     Rsa3k = 1,
15869f5cd67SAlexandre Courbot }
1591b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconModSelAlgo);
16069f5cd67SAlexandre Courbot 
1613606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
16269f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconModSelAlgo {
16369f5cd67SAlexandre Courbot     type Error = Error;
16469f5cd67SAlexandre Courbot 
try_from(value: u8) -> Result<Self>16569f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
16669f5cd67SAlexandre Courbot         match value {
16769f5cd67SAlexandre Courbot             1 => Ok(FalconModSelAlgo::Rsa3k),
16869f5cd67SAlexandre Courbot             _ => Err(EINVAL),
16969f5cd67SAlexandre Courbot         }
17069f5cd67SAlexandre Courbot     }
17169f5cd67SAlexandre Courbot }
17269f5cd67SAlexandre Courbot 
17369f5cd67SAlexandre Courbot /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register.
17469f5cd67SAlexandre Courbot #[repr(u8)]
17569f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
17669f5cd67SAlexandre Courbot pub(crate) enum DmaTrfCmdSize {
17769f5cd67SAlexandre Courbot     /// 256 bytes transfer.
17869f5cd67SAlexandre Courbot     #[default]
17969f5cd67SAlexandre Courbot     Size256B = 0x6,
18069f5cd67SAlexandre Courbot }
1811b8233bbSDanilo Krummrich impl_from_enum_to_u32!(DmaTrfCmdSize);
18269f5cd67SAlexandre Courbot 
1833606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
18469f5cd67SAlexandre Courbot impl TryFrom<u8> for DmaTrfCmdSize {
18569f5cd67SAlexandre Courbot     type Error = Error;
18669f5cd67SAlexandre Courbot 
try_from(value: u8) -> Result<Self>18769f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
18869f5cd67SAlexandre Courbot         match value {
18969f5cd67SAlexandre Courbot             0x6 => Ok(Self::Size256B),
19069f5cd67SAlexandre Courbot             _ => Err(EINVAL),
19169f5cd67SAlexandre Courbot         }
19269f5cd67SAlexandre Courbot     }
19369f5cd67SAlexandre Courbot }
19469f5cd67SAlexandre Courbot 
19569f5cd67SAlexandre Courbot /// Currently active core on a dual falcon/riscv (Peregrine) controller.
19669f5cd67SAlexandre Courbot #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
19769f5cd67SAlexandre Courbot pub(crate) enum PeregrineCoreSelect {
19869f5cd67SAlexandre Courbot     /// Falcon core is active.
19969f5cd67SAlexandre Courbot     #[default]
20069f5cd67SAlexandre Courbot     Falcon = 0,
20169f5cd67SAlexandre Courbot     /// RISC-V core is active.
20269f5cd67SAlexandre Courbot     Riscv = 1,
20369f5cd67SAlexandre Courbot }
2041b8233bbSDanilo Krummrich impl_from_enum_to_u32!(PeregrineCoreSelect);
20569f5cd67SAlexandre Courbot 
20669f5cd67SAlexandre Courbot impl From<bool> for PeregrineCoreSelect {
from(value: bool) -> Self20769f5cd67SAlexandre Courbot     fn from(value: bool) -> Self {
20869f5cd67SAlexandre Courbot         match value {
20969f5cd67SAlexandre Courbot             false => PeregrineCoreSelect::Falcon,
21069f5cd67SAlexandre Courbot             true => PeregrineCoreSelect::Riscv,
21169f5cd67SAlexandre Courbot         }
21269f5cd67SAlexandre Courbot     }
21369f5cd67SAlexandre Courbot }
21469f5cd67SAlexandre Courbot 
21569f5cd67SAlexandre Courbot /// Different types of memory present in a falcon core.
21669f5cd67SAlexandre Courbot #[derive(Debug, Clone, Copy, PartialEq, Eq)]
21769f5cd67SAlexandre Courbot pub(crate) enum FalconMem {
21869f5cd67SAlexandre Courbot     /// Instruction Memory.
21969f5cd67SAlexandre Courbot     Imem,
22069f5cd67SAlexandre Courbot     /// Data Memory.
22169f5cd67SAlexandre Courbot     Dmem,
22269f5cd67SAlexandre Courbot }
22369f5cd67SAlexandre Courbot 
224*0b980688SJoel Fernandes /// Defines the Framebuffer Interface (FBIF) aperture type.
225*0b980688SJoel Fernandes /// This determines the memory type for external memory access during a DMA transfer, which is
226*0b980688SJoel Fernandes /// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details.
22769f5cd67SAlexandre Courbot #[derive(Debug, Clone, Default)]
22869f5cd67SAlexandre Courbot pub(crate) enum FalconFbifTarget {
22969f5cd67SAlexandre Courbot     /// VRAM.
23069f5cd67SAlexandre Courbot     #[default]
231*0b980688SJoel Fernandes     /// Local Framebuffer (GPU's VRAM memory).
23269f5cd67SAlexandre Courbot     LocalFb = 0,
233*0b980688SJoel Fernandes     /// Coherent system memory (System DRAM).
23469f5cd67SAlexandre Courbot     CoherentSysmem = 1,
235*0b980688SJoel Fernandes     /// Non-coherent system memory (System DRAM).
23669f5cd67SAlexandre Courbot     NoncoherentSysmem = 2,
23769f5cd67SAlexandre Courbot }
2381b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconFbifTarget);
23969f5cd67SAlexandre Courbot 
2403606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
24169f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconFbifTarget {
24269f5cd67SAlexandre Courbot     type Error = Error;
24369f5cd67SAlexandre Courbot 
try_from(value: u8) -> Result<Self>24469f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
24569f5cd67SAlexandre Courbot         let res = match value {
24669f5cd67SAlexandre Courbot             0 => Self::LocalFb,
24769f5cd67SAlexandre Courbot             1 => Self::CoherentSysmem,
24869f5cd67SAlexandre Courbot             2 => Self::NoncoherentSysmem,
24969f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
25069f5cd67SAlexandre Courbot         };
25169f5cd67SAlexandre Courbot 
25269f5cd67SAlexandre Courbot         Ok(res)
25369f5cd67SAlexandre Courbot     }
25469f5cd67SAlexandre Courbot }
25569f5cd67SAlexandre Courbot 
25669f5cd67SAlexandre Courbot /// Type of memory addresses to use.
25769f5cd67SAlexandre Courbot #[derive(Debug, Clone, Default)]
25869f5cd67SAlexandre Courbot pub(crate) enum FalconFbifMemType {
25969f5cd67SAlexandre Courbot     /// Virtual memory addresses.
26069f5cd67SAlexandre Courbot     #[default]
26169f5cd67SAlexandre Courbot     Virtual = 0,
26269f5cd67SAlexandre Courbot     /// Physical memory addresses.
26369f5cd67SAlexandre Courbot     Physical = 1,
26469f5cd67SAlexandre Courbot }
2651b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconFbifMemType);
26669f5cd67SAlexandre Courbot 
26769f5cd67SAlexandre Courbot /// Conversion from a single-bit register field.
26869f5cd67SAlexandre Courbot impl From<bool> for FalconFbifMemType {
from(value: bool) -> Self26969f5cd67SAlexandre Courbot     fn from(value: bool) -> Self {
27069f5cd67SAlexandre Courbot         match value {
27169f5cd67SAlexandre Courbot             false => Self::Virtual,
27269f5cd67SAlexandre Courbot             true => Self::Physical,
27369f5cd67SAlexandre Courbot         }
27469f5cd67SAlexandre Courbot     }
27569f5cd67SAlexandre Courbot }
27669f5cd67SAlexandre Courbot 
27769f5cd67SAlexandre Courbot /// Trait defining the parameters of a given Falcon instance.
27869f5cd67SAlexandre Courbot pub(crate) trait FalconEngine: Sync {
27969f5cd67SAlexandre Courbot     /// Base I/O address for the falcon, relative from which its registers are accessed.
28069f5cd67SAlexandre Courbot     const BASE: usize;
28169f5cd67SAlexandre Courbot }
28269f5cd67SAlexandre Courbot 
28369f5cd67SAlexandre Courbot /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
28469f5cd67SAlexandre Courbot #[derive(Debug)]
28569f5cd67SAlexandre Courbot pub(crate) struct FalconLoadTarget {
28669f5cd67SAlexandre Courbot     /// Offset from the start of the source object to copy from.
28769f5cd67SAlexandre Courbot     pub(crate) src_start: u32,
28869f5cd67SAlexandre Courbot     /// Offset from the start of the destination memory to copy into.
28969f5cd67SAlexandre Courbot     pub(crate) dst_start: u32,
29069f5cd67SAlexandre Courbot     /// Number of bytes to copy.
29169f5cd67SAlexandre Courbot     pub(crate) len: u32,
29269f5cd67SAlexandre Courbot }
29369f5cd67SAlexandre Courbot 
29469f5cd67SAlexandre Courbot /// Parameters for the falcon boot ROM.
29569f5cd67SAlexandre Courbot #[derive(Debug)]
29669f5cd67SAlexandre Courbot pub(crate) struct FalconBromParams {
29769f5cd67SAlexandre Courbot     /// Offset in `DMEM`` of the firmware's signature.
29869f5cd67SAlexandre Courbot     pub(crate) pkc_data_offset: u32,
29969f5cd67SAlexandre Courbot     /// Mask of engines valid for this firmware.
30069f5cd67SAlexandre Courbot     pub(crate) engine_id_mask: u16,
30169f5cd67SAlexandre Courbot     /// ID of the ucode used to infer a fuse register to validate the signature.
30269f5cd67SAlexandre Courbot     pub(crate) ucode_id: u8,
30369f5cd67SAlexandre Courbot }
30469f5cd67SAlexandre Courbot 
30569f5cd67SAlexandre Courbot /// Trait for providing load parameters of falcon firmwares.
30669f5cd67SAlexandre Courbot pub(crate) trait FalconLoadParams {
30769f5cd67SAlexandre Courbot     /// Returns the load parameters for `IMEM`.
imem_load_params(&self) -> FalconLoadTarget30869f5cd67SAlexandre Courbot     fn imem_load_params(&self) -> FalconLoadTarget;
30969f5cd67SAlexandre Courbot 
31069f5cd67SAlexandre Courbot     /// Returns the load parameters for `DMEM`.
dmem_load_params(&self) -> FalconLoadTarget31169f5cd67SAlexandre Courbot     fn dmem_load_params(&self) -> FalconLoadTarget;
31269f5cd67SAlexandre Courbot 
31369f5cd67SAlexandre Courbot     /// Returns the parameters to write into the BROM registers.
brom_params(&self) -> FalconBromParams31469f5cd67SAlexandre Courbot     fn brom_params(&self) -> FalconBromParams;
31569f5cd67SAlexandre Courbot 
31669f5cd67SAlexandre Courbot     /// Returns the start address of the firmware.
boot_addr(&self) -> u3231769f5cd67SAlexandre Courbot     fn boot_addr(&self) -> u32;
31869f5cd67SAlexandre Courbot }
31969f5cd67SAlexandre Courbot 
32069f5cd67SAlexandre Courbot /// Trait for a falcon firmware.
32169f5cd67SAlexandre Courbot ///
32269f5cd67SAlexandre Courbot /// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
32369f5cd67SAlexandre Courbot /// object.
32469f5cd67SAlexandre Courbot pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
32569f5cd67SAlexandre Courbot     /// Engine on which this firmware is to be loaded.
32669f5cd67SAlexandre Courbot     type Target: FalconEngine;
32769f5cd67SAlexandre Courbot }
32869f5cd67SAlexandre Courbot 
32969f5cd67SAlexandre Courbot /// Contains the base parameters common to all Falcon instances.
33069f5cd67SAlexandre Courbot pub(crate) struct Falcon<E: FalconEngine> {
33169f5cd67SAlexandre Courbot     hal: KBox<dyn FalconHal<E>>,
33269f5cd67SAlexandre Courbot     dev: ARef<device::Device>,
33369f5cd67SAlexandre Courbot }
33469f5cd67SAlexandre Courbot 
33569f5cd67SAlexandre Courbot impl<E: FalconEngine + 'static> Falcon<E> {
33669f5cd67SAlexandre Courbot     /// Create a new falcon instance.
33769f5cd67SAlexandre Courbot     ///
33869f5cd67SAlexandre Courbot     /// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv
33969f5cd67SAlexandre Courbot     /// controller.
new( dev: &device::Device, chipset: Chipset, bar: &Bar0, need_riscv: bool, ) -> Result<Self>34069f5cd67SAlexandre Courbot     pub(crate) fn new(
34169f5cd67SAlexandre Courbot         dev: &device::Device,
34269f5cd67SAlexandre Courbot         chipset: Chipset,
34369f5cd67SAlexandre Courbot         bar: &Bar0,
34469f5cd67SAlexandre Courbot         need_riscv: bool,
34569f5cd67SAlexandre Courbot     ) -> Result<Self> {
34669f5cd67SAlexandre Courbot         let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, E::BASE);
34769f5cd67SAlexandre Courbot         // Check that the revision and security model contain valid values.
34869f5cd67SAlexandre Courbot         let _ = hwcfg1.core_rev()?;
34969f5cd67SAlexandre Courbot         let _ = hwcfg1.security_model()?;
35069f5cd67SAlexandre Courbot 
35169f5cd67SAlexandre Courbot         if need_riscv {
35269f5cd67SAlexandre Courbot             let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
35369f5cd67SAlexandre Courbot             if !hwcfg2.riscv() {
35469f5cd67SAlexandre Courbot                 dev_err!(
35569f5cd67SAlexandre Courbot                     dev,
35669f5cd67SAlexandre Courbot                     "riscv support requested on a controller that does not support it\n"
35769f5cd67SAlexandre Courbot                 );
35869f5cd67SAlexandre Courbot                 return Err(EINVAL);
35969f5cd67SAlexandre Courbot             }
36069f5cd67SAlexandre Courbot         }
36169f5cd67SAlexandre Courbot 
36269f5cd67SAlexandre Courbot         Ok(Self {
36369f5cd67SAlexandre Courbot             hal: hal::falcon_hal(chipset)?,
36469f5cd67SAlexandre Courbot             dev: dev.into(),
36569f5cd67SAlexandre Courbot         })
36669f5cd67SAlexandre Courbot     }
36769f5cd67SAlexandre Courbot 
36869f5cd67SAlexandre Courbot     /// Wait for memory scrubbing to complete.
reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result36969f5cd67SAlexandre Courbot     fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
37069f5cd67SAlexandre Courbot         // TIMEOUT: memory scrubbing should complete in less than 20ms.
3714092e1b4SAlexandre Courbot         util::wait_on(Delta::from_millis(20), || {
37269f5cd67SAlexandre Courbot             if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE).mem_scrubbing_done() {
37369f5cd67SAlexandre Courbot                 Some(())
37469f5cd67SAlexandre Courbot             } else {
37569f5cd67SAlexandre Courbot                 None
37669f5cd67SAlexandre Courbot             }
37769f5cd67SAlexandre Courbot         })
37869f5cd67SAlexandre Courbot     }
37969f5cd67SAlexandre Courbot 
38069f5cd67SAlexandre Courbot     /// Reset the falcon engine.
reset_eng(&self, bar: &Bar0) -> Result38169f5cd67SAlexandre Courbot     fn reset_eng(&self, bar: &Bar0) -> Result {
38269f5cd67SAlexandre Courbot         let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
38369f5cd67SAlexandre Courbot 
38469f5cd67SAlexandre Courbot         // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
38569f5cd67SAlexandre Courbot         // RESET_READY so a non-failing timeout is used.
3864092e1b4SAlexandre Courbot         let _ = util::wait_on(Delta::from_micros(150), || {
38769f5cd67SAlexandre Courbot             let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
38869f5cd67SAlexandre Courbot             if r.reset_ready() {
38969f5cd67SAlexandre Courbot                 Some(())
39069f5cd67SAlexandre Courbot             } else {
39169f5cd67SAlexandre Courbot                 None
39269f5cd67SAlexandre Courbot             }
39369f5cd67SAlexandre Courbot         });
39469f5cd67SAlexandre Courbot 
39569f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(true));
39669f5cd67SAlexandre Courbot 
3973606620bSAlexandre Courbot         // TODO[DLAY]: replace with udelay() or equivalent once available.
39869f5cd67SAlexandre Courbot         // TIMEOUT: falcon engine should not take more than 10us to reset.
3994092e1b4SAlexandre Courbot         let _: Result = util::wait_on(Delta::from_micros(10), || None);
40069f5cd67SAlexandre Courbot 
40169f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(false));
40269f5cd67SAlexandre Courbot 
40369f5cd67SAlexandre Courbot         self.reset_wait_mem_scrubbing(bar)?;
40469f5cd67SAlexandre Courbot 
40569f5cd67SAlexandre Courbot         Ok(())
40669f5cd67SAlexandre Courbot     }
40769f5cd67SAlexandre Courbot 
40869f5cd67SAlexandre Courbot     /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
reset(&self, bar: &Bar0) -> Result40969f5cd67SAlexandre Courbot     pub(crate) fn reset(&self, bar: &Bar0) -> Result {
41069f5cd67SAlexandre Courbot         self.reset_eng(bar)?;
41169f5cd67SAlexandre Courbot         self.hal.select_core(self, bar)?;
41269f5cd67SAlexandre Courbot         self.reset_wait_mem_scrubbing(bar)?;
41369f5cd67SAlexandre Courbot 
41469f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_RM::default()
41569f5cd67SAlexandre Courbot             .set_value(regs::NV_PMC_BOOT_0::read(bar).into())
41669f5cd67SAlexandre Courbot             .write(bar, E::BASE);
41769f5cd67SAlexandre Courbot 
41869f5cd67SAlexandre Courbot         Ok(())
41969f5cd67SAlexandre Courbot     }
42069f5cd67SAlexandre Courbot 
42169f5cd67SAlexandre Courbot     /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
42269f5cd67SAlexandre Courbot     /// `target_mem`.
42369f5cd67SAlexandre Courbot     ///
42469f5cd67SAlexandre Courbot     /// `sec` is set if the loaded firmware is expected to run in secure mode.
dma_wr<F: FalconFirmware<Target = E>>( &self, bar: &Bar0, fw: &F, target_mem: FalconMem, load_offsets: FalconLoadTarget, sec: bool, ) -> Result42569f5cd67SAlexandre Courbot     fn dma_wr<F: FalconFirmware<Target = E>>(
42669f5cd67SAlexandre Courbot         &self,
42769f5cd67SAlexandre Courbot         bar: &Bar0,
42869f5cd67SAlexandre Courbot         fw: &F,
42969f5cd67SAlexandre Courbot         target_mem: FalconMem,
43069f5cd67SAlexandre Courbot         load_offsets: FalconLoadTarget,
43169f5cd67SAlexandre Courbot         sec: bool,
43269f5cd67SAlexandre Courbot     ) -> Result {
43369f5cd67SAlexandre Courbot         const DMA_LEN: u32 = 256;
43469f5cd67SAlexandre Courbot 
43569f5cd67SAlexandre Courbot         // For IMEM, we want to use the start offset as a virtual address tag for each page, since
43669f5cd67SAlexandre Courbot         // code addresses in the firmware (and the boot vector) are virtual.
43769f5cd67SAlexandre Courbot         //
43869f5cd67SAlexandre Courbot         // For DMEM we can fold the start offset into the DMA handle.
43969f5cd67SAlexandre Courbot         let (src_start, dma_start) = match target_mem {
44069f5cd67SAlexandre Courbot             FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()),
44169f5cd67SAlexandre Courbot             FalconMem::Dmem => (
44269f5cd67SAlexandre Courbot                 0,
44369f5cd67SAlexandre Courbot                 fw.dma_handle_with_offset(load_offsets.src_start as usize)?,
44469f5cd67SAlexandre Courbot             ),
44569f5cd67SAlexandre Courbot         };
44643ad65ecSDanilo Krummrich         if dma_start % bindings::dma_addr_t::from(DMA_LEN) > 0 {
44769f5cd67SAlexandre Courbot             dev_err!(
44869f5cd67SAlexandre Courbot                 self.dev,
44969f5cd67SAlexandre Courbot                 "DMA transfer start addresses must be a multiple of {}",
45069f5cd67SAlexandre Courbot                 DMA_LEN
45169f5cd67SAlexandre Courbot             );
45269f5cd67SAlexandre Courbot             return Err(EINVAL);
45369f5cd67SAlexandre Courbot         }
45469f5cd67SAlexandre Courbot         if load_offsets.len % DMA_LEN > 0 {
45569f5cd67SAlexandre Courbot             dev_err!(
45669f5cd67SAlexandre Courbot                 self.dev,
45769f5cd67SAlexandre Courbot                 "DMA transfer length must be a multiple of {}",
45869f5cd67SAlexandre Courbot                 DMA_LEN
45969f5cd67SAlexandre Courbot             );
46069f5cd67SAlexandre Courbot             return Err(EINVAL);
46169f5cd67SAlexandre Courbot         }
46269f5cd67SAlexandre Courbot 
46369f5cd67SAlexandre Courbot         // Set up the base source DMA address.
46469f5cd67SAlexandre Courbot 
46569f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMATRFBASE::default()
46669f5cd67SAlexandre Courbot             .set_base((dma_start >> 8) as u32)
46769f5cd67SAlexandre Courbot             .write(bar, E::BASE);
46869f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
46969f5cd67SAlexandre Courbot             .set_base((dma_start >> 40) as u16)
47069f5cd67SAlexandre Courbot             .write(bar, E::BASE);
47169f5cd67SAlexandre Courbot 
47269f5cd67SAlexandre Courbot         let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
47369f5cd67SAlexandre Courbot             .set_size(DmaTrfCmdSize::Size256B)
47469f5cd67SAlexandre Courbot             .set_imem(target_mem == FalconMem::Imem)
47569f5cd67SAlexandre Courbot             .set_sec(if sec { 1 } else { 0 });
47669f5cd67SAlexandre Courbot 
47769f5cd67SAlexandre Courbot         for pos in (0..load_offsets.len).step_by(DMA_LEN as usize) {
47869f5cd67SAlexandre Courbot             // Perform a transfer of size `DMA_LEN`.
47969f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
48069f5cd67SAlexandre Courbot                 .set_offs(load_offsets.dst_start + pos)
48169f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
48269f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
48369f5cd67SAlexandre Courbot                 .set_offs(src_start + pos)
48469f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
48569f5cd67SAlexandre Courbot             cmd.write(bar, E::BASE);
48669f5cd67SAlexandre Courbot 
48769f5cd67SAlexandre Courbot             // Wait for the transfer to complete.
48869f5cd67SAlexandre Courbot             // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
48969f5cd67SAlexandre Courbot             // should ever take that long.
4904092e1b4SAlexandre Courbot             util::wait_on(Delta::from_secs(2), || {
49169f5cd67SAlexandre Courbot                 let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, E::BASE);
49269f5cd67SAlexandre Courbot                 if r.idle() {
49369f5cd67SAlexandre Courbot                     Some(())
49469f5cd67SAlexandre Courbot                 } else {
49569f5cd67SAlexandre Courbot                     None
49669f5cd67SAlexandre Courbot                 }
49769f5cd67SAlexandre Courbot             })?;
49869f5cd67SAlexandre Courbot         }
49969f5cd67SAlexandre Courbot 
50069f5cd67SAlexandre Courbot         Ok(())
50169f5cd67SAlexandre Courbot     }
50269f5cd67SAlexandre Courbot 
50369f5cd67SAlexandre Courbot     /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result50469f5cd67SAlexandre Courbot     pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
50569f5cd67SAlexandre Courbot         regs::NV_PFALCON_FBIF_CTL::alter(bar, E::BASE, |v| v.set_allow_phys_no_ctx(true));
50669f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, E::BASE);
50769f5cd67SAlexandre Courbot         regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, E::BASE, |v| {
50869f5cd67SAlexandre Courbot             v.set_target(FalconFbifTarget::CoherentSysmem)
50969f5cd67SAlexandre Courbot                 .set_mem_type(FalconFbifMemType::Physical)
51069f5cd67SAlexandre Courbot         });
51169f5cd67SAlexandre Courbot 
51269f5cd67SAlexandre Courbot         self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?;
51369f5cd67SAlexandre Courbot         self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?;
51469f5cd67SAlexandre Courbot 
51569f5cd67SAlexandre Courbot         self.hal.program_brom(self, bar, &fw.brom_params())?;
51669f5cd67SAlexandre Courbot 
51769f5cd67SAlexandre Courbot         // Set `BootVec` to start of non-secure code.
51869f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_BOOTVEC::default()
51969f5cd67SAlexandre Courbot             .set_value(fw.boot_addr())
52069f5cd67SAlexandre Courbot             .write(bar, E::BASE);
52169f5cd67SAlexandre Courbot 
52269f5cd67SAlexandre Courbot         Ok(())
52369f5cd67SAlexandre Courbot     }
52469f5cd67SAlexandre Courbot 
52569f5cd67SAlexandre Courbot     /// Runs the loaded firmware and waits for its completion.
52669f5cd67SAlexandre Courbot     ///
52769f5cd67SAlexandre Courbot     /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
52869f5cd67SAlexandre Courbot     /// prior to running.
52969f5cd67SAlexandre Courbot     ///
53069f5cd67SAlexandre Courbot     /// Wait up to two seconds for the firmware to complete, and return its exit status read from
53169f5cd67SAlexandre Courbot     /// the `MBOX0` and `MBOX1` registers.
boot( &self, bar: &Bar0, mbox0: Option<u32>, mbox1: Option<u32>, ) -> Result<(u32, u32)>53269f5cd67SAlexandre Courbot     pub(crate) fn boot(
53369f5cd67SAlexandre Courbot         &self,
53469f5cd67SAlexandre Courbot         bar: &Bar0,
53569f5cd67SAlexandre Courbot         mbox0: Option<u32>,
53669f5cd67SAlexandre Courbot         mbox1: Option<u32>,
53769f5cd67SAlexandre Courbot     ) -> Result<(u32, u32)> {
53869f5cd67SAlexandre Courbot         if let Some(mbox0) = mbox0 {
53969f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX0::default()
54069f5cd67SAlexandre Courbot                 .set_value(mbox0)
54169f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
54269f5cd67SAlexandre Courbot         }
54369f5cd67SAlexandre Courbot 
54469f5cd67SAlexandre Courbot         if let Some(mbox1) = mbox1 {
54569f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX1::default()
54669f5cd67SAlexandre Courbot                 .set_value(mbox1)
54769f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
54869f5cd67SAlexandre Courbot         }
54969f5cd67SAlexandre Courbot 
55069f5cd67SAlexandre Courbot         match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE).alias_en() {
55169f5cd67SAlexandre Courbot             true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
55269f5cd67SAlexandre Courbot                 .set_startcpu(true)
55369f5cd67SAlexandre Courbot                 .write(bar, E::BASE),
55469f5cd67SAlexandre Courbot             false => regs::NV_PFALCON_FALCON_CPUCTL::default()
55569f5cd67SAlexandre Courbot                 .set_startcpu(true)
55669f5cd67SAlexandre Courbot                 .write(bar, E::BASE),
55769f5cd67SAlexandre Courbot         }
55869f5cd67SAlexandre Courbot 
55969f5cd67SAlexandre Courbot         // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
5604092e1b4SAlexandre Courbot         util::wait_on(Delta::from_secs(2), || {
56169f5cd67SAlexandre Courbot             let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE);
56269f5cd67SAlexandre Courbot             if r.halted() {
56369f5cd67SAlexandre Courbot                 Some(())
56469f5cd67SAlexandre Courbot             } else {
56569f5cd67SAlexandre Courbot                 None
56669f5cd67SAlexandre Courbot             }
56769f5cd67SAlexandre Courbot         })?;
56869f5cd67SAlexandre Courbot 
56969f5cd67SAlexandre Courbot         let (mbox0, mbox1) = (
57069f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, E::BASE).value(),
57169f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, E::BASE).value(),
57269f5cd67SAlexandre Courbot         );
57369f5cd67SAlexandre Courbot 
57469f5cd67SAlexandre Courbot         Ok((mbox0, mbox1))
57569f5cd67SAlexandre Courbot     }
57669f5cd67SAlexandre Courbot 
57769f5cd67SAlexandre Courbot     /// Returns the fused version of the signature to use in order to run a HS firmware on this
57869f5cd67SAlexandre Courbot     /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
signature_reg_fuse_version( &self, bar: &Bar0, engine_id_mask: u16, ucode_id: u8, ) -> Result<u32>57969f5cd67SAlexandre Courbot     pub(crate) fn signature_reg_fuse_version(
58069f5cd67SAlexandre Courbot         &self,
58169f5cd67SAlexandre Courbot         bar: &Bar0,
58269f5cd67SAlexandre Courbot         engine_id_mask: u16,
58369f5cd67SAlexandre Courbot         ucode_id: u8,
58469f5cd67SAlexandre Courbot     ) -> Result<u32> {
58569f5cd67SAlexandre Courbot         self.hal
58669f5cd67SAlexandre Courbot             .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
58769f5cd67SAlexandre Courbot     }
58869f5cd67SAlexandre Courbot }
589