| // SPDX-License-Identifier: GPL-2.0 | |
| //! Generic memory-mapped IO. | |
| use core::ops::Deref; | |
| use crate::c_str; | |
| use crate::device::Bound; | |
| use crate::device::Device; | |
| use crate::devres::Devres; | |
| use crate::io; | |
| use crate::io::resource::Region; | |
| use crate::io::resource::Resource; | |
| use crate::io::Io; | |
| use crate::io::IoRaw; | |
| use crate::prelude::*; | |
| /// An IO request for a specific device and resource. | |
| pub struct IoRequest<'a> { | |
| device: &'a Device<Bound>, | |
| resource: &'a Resource, | |
| } | |
| impl<'a> IoRequest<'a> { | |
| /// Creates a new [`IoRequest`] instance. | |
| /// | |
| /// # Safety | |
| /// | |
| /// Callers must ensure that `resource` is valid for `device` during the | |
| /// lifetime `'a`. | |
| pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self { | |
| IoRequest { device, resource } | |
| } | |
| /// Maps an [`IoRequest`] where the size is known at compile time. | |
| /// | |
| /// This uses the [`ioremap()`] C API. | |
| /// | |
| /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device | |
| /// | |
| /// # Examples | |
| /// | |
| /// The following example uses a [`kernel::platform::Device`] for | |
| /// illustration purposes. | |
| /// | |
| /// ```no_run | |
| /// use kernel::{bindings, c_str, platform, of, device::Core}; | |
| /// struct SampleDriver; | |
| /// | |
| /// impl platform::Driver for SampleDriver { | |
| /// # type IdInfo = (); | |
| /// | |
| /// fn probe( | |
| /// pdev: &platform::Device<Core>, | |
| /// info: Option<&Self::IdInfo>, | |
| /// ) -> Result<Pin<KBox<Self>>> { | |
| /// 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())?; | |
| /// | |
| /// // Read and write a 32-bit value at `offset`. | |
| /// let data = io.read32_relaxed(offset); | |
| /// | |
| /// io.write32_relaxed(data, offset); | |
| /// | |
| /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) | |
| /// } | |
| /// } | |
| /// ``` | |
| pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a { | |
| IoMem::new(self) | |
| } | |
| /// Same as [`Self::iomap_sized`] but with exclusive access to the | |
| /// underlying region. | |
| /// | |
| /// 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) | |
| } | |
| /// Maps an [`IoRequest`] where the size is not known at compile time, | |
| /// | |
| /// This uses the [`ioremap()`] C API. | |
| /// | |
| /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device | |
| /// | |
| /// # Examples | |
| /// | |
| /// The following example uses a [`kernel::platform::Device`] for | |
| /// illustration purposes. | |
| /// | |
| /// ```no_run | |
| /// use kernel::{bindings, c_str, platform, of, device::Core}; | |
| /// struct SampleDriver; | |
| /// | |
| /// impl platform::Driver for SampleDriver { | |
| /// # type IdInfo = (); | |
| /// | |
| /// fn probe( | |
| /// pdev: &platform::Device<Core>, | |
| /// info: Option<&Self::IdInfo>, | |
| /// ) -> Result<Pin<KBox<Self>>> { | |
| /// let offset = 0; // Some offset. | |
| /// | |
| /// // Unlike [`Self::iomap_sized`], here the size of the memory region | |
| /// // is not known at compile time, so only the `try_read*` and `try_write*` | |
| /// // 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 data = io.try_read32_relaxed(offset)?; | |
| /// | |
| /// io.try_write32_relaxed(data, offset)?; | |
| /// | |
| /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) | |
| /// } | |
| /// } | |
| /// ``` | |
| pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a { | |
| Self::iomap_sized::<0>(self) | |
| } | |
| /// 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) | |
| } | |
| } | |
| /// An exclusive memory-mapped IO region. | |
| /// | |
| /// # Invariants | |
| /// | |
| /// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`]. | |
| pub struct ExclusiveIoMem<const SIZE: usize> { | |
| /// The underlying `IoMem` instance. | |
| iomem: IoMem<SIZE>, | |
| /// The region abstraction. This represents exclusive access to the | |
| /// range represented by the underlying `iomem`. | |
| /// | |
| /// This field is needed for ownership of the region. | |
| _region: Region, | |
| } | |
| impl<const SIZE: usize> ExclusiveIoMem<SIZE> { | |
| /// Creates a new `ExclusiveIoMem` instance. | |
| fn ioremap(resource: &Resource) -> Result<Self> { | |
| let start = resource.start(); | |
| let size = resource.size(); | |
| let name = resource.name().unwrap_or(c_str!("")); | |
| let region = resource | |
| .request_region( | |
| start, | |
| size, | |
| name.to_cstring()?, | |
| io::resource::Flags::IORESOURCE_MEM, | |
| ) | |
| .ok_or(EBUSY)?; | |
| let iomem = IoMem::ioremap(resource)?; | |
| let iomem = 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)) | |
| } | |
| } | |
| impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> { | |
| type Target = Io<SIZE>; | |
| fn deref(&self) -> &Self::Target { | |
| &self.iomem | |
| } | |
| } | |
| /// A generic memory-mapped IO region. | |
| /// | |
| /// Accesses to the underlying region is checked either at compile time, if the | |
| /// region's size is known at that point, or at runtime otherwise. | |
| /// | |
| /// # Invariants | |
| /// | |
| /// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the | |
| /// start of the I/O memory mapped region. | |
| pub struct IoMem<const SIZE: usize = 0> { | |
| io: IoRaw<SIZE>, | |
| } | |
| impl<const SIZE: usize> IoMem<SIZE> { | |
| fn ioremap(resource: &Resource) -> Result<Self> { | |
| // Note: Some ioremap() implementations use types that depend on the CPU | |
| // word width rather than the bus address width. | |
| // | |
| // TODO: Properly address this in the C code to avoid this `try_into`. | |
| let size = resource.size().try_into()?; | |
| if size == 0 { | |
| return Err(EINVAL); | |
| } | |
| let res_start = resource.start(); | |
| let addr = if resource | |
| .flags() | |
| .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED) | |
| { | |
| // SAFETY: | |
| // - `res_start` and `size` are read from a presumably valid `struct resource`. | |
| // - `size` is known not to be zero at this point. | |
| unsafe { bindings::ioremap_np(res_start, size) } | |
| } else { | |
| // SAFETY: | |
| // - `res_start` and `size` are read from a presumably valid `struct resource`. | |
| // - `size` is known not to be zero at this point. | |
| unsafe { bindings::ioremap(res_start, size) } | |
| }; | |
| if addr.is_null() { | |
| return Err(ENOMEM); | |
| } | |
| let io = IoRaw::new(addr as usize, size)?; | |
| let io = IoMem { io }; | |
| Ok(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)) | |
| } | |
| } | |
| 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 = Io<SIZE>; | |
| fn deref(&self) -> &Self::Target { | |
| // SAFETY: Safe as by the invariant of `IoMem`. | |
| unsafe { Io::from_raw(&self.io) } | |
| } | |
| } |