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);