// SPDX-License-Identifier: GPL-2.012//! Generic memory-mapped IO.34use core::ops::Deref;56use crate::{7c_str,8device::{9Bound,10Device, //11},12devres::Devres,13io::{14self,15resource::{16Region,17Resource, //18},19Io,20IoRaw, //21},22prelude::*,23};2425/// An IO request for a specific device and resource.26pub struct IoRequest<'a> {27device: &'a Device<Bound>,28resource: &'a Resource,29}3031impl<'a> IoRequest<'a> {32/// Creates a new [`IoRequest`] instance.33///34/// # Safety35///36/// Callers must ensure that `resource` is valid for `device` during the37/// lifetime `'a`.38pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self {39IoRequest { device, resource }40}4142/// Maps an [`IoRequest`] where the size is known at compile time.43///44/// This uses the [`ioremap()`] C API.45///46/// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device47///48/// # Examples49///50/// The following example uses a [`kernel::platform::Device`] for51/// illustration purposes.52///53/// ```no_run54/// use kernel::{bindings, c_str, platform, of, device::Core};55/// struct SampleDriver;56///57/// impl platform::Driver for SampleDriver {58/// # type IdInfo = ();59///60/// fn probe(61/// pdev: &platform::Device<Core>,62/// info: Option<&Self::IdInfo>,63/// ) -> impl PinInit<Self, Error> {64/// let offset = 0; // Some offset.65///66/// // If the size is known at compile time, use [`Self::iomap_sized`].67/// //68/// // No runtime checks will apply when reading and writing.69/// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;70/// let iomem = request.iomap_sized::<42>();71/// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;72///73/// let io = iomem.access(pdev.as_ref())?;74///75/// // Read and write a 32-bit value at `offset`.76/// let data = io.read32_relaxed(offset);77///78/// io.write32_relaxed(data, offset);79///80/// # Ok(SampleDriver)81/// }82/// }83/// ```84pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a {85IoMem::new(self)86}8788/// Same as [`Self::iomap_sized`] but with exclusive access to the89/// underlying region.90///91/// This uses the [`ioremap()`] C API.92///93/// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device94pub fn iomap_exclusive_sized<const SIZE: usize>(95self,96) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a {97ExclusiveIoMem::new(self)98}99100/// Maps an [`IoRequest`] where the size is not known at compile time,101///102/// This uses the [`ioremap()`] C API.103///104/// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device105///106/// # Examples107///108/// The following example uses a [`kernel::platform::Device`] for109/// illustration purposes.110///111/// ```no_run112/// use kernel::{bindings, c_str, platform, of, device::Core};113/// struct SampleDriver;114///115/// impl platform::Driver for SampleDriver {116/// # type IdInfo = ();117///118/// fn probe(119/// pdev: &platform::Device<Core>,120/// info: Option<&Self::IdInfo>,121/// ) -> impl PinInit<Self, Error> {122/// let offset = 0; // Some offset.123///124/// // Unlike [`Self::iomap_sized`], here the size of the memory region125/// // is not known at compile time, so only the `try_read*` and `try_write*`126/// // family of functions should be used, leading to runtime checks on every127/// // access.128/// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;129/// let iomem = request.iomap();130/// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;131///132/// let io = iomem.access(pdev.as_ref())?;133///134/// let data = io.try_read32_relaxed(offset)?;135///136/// io.try_write32_relaxed(data, offset)?;137///138/// # Ok(SampleDriver)139/// }140/// }141/// ```142pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a {143Self::iomap_sized::<0>(self)144}145146/// Same as [`Self::iomap`] but with exclusive access to the underlying147/// region.148pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a {149Self::iomap_exclusive_sized::<0>(self)150}151}152153/// An exclusive memory-mapped IO region.154///155/// # Invariants156///157/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].158pub struct ExclusiveIoMem<const SIZE: usize> {159/// The underlying `IoMem` instance.160iomem: IoMem<SIZE>,161162/// The region abstraction. This represents exclusive access to the163/// range represented by the underlying `iomem`.164///165/// This field is needed for ownership of the region.166_region: Region,167}168169impl<const SIZE: usize> ExclusiveIoMem<SIZE> {170/// Creates a new `ExclusiveIoMem` instance.171fn ioremap(resource: &Resource) -> Result<Self> {172let start = resource.start();173let size = resource.size();174let name = resource.name().unwrap_or(c_str!(""));175176let region = resource177.request_region(178start,179size,180name.to_cstring()?,181io::resource::Flags::IORESOURCE_MEM,182)183.ok_or(EBUSY)?;184185let iomem = IoMem::ioremap(resource)?;186187let iomem = ExclusiveIoMem {188iomem,189_region: region,190};191192Ok(iomem)193}194195/// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`].196pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {197let dev = io_request.device;198let res = io_request.resource;199200Devres::new(dev, Self::ioremap(res))201}202}203204impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {205type Target = Io<SIZE>;206207fn deref(&self) -> &Self::Target {208&self.iomem209}210}211212/// A generic memory-mapped IO region.213///214/// Accesses to the underlying region is checked either at compile time, if the215/// region's size is known at that point, or at runtime otherwise.216///217/// # Invariants218///219/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the220/// start of the I/O memory mapped region.221pub struct IoMem<const SIZE: usize = 0> {222io: IoRaw<SIZE>,223}224225impl<const SIZE: usize> IoMem<SIZE> {226fn ioremap(resource: &Resource) -> Result<Self> {227// Note: Some ioremap() implementations use types that depend on the CPU228// word width rather than the bus address width.229//230// TODO: Properly address this in the C code to avoid this `try_into`.231let size = resource.size().try_into()?;232if size == 0 {233return Err(EINVAL);234}235236let res_start = resource.start();237238let addr = if resource239.flags()240.contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED)241{242// SAFETY:243// - `res_start` and `size` are read from a presumably valid `struct resource`.244// - `size` is known not to be zero at this point.245unsafe { bindings::ioremap_np(res_start, size) }246} else {247// SAFETY:248// - `res_start` and `size` are read from a presumably valid `struct resource`.249// - `size` is known not to be zero at this point.250unsafe { bindings::ioremap(res_start, size) }251};252253if addr.is_null() {254return Err(ENOMEM);255}256257let io = IoRaw::new(addr as usize, size)?;258let io = IoMem { io };259260Ok(io)261}262263/// Creates a new `IoMem` instance from a previously acquired [`IoRequest`].264pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {265let dev = io_request.device;266let res = io_request.resource;267268Devres::new(dev, Self::ioremap(res))269}270}271272impl<const SIZE: usize> Drop for IoMem<SIZE> {273fn drop(&mut self) {274// SAFETY: Safe as by the invariant of `Io`.275unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }276}277}278279impl<const SIZE: usize> Deref for IoMem<SIZE> {280type Target = Io<SIZE>;281282fn deref(&self) -> &Self::Target {283// SAFETY: Safe as by the invariant of `IoMem`.284unsafe { Io::from_raw(&self.io) }285}286}287288289