xref: /linux/drivers/gpu/drm/tyr/mmu.rs (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0 or MIT
2 
3 //! Memory Management Unit (MMU) module.
4 //!
5 //! The GPU MMU provides a limited number of memory address spaces for use by command streams.
6 //! The MMU translates virtual addresses to physical addresses and manages memory configuration
7 //! and access permissions.
8 //!
9 //! This MMU module is essentially a locked wrapper around a [`SlotManager`] instance.
10 //! The [`SlotManager`] manages the assignment of virtual address spaces to hardware address-space
11 //! (AS) slots. MMU commands such as updates and flushes are carried out by the
12 //! [`AddressSpaceManager`] which actually writes to the MMU registers.
13 
14 use core::ops::Range;
15 
16 use kernel::{
17     device::{
18         Bound,
19         Device, //
20     },
21     new_mutex,
22     prelude::*,
23     sync::{
24         Arc,
25         ArcBorrow,
26         Mutex, //
27     }, //
28 };
29 
30 use crate::{
31     driver::IoMem,
32     gpu::GpuInfo,
33     mmu::address_space::{
34         AddressSpaceManager,
35         VmAsData, //
36     },
37     regs::{
38         gpu_control::AS_PRESENT,
39         MAX_AS, //
40     },
41     slot::SlotManager, //
42 };
43 
44 pub(crate) mod address_space;
45 
46 pub(crate) type AsSlotManager<'drm> = SlotManager<AddressSpaceManager<'drm>, MAX_AS>;
47 
48 /// Locked wrapper for carrying out virtual memory (VM) operations on the MMU.
49 #[pin_data]
50 pub(crate) struct Mmu<'drm> {
51     /// Slot Manager instance used to allocate hardware slots and write to MMU registers.
52     #[pin]
53     pub(crate) as_manager: Mutex<AsSlotManager<'drm>>,
54 }
55 
56 impl<'drm> Mmu<'drm> {
57     /// Create an MMU component for this device.
58     pub(crate) fn new(
59         dev: &'drm Device<Bound>,
60         iomem: ArcBorrow<'_, IoMem<'drm>>,
61         gpu_info: &GpuInfo,
62     ) -> Result<Arc<Mmu<'drm>>> {
63         let present = AS_PRESENT::from_raw(gpu_info.as_present).present().get();
64         let slot_count = present.count_ones().try_into()?;
65 
66         let address_space_manager = AddressSpaceManager::new(dev, iomem.into(), present)?;
67         let as_slot_manager =
68             SlotManager::new(address_space_manager, slot_count).inspect_err(|e| {
69                 dev_err!(
70                     dev,
71                     "Failed to initialize MMU slot manager with {} slots: {:?}",
72                     slot_count,
73                     e
74                 );
75             })?;
76         let mmu_init = try_pin_init!(Self{
77             as_manager <- new_mutex!(as_slot_manager),
78         });
79         Arc::pin_init(mmu_init, GFP_KERNEL)
80     }
81 
82     /// Assign a VM to an AS slot, provide a translation table,
83     /// and update the MMU to make the VM resident.
84     pub(crate) fn activate_vm(&self, vm_as_data: ArcBorrow<'_, VmAsData<'drm>>) -> Result {
85         self.as_manager.lock().activate_vm(vm_as_data)
86     }
87 
88     /// Evict a VM from its AS slot and flush the MMU.
89     pub(crate) fn deactivate_vm(&self, vm_as_data: &VmAsData<'drm>) -> Result {
90         self.as_manager.lock().deactivate_vm(vm_as_data)
91     }
92 
93     /// Flush MMU translation caches after a VM update.
94     pub(crate) fn flush_vm(&self, vm_as_data: &VmAsData<'drm>) -> Result {
95         self.as_manager.lock().flush_vm(vm_as_data)
96     }
97 
98     /// Flags the start of a VM update.
99     ///
100     /// If the VM is resident, any GPU access on the memory range being
101     /// updated will be blocked until `Mmu::end_vm_update()` is called.
102     /// This guarantees the atomicity of a VM update.
103     /// If the VM is not resident, this is a NOP.
104     pub(crate) fn start_vm_update(
105         &self,
106         vm_as_data: &VmAsData<'drm>,
107         region: &Range<u64>,
108     ) -> Result {
109         self.as_manager.lock().start_vm_update(vm_as_data, region)
110     }
111 
112     /// Flags the end of a VM update.
113     ///
114     /// If the VM is resident, this will let GPU accesses on the updated
115     /// range go through, in case any of them were blocked.
116     /// If the VM is not resident, this is a NOP.
117     pub(crate) fn end_vm_update(&self, vm_as_data: &VmAsData<'drm>) -> Result {
118         self.as_manager.lock().end_vm_update(vm_as_data)
119     }
120 }
121