xref: /linux/drivers/gpu/nova-core/falcon.rs (revision 4092e1b41202ff39aad75a40a03ac1d318443670)
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::*;
10*4092e1b4SAlexandre 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 
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 
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)]
10869f5cd67SAlexandre Courbot pub(crate) enum FalconSecurityModel {
10969f5cd67SAlexandre Courbot     /// Non-Secure: runs unsigned code without privileges.
11069f5cd67SAlexandre Courbot     #[default]
11169f5cd67SAlexandre Courbot     None = 0,
11269f5cd67SAlexandre Courbot     /// Low-Secure: runs code with some privileges. Can only be entered from `Heavy` mode, which
11369f5cd67SAlexandre Courbot     /// will typically validate the LS code through some signature.
11469f5cd67SAlexandre Courbot     Light = 2,
11569f5cd67SAlexandre Courbot     /// High-Secure: runs signed code with full privileges. Signature is validated by boot ROM.
11669f5cd67SAlexandre Courbot     Heavy = 3,
11769f5cd67SAlexandre Courbot }
1181b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconSecurityModel);
11969f5cd67SAlexandre Courbot 
1203606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
12169f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconSecurityModel {
12269f5cd67SAlexandre Courbot     type Error = Error;
12369f5cd67SAlexandre Courbot 
12469f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
12569f5cd67SAlexandre Courbot         use FalconSecurityModel::*;
12669f5cd67SAlexandre Courbot 
12769f5cd67SAlexandre Courbot         let sec_model = match value {
12869f5cd67SAlexandre Courbot             0 => None,
12969f5cd67SAlexandre Courbot             2 => Light,
13069f5cd67SAlexandre Courbot             3 => Heavy,
13169f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
13269f5cd67SAlexandre Courbot         };
13369f5cd67SAlexandre Courbot 
13469f5cd67SAlexandre Courbot         Ok(sec_model)
13569f5cd67SAlexandre Courbot     }
13669f5cd67SAlexandre Courbot }
13769f5cd67SAlexandre Courbot 
13869f5cd67SAlexandre Courbot /// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`]
13969f5cd67SAlexandre Courbot /// register.
14069f5cd67SAlexandre Courbot #[repr(u8)]
14169f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
14269f5cd67SAlexandre Courbot pub(crate) enum FalconModSelAlgo {
14369f5cd67SAlexandre Courbot     /// RSA3K.
14469f5cd67SAlexandre Courbot     #[default]
14569f5cd67SAlexandre Courbot     Rsa3k = 1,
14669f5cd67SAlexandre Courbot }
1471b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconModSelAlgo);
14869f5cd67SAlexandre Courbot 
1493606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
15069f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconModSelAlgo {
15169f5cd67SAlexandre Courbot     type Error = Error;
15269f5cd67SAlexandre Courbot 
15369f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
15469f5cd67SAlexandre Courbot         match value {
15569f5cd67SAlexandre Courbot             1 => Ok(FalconModSelAlgo::Rsa3k),
15669f5cd67SAlexandre Courbot             _ => Err(EINVAL),
15769f5cd67SAlexandre Courbot         }
15869f5cd67SAlexandre Courbot     }
15969f5cd67SAlexandre Courbot }
16069f5cd67SAlexandre Courbot 
16169f5cd67SAlexandre Courbot /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register.
16269f5cd67SAlexandre Courbot #[repr(u8)]
16369f5cd67SAlexandre Courbot #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
16469f5cd67SAlexandre Courbot pub(crate) enum DmaTrfCmdSize {
16569f5cd67SAlexandre Courbot     /// 256 bytes transfer.
16669f5cd67SAlexandre Courbot     #[default]
16769f5cd67SAlexandre Courbot     Size256B = 0x6,
16869f5cd67SAlexandre Courbot }
1691b8233bbSDanilo Krummrich impl_from_enum_to_u32!(DmaTrfCmdSize);
17069f5cd67SAlexandre Courbot 
1713606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
17269f5cd67SAlexandre Courbot impl TryFrom<u8> for DmaTrfCmdSize {
17369f5cd67SAlexandre Courbot     type Error = Error;
17469f5cd67SAlexandre Courbot 
17569f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
17669f5cd67SAlexandre Courbot         match value {
17769f5cd67SAlexandre Courbot             0x6 => Ok(Self::Size256B),
17869f5cd67SAlexandre Courbot             _ => Err(EINVAL),
17969f5cd67SAlexandre Courbot         }
18069f5cd67SAlexandre Courbot     }
18169f5cd67SAlexandre Courbot }
18269f5cd67SAlexandre Courbot 
18369f5cd67SAlexandre Courbot /// Currently active core on a dual falcon/riscv (Peregrine) controller.
18469f5cd67SAlexandre Courbot #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
18569f5cd67SAlexandre Courbot pub(crate) enum PeregrineCoreSelect {
18669f5cd67SAlexandre Courbot     /// Falcon core is active.
18769f5cd67SAlexandre Courbot     #[default]
18869f5cd67SAlexandre Courbot     Falcon = 0,
18969f5cd67SAlexandre Courbot     /// RISC-V core is active.
19069f5cd67SAlexandre Courbot     Riscv = 1,
19169f5cd67SAlexandre Courbot }
1921b8233bbSDanilo Krummrich impl_from_enum_to_u32!(PeregrineCoreSelect);
19369f5cd67SAlexandre Courbot 
19469f5cd67SAlexandre Courbot impl From<bool> for PeregrineCoreSelect {
19569f5cd67SAlexandre Courbot     fn from(value: bool) -> Self {
19669f5cd67SAlexandre Courbot         match value {
19769f5cd67SAlexandre Courbot             false => PeregrineCoreSelect::Falcon,
19869f5cd67SAlexandre Courbot             true => PeregrineCoreSelect::Riscv,
19969f5cd67SAlexandre Courbot         }
20069f5cd67SAlexandre Courbot     }
20169f5cd67SAlexandre Courbot }
20269f5cd67SAlexandre Courbot 
20369f5cd67SAlexandre Courbot /// Different types of memory present in a falcon core.
20469f5cd67SAlexandre Courbot #[derive(Debug, Clone, Copy, PartialEq, Eq)]
20569f5cd67SAlexandre Courbot pub(crate) enum FalconMem {
20669f5cd67SAlexandre Courbot     /// Instruction Memory.
20769f5cd67SAlexandre Courbot     Imem,
20869f5cd67SAlexandre Courbot     /// Data Memory.
20969f5cd67SAlexandre Courbot     Dmem,
21069f5cd67SAlexandre Courbot }
21169f5cd67SAlexandre Courbot 
21269f5cd67SAlexandre Courbot /// Target/source of a DMA transfer to/from falcon memory.
21369f5cd67SAlexandre Courbot #[derive(Debug, Clone, Default)]
21469f5cd67SAlexandre Courbot pub(crate) enum FalconFbifTarget {
21569f5cd67SAlexandre Courbot     /// VRAM.
21669f5cd67SAlexandre Courbot     #[default]
21769f5cd67SAlexandre Courbot     LocalFb = 0,
21869f5cd67SAlexandre Courbot     /// Coherent system memory.
21969f5cd67SAlexandre Courbot     CoherentSysmem = 1,
22069f5cd67SAlexandre Courbot     /// Non-coherent system memory.
22169f5cd67SAlexandre Courbot     NoncoherentSysmem = 2,
22269f5cd67SAlexandre Courbot }
2231b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconFbifTarget);
22469f5cd67SAlexandre Courbot 
2253606620bSAlexandre Courbot // TODO[FPRI]: replace with `FromPrimitive`.
22669f5cd67SAlexandre Courbot impl TryFrom<u8> for FalconFbifTarget {
22769f5cd67SAlexandre Courbot     type Error = Error;
22869f5cd67SAlexandre Courbot 
22969f5cd67SAlexandre Courbot     fn try_from(value: u8) -> Result<Self> {
23069f5cd67SAlexandre Courbot         let res = match value {
23169f5cd67SAlexandre Courbot             0 => Self::LocalFb,
23269f5cd67SAlexandre Courbot             1 => Self::CoherentSysmem,
23369f5cd67SAlexandre Courbot             2 => Self::NoncoherentSysmem,
23469f5cd67SAlexandre Courbot             _ => return Err(EINVAL),
23569f5cd67SAlexandre Courbot         };
23669f5cd67SAlexandre Courbot 
23769f5cd67SAlexandre Courbot         Ok(res)
23869f5cd67SAlexandre Courbot     }
23969f5cd67SAlexandre Courbot }
24069f5cd67SAlexandre Courbot 
24169f5cd67SAlexandre Courbot /// Type of memory addresses to use.
24269f5cd67SAlexandre Courbot #[derive(Debug, Clone, Default)]
24369f5cd67SAlexandre Courbot pub(crate) enum FalconFbifMemType {
24469f5cd67SAlexandre Courbot     /// Virtual memory addresses.
24569f5cd67SAlexandre Courbot     #[default]
24669f5cd67SAlexandre Courbot     Virtual = 0,
24769f5cd67SAlexandre Courbot     /// Physical memory addresses.
24869f5cd67SAlexandre Courbot     Physical = 1,
24969f5cd67SAlexandre Courbot }
2501b8233bbSDanilo Krummrich impl_from_enum_to_u32!(FalconFbifMemType);
25169f5cd67SAlexandre Courbot 
25269f5cd67SAlexandre Courbot /// Conversion from a single-bit register field.
25369f5cd67SAlexandre Courbot impl From<bool> for FalconFbifMemType {
25469f5cd67SAlexandre Courbot     fn from(value: bool) -> Self {
25569f5cd67SAlexandre Courbot         match value {
25669f5cd67SAlexandre Courbot             false => Self::Virtual,
25769f5cd67SAlexandre Courbot             true => Self::Physical,
25869f5cd67SAlexandre Courbot         }
25969f5cd67SAlexandre Courbot     }
26069f5cd67SAlexandre Courbot }
26169f5cd67SAlexandre Courbot 
26269f5cd67SAlexandre Courbot /// Trait defining the parameters of a given Falcon instance.
26369f5cd67SAlexandre Courbot pub(crate) trait FalconEngine: Sync {
26469f5cd67SAlexandre Courbot     /// Base I/O address for the falcon, relative from which its registers are accessed.
26569f5cd67SAlexandre Courbot     const BASE: usize;
26669f5cd67SAlexandre Courbot }
26769f5cd67SAlexandre Courbot 
26869f5cd67SAlexandre Courbot /// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
26969f5cd67SAlexandre Courbot #[derive(Debug)]
27069f5cd67SAlexandre Courbot pub(crate) struct FalconLoadTarget {
27169f5cd67SAlexandre Courbot     /// Offset from the start of the source object to copy from.
27269f5cd67SAlexandre Courbot     pub(crate) src_start: u32,
27369f5cd67SAlexandre Courbot     /// Offset from the start of the destination memory to copy into.
27469f5cd67SAlexandre Courbot     pub(crate) dst_start: u32,
27569f5cd67SAlexandre Courbot     /// Number of bytes to copy.
27669f5cd67SAlexandre Courbot     pub(crate) len: u32,
27769f5cd67SAlexandre Courbot }
27869f5cd67SAlexandre Courbot 
27969f5cd67SAlexandre Courbot /// Parameters for the falcon boot ROM.
28069f5cd67SAlexandre Courbot #[derive(Debug)]
28169f5cd67SAlexandre Courbot pub(crate) struct FalconBromParams {
28269f5cd67SAlexandre Courbot     /// Offset in `DMEM`` of the firmware's signature.
28369f5cd67SAlexandre Courbot     pub(crate) pkc_data_offset: u32,
28469f5cd67SAlexandre Courbot     /// Mask of engines valid for this firmware.
28569f5cd67SAlexandre Courbot     pub(crate) engine_id_mask: u16,
28669f5cd67SAlexandre Courbot     /// ID of the ucode used to infer a fuse register to validate the signature.
28769f5cd67SAlexandre Courbot     pub(crate) ucode_id: u8,
28869f5cd67SAlexandre Courbot }
28969f5cd67SAlexandre Courbot 
29069f5cd67SAlexandre Courbot /// Trait for providing load parameters of falcon firmwares.
29169f5cd67SAlexandre Courbot pub(crate) trait FalconLoadParams {
29269f5cd67SAlexandre Courbot     /// Returns the load parameters for `IMEM`.
29369f5cd67SAlexandre Courbot     fn imem_load_params(&self) -> FalconLoadTarget;
29469f5cd67SAlexandre Courbot 
29569f5cd67SAlexandre Courbot     /// Returns the load parameters for `DMEM`.
29669f5cd67SAlexandre Courbot     fn dmem_load_params(&self) -> FalconLoadTarget;
29769f5cd67SAlexandre Courbot 
29869f5cd67SAlexandre Courbot     /// Returns the parameters to write into the BROM registers.
29969f5cd67SAlexandre Courbot     fn brom_params(&self) -> FalconBromParams;
30069f5cd67SAlexandre Courbot 
30169f5cd67SAlexandre Courbot     /// Returns the start address of the firmware.
30269f5cd67SAlexandre Courbot     fn boot_addr(&self) -> u32;
30369f5cd67SAlexandre Courbot }
30469f5cd67SAlexandre Courbot 
30569f5cd67SAlexandre Courbot /// Trait for a falcon firmware.
30669f5cd67SAlexandre Courbot ///
30769f5cd67SAlexandre Courbot /// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
30869f5cd67SAlexandre Courbot /// object.
30969f5cd67SAlexandre Courbot pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
31069f5cd67SAlexandre Courbot     /// Engine on which this firmware is to be loaded.
31169f5cd67SAlexandre Courbot     type Target: FalconEngine;
31269f5cd67SAlexandre Courbot }
31369f5cd67SAlexandre Courbot 
31469f5cd67SAlexandre Courbot /// Contains the base parameters common to all Falcon instances.
31569f5cd67SAlexandre Courbot pub(crate) struct Falcon<E: FalconEngine> {
31669f5cd67SAlexandre Courbot     hal: KBox<dyn FalconHal<E>>,
31769f5cd67SAlexandre Courbot     dev: ARef<device::Device>,
31869f5cd67SAlexandre Courbot }
31969f5cd67SAlexandre Courbot 
32069f5cd67SAlexandre Courbot impl<E: FalconEngine + 'static> Falcon<E> {
32169f5cd67SAlexandre Courbot     /// Create a new falcon instance.
32269f5cd67SAlexandre Courbot     ///
32369f5cd67SAlexandre Courbot     /// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv
32469f5cd67SAlexandre Courbot     /// controller.
32569f5cd67SAlexandre Courbot     pub(crate) fn new(
32669f5cd67SAlexandre Courbot         dev: &device::Device,
32769f5cd67SAlexandre Courbot         chipset: Chipset,
32869f5cd67SAlexandre Courbot         bar: &Bar0,
32969f5cd67SAlexandre Courbot         need_riscv: bool,
33069f5cd67SAlexandre Courbot     ) -> Result<Self> {
33169f5cd67SAlexandre Courbot         let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, E::BASE);
33269f5cd67SAlexandre Courbot         // Check that the revision and security model contain valid values.
33369f5cd67SAlexandre Courbot         let _ = hwcfg1.core_rev()?;
33469f5cd67SAlexandre Courbot         let _ = hwcfg1.security_model()?;
33569f5cd67SAlexandre Courbot 
33669f5cd67SAlexandre Courbot         if need_riscv {
33769f5cd67SAlexandre Courbot             let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
33869f5cd67SAlexandre Courbot             if !hwcfg2.riscv() {
33969f5cd67SAlexandre Courbot                 dev_err!(
34069f5cd67SAlexandre Courbot                     dev,
34169f5cd67SAlexandre Courbot                     "riscv support requested on a controller that does not support it\n"
34269f5cd67SAlexandre Courbot                 );
34369f5cd67SAlexandre Courbot                 return Err(EINVAL);
34469f5cd67SAlexandre Courbot             }
34569f5cd67SAlexandre Courbot         }
34669f5cd67SAlexandre Courbot 
34769f5cd67SAlexandre Courbot         Ok(Self {
34869f5cd67SAlexandre Courbot             hal: hal::falcon_hal(chipset)?,
34969f5cd67SAlexandre Courbot             dev: dev.into(),
35069f5cd67SAlexandre Courbot         })
35169f5cd67SAlexandre Courbot     }
35269f5cd67SAlexandre Courbot 
35369f5cd67SAlexandre Courbot     /// Wait for memory scrubbing to complete.
35469f5cd67SAlexandre Courbot     fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
35569f5cd67SAlexandre Courbot         // TIMEOUT: memory scrubbing should complete in less than 20ms.
356*4092e1b4SAlexandre Courbot         util::wait_on(Delta::from_millis(20), || {
35769f5cd67SAlexandre Courbot             if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE).mem_scrubbing_done() {
35869f5cd67SAlexandre Courbot                 Some(())
35969f5cd67SAlexandre Courbot             } else {
36069f5cd67SAlexandre Courbot                 None
36169f5cd67SAlexandre Courbot             }
36269f5cd67SAlexandre Courbot         })
36369f5cd67SAlexandre Courbot     }
36469f5cd67SAlexandre Courbot 
36569f5cd67SAlexandre Courbot     /// Reset the falcon engine.
36669f5cd67SAlexandre Courbot     fn reset_eng(&self, bar: &Bar0) -> Result {
36769f5cd67SAlexandre Courbot         let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
36869f5cd67SAlexandre Courbot 
36969f5cd67SAlexandre Courbot         // According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
37069f5cd67SAlexandre Courbot         // RESET_READY so a non-failing timeout is used.
371*4092e1b4SAlexandre Courbot         let _ = util::wait_on(Delta::from_micros(150), || {
37269f5cd67SAlexandre Courbot             let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
37369f5cd67SAlexandre Courbot             if r.reset_ready() {
37469f5cd67SAlexandre Courbot                 Some(())
37569f5cd67SAlexandre Courbot             } else {
37669f5cd67SAlexandre Courbot                 None
37769f5cd67SAlexandre Courbot             }
37869f5cd67SAlexandre Courbot         });
37969f5cd67SAlexandre Courbot 
38069f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(true));
38169f5cd67SAlexandre Courbot 
3823606620bSAlexandre Courbot         // TODO[DLAY]: replace with udelay() or equivalent once available.
38369f5cd67SAlexandre Courbot         // TIMEOUT: falcon engine should not take more than 10us to reset.
384*4092e1b4SAlexandre Courbot         let _: Result = util::wait_on(Delta::from_micros(10), || None);
38569f5cd67SAlexandre Courbot 
38669f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(false));
38769f5cd67SAlexandre Courbot 
38869f5cd67SAlexandre Courbot         self.reset_wait_mem_scrubbing(bar)?;
38969f5cd67SAlexandre Courbot 
39069f5cd67SAlexandre Courbot         Ok(())
39169f5cd67SAlexandre Courbot     }
39269f5cd67SAlexandre Courbot 
39369f5cd67SAlexandre Courbot     /// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
39469f5cd67SAlexandre Courbot     pub(crate) fn reset(&self, bar: &Bar0) -> Result {
39569f5cd67SAlexandre Courbot         self.reset_eng(bar)?;
39669f5cd67SAlexandre Courbot         self.hal.select_core(self, bar)?;
39769f5cd67SAlexandre Courbot         self.reset_wait_mem_scrubbing(bar)?;
39869f5cd67SAlexandre Courbot 
39969f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_RM::default()
40069f5cd67SAlexandre Courbot             .set_value(regs::NV_PMC_BOOT_0::read(bar).into())
40169f5cd67SAlexandre Courbot             .write(bar, E::BASE);
40269f5cd67SAlexandre Courbot 
40369f5cd67SAlexandre Courbot         Ok(())
40469f5cd67SAlexandre Courbot     }
40569f5cd67SAlexandre Courbot 
40669f5cd67SAlexandre Courbot     /// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
40769f5cd67SAlexandre Courbot     /// `target_mem`.
40869f5cd67SAlexandre Courbot     ///
40969f5cd67SAlexandre Courbot     /// `sec` is set if the loaded firmware is expected to run in secure mode.
41069f5cd67SAlexandre Courbot     fn dma_wr<F: FalconFirmware<Target = E>>(
41169f5cd67SAlexandre Courbot         &self,
41269f5cd67SAlexandre Courbot         bar: &Bar0,
41369f5cd67SAlexandre Courbot         fw: &F,
41469f5cd67SAlexandre Courbot         target_mem: FalconMem,
41569f5cd67SAlexandre Courbot         load_offsets: FalconLoadTarget,
41669f5cd67SAlexandre Courbot         sec: bool,
41769f5cd67SAlexandre Courbot     ) -> Result {
41869f5cd67SAlexandre Courbot         const DMA_LEN: u32 = 256;
41969f5cd67SAlexandre Courbot 
42069f5cd67SAlexandre Courbot         // For IMEM, we want to use the start offset as a virtual address tag for each page, since
42169f5cd67SAlexandre Courbot         // code addresses in the firmware (and the boot vector) are virtual.
42269f5cd67SAlexandre Courbot         //
42369f5cd67SAlexandre Courbot         // For DMEM we can fold the start offset into the DMA handle.
42469f5cd67SAlexandre Courbot         let (src_start, dma_start) = match target_mem {
42569f5cd67SAlexandre Courbot             FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()),
42669f5cd67SAlexandre Courbot             FalconMem::Dmem => (
42769f5cd67SAlexandre Courbot                 0,
42869f5cd67SAlexandre Courbot                 fw.dma_handle_with_offset(load_offsets.src_start as usize)?,
42969f5cd67SAlexandre Courbot             ),
43069f5cd67SAlexandre Courbot         };
43143ad65ecSDanilo Krummrich         if dma_start % bindings::dma_addr_t::from(DMA_LEN) > 0 {
43269f5cd67SAlexandre Courbot             dev_err!(
43369f5cd67SAlexandre Courbot                 self.dev,
43469f5cd67SAlexandre Courbot                 "DMA transfer start addresses must be a multiple of {}",
43569f5cd67SAlexandre Courbot                 DMA_LEN
43669f5cd67SAlexandre Courbot             );
43769f5cd67SAlexandre Courbot             return Err(EINVAL);
43869f5cd67SAlexandre Courbot         }
43969f5cd67SAlexandre Courbot         if load_offsets.len % DMA_LEN > 0 {
44069f5cd67SAlexandre Courbot             dev_err!(
44169f5cd67SAlexandre Courbot                 self.dev,
44269f5cd67SAlexandre Courbot                 "DMA transfer length must be a multiple of {}",
44369f5cd67SAlexandre Courbot                 DMA_LEN
44469f5cd67SAlexandre Courbot             );
44569f5cd67SAlexandre Courbot             return Err(EINVAL);
44669f5cd67SAlexandre Courbot         }
44769f5cd67SAlexandre Courbot 
44869f5cd67SAlexandre Courbot         // Set up the base source DMA address.
44969f5cd67SAlexandre Courbot 
45069f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMATRFBASE::default()
45169f5cd67SAlexandre Courbot             .set_base((dma_start >> 8) as u32)
45269f5cd67SAlexandre Courbot             .write(bar, E::BASE);
45369f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
45469f5cd67SAlexandre Courbot             .set_base((dma_start >> 40) as u16)
45569f5cd67SAlexandre Courbot             .write(bar, E::BASE);
45669f5cd67SAlexandre Courbot 
45769f5cd67SAlexandre Courbot         let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
45869f5cd67SAlexandre Courbot             .set_size(DmaTrfCmdSize::Size256B)
45969f5cd67SAlexandre Courbot             .set_imem(target_mem == FalconMem::Imem)
46069f5cd67SAlexandre Courbot             .set_sec(if sec { 1 } else { 0 });
46169f5cd67SAlexandre Courbot 
46269f5cd67SAlexandre Courbot         for pos in (0..load_offsets.len).step_by(DMA_LEN as usize) {
46369f5cd67SAlexandre Courbot             // Perform a transfer of size `DMA_LEN`.
46469f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
46569f5cd67SAlexandre Courbot                 .set_offs(load_offsets.dst_start + pos)
46669f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
46769f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
46869f5cd67SAlexandre Courbot                 .set_offs(src_start + pos)
46969f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
47069f5cd67SAlexandre Courbot             cmd.write(bar, E::BASE);
47169f5cd67SAlexandre Courbot 
47269f5cd67SAlexandre Courbot             // Wait for the transfer to complete.
47369f5cd67SAlexandre Courbot             // TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
47469f5cd67SAlexandre Courbot             // should ever take that long.
475*4092e1b4SAlexandre Courbot             util::wait_on(Delta::from_secs(2), || {
47669f5cd67SAlexandre Courbot                 let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, E::BASE);
47769f5cd67SAlexandre Courbot                 if r.idle() {
47869f5cd67SAlexandre Courbot                     Some(())
47969f5cd67SAlexandre Courbot                 } else {
48069f5cd67SAlexandre Courbot                     None
48169f5cd67SAlexandre Courbot                 }
48269f5cd67SAlexandre Courbot             })?;
48369f5cd67SAlexandre Courbot         }
48469f5cd67SAlexandre Courbot 
48569f5cd67SAlexandre Courbot         Ok(())
48669f5cd67SAlexandre Courbot     }
48769f5cd67SAlexandre Courbot 
48869f5cd67SAlexandre Courbot     /// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
48969f5cd67SAlexandre Courbot     pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
49069f5cd67SAlexandre Courbot         regs::NV_PFALCON_FBIF_CTL::alter(bar, E::BASE, |v| v.set_allow_phys_no_ctx(true));
49169f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, E::BASE);
49269f5cd67SAlexandre Courbot         regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, E::BASE, |v| {
49369f5cd67SAlexandre Courbot             v.set_target(FalconFbifTarget::CoherentSysmem)
49469f5cd67SAlexandre Courbot                 .set_mem_type(FalconFbifMemType::Physical)
49569f5cd67SAlexandre Courbot         });
49669f5cd67SAlexandre Courbot 
49769f5cd67SAlexandre Courbot         self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?;
49869f5cd67SAlexandre Courbot         self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?;
49969f5cd67SAlexandre Courbot 
50069f5cd67SAlexandre Courbot         self.hal.program_brom(self, bar, &fw.brom_params())?;
50169f5cd67SAlexandre Courbot 
50269f5cd67SAlexandre Courbot         // Set `BootVec` to start of non-secure code.
50369f5cd67SAlexandre Courbot         regs::NV_PFALCON_FALCON_BOOTVEC::default()
50469f5cd67SAlexandre Courbot             .set_value(fw.boot_addr())
50569f5cd67SAlexandre Courbot             .write(bar, E::BASE);
50669f5cd67SAlexandre Courbot 
50769f5cd67SAlexandre Courbot         Ok(())
50869f5cd67SAlexandre Courbot     }
50969f5cd67SAlexandre Courbot 
51069f5cd67SAlexandre Courbot     /// Runs the loaded firmware and waits for its completion.
51169f5cd67SAlexandre Courbot     ///
51269f5cd67SAlexandre Courbot     /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
51369f5cd67SAlexandre Courbot     /// prior to running.
51469f5cd67SAlexandre Courbot     ///
51569f5cd67SAlexandre Courbot     /// Wait up to two seconds for the firmware to complete, and return its exit status read from
51669f5cd67SAlexandre Courbot     /// the `MBOX0` and `MBOX1` registers.
51769f5cd67SAlexandre Courbot     pub(crate) fn boot(
51869f5cd67SAlexandre Courbot         &self,
51969f5cd67SAlexandre Courbot         bar: &Bar0,
52069f5cd67SAlexandre Courbot         mbox0: Option<u32>,
52169f5cd67SAlexandre Courbot         mbox1: Option<u32>,
52269f5cd67SAlexandre Courbot     ) -> Result<(u32, u32)> {
52369f5cd67SAlexandre Courbot         if let Some(mbox0) = mbox0 {
52469f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX0::default()
52569f5cd67SAlexandre Courbot                 .set_value(mbox0)
52669f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
52769f5cd67SAlexandre Courbot         }
52869f5cd67SAlexandre Courbot 
52969f5cd67SAlexandre Courbot         if let Some(mbox1) = mbox1 {
53069f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX1::default()
53169f5cd67SAlexandre Courbot                 .set_value(mbox1)
53269f5cd67SAlexandre Courbot                 .write(bar, E::BASE);
53369f5cd67SAlexandre Courbot         }
53469f5cd67SAlexandre Courbot 
53569f5cd67SAlexandre Courbot         match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE).alias_en() {
53669f5cd67SAlexandre Courbot             true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
53769f5cd67SAlexandre Courbot                 .set_startcpu(true)
53869f5cd67SAlexandre Courbot                 .write(bar, E::BASE),
53969f5cd67SAlexandre Courbot             false => regs::NV_PFALCON_FALCON_CPUCTL::default()
54069f5cd67SAlexandre Courbot                 .set_startcpu(true)
54169f5cd67SAlexandre Courbot                 .write(bar, E::BASE),
54269f5cd67SAlexandre Courbot         }
54369f5cd67SAlexandre Courbot 
54469f5cd67SAlexandre Courbot         // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
545*4092e1b4SAlexandre Courbot         util::wait_on(Delta::from_secs(2), || {
54669f5cd67SAlexandre Courbot             let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE);
54769f5cd67SAlexandre Courbot             if r.halted() {
54869f5cd67SAlexandre Courbot                 Some(())
54969f5cd67SAlexandre Courbot             } else {
55069f5cd67SAlexandre Courbot                 None
55169f5cd67SAlexandre Courbot             }
55269f5cd67SAlexandre Courbot         })?;
55369f5cd67SAlexandre Courbot 
55469f5cd67SAlexandre Courbot         let (mbox0, mbox1) = (
55569f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, E::BASE).value(),
55669f5cd67SAlexandre Courbot             regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, E::BASE).value(),
55769f5cd67SAlexandre Courbot         );
55869f5cd67SAlexandre Courbot 
55969f5cd67SAlexandre Courbot         Ok((mbox0, mbox1))
56069f5cd67SAlexandre Courbot     }
56169f5cd67SAlexandre Courbot 
56269f5cd67SAlexandre Courbot     /// Returns the fused version of the signature to use in order to run a HS firmware on this
56369f5cd67SAlexandre Courbot     /// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
56469f5cd67SAlexandre Courbot     pub(crate) fn signature_reg_fuse_version(
56569f5cd67SAlexandre Courbot         &self,
56669f5cd67SAlexandre Courbot         bar: &Bar0,
56769f5cd67SAlexandre Courbot         engine_id_mask: u16,
56869f5cd67SAlexandre Courbot         ucode_id: u8,
56969f5cd67SAlexandre Courbot     ) -> Result<u32> {
57069f5cd67SAlexandre Courbot         self.hal
57169f5cd67SAlexandre Courbot             .signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
57269f5cd67SAlexandre Courbot     }
57369f5cd67SAlexandre Courbot }
574