xref: /linux/drivers/gpu/drm/tyr/fw.rs (revision 44e7e7f7cffb10a93bb88e7cb59b7b8b3e2deb1c)
1 // SPDX-License-Identifier: GPL-2.0 or MIT
2 
3 //! Firmware loading and management for Mali CSF GPU.
4 //!
5 //! This module handles loading the Mali GPU firmware binary, parsing it into sections,
6 //! and mapping those sections into the MCU's virtual address space. Each firmware section
7 //! has specific properties (read/write/execute permissions, cache modes) and must be loaded
8 //! at specific virtual addresses expected by the MCU.
9 //!
10 //! See [`Firmware`] for the main firmware management interface and [`Section`] for
11 //! individual firmware sections.
12 //!
13 //! [`Firmware`]: crate::fw::Firmware
14 //! [`Section`]: crate::fw::Section
15 
16 use kernel::{
17     device::{
18         Bound,
19         Device, //
20     },
21     drm::{
22         gem::BaseObject, //
23     },
24     io::{
25         poll,
26         Io, //
27     },
28     num::Bounded,
29     prelude::*,
30     register,
31     str::CString,
32     sync::{
33         Arc,
34         ArcBorrow, //
35     },
36     time, //
37 };
38 
39 use crate::{
40     driver::{
41         IoMem,
42         TyrDrmDevice, //
43     },
44     fw::parser::{
45         FwParser,
46         ParsedSection, //
47     },
48     gem,
49     gem::{
50         KernelBo,
51         KernelBoVaAlloc, //
52     },
53     gpu::GpuInfo,
54 
55     mmu::Mmu,
56     regs::{
57         gpu_control::{
58             McuControlMode,
59             McuStatus,
60             GPU_ID,
61             MCU_CONTROL,
62             MCU_STATUS, //
63         }, //
64         job_control::{
65             JOB_IRQ_CLEAR,
66             JOB_IRQ_RAWSTAT, //
67         }, //
68     },
69     vm::Vm, //
70 };
71 
72 mod parser;
73 
74 pub(super) const CSF_MCU_SHARED_REGION_START: u32 = 0x04000000;
75 
76 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
77 #[repr(u8)]
78 pub(super) enum CacheMode {
79     None = 0,
80     Cached = 1,
81     UncachedCoherent = 2,
82     CachedCoherent = 3,
83 }
84 
85 impl From<Bounded<u32, 2>> for CacheMode {
86     fn from(value: Bounded<u32, 2>) -> Self {
87         match value.get() {
88             0 => Self::None,
89             1 => Self::Cached,
90             2 => Self::UncachedCoherent,
91             3 => Self::CachedCoherent,
92             _ => unreachable!(),
93         }
94     }
95 }
96 
97 impl From<CacheMode> for Bounded<u32, 2> {
98     fn from(value: CacheMode) -> Self {
99         Bounded::try_new(value as u32).unwrap()
100     }
101 }
102 
103 register! {
104      #[allow(non_upper_case_globals)]
105     pub(super) SectionFlags(u32) @ 0x0 {
106         0:0 read => bool;
107         1:1 write => bool;
108         2:2 exec => bool;
109         4:3 cache_mode => CacheMode;
110         5:5 prot => bool;
111         30:30 shared => bool;
112         31:31 zero => bool;
113     }
114 }
115 
116 impl SectionFlags {
117     const VALID_MASK: u32 = Self::READ_MASK
118         | Self::WRITE_MASK
119         | Self::EXEC_MASK
120         | Self::CACHE_MODE_MASK
121         | Self::PROT_MASK
122         | Self::SHARED_MASK
123         | Self::ZERO_MASK;
124 
125     fn try_from_fw(value: u32) -> Result<Self> {
126         if value & !Self::VALID_MASK != 0 {
127             Err(EINVAL)
128         } else {
129             Ok(Self::from_raw(value))
130         }
131     }
132 }
133 
134 /// A parsed section of the firmware binary.
135 struct Section<'drm> {
136     // Raw firmware section data for reset purposes
137     #[expect(dead_code)]
138     data: KVec<u8>,
139 
140     // Keep the BO backing this firmware section so that both the
141     // GPU mapping and CPU mapping remain valid until the Section is dropped.
142     #[expect(dead_code)]
143     mem: gem::KernelBo<'drm>,
144 }
145 
146 /// Loaded firmware with sections mapped into MCU VM.
147 pub(crate) struct Firmware<'drm> {
148     /// Iomem need to access registers.
149     iomem: Arc<IoMem<'drm>>,
150 
151     /// MCU VM.
152     vm: Arc<Vm<'drm>>,
153 
154     /// List of firmware sections.
155     #[expect(dead_code)]
156     sections: KVec<Section<'drm>>,
157 }
158 
159 impl<'drm> Drop for Firmware<'drm> {
160     fn drop(&mut self) {
161         // Stop the MCU before releasing its firmware mappings and memory.
162         let _ = self.stop();
163 
164         // AS slots retain a VM ref, we need to kill the circular ref manually.
165         self.vm.kill();
166     }
167 }
168 
169 impl<'drm> Firmware<'drm> {
170     fn init_section_mem(dev: &Device, mem: &mut KernelBo<'drm>, data: &KVec<u8>) -> Result {
171         if data.is_empty() {
172             return Ok(());
173         }
174 
175         let vmap = mem.bo().vmap::<0>()?;
176         let size = mem.bo().size();
177 
178         if data.len() > size {
179             dev_err!(dev, "fw section {} bigger than BO {}", data.len(), size);
180             return Err(EINVAL);
181         }
182 
183         for (i, &byte) in data.iter().enumerate() {
184             vmap.try_write8(byte, i)?;
185         }
186 
187         Ok(())
188     }
189 
190     fn request(ddev: &TyrDrmDevice, gpu_info: &GpuInfo) -> Result<kernel::firmware::Firmware> {
191         let gpu_id = GPU_ID::from_raw(gpu_info.gpu_id);
192 
193         let path = CString::try_from_fmt(fmt!(
194             "arm/mali/arch{}.{}/mali_csffw.bin",
195             gpu_id.arch_major().get(),
196             gpu_id.arch_minor().get()
197         ))?;
198 
199         kernel::firmware::Firmware::request(&path, ddev.as_ref().as_ref())
200     }
201 
202     fn load(
203         dev: &Device,
204         ddev: &TyrDrmDevice,
205         gpu_info: &GpuInfo,
206     ) -> Result<(kernel::firmware::Firmware, KVec<ParsedSection>)> {
207         let fw = Self::request(ddev, gpu_info)?;
208         let mut parser = FwParser::new(dev, fw.data());
209 
210         let parsed_sections = parser.parse()?;
211 
212         Ok((fw, parsed_sections))
213     }
214 
215     /// Load firmware and map sections into MCU VM.
216     pub(crate) fn new(
217         dev: &'drm Device<Bound>,
218         iomem: Arc<IoMem<'drm>>,
219         ddev: &TyrDrmDevice,
220         mmu: ArcBorrow<'_, Mmu<'drm>>,
221         gpu_info: &GpuInfo,
222     ) -> Result<Firmware<'drm>> {
223         let vm = Vm::new(dev, ddev, mmu, gpu_info)?;
224         vm.activate()?;
225 
226         let result = (|| {
227             let (fw, parsed_sections) = Self::load(dev, ddev, gpu_info)?;
228             let mut sections = KVec::new();
229             for parsed in parsed_sections {
230                 let size = u64::from(parsed.va.end.checked_sub(parsed.va.start).ok_or(EINVAL)?);
231 
232                 let va = u64::from(parsed.va.start);
233 
234                 let mut mem = KernelBo::new(
235                     ddev,
236                     vm.clone(),
237                     size,
238                     KernelBoVaAlloc::Explicit(va),
239                     parsed.vm_map_flags,
240                 )?;
241 
242                 let section_start = parsed.data_range.start as usize;
243                 let section_end = parsed.data_range.end as usize;
244                 let mut data = KVec::new();
245 
246                 // Ensure that the firmware slice is not out of bounds.
247                 let fw_data = fw.data();
248                 let bytes = fw_data.get(section_start..section_end).ok_or(EINVAL)?;
249                 data.extend_from_slice(bytes, GFP_KERNEL)?;
250 
251                 Self::init_section_mem(dev, &mut mem, &data)?;
252 
253                 sections.push(Section { data, mem }, GFP_KERNEL)?;
254             }
255 
256             Ok(Firmware {
257                 iomem,
258                 vm: vm.clone(),
259                 sections,
260             })
261         })();
262 
263         if result.is_err() {
264             vm.kill();
265         }
266 
267         result
268     }
269 
270     pub(crate) fn boot(&self) -> Result {
271         let io = &self.iomem;
272 
273         // Discard any stale global interrupt.
274         io.write_reg(JOB_IRQ_CLEAR::zeroed().with_glb(true));
275 
276         io.write_reg(MCU_CONTROL::zeroed().with_req(McuControlMode::Auto));
277 
278         if let Err(e) = poll::read_poll_timeout(
279             || Ok((io.read(MCU_STATUS), io.read(JOB_IRQ_RAWSTAT))),
280             |(mcu_status, irq_rawstat)| {
281                 mcu_status.value() == McuStatus::Enabled && irq_rawstat.glb()
282             },
283             time::Delta::from_millis(1),
284             time::Delta::from_millis(100),
285         ) {
286             let status = io.read(MCU_STATUS);
287             dev_err!(
288                 self.vm.dev(),
289                 "MCU failed to boot, status: {:?}",
290                 status.value()
291             );
292             return Err(e);
293         }
294 
295         io.write_reg(JOB_IRQ_CLEAR::zeroed().with_glb(true));
296 
297         Ok(())
298     }
299 
300     fn stop(&self) -> Result {
301         let io = &self.iomem;
302         io.write_reg(MCU_CONTROL::zeroed().with_req(McuControlMode::Disable));
303 
304         if let Err(e) = poll::read_poll_timeout(
305             || Ok(io.read(MCU_STATUS)),
306             |status| status.value() == McuStatus::Disabled,
307             time::Delta::from_micros(10),
308             time::Delta::from_millis(100),
309         ) {
310             let status = io.read(MCU_STATUS);
311             dev_err!(
312                 self.vm.dev(),
313                 "MCU failed to stop, status: {:?}",
314                 status.value()
315             );
316             return Err(e);
317         }
318 
319         Ok(())
320     }
321 }
322