1 // SPDX-License-Identifier: GPL-2.0 2 3 use core::ops::Range; 4 5 use kernel::prelude::*; 6 use kernel::ptr::{Alignable, Alignment}; 7 use kernel::sizes::*; 8 use kernel::sync::aref::ARef; 9 use kernel::{dev_warn, device}; 10 11 use crate::dma::DmaObject; 12 use crate::driver::Bar0; 13 use crate::gpu::Chipset; 14 use crate::regs; 15 16 mod hal; 17 18 /// Type holding the sysmem flush memory page, a page of memory to be written into the 19 /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency. 20 /// 21 /// A system memory page is required for `sysmembar`, which is a GPU-initiated hardware 22 /// memory-barrier operation that flushes all pending GPU-side memory writes that were done through 23 /// PCIE to system memory. It is required for falcons to be reset as the reset operation involves a 24 /// reset handshake. When the falcon acknowledges a reset, it writes into system memory. To ensure 25 /// this write is visible to the host and prevent driver timeouts, the falcon must perform a 26 /// sysmembar operation to flush its writes. 27 /// 28 /// Because of this, the sysmem flush memory page must be registered as early as possible during 29 /// driver initialization, and before any falcon is reset. 30 /// 31 /// Users are responsible for manually calling [`Self::unregister`] before dropping this object, 32 /// otherwise the GPU might still use it even after it has been freed. 33 pub(crate) struct SysmemFlush { 34 /// Chipset we are operating on. 35 chipset: Chipset, 36 device: ARef<device::Device>, 37 /// Keep the page alive as long as we need it. 38 page: DmaObject, 39 } 40 41 impl SysmemFlush { 42 /// Allocate a memory page and register it as the sysmem flush page. 43 pub(crate) fn register( 44 dev: &device::Device<device::Bound>, 45 bar: &Bar0, 46 chipset: Chipset, 47 ) -> Result<Self> { 48 let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?; 49 50 hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?; 51 52 Ok(Self { 53 chipset, 54 device: dev.into(), 55 page, 56 }) 57 } 58 59 /// Unregister the managed sysmem flush page. 60 /// 61 /// In order to gracefully tear down the GPU, users must make sure to call this method before 62 /// dropping the object. 63 pub(crate) fn unregister(&self, bar: &Bar0) { 64 let hal = hal::fb_hal(self.chipset); 65 66 if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() { 67 let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| { 68 dev_warn!( 69 &self.device, 70 "failed to unregister sysmem flush page: {:?}", 71 e 72 ) 73 }); 74 } else { 75 // Another page has been registered after us for some reason - warn as this is a bug. 76 dev_warn!( 77 &self.device, 78 "attempt to unregister a sysmem flush page that is not active\n" 79 ); 80 } 81 } 82 } 83 84 /// Layout of the GPU framebuffer memory. 85 /// 86 /// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process. 87 #[derive(Debug)] 88 #[expect(dead_code)] 89 pub(crate) struct FbLayout { 90 pub(crate) fb: Range<u64>, 91 pub(crate) vga_workspace: Range<u64>, 92 pub(crate) frts: Range<u64>, 93 } 94 95 impl FbLayout { 96 /// Computes the FB layout. 97 pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> { 98 let hal = hal::fb_hal(chipset); 99 100 let fb = { 101 let fb_size = hal.vidmem_size(bar); 102 103 0..fb_size 104 }; 105 106 let vga_workspace = { 107 let vga_base = { 108 const NV_PRAMIN_SIZE: u64 = SZ_1M as u64; 109 let base = fb.end - NV_PRAMIN_SIZE; 110 111 if hal.supports_display(bar) { 112 match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() { 113 Some(addr) => { 114 if addr < base { 115 const VBIOS_WORKSPACE_SIZE: u64 = SZ_128K as u64; 116 117 // Point workspace address to end of framebuffer. 118 fb.end - VBIOS_WORKSPACE_SIZE 119 } else { 120 addr 121 } 122 } 123 None => base, 124 } 125 } else { 126 base 127 } 128 }; 129 130 vga_base..fb.end 131 }; 132 133 let frts = { 134 const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>(); 135 const FRTS_SIZE: u64 = SZ_1M as u64; 136 let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE; 137 138 frts_base..frts_base + FRTS_SIZE 139 }; 140 141 Ok(Self { 142 fb, 143 vga_workspace, 144 frts, 145 }) 146 } 147 } 148