Skip to main content

kernel/
io.rs

1// SPDX-License-Identifier: GPL-2.0
2
3//! Memory-mapped IO.
4//!
5//! C header: [`include/asm-generic/io.h`](srctree/include/asm-generic/io.h)
6
7use crate::{
8    bindings,
9    prelude::*, //
10};
11
12#[cfg(CONFIG_HAS_IOMEM)]
13pub mod mem;
14pub mod poll;
15pub mod register;
16pub mod resource;
17
18pub use crate::register;
19pub use resource::Resource;
20
21use register::LocatedRegister;
22
23/// Physical address type.
24///
25/// This is a type alias to either `u32` or `u64` depending on the config option
26/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
27pub type PhysAddr = bindings::phys_addr_t;
28
29/// Resource Size type.
30///
31/// This is a type alias to either `u32` or `u64` depending on the config option
32/// `CONFIG_PHYS_ADDR_T_64BIT`, and it can be a u64 even on 32-bit architectures.
33pub type ResourceSize = bindings::resource_size_t;
34
35/// Raw representation of an MMIO region.
36///
37/// By itself, the existence of an instance of this structure does not provide any guarantees that
38/// the represented MMIO region does exist or is properly mapped.
39///
40/// Instead, the bus specific MMIO implementation must convert this raw representation into an
41/// `Mmio` instance providing the actual memory accessors. Only by the conversion into an `Mmio`
42/// structure any guarantees are given.
43pub struct MmioRaw<const SIZE: usize = 0> {
44    addr: usize,
45    maxsize: usize,
46}
47
48impl<const SIZE: usize> MmioRaw<SIZE> {
49    /// Returns a new `MmioRaw` instance on success, an error otherwise.
50    pub fn new(addr: usize, maxsize: usize) -> Result<Self> {
51        if maxsize < SIZE {
52            return Err(EINVAL);
53        }
54
55        Ok(Self { addr, maxsize })
56    }
57
58    /// Returns the base address of the MMIO region.
59    #[inline]
60    pub fn addr(&self) -> usize {
61        self.addr
62    }
63
64    /// Returns the maximum size of the MMIO region.
65    #[inline]
66    pub fn maxsize(&self) -> usize {
67        self.maxsize
68    }
69}
70
71/// IO-mapped memory region.
72///
73/// The creator (usually a subsystem / bus such as PCI) is responsible for creating the
74/// mapping, performing an additional region request etc.
75///
76/// # Invariant
77///
78/// `addr` is the start and `maxsize` the length of valid I/O mapped memory region of size
79/// `maxsize`.
80///
81/// # Examples
82///
83/// ```no_run
84/// # #![cfg(CONFIG_HAS_IOMEM)]
85/// use kernel::{
86///     bindings,
87///     ffi::c_void,
88///     io::{
89///         Io,
90///         IoKnownSize,
91///         Mmio,
92///         MmioRaw,
93///         PhysAddr,
94///     },
95/// };
96/// use core::ops::Deref;
97///
98/// // See also `pci::Bar` for a real example.
99/// struct IoMem<const SIZE: usize>(MmioRaw<SIZE>);
100///
101/// impl<const SIZE: usize> IoMem<SIZE> {
102///     /// # Safety
103///     ///
104///     /// [`paddr`, `paddr` + `SIZE`) must be a valid MMIO region that is mappable into the CPUs
105///     /// virtual address space.
106///     unsafe fn new(paddr: usize) -> Result<Self>{
107///         // SAFETY: By the safety requirements of this function [`paddr`, `paddr` + `SIZE`) is
108///         // valid for `ioremap`.
109///         let addr = unsafe { bindings::ioremap(paddr as PhysAddr, SIZE) };
110///         if addr.is_null() {
111///             return Err(ENOMEM);
112///         }
113///
114///         Ok(IoMem(MmioRaw::new(addr as usize, SIZE)?))
115///     }
116/// }
117///
118/// impl<const SIZE: usize> Drop for IoMem<SIZE> {
119///     fn drop(&mut self) {
120///         // SAFETY: `self.0.addr()` is guaranteed to be properly mapped by `Self::new`.
121///         unsafe { bindings::iounmap(self.0.addr() as *mut c_void); };
122///     }
123/// }
124///
125/// impl<const SIZE: usize> Deref for IoMem<SIZE> {
126///    type Target = Mmio<SIZE>;
127///
128///    fn deref(&self) -> &Self::Target {
129///         // SAFETY: The memory range stored in `self` has been properly mapped in `Self::new`.
130///         unsafe { Mmio::from_raw(&self.0) }
131///    }
132/// }
133///
134///# fn no_run() -> Result<(), Error> {
135/// // SAFETY: Invalid usage for example purposes.
136/// let iomem = unsafe { IoMem::<{ core::mem::size_of::<u32>() }>::new(0xBAAAAAAD)? };
137/// iomem.write32(0x42, 0x0);
138/// assert!(iomem.try_write32(0x42, 0x0).is_ok());
139/// assert!(iomem.try_write32(0x42, 0x4).is_err());
140/// # Ok(())
141/// # }
142/// ```
143#[repr(transparent)]
144pub struct Mmio<const SIZE: usize = 0>(MmioRaw<SIZE>);
145
146/// Checks whether an access of type `U` at the given `offset`
147/// is valid within this region.
148#[inline]
149const fn offset_valid<U>(offset: usize, size: usize) -> bool {
150    let type_size = core::mem::size_of::<U>();
151    if let Some(end) = offset.checked_add(type_size) {
152        end <= size && offset % type_size == 0
153    } else {
154        false
155    }
156}
157
158/// Trait indicating that an I/O backend supports operations of a certain type and providing an
159/// implementation for these operations.
160///
161/// Different I/O backends can implement this trait to expose only the operations they support.
162///
163/// For example, a PCI configuration space may implement `IoCapable<u8>`, `IoCapable<u16>`,
164/// and `IoCapable<u32>`, but not `IoCapable<u64>`, while an MMIO region on a 64-bit
165/// system might implement all four.
166pub trait IoCapable<T> {
167    /// Performs an I/O read of type `T` at `address` and returns the result.
168    ///
169    /// # Safety
170    ///
171    /// The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`.
172    unsafe fn io_read(&self, address: usize) -> T;
173
174    /// Performs an I/O write of `value` at `address`.
175    ///
176    /// # Safety
177    ///
178    /// The range `[address..address + size_of::<T>()]` must be within the bounds of `Self`.
179    unsafe fn io_write(&self, value: T, address: usize);
180}
181
182/// Describes a given I/O location: its offset, width, and type to convert the raw value from and
183/// into.
184///
185/// This trait is the key abstraction allowing [`Io::read`], [`Io::write`], and [`Io::update`] (and
186/// their fallible [`try_read`](Io::try_read), [`try_write`](Io::try_write) and
187/// [`try_update`](Io::try_update) counterparts) to work uniformly with both raw [`usize`] offsets
188/// (for primitive types like [`u32`]) and typed ones (like those generated by the [`register!`]
189/// macro).
190///
191/// An `IoLoc<T>` carries three pieces of information:
192///
193/// - The offset to access (returned by [`IoLoc::offset`]),
194/// - The width of the access (determined by [`IoLoc::IoType`]),
195/// - The type `T` in which the raw data is returned or provided.
196///
197/// `T` and `IoLoc::IoType` may differ: for instance, a typed register has `T` = the register type
198/// with its bitfields, and `IoType` = its backing primitive (e.g. `u32`).
199pub trait IoLoc<T> {
200    /// Size ([`u8`], [`u16`], etc) of the I/O performed on the returned [`offset`](IoLoc::offset).
201    type IoType: Into<T> + From<T>;
202
203    /// Consumes `self` and returns the offset of this location.
204    fn offset(self) -> usize;
205}
206
207/// Implements [`IoLoc<$ty>`] for [`usize`], allowing [`usize`] to be used as a parameter of
208/// [`Io::read`] and [`Io::write`].
209macro_rules! impl_usize_ioloc {
210    ($($ty:ty),*) => {
211        $(
212            impl IoLoc<$ty> for usize {
213                type IoType = $ty;
214
215                #[inline(always)]
216                fn offset(self) -> usize {
217                    self
218                }
219            }
220        )*
221    }
222}
223
224// Provide the ability to read any primitive type from a [`usize`].
225impl_usize_ioloc!(u8, u16, u32, u64);
226
227/// Types implementing this trait (e.g. MMIO BARs or PCI config regions)
228/// can perform I/O operations on regions of memory.
229///
230/// This is an abstract representation to be implemented by arbitrary I/O
231/// backends (e.g. MMIO, PCI config space, etc.).
232///
233/// The [`Io`] trait provides:
234/// - Base address and size information
235/// - Helper methods for offset validation and address calculation
236/// - Fallible (runtime checked) accessors for different data widths
237///
238/// Which I/O methods are available depends on which [`IoCapable<T>`] traits
239/// are implemented for the type.
240///
241/// # Examples
242///
243/// For MMIO regions, all widths (u8, u16, u32, and u64 on 64-bit systems) are typically
244/// supported. For PCI configuration space, u8, u16, and u32 are supported but u64 is not.
245pub trait Io {
246    /// Returns the base address of this mapping.
247    fn addr(&self) -> usize;
248
249    /// Returns the maximum size of this mapping.
250    fn maxsize(&self) -> usize;
251
252    /// Returns the absolute I/O address for a given `offset`,
253    /// performing runtime bound checks.
254    #[inline]
255    fn io_addr<U>(&self, offset: usize) -> Result<usize> {
256        if !offset_valid::<U>(offset, self.maxsize()) {
257            return Err(EINVAL);
258        }
259
260        // Probably no need to check, since the safety requirements of `Self::new` guarantee that
261        // this can't overflow.
262        self.addr().checked_add(offset).ok_or(EINVAL)
263    }
264
265    /// Fallible 8-bit read with runtime bounds check.
266    #[inline(always)]
267    fn try_read8(&self, offset: usize) -> Result<u8>
268    where
269        Self: IoCapable<u8>,
270    {
271        self.try_read(offset)
272    }
273
274    /// Fallible 16-bit read with runtime bounds check.
275    #[inline(always)]
276    fn try_read16(&self, offset: usize) -> Result<u16>
277    where
278        Self: IoCapable<u16>,
279    {
280        self.try_read(offset)
281    }
282
283    /// Fallible 32-bit read with runtime bounds check.
284    #[inline(always)]
285    fn try_read32(&self, offset: usize) -> Result<u32>
286    where
287        Self: IoCapable<u32>,
288    {
289        self.try_read(offset)
290    }
291
292    /// Fallible 64-bit read with runtime bounds check.
293    #[inline(always)]
294    fn try_read64(&self, offset: usize) -> Result<u64>
295    where
296        Self: IoCapable<u64>,
297    {
298        self.try_read(offset)
299    }
300
301    /// Fallible 8-bit write with runtime bounds check.
302    #[inline(always)]
303    fn try_write8(&self, value: u8, offset: usize) -> Result
304    where
305        Self: IoCapable<u8>,
306    {
307        self.try_write(offset, value)
308    }
309
310    /// Fallible 16-bit write with runtime bounds check.
311    #[inline(always)]
312    fn try_write16(&self, value: u16, offset: usize) -> Result
313    where
314        Self: IoCapable<u16>,
315    {
316        self.try_write(offset, value)
317    }
318
319    /// Fallible 32-bit write with runtime bounds check.
320    #[inline(always)]
321    fn try_write32(&self, value: u32, offset: usize) -> Result
322    where
323        Self: IoCapable<u32>,
324    {
325        self.try_write(offset, value)
326    }
327
328    /// Fallible 64-bit write with runtime bounds check.
329    #[inline(always)]
330    fn try_write64(&self, value: u64, offset: usize) -> Result
331    where
332        Self: IoCapable<u64>,
333    {
334        self.try_write(offset, value)
335    }
336
337    /// Infallible 8-bit read with compile-time bounds check.
338    #[inline(always)]
339    fn read8(&self, offset: usize) -> u8
340    where
341        Self: IoKnownSize + IoCapable<u8>,
342    {
343        self.read(offset)
344    }
345
346    /// Infallible 16-bit read with compile-time bounds check.
347    #[inline(always)]
348    fn read16(&self, offset: usize) -> u16
349    where
350        Self: IoKnownSize + IoCapable<u16>,
351    {
352        self.read(offset)
353    }
354
355    /// Infallible 32-bit read with compile-time bounds check.
356    #[inline(always)]
357    fn read32(&self, offset: usize) -> u32
358    where
359        Self: IoKnownSize + IoCapable<u32>,
360    {
361        self.read(offset)
362    }
363
364    /// Infallible 64-bit read with compile-time bounds check.
365    #[inline(always)]
366    fn read64(&self, offset: usize) -> u64
367    where
368        Self: IoKnownSize + IoCapable<u64>,
369    {
370        self.read(offset)
371    }
372
373    /// Infallible 8-bit write with compile-time bounds check.
374    #[inline(always)]
375    fn write8(&self, value: u8, offset: usize)
376    where
377        Self: IoKnownSize + IoCapable<u8>,
378    {
379        self.write(offset, value)
380    }
381
382    /// Infallible 16-bit write with compile-time bounds check.
383    #[inline(always)]
384    fn write16(&self, value: u16, offset: usize)
385    where
386        Self: IoKnownSize + IoCapable<u16>,
387    {
388        self.write(offset, value)
389    }
390
391    /// Infallible 32-bit write with compile-time bounds check.
392    #[inline(always)]
393    fn write32(&self, value: u32, offset: usize)
394    where
395        Self: IoKnownSize + IoCapable<u32>,
396    {
397        self.write(offset, value)
398    }
399
400    /// Infallible 64-bit write with compile-time bounds check.
401    #[inline(always)]
402    fn write64(&self, value: u64, offset: usize)
403    where
404        Self: IoKnownSize + IoCapable<u64>,
405    {
406        self.write(offset, value)
407    }
408
409    /// Generic fallible read with runtime bounds check.
410    ///
411    /// # Examples
412    ///
413    /// Read a primitive type from an I/O address:
414    ///
415    /// ```no_run
416    /// use kernel::io::{
417    ///     Io,
418    ///     Mmio,
419    /// };
420    ///
421    /// fn do_reads(io: &Mmio) -> Result {
422    ///     // 32-bit read from address `0x10`.
423    ///     let v: u32 = io.try_read(0x10)?;
424    ///
425    ///     // 8-bit read from address `0xfff`.
426    ///     let v: u8 = io.try_read(0xfff)?;
427    ///
428    ///     Ok(())
429    /// }
430    /// ```
431    #[inline(always)]
432    fn try_read<T, L>(&self, location: L) -> Result<T>
433    where
434        L: IoLoc<T>,
435        Self: IoCapable<L::IoType>,
436    {
437        let address = self.io_addr::<L::IoType>(location.offset())?;
438
439        // SAFETY: `address` has been validated by `io_addr`.
440        Ok(unsafe { self.io_read(address) }.into())
441    }
442
443    /// Generic fallible write with runtime bounds check.
444    ///
445    /// # Examples
446    ///
447    /// Write a primitive type to an I/O address:
448    ///
449    /// ```no_run
450    /// use kernel::io::{
451    ///     Io,
452    ///     Mmio,
453    /// };
454    ///
455    /// fn do_writes(io: &Mmio) -> Result {
456    ///     // 32-bit write of value `1` at address `0x10`.
457    ///     io.try_write(0x10, 1u32)?;
458    ///
459    ///     // 8-bit write of value `0xff` at address `0xfff`.
460    ///     io.try_write(0xfff, 0xffu8)?;
461    ///
462    ///     Ok(())
463    /// }
464    /// ```
465    #[inline(always)]
466    fn try_write<T, L>(&self, location: L, value: T) -> Result
467    where
468        L: IoLoc<T>,
469        Self: IoCapable<L::IoType>,
470    {
471        let address = self.io_addr::<L::IoType>(location.offset())?;
472        let io_value = value.into();
473
474        // SAFETY: `address` has been validated by `io_addr`.
475        unsafe { self.io_write(io_value, address) }
476
477        Ok(())
478    }
479
480    /// Generic fallible write of a fully-located register value.
481    ///
482    /// # Examples
483    ///
484    /// Tuples carrying a location and a value can be used with this method:
485    ///
486    /// ```no_run
487    /// use kernel::io::{
488    ///     register,
489    ///     Io,
490    ///     Mmio,
491    /// };
492    ///
493    /// register! {
494    ///     VERSION(u32) @ 0x100 {
495    ///         15:8 major;
496    ///         7:0  minor;
497    ///     }
498    /// }
499    ///
500    /// impl VERSION {
501    ///     fn new(major: u8, minor: u8) -> Self {
502    ///         VERSION::zeroed().with_major(major).with_minor(minor)
503    ///     }
504    /// }
505    ///
506    /// fn do_write_reg(io: &Mmio) -> Result {
507    ///
508    ///     io.try_write_reg(VERSION::new(1, 0))
509    /// }
510    /// ```
511    #[inline(always)]
512    fn try_write_reg<T, L, V>(&self, value: V) -> Result
513    where
514        L: IoLoc<T>,
515        V: LocatedRegister<Location = L, Value = T>,
516        Self: IoCapable<L::IoType>,
517    {
518        let (location, value) = value.into_io_op();
519
520        self.try_write(location, value)
521    }
522
523    /// Generic fallible update with runtime bounds check.
524    ///
525    /// Note: this does not perform any synchronization. The caller is responsible for ensuring
526    /// exclusive access if required.
527    ///
528    /// # Examples
529    ///
530    /// Read the u32 value at address `0x10`, increment it, and store the updated value back:
531    ///
532    /// ```no_run
533    /// use kernel::io::{
534    ///     Io,
535    ///     Mmio,
536    /// };
537    ///
538    /// fn do_update(io: &Mmio<0x1000>) -> Result {
539    ///     io.try_update(0x10, |v: u32| {
540    ///         v + 1
541    ///     })
542    /// }
543    /// ```
544    #[inline(always)]
545    fn try_update<T, L, F>(&self, location: L, f: F) -> Result
546    where
547        L: IoLoc<T>,
548        Self: IoCapable<L::IoType>,
549        F: FnOnce(T) -> T,
550    {
551        let address = self.io_addr::<L::IoType>(location.offset())?;
552
553        // SAFETY: `address` has been validated by `io_addr`.
554        let value: T = unsafe { self.io_read(address) }.into();
555        let io_value = f(value).into();
556
557        // SAFETY: `address` has been validated by `io_addr`.
558        unsafe { self.io_write(io_value, address) }
559
560        Ok(())
561    }
562
563    /// Generic infallible read with compile-time bounds check.
564    ///
565    /// # Examples
566    ///
567    /// Read a primitive type from an I/O address:
568    ///
569    /// ```no_run
570    /// use kernel::io::{
571    ///     Io,
572    ///     Mmio,
573    /// };
574    ///
575    /// fn do_reads(io: &Mmio<0x1000>) {
576    ///     // 32-bit read from address `0x10`.
577    ///     let v: u32 = io.read(0x10);
578    ///
579    ///     // 8-bit read from the top of the I/O space.
580    ///     let v: u8 = io.read(0xfff);
581    /// }
582    /// ```
583    #[inline(always)]
584    fn read<T, L>(&self, location: L) -> T
585    where
586        L: IoLoc<T>,
587        Self: IoKnownSize + IoCapable<L::IoType>,
588    {
589        let address = self.io_addr_assert::<L::IoType>(location.offset());
590
591        // SAFETY: `address` has been validated by `io_addr_assert`.
592        unsafe { self.io_read(address) }.into()
593    }
594
595    /// Generic infallible write with compile-time bounds check.
596    ///
597    /// # Examples
598    ///
599    /// Write a primitive type to an I/O address:
600    ///
601    /// ```no_run
602    /// use kernel::io::{
603    ///     Io,
604    ///     Mmio,
605    /// };
606    ///
607    /// fn do_writes(io: &Mmio<0x1000>) {
608    ///     // 32-bit write of value `1` at address `0x10`.
609    ///     io.write(0x10, 1u32);
610    ///
611    ///     // 8-bit write of value `0xff` at the top of the I/O space.
612    ///     io.write(0xfff, 0xffu8);
613    /// }
614    /// ```
615    #[inline(always)]
616    fn write<T, L>(&self, location: L, value: T)
617    where
618        L: IoLoc<T>,
619        Self: IoKnownSize + IoCapable<L::IoType>,
620    {
621        let address = self.io_addr_assert::<L::IoType>(location.offset());
622        let io_value = value.into();
623
624        // SAFETY: `address` has been validated by `io_addr_assert`.
625        unsafe { self.io_write(io_value, address) }
626    }
627
628    /// Generic infallible write of a fully-located register value.
629    ///
630    /// # Examples
631    ///
632    /// Tuples carrying a location and a value can be used with this method:
633    ///
634    /// ```no_run
635    /// use kernel::io::{
636    ///     register,
637    ///     Io,
638    ///     Mmio,
639    /// };
640    ///
641    /// register! {
642    ///     VERSION(u32) @ 0x100 {
643    ///         15:8 major;
644    ///         7:0  minor;
645    ///     }
646    /// }
647    ///
648    /// impl VERSION {
649    ///     fn new(major: u8, minor: u8) -> Self {
650    ///         VERSION::zeroed().with_major(major).with_minor(minor)
651    ///     }
652    /// }
653    ///
654    /// fn do_write_reg(io: &Mmio<0x1000>) {
655    ///     io.write_reg(VERSION::new(1, 0));
656    /// }
657    /// ```
658    #[inline(always)]
659    fn write_reg<T, L, V>(&self, value: V)
660    where
661        L: IoLoc<T>,
662        V: LocatedRegister<Location = L, Value = T>,
663        Self: IoKnownSize + IoCapable<L::IoType>,
664    {
665        let (location, value) = value.into_io_op();
666
667        self.write(location, value)
668    }
669
670    /// Generic infallible update with compile-time bounds check.
671    ///
672    /// Note: this does not perform any synchronization. The caller is responsible for ensuring
673    /// exclusive access if required.
674    ///
675    /// # Examples
676    ///
677    /// Read the u32 value at address `0x10`, increment it, and store the updated value back:
678    ///
679    /// ```no_run
680    /// use kernel::io::{
681    ///     Io,
682    ///     Mmio,
683    /// };
684    ///
685    /// fn do_update(io: &Mmio<0x1000>) {
686    ///     io.update(0x10, |v: u32| {
687    ///         v + 1
688    ///     })
689    /// }
690    /// ```
691    #[inline(always)]
692    fn update<T, L, F>(&self, location: L, f: F)
693    where
694        L: IoLoc<T>,
695        Self: IoKnownSize + IoCapable<L::IoType> + Sized,
696        F: FnOnce(T) -> T,
697    {
698        let address = self.io_addr_assert::<L::IoType>(location.offset());
699
700        // SAFETY: `address` has been validated by `io_addr_assert`.
701        let value: T = unsafe { self.io_read(address) }.into();
702        let io_value = f(value).into();
703
704        // SAFETY: `address` has been validated by `io_addr_assert`.
705        unsafe { self.io_write(io_value, address) }
706    }
707}
708
709/// Trait for types with a known size at compile time.
710///
711/// This trait is implemented by I/O backends that have a compile-time known size,
712/// enabling the use of infallible I/O accessors with compile-time bounds checking.
713///
714/// Types implementing this trait can use the infallible methods in [`Io`] trait
715/// (e.g., `read8`, `write32`), which require `Self: IoKnownSize` bound.
716pub trait IoKnownSize: Io {
717    /// Minimum usable size of this region.
718    const MIN_SIZE: usize;
719
720    /// Returns the absolute I/O address for a given `offset`,
721    /// performing compile-time bound checks.
722    // Always inline to optimize out error path of `build_assert`.
723    #[inline(always)]
724    fn io_addr_assert<U>(&self, offset: usize) -> usize {
725        build_assert!(offset_valid::<U>(offset, Self::MIN_SIZE));
726
727        self.addr() + offset
728    }
729}
730
731/// Implements [`IoCapable`] on `$mmio` for `$ty` using `$read_fn` and `$write_fn`.
732macro_rules! impl_mmio_io_capable {
733    ($mmio:ident, $(#[$attr:meta])* $ty:ty, $read_fn:ident, $write_fn:ident) => {
734        $(#[$attr])*
735        impl<const SIZE: usize> IoCapable<$ty> for $mmio<SIZE> {
736            unsafe fn io_read(&self, address: usize) -> $ty {
737                // SAFETY: By the trait invariant `address` is a valid address for MMIO operations.
738                unsafe { bindings::$read_fn(address as *const c_void) }
739            }
740
741            unsafe fn io_write(&self, value: $ty, address: usize) {
742                // SAFETY: By the trait invariant `address` is a valid address for MMIO operations.
743                unsafe { bindings::$write_fn(value, address as *mut c_void) }
744            }
745        }
746    };
747}
748
749// MMIO regions support 8, 16, and 32-bit accesses.
750impl_mmio_io_capable!(Mmio, u8, readb, writeb);
751impl_mmio_io_capable!(Mmio, u16, readw, writew);
752impl_mmio_io_capable!(Mmio, u32, readl, writel);
753// MMIO regions on 64-bit systems also support 64-bit accesses.
754impl_mmio_io_capable!(
755    Mmio,
756    #[cfg(CONFIG_64BIT)]
757    u64,
758    readq,
759    writeq
760);
761
762impl<const SIZE: usize> Io for Mmio<SIZE> {
763    /// Returns the base address of this mapping.
764    #[inline]
765    fn addr(&self) -> usize {
766        self.0.addr()
767    }
768
769    /// Returns the maximum size of this mapping.
770    #[inline]
771    fn maxsize(&self) -> usize {
772        self.0.maxsize()
773    }
774}
775
776impl<const SIZE: usize> IoKnownSize for Mmio<SIZE> {
777    const MIN_SIZE: usize = SIZE;
778}
779
780impl<const SIZE: usize> Mmio<SIZE> {
781    /// Converts an `MmioRaw` into an `Mmio` instance, providing the accessors to the MMIO mapping.
782    ///
783    /// # Safety
784    ///
785    /// Callers must ensure that `addr` is the start of a valid I/O mapped memory region of size
786    /// `maxsize`.
787    pub unsafe fn from_raw(raw: &MmioRaw<SIZE>) -> &Self {
788        // SAFETY: `Mmio` is a transparent wrapper around `MmioRaw`.
789        unsafe { &*core::ptr::from_ref(raw).cast() }
790    }
791}
792
793/// [`Mmio`] wrapper using relaxed accessors.
794///
795/// This type provides an implementation of [`Io`] that uses relaxed I/O MMIO operands instead of
796/// the regular ones.
797///
798/// See [`Mmio::relaxed`] for a usage example.
799#[repr(transparent)]
800pub struct RelaxedMmio<const SIZE: usize = 0>(Mmio<SIZE>);
801
802impl<const SIZE: usize> Io for RelaxedMmio<SIZE> {
803    #[inline]
804    fn addr(&self) -> usize {
805        self.0.addr()
806    }
807
808    #[inline]
809    fn maxsize(&self) -> usize {
810        self.0.maxsize()
811    }
812}
813
814impl<const SIZE: usize> IoKnownSize for RelaxedMmio<SIZE> {
815    const MIN_SIZE: usize = SIZE;
816}
817
818impl<const SIZE: usize> Mmio<SIZE> {
819    /// Returns a [`RelaxedMmio`] reference that performs relaxed I/O operations.
820    ///
821    /// Relaxed accessors do not provide ordering guarantees with respect to DMA or memory accesses
822    /// and can be used when such ordering is not required.
823    ///
824    /// # Examples
825    ///
826    /// ```no_run
827    /// use kernel::io::{
828    ///     Io,
829    ///     Mmio,
830    ///     RelaxedMmio,
831    /// };
832    ///
833    /// fn do_io(io: &Mmio<0x100>) {
834    ///     // The access is performed using `readl_relaxed` instead of `readl`.
835    ///     let v = io.relaxed().read32(0x10);
836    /// }
837    ///
838    /// ```
839    pub fn relaxed(&self) -> &RelaxedMmio<SIZE> {
840        // SAFETY: `RelaxedMmio` is `#[repr(transparent)]` over `Mmio`, so `Mmio<SIZE>` and
841        // `RelaxedMmio<SIZE>` have identical layout.
842        unsafe { core::mem::transmute(self) }
843    }
844}
845
846// MMIO regions support 8, 16, and 32-bit accesses.
847impl_mmio_io_capable!(RelaxedMmio, u8, readb_relaxed, writeb_relaxed);
848impl_mmio_io_capable!(RelaxedMmio, u16, readw_relaxed, writew_relaxed);
849impl_mmio_io_capable!(RelaxedMmio, u32, readl_relaxed, writel_relaxed);
850// MMIO regions on 64-bit systems also support 64-bit accesses.
851impl_mmio_io_capable!(
852    RelaxedMmio,
853    #[cfg(CONFIG_64BIT)]
854    u64,
855    readq_relaxed,
856    writeq_relaxed
857);