File Index
Files and directories
Sign in
FILE

gpu.rs

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/nova-core/gpu.rs RUST
  1// SPDX-License-Identifier: GPL-2.0
  2
  3use kernel::{
  4    device,
  5    devres::Devres,
  6    fmt,
  7    pci,
  8    prelude::*,
  9    sync::Arc, //
 10};
 11
 12use crate::{
 13    driver::Bar0,
 14    falcon::{
 15        gsp::Gsp as GspFalcon,
 16        sec2::Sec2 as Sec2Falcon,
 17        Falcon, //
 18    },
 19    fb::SysmemFlush,
 20    gfw,
 21    gsp::Gsp,
 22    regs,
 23};
 24
 25macro_rules! define_chipset {
 26    ({ $($variant:ident = $value:expr),* $(,)* }) =>
 27    {
 28        /// Enum representation of the GPU chipset.
 29        #[derive(fmt::Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
 30        pub(crate) enum Chipset {
 31            $($variant = $value),*,
 32        }
 33
 34        impl Chipset {
 35            pub(crate) const ALL: &'static [Chipset] = &[
 36                $( Chipset::$variant, )*
 37            ];
 38
 39            ::kernel::macros::paste!(
 40            /// Returns the name of this chipset, in lowercase.
 41            ///
 42            /// # Examples
 43            ///
 44            /// ```
 45            /// let chipset = Chipset::GA102;
 46            /// assert_eq!(chipset.name(), "ga102");
 47            /// ```
 48            pub(crate) const fn name(&self) -> &'static str {
 49                match *self {
 50                $(
 51                    Chipset::$variant => stringify!([<$variant:lower>]),
 52                )*
 53                }
 54            }
 55            );
 56        }
 57
 58        // TODO[FPRI]: replace with something like derive(FromPrimitive)
 59        impl TryFrom<u32> for Chipset {
 60            type Error = kernel::error::Error;
 61
 62            fn try_from(value: u32) -> Result<Self, Self::Error> {
 63                match value {
 64                    $( $value => Ok(Chipset::$variant), )*
 65                    _ => Err(ENODEV),
 66                }
 67            }
 68        }
 69    }
 70}
 71
 72define_chipset!({
 73    // Turing
 74    TU102 = 0x162,
 75    TU104 = 0x164,
 76    TU106 = 0x166,
 77    TU117 = 0x167,
 78    TU116 = 0x168,
 79    // Ampere
 80    GA100 = 0x170,
 81    GA102 = 0x172,
 82    GA103 = 0x173,
 83    GA104 = 0x174,
 84    GA106 = 0x176,
 85    GA107 = 0x177,
 86    // Ada
 87    AD102 = 0x192,
 88    AD103 = 0x193,
 89    AD104 = 0x194,
 90    AD106 = 0x196,
 91    AD107 = 0x197,
 92});
 93
 94impl Chipset {
 95    pub(crate) fn arch(&self) -> Architecture {
 96        match self {
 97            Self::TU102 | Self::TU104 | Self::TU106 | Self::TU117 | Self::TU116 => {
 98                Architecture::Turing
 99            }
100            Self::GA100 | Self::GA102 | Self::GA103 | Self::GA104 | Self::GA106 | Self::GA107 => {
101                Architecture::Ampere
102            }
103            Self::AD102 | Self::AD103 | Self::AD104 | Self::AD106 | Self::AD107 => {
104                Architecture::Ada
105            }
106        }
107    }
108}
109
110// TODO
111//
112// The resulting strings are used to generate firmware paths, hence the
113// generated strings have to be stable.
114//
115// Hence, replace with something like strum_macros derive(Display).
116//
117// For now, redirect to fmt::Debug for convenience.
118impl fmt::Display for Chipset {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        write!(f, "{self:?}")
121    }
122}
123
124/// Enum representation of the GPU generation.
125///
126/// TODO: remove the `Default` trait implementation, and the `#[default]`
127/// attribute, once the register!() macro (which creates Architecture items) no
128/// longer requires it for read-only fields.
129#[derive(fmt::Debug, Default, Copy, Clone)]
130#[repr(u8)]
131pub(crate) enum Architecture {
132    #[default]
133    Turing = 0x16,
134    Ampere = 0x17,
135    Ada = 0x19,
136}
137
138impl TryFrom<u8> for Architecture {
139    type Error = Error;
140
141    fn try_from(value: u8) -> Result<Self> {
142        match value {
143            0x16 => Ok(Self::Turing),
144            0x17 => Ok(Self::Ampere),
145            0x19 => Ok(Self::Ada),
146            _ => Err(ENODEV),
147        }
148    }
149}
150
151impl From<Architecture> for u8 {
152    fn from(value: Architecture) -> Self {
153        // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless.
154        value as u8
155    }
156}
157
158pub(crate) struct Revision {
159    major: u8,
160    minor: u8,
161}
162
163impl From<regs::NV_PMC_BOOT_42> for Revision {
164    fn from(boot0: regs::NV_PMC_BOOT_42) -> Self {
165        Self {
166            major: boot0.major_revision(),
167            minor: boot0.minor_revision(),
168        }
169    }
170}
171
172impl fmt::Display for Revision {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        write!(f, "{:x}.{:x}", self.major, self.minor)
175    }
176}
177
178/// Structure holding a basic description of the GPU: `Chipset` and `Revision`.
179pub(crate) struct Spec {
180    chipset: Chipset,
181    revision: Revision,
182}
183
184impl Spec {
185    fn new(dev: &device::Device, bar: &Bar0) -> Result<Spec> {
186        // Some brief notes about boot0 and boot42, in chronological order:
187        //
188        // NV04 through NV50:
189        //
190        //    Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs.
191        //    boot42 may not even exist on some of these GPUs.
192        //
193        // Fermi through Volta:
194        //
195        //     Not supported by Nova. boot0 is still sufficient to identify these GPUs, but boot42
196        //     is also guaranteed to be both present and accurate.
197        //
198        // Turing and later:
199        //
200        //     Supported by Nova. Identified by first checking boot0 to ensure that the GPU is not
201        //     from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU.
202        //     Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs.
203
204        let boot0 = regs::NV_PMC_BOOT_0::read(bar);
205
206        if boot0.is_older_than_fermi() {
207            return Err(ENODEV);
208        }
209
210        let boot42 = regs::NV_PMC_BOOT_42::read(bar);
211        Spec::try_from(boot42).inspect_err(|_| {
212            dev_err!(dev, "Unsupported chipset: {}\n", boot42);
213        })
214    }
215}
216
217impl TryFrom<regs::NV_PMC_BOOT_42> for Spec {
218    type Error = Error;
219
220    fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> {
221        Ok(Self {
222            chipset: boot42.chipset()?,
223            revision: boot42.into(),
224        })
225    }
226}
227
228impl fmt::Display for Spec {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        f.write_fmt(fmt!(
231            "Chipset: {}, Architecture: {:?}, Revision: {}",
232            self.chipset,
233            self.chipset.arch(),
234            self.revision
235        ))
236    }
237}
238
239/// Structure holding the resources required to operate the GPU.
240#[pin_data]
241pub(crate) struct Gpu {
242    spec: Spec,
243    /// MMIO mapping of PCI BAR 0
244    bar: Arc<Devres<Bar0>>,
245    /// System memory page required for flushing all pending GPU-side memory writes done through
246    /// PCIE into system memory, via sysmembar (A GPU-initiated HW memory-barrier operation).
247    sysmem_flush: SysmemFlush,
248    /// GSP falcon instance, used for GSP boot up and cleanup.
249    gsp_falcon: Falcon<GspFalcon>,
250    /// SEC2 falcon instance, used for GSP boot up and cleanup.
251    sec2_falcon: Falcon<Sec2Falcon>,
252    /// GSP runtime data. Temporarily an empty placeholder.
253    #[pin]
254    gsp: Gsp,
255}
256
257impl Gpu {
258    pub(crate) fn new<'a>(
259        pdev: &'a pci::Device<device::Bound>,
260        devres_bar: Arc<Devres<Bar0>>,
261        bar: &'a Bar0,
262    ) -> impl PinInit<Self, Error> + 'a {
263        try_pin_init!(Self {
264            spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| {
265                dev_info!(pdev.as_ref(),"NVIDIA ({})\n", spec);
266            })?,
267
268            // We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
269            _: {
270                gfw::wait_gfw_boot_completion(bar)
271                    .inspect_err(|_| dev_err!(pdev.as_ref(), "GFW boot did not complete"))?;
272            },
273
274            sysmem_flush: SysmemFlush::register(pdev.as_ref(), bar, spec.chipset)?,
275
276            gsp_falcon: Falcon::new(
277                pdev.as_ref(),
278                spec.chipset,
279            )
280            .inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
281
282            sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
283
284            gsp <- Gsp::new(pdev)?,
285
286            _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
287
288            bar: devres_bar,
289        })
290    }
291
292    /// Called when the corresponding [`Device`](device::Device) is unbound.
293    ///
294    /// Note: This method must only be called from `Driver::unbind`.
295    pub(crate) fn unbind(&self, dev: &device::Device<device::Core>) {
296        kernel::warn_on!(self
297            .bar
298            .access(dev)
299            .inspect(|bar| self.sysmem_flush.unregister(bar))
300            .is_err());
301    }
302}
~/ Read-only file preview.