1*6554ad65SAlexandre Courbot // SPDX-License-Identifier: GPL-2.0 2*6554ad65SAlexandre Courbot 3*6554ad65SAlexandre Courbot use kernel::prelude::*; 4*6554ad65SAlexandre Courbot use kernel::types::ARef; 5*6554ad65SAlexandre Courbot use kernel::{dev_warn, device}; 6*6554ad65SAlexandre Courbot 7*6554ad65SAlexandre Courbot use crate::dma::DmaObject; 8*6554ad65SAlexandre Courbot use crate::driver::Bar0; 9*6554ad65SAlexandre Courbot use crate::gpu::Chipset; 10*6554ad65SAlexandre Courbot 11*6554ad65SAlexandre Courbot mod hal; 12*6554ad65SAlexandre Courbot 13*6554ad65SAlexandre Courbot /// Type holding the sysmem flush memory page, a page of memory to be written into the 14*6554ad65SAlexandre Courbot /// `NV_PFB_NISO_FLUSH_SYSMEM_ADDR*` registers and used to maintain memory coherency. 15*6554ad65SAlexandre Courbot /// 16*6554ad65SAlexandre Courbot /// Users are responsible for manually calling [`Self::unregister`] before dropping this object, 17*6554ad65SAlexandre Courbot /// otherwise the GPU might still use it even after it has been freed. 18*6554ad65SAlexandre Courbot pub(crate) struct SysmemFlush { 19*6554ad65SAlexandre Courbot /// Chipset we are operating on. 20*6554ad65SAlexandre Courbot chipset: Chipset, 21*6554ad65SAlexandre Courbot device: ARef<device::Device>, 22*6554ad65SAlexandre Courbot /// Keep the page alive as long as we need it. 23*6554ad65SAlexandre Courbot page: DmaObject, 24*6554ad65SAlexandre Courbot } 25*6554ad65SAlexandre Courbot 26*6554ad65SAlexandre Courbot impl SysmemFlush { 27*6554ad65SAlexandre Courbot /// Allocate a memory page and register it as the sysmem flush page. 28*6554ad65SAlexandre Courbot pub(crate) fn register( 29*6554ad65SAlexandre Courbot dev: &device::Device<device::Bound>, 30*6554ad65SAlexandre Courbot bar: &Bar0, 31*6554ad65SAlexandre Courbot chipset: Chipset, 32*6554ad65SAlexandre Courbot ) -> Result<Self> { 33*6554ad65SAlexandre Courbot let page = DmaObject::new(dev, kernel::page::PAGE_SIZE)?; 34*6554ad65SAlexandre Courbot 35*6554ad65SAlexandre Courbot hal::fb_hal(chipset).write_sysmem_flush_page(bar, page.dma_handle())?; 36*6554ad65SAlexandre Courbot 37*6554ad65SAlexandre Courbot Ok(Self { 38*6554ad65SAlexandre Courbot chipset, 39*6554ad65SAlexandre Courbot device: dev.into(), 40*6554ad65SAlexandre Courbot page, 41*6554ad65SAlexandre Courbot }) 42*6554ad65SAlexandre Courbot } 43*6554ad65SAlexandre Courbot 44*6554ad65SAlexandre Courbot /// Unregister the managed sysmem flush page. 45*6554ad65SAlexandre Courbot /// 46*6554ad65SAlexandre Courbot /// In order to gracefully tear down the GPU, users must make sure to call this method before 47*6554ad65SAlexandre Courbot /// dropping the object. 48*6554ad65SAlexandre Courbot pub(crate) fn unregister(&self, bar: &Bar0) { 49*6554ad65SAlexandre Courbot let hal = hal::fb_hal(self.chipset); 50*6554ad65SAlexandre Courbot 51*6554ad65SAlexandre Courbot if hal.read_sysmem_flush_page(bar) == self.page.dma_handle() { 52*6554ad65SAlexandre Courbot let _ = hal.write_sysmem_flush_page(bar, 0).inspect_err(|e| { 53*6554ad65SAlexandre Courbot dev_warn!( 54*6554ad65SAlexandre Courbot &self.device, 55*6554ad65SAlexandre Courbot "failed to unregister sysmem flush page: {:?}", 56*6554ad65SAlexandre Courbot e 57*6554ad65SAlexandre Courbot ) 58*6554ad65SAlexandre Courbot }); 59*6554ad65SAlexandre Courbot } else { 60*6554ad65SAlexandre Courbot // Another page has been registered after us for some reason - warn as this is a bug. 61*6554ad65SAlexandre Courbot dev_warn!( 62*6554ad65SAlexandre Courbot &self.device, 63*6554ad65SAlexandre Courbot "attempt to unregister a sysmem flush page that is not active\n" 64*6554ad65SAlexandre Courbot ); 65*6554ad65SAlexandre Courbot } 66*6554ad65SAlexandre Courbot } 67*6554ad65SAlexandre Courbot } 68