summaryrefslogtreecommitdiff
path: root/rust/kernel/io/mem.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel/io/mem.rs')
-rw-r--r--rust/kernel/io/mem.rs175
1 files changed, 104 insertions, 71 deletions
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index 620022cff401..32a919099dcd 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -2,24 +2,28 @@
//! Generic memory-mapped IO.
-use core::ops::Deref;
-
use crate::{
device::{
Bound,
Device, //
},
- devres::Devres,
+ devres::DevresLt,
io::{
self,
resource::{
Region,
Resource, //
},
+ IoBase,
Mmio,
+ MmioBackend,
MmioRaw, //
},
prelude::*,
+ types::{
+ CovariantForLt,
+ ForLt, //
+ },
};
/// An IO request for a specific device and resource.
@@ -54,6 +58,7 @@ impl<'a> IoRequest<'a> {
/// use kernel::{
/// bindings,
/// device::Core,
+ /// io::Io,
/// of,
/// platform,
/// };
@@ -61,33 +66,31 @@ impl<'a> IoRequest<'a> {
///
/// impl platform::Driver for SampleDriver {
/// # type IdInfo = ();
+ /// # type Data<'bound> = Self;
///
- /// fn probe(
- /// pdev: &platform::Device<Core>,
- /// info: Option<&Self::IdInfo>,
- /// ) -> impl PinInit<Self, Error> {
+ /// fn probe<'bound>(
+ /// pdev: &'bound platform::Device<Core<'_>>,
+ /// info: Option<&'bound Self::IdInfo>,
+ /// ) -> impl PinInit<Self, Error> + 'bound {
/// let offset = 0; // Some offset.
///
/// // If the size is known at compile time, use [`Self::iomap_sized`].
/// //
/// // No runtime checks will apply when reading and writing.
/// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
- /// let iomem = request.iomap_sized::<42>();
- /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
- ///
- /// let io = iomem.access(pdev.as_ref())?;
+ /// let iomem = request.iomap_sized::<42>()?;
///
/// // Read and write a 32-bit value at `offset`.
- /// let data = io.read32_relaxed(offset);
+ /// let data = iomem.read32(offset);
///
- /// io.write32_relaxed(data, offset);
+ /// iomem.write32(data, offset);
///
/// # Ok(SampleDriver)
/// }
/// }
/// ```
- pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a {
- IoMem::new(self)
+ pub fn iomap_sized<const SIZE: usize>(self) -> Result<IoMem<'a, SIZE>> {
+ IoMem::ioremap(self.device, self.resource)
}
/// Same as [`Self::iomap_sized`] but with exclusive access to the
@@ -96,10 +99,8 @@ impl<'a> IoRequest<'a> {
/// This uses the [`ioremap()`] C API.
///
/// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
- pub fn iomap_exclusive_sized<const SIZE: usize>(
- self,
- ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a {
- ExclusiveIoMem::new(self)
+ pub fn iomap_exclusive_sized<const SIZE: usize>(self) -> Result<ExclusiveIoMem<'a, SIZE>> {
+ ExclusiveIoMem::ioremap(self.device, self.resource)
}
/// Maps an [`IoRequest`] where the size is not known at compile time,
@@ -117,6 +118,7 @@ impl<'a> IoRequest<'a> {
/// use kernel::{
/// bindings,
/// device::Core,
+ /// io::Io,
/// of,
/// platform,
/// };
@@ -124,11 +126,12 @@ impl<'a> IoRequest<'a> {
///
/// impl platform::Driver for SampleDriver {
/// # type IdInfo = ();
+ /// # type Data<'bound> = Self;
///
- /// fn probe(
- /// pdev: &platform::Device<Core>,
- /// info: Option<&Self::IdInfo>,
- /// ) -> impl PinInit<Self, Error> {
+ /// fn probe<'bound>(
+ /// pdev: &'bound platform::Device<Core<'_>>,
+ /// info: Option<&'bound Self::IdInfo>,
+ /// ) -> impl PinInit<Self, Error> + 'bound {
/// let offset = 0; // Some offset.
///
/// // Unlike [`Self::iomap_sized`], here the size of the memory region
@@ -136,27 +139,24 @@ impl<'a> IoRequest<'a> {
/// // family of functions should be used, leading to runtime checks on every
/// // access.
/// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
- /// let iomem = request.iomap();
- /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
- ///
- /// let io = iomem.access(pdev.as_ref())?;
+ /// let iomem = request.iomap()?;
///
- /// let data = io.try_read32_relaxed(offset)?;
+ /// let data = iomem.try_read32(offset)?;
///
- /// io.try_write32_relaxed(data, offset)?;
+ /// iomem.try_write32(data, offset)?;
///
/// # Ok(SampleDriver)
/// }
/// }
/// ```
- pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a {
- Self::iomap_sized::<0>(self)
+ pub fn iomap(self) -> Result<IoMem<'a>> {
+ self.iomap_sized::<0>()
}
/// Same as [`Self::iomap`] but with exclusive access to the underlying
/// region.
- pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a {
- Self::iomap_exclusive_sized::<0>(self)
+ pub fn iomap_exclusive(self) -> Result<ExclusiveIoMem<'a, 0>> {
+ self.iomap_exclusive_sized::<0>()
}
}
@@ -165,9 +165,9 @@ impl<'a> IoRequest<'a> {
/// # Invariants
///
/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
-pub struct ExclusiveIoMem<const SIZE: usize> {
+pub struct ExclusiveIoMem<'a, const SIZE: usize> {
/// The underlying `IoMem` instance.
- iomem: IoMem<SIZE>,
+ iomem: IoMem<'a, SIZE>,
/// The region abstraction. This represents exclusive access to the
/// range represented by the underlying `iomem`.
@@ -176,9 +176,22 @@ pub struct ExclusiveIoMem<const SIZE: usize> {
_region: Region,
}
-impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
+impl<const SIZE: usize> ForLt for ExclusiveIoMem<'static, SIZE> {
+ type Of<'a> = ExclusiveIoMem<'a, SIZE>;
+}
+
+// SAFETY: `ExclusiveIoMem<'a, SIZE>` is covariant over `'a`; it holds an `IoMem<'a, SIZE>`,
+// which holds `&'a Device<Bound>`, which is covariant.
+unsafe impl<const SIZE: usize> CovariantForLt for ExclusiveIoMem<'static, SIZE> {}
+
+/// A device-managed exclusive I/O memory region.
+///
+/// See [`ExclusiveIoMem::into_devres`].
+pub type DevresExclusiveIoMem<const SIZE: usize> = DevresLt<ExclusiveIoMem<'static, SIZE>>;
+
+impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
/// Creates a new `ExclusiveIoMem` instance.
- fn ioremap(resource: &Resource) -> Result<Self> {
+ fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
let start = resource.start();
let size = resource.size();
let name = resource.name().unwrap_or_default();
@@ -192,30 +205,33 @@ impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
)
.ok_or(EBUSY)?;
- let iomem = IoMem::ioremap(resource)?;
+ let iomem = IoMem::ioremap(dev, resource)?;
- let iomem = ExclusiveIoMem {
+ Ok(ExclusiveIoMem {
iomem,
_region: region,
- };
-
- Ok(iomem)
+ })
}
- /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`].
- pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
- let dev = io_request.device;
- let res = io_request.resource;
-
- Devres::new(dev, Self::ioremap(res))
+ /// Consume the `ExclusiveIoMem` and register it as a device-managed resource.
+ ///
+ /// The returned [`DevresExclusiveIoMem`] can outlive the original borrow and be stored in
+ /// driver data. Access to the I/O memory is revoked automatically when the device is unbound.
+ pub fn into_devres(self) -> Result<DevresExclusiveIoMem<SIZE>> {
+ let dev = self.iomem.dev;
+ // SAFETY: `ExclusiveIoMem` only holds a device reference and an I/O mapping, both of
+ // which remain valid for the device's full bound scope, not just for `'a`.
+ unsafe { DevresLt::new(dev, self) }
}
}
-impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
- type Target = Mmio<SIZE>;
+impl<'a, const SIZE: usize> IoBase<'a> for &'a ExclusiveIoMem<'_, SIZE> {
+ type Backend = MmioBackend;
+ type Target = super::Region<SIZE>;
- fn deref(&self) -> &Self::Target {
- &self.iomem
+ #[inline]
+ fn as_view(self) -> Mmio<'a, Self::Target> {
+ self.iomem.as_view()
}
}
@@ -228,12 +244,26 @@ impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
///
/// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the
/// start of the I/O memory mapped region.
-pub struct IoMem<const SIZE: usize = 0> {
- io: MmioRaw<SIZE>,
+pub struct IoMem<'a, const SIZE: usize = 0> {
+ dev: &'a Device<Bound>,
+ io: MmioRaw<super::Region<SIZE>>,
}
-impl<const SIZE: usize> IoMem<SIZE> {
- fn ioremap(resource: &Resource) -> Result<Self> {
+impl<const SIZE: usize> ForLt for IoMem<'static, SIZE> {
+ type Of<'a> = IoMem<'a, SIZE>;
+}
+
+// SAFETY: `IoMem<'a, SIZE>` is covariant over `'a`; it holds `&'a Device<Bound>`,
+// which is covariant.
+unsafe impl<const SIZE: usize> CovariantForLt for IoMem<'static, SIZE> {}
+
+/// A device-managed I/O memory region.
+///
+/// See [`IoMem::into_devres`].
+pub type DevresIoMem<const SIZE: usize = 0> = DevresLt<IoMem<'static, SIZE>>;
+
+impl<'a, const SIZE: usize> IoMem<'a, SIZE> {
+ fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
// Note: Some ioremap() implementations use types that depend on the CPU
// word width rather than the bus address width.
//
@@ -264,33 +294,36 @@ impl<const SIZE: usize> IoMem<SIZE> {
return Err(ENOMEM);
}
- let io = MmioRaw::new(addr as usize, size)?;
- let io = IoMem { io };
-
- Ok(io)
+ let io = MmioRaw::new_region(addr as usize, size)?;
+ Ok(IoMem { dev, io })
}
- /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`].
- pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
- let dev = io_request.device;
- let res = io_request.resource;
-
- Devres::new(dev, Self::ioremap(res))
+ /// Consume the `IoMem` and register it as a device-managed resource.
+ ///
+ /// The returned [`DevresIoMem`] can outlive the original borrow and be stored in driver data.
+ /// Access to the I/O memory is revoked automatically when the device is unbound.
+ pub fn into_devres(self) -> Result<DevresIoMem<SIZE>> {
+ let dev = self.dev;
+ // SAFETY: `IoMem` only holds a device reference and an I/O mapping, both of which
+ // remain valid for the device's full bound scope, not just for `'a`.
+ unsafe { DevresLt::new(dev, self) }
}
}
-impl<const SIZE: usize> Drop for IoMem<SIZE> {
+impl<const SIZE: usize> Drop for IoMem<'_, SIZE> {
fn drop(&mut self) {
// SAFETY: Safe as by the invariant of `Io`.
unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }
}
}
-impl<const SIZE: usize> Deref for IoMem<SIZE> {
- type Target = Mmio<SIZE>;
+impl<'a, const SIZE: usize> IoBase<'a> for &'a IoMem<'_, SIZE> {
+ type Backend = MmioBackend;
+ type Target = super::Region<SIZE>;
- fn deref(&self) -> &Self::Target {
+ #[inline]
+ fn as_view(self) -> Mmio<'a, Self::Target> {
// SAFETY: Safe as by the invariant of `IoMem`.
- unsafe { Mmio::from_raw(&self.io) }
+ unsafe { Mmio::from_raw(self.io) }
}
}