// SPDX-License-Identifier: GPL-2.0
-use core::{
- cmp,
- mem, //
-};
+mod continuation;
+
- use core::{
- mem,
- sync::atomic::{
- fence,
- Ordering, //
- }, //
- };
++use core::mem;
use kernel::{
device,
/// Structure shared between the driver and the GSP and containing the command and message queues.
#[repr(C)]
- struct GspMem {
+ // TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
+ pub(super) struct GspMem {
/// Self-mapping page table entries.
- ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>,
+ ptes: PteArray<{ Self::PTE_ARRAY_SIZE }>,
/// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
- /// write and read pointers that the CPU updates.
+ /// write and read pointers that the CPU updates. This means that the read pointer here is an
+ /// index into the GSP queue.
///
/// This member is read-only for the GSP.
- cpuq: Msgq,
+ pub(super) cpuq: Msgq,
/// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
- /// write and read pointers that the GSP updates.
+ /// write and read pointers that the GSP updates. This means that the read pointer here is an
+ /// index into the CPU queue.
///
/// This member is read-only for the driver.
- gspq: Msgq,
+ pub(super) gspq: Msgq,
+ }
+
+ impl GspMem {
+ const PTE_ARRAY_SIZE: usize = GSP_PAGE_SIZE / size_of::<u64>();
}
// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
//
// # Invariants
//
- // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ // - The returned value is within `0..MSGQ_NUM_PAGES`.
fn gsp_write_ptr(&self) -> u32 {
- let gsp_mem = self.0.start_ptr();
-
- // SAFETY:
- // - The 'CoherentAllocation' contains at least one object.
- // - By the invariants of `CoherentAllocation` the pointer is valid.
- (unsafe { (*gsp_mem).gspq.tx.write_ptr() } % MSGQ_NUM_PAGES)
+ super::fw::gsp_mem::gsp_write_ptr(&self.0)
}
// Returns the index of the memory page the GSP will read the next command from.
//
// # Invariants
//
- // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ // - The returned value is within `0..MSGQ_NUM_PAGES`.
fn gsp_read_ptr(&self) -> u32 {
- let gsp_mem = self.0.start_ptr();
-
- // SAFETY:
- // - The 'CoherentAllocation' contains at least one object.
- // - By the invariants of `CoherentAllocation` the pointer is valid.
- (unsafe { (*gsp_mem).gspq.rx.read_ptr() } % MSGQ_NUM_PAGES)
+ super::fw::gsp_mem::gsp_read_ptr(&self.0)
}
// Returns the index of the memory page the CPU can read the next message from.
//
// # Invariants
//
- // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ // - The returned value is within `0..MSGQ_NUM_PAGES`.
fn cpu_read_ptr(&self) -> u32 {
- let gsp_mem = self.0.start_ptr();
-
- // SAFETY:
- // - The ['CoherentAllocation'] contains at least one object.
- // - By the invariants of CoherentAllocation the pointer is valid.
- (unsafe { (*gsp_mem).cpuq.rx.read_ptr() } % MSGQ_NUM_PAGES)
+ super::fw::gsp_mem::cpu_read_ptr(&self.0)
}
// Informs the GSP that it can send `elem_count` new pages into the message queue.
//
// # Invariants
//
- // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ // - The returned value is within `0..MSGQ_NUM_PAGES`.
fn cpu_write_ptr(&self) -> u32 {
- let gsp_mem = self.0.start_ptr();
-
- // SAFETY:
- // - The 'CoherentAllocation' contains at least one object.
- // - By the invariants of `CoherentAllocation` the pointer is valid.
- (unsafe { (*gsp_mem).cpuq.tx.write_ptr() } % MSGQ_NUM_PAGES)
+ super::fw::gsp_mem::cpu_write_ptr(&self.0)
}
// Informs the GSP that it can process `elem_count` new pages from the command queue.
},
};
+ // TODO: Replace with `IoView` projections once available; the `unwrap()` calls go away once we
+ // switch to the new `dma::Coherent` API.
+ pub(super) mod gsp_mem {
+ use core::sync::atomic::{
+ fence,
+ Ordering, //
+ };
+
+ use kernel::{
+ dma::CoherentAllocation,
+ dma_read,
+ dma_write,
+ prelude::*, //
+ };
+
+ use crate::gsp::cmdq::{
+ GspMem,
+ MSGQ_NUM_PAGES, //
+ };
+
+ pub(in crate::gsp) fn gsp_write_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result<u32> { Ok(dma_read!(qs, [0]?.gspq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap()
+ }
+
+ pub(in crate::gsp) fn gsp_read_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result<u32> { Ok(dma_read!(qs, [0]?.gspq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap()
+ }
+
+ pub(in crate::gsp) fn cpu_read_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result<u32> { Ok(dma_read!(qs, [0]?.cpuq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap()
+ }
+
+ pub(in crate::gsp) fn advance_cpu_read_ptr(qs: &CoherentAllocation<GspMem>, count: u32) {
+ let rptr = cpu_read_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
+
+ // Ensure read pointer is properly ordered.
+ fence(Ordering::SeqCst);
+
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result {
+ dma_write!(qs, [0]?.cpuq.rx.0.readPtr, rptr);
+ Ok(())
+ }()
+ .unwrap()
+ }
+
+ pub(in crate::gsp) fn cpu_write_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result<u32> { Ok(dma_read!(qs, [0]?.cpuq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap()
+ }
+
+ pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &CoherentAllocation<GspMem>, count: u32) {
+ let wptr = cpu_write_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
+
+ // PANIC: A `dma::CoherentAllocation` always contains at least one element.
+ || -> Result {
+ dma_write!(qs, [0]?.cpuq.tx.0.writePtr, wptr);
+ Ok(())
+ }()
+ .unwrap();
+
+ // Ensure all command data is visible before triggering the GSP read.
+ fence(Ordering::SeqCst);
+ }
+ }
+
+/// Maximum size of a single GSP message queue element in bytes.
+pub(crate) const GSP_MSG_QUEUE_ELEMENT_SIZE_MAX: usize =
+ num::u32_as_usize(bindings::GSP_MSG_QUEUE_ELEMENT_SIZE_MAX);
+
/// Empty type to group methods related to heap parameters for running the GSP firmware.
enum GspFwHeapParams {}