kernel/io/
mem.rs

1// SPDX-License-Identifier: GPL-2.0
2
3//! Generic memory-mapped IO.
4
5use core::ops::Deref;
6
7use crate::c_str;
8use crate::device::Bound;
9use crate::device::Device;
10use crate::devres::Devres;
11use crate::io;
12use crate::io::resource::Region;
13use crate::io::resource::Resource;
14use crate::io::Io;
15use crate::io::IoRaw;
16use crate::prelude::*;
17
18/// An IO request for a specific device and resource.
19pub struct IoRequest<'a> {
20    device: &'a Device<Bound>,
21    resource: &'a Resource,
22}
23
24impl<'a> IoRequest<'a> {
25    /// Creates a new [`IoRequest`] instance.
26    ///
27    /// # Safety
28    ///
29    /// Callers must ensure that `resource` is valid for `device` during the
30    /// lifetime `'a`.
31    pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self {
32        IoRequest { device, resource }
33    }
34
35    /// Maps an [`IoRequest`] where the size is known at compile time.
36    ///
37    /// This uses the [`ioremap()`] C API.
38    ///
39    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
40    ///
41    /// # Examples
42    ///
43    /// The following example uses a [`kernel::platform::Device`] for
44    /// illustration purposes.
45    ///
46    /// ```no_run
47    /// use kernel::{bindings, c_str, platform, of, device::Core};
48    /// struct SampleDriver;
49    ///
50    /// impl platform::Driver for SampleDriver {
51    ///    # type IdInfo = ();
52    ///
53    ///    fn probe(
54    ///       pdev: &platform::Device<Core>,
55    ///       info: Option<&Self::IdInfo>,
56    ///    ) -> Result<Pin<KBox<Self>>> {
57    ///       let offset = 0; // Some offset.
58    ///
59    ///       // If the size is known at compile time, use [`Self::iomap_sized`].
60    ///       //
61    ///       // No runtime checks will apply when reading and writing.
62    ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
63    ///       let iomem = request.iomap_sized::<42>();
64    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
65    ///
66    ///       let io = iomem.access(pdev.as_ref())?;
67    ///
68    ///       // Read and write a 32-bit value at `offset`.
69    ///       let data = io.read32_relaxed(offset);
70    ///
71    ///       io.write32_relaxed(data, offset);
72    ///
73    ///       # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into())
74    ///     }
75    /// }
76    /// ```
77    pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a {
78        IoMem::new(self)
79    }
80
81    /// Same as [`Self::iomap_sized`] but with exclusive access to the
82    /// underlying region.
83    ///
84    /// This uses the [`ioremap()`] C API.
85    ///
86    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
87    pub fn iomap_exclusive_sized<const SIZE: usize>(
88        self,
89    ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a {
90        ExclusiveIoMem::new(self)
91    }
92
93    /// Maps an [`IoRequest`] where the size is not known at compile time,
94    ///
95    /// This uses the [`ioremap()`] C API.
96    ///
97    /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
98    ///
99    /// # Examples
100    ///
101    /// The following example uses a [`kernel::platform::Device`] for
102    /// illustration purposes.
103    ///
104    /// ```no_run
105    /// use kernel::{bindings, c_str, platform, of, device::Core};
106    /// struct SampleDriver;
107    ///
108    /// impl platform::Driver for SampleDriver {
109    ///    # type IdInfo = ();
110    ///
111    ///    fn probe(
112    ///       pdev: &platform::Device<Core>,
113    ///       info: Option<&Self::IdInfo>,
114    ///    ) -> Result<Pin<KBox<Self>>> {
115    ///       let offset = 0; // Some offset.
116    ///
117    ///       // Unlike [`Self::iomap_sized`], here the size of the memory region
118    ///       // is not known at compile time, so only the `try_read*` and `try_write*`
119    ///       // family of functions should be used, leading to runtime checks on every
120    ///       // access.
121    ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
122    ///       let iomem = request.iomap();
123    ///       let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
124    ///
125    ///       let io = iomem.access(pdev.as_ref())?;
126    ///
127    ///       let data = io.try_read32_relaxed(offset)?;
128    ///
129    ///       io.try_write32_relaxed(data, offset)?;
130    ///
131    ///       # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into())
132    ///     }
133    /// }
134    /// ```
135    pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a {
136        Self::iomap_sized::<0>(self)
137    }
138
139    /// Same as [`Self::iomap`] but with exclusive access to the underlying
140    /// region.
141    pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a {
142        Self::iomap_exclusive_sized::<0>(self)
143    }
144}
145
146/// An exclusive memory-mapped IO region.
147///
148/// # Invariants
149///
150/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
151pub struct ExclusiveIoMem<const SIZE: usize> {
152    /// The underlying `IoMem` instance.
153    iomem: IoMem<SIZE>,
154
155    /// The region abstraction. This represents exclusive access to the
156    /// range represented by the underlying `iomem`.
157    ///
158    /// This field is needed for ownership of the region.
159    _region: Region,
160}
161
162impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
163    /// Creates a new `ExclusiveIoMem` instance.
164    fn ioremap(resource: &Resource) -> Result<Self> {
165        let start = resource.start();
166        let size = resource.size();
167        let name = resource.name().unwrap_or(c_str!(""));
168
169        let region = resource
170            .request_region(
171                start,
172                size,
173                name.to_cstring()?,
174                io::resource::Flags::IORESOURCE_MEM,
175            )
176            .ok_or(EBUSY)?;
177
178        let iomem = IoMem::ioremap(resource)?;
179
180        let iomem = ExclusiveIoMem {
181            iomem,
182            _region: region,
183        };
184
185        Ok(iomem)
186    }
187
188    /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`].
189    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
190        let dev = io_request.device;
191        let res = io_request.resource;
192
193        Devres::new(dev, Self::ioremap(res))
194    }
195}
196
197impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
198    type Target = Io<SIZE>;
199
200    fn deref(&self) -> &Self::Target {
201        &self.iomem
202    }
203}
204
205/// A generic memory-mapped IO region.
206///
207/// Accesses to the underlying region is checked either at compile time, if the
208/// region's size is known at that point, or at runtime otherwise.
209///
210/// # Invariants
211///
212/// [`IoMem`] always holds an [`IoRaw`] instance that holds a valid pointer to the
213/// start of the I/O memory mapped region.
214pub struct IoMem<const SIZE: usize = 0> {
215    io: IoRaw<SIZE>,
216}
217
218impl<const SIZE: usize> IoMem<SIZE> {
219    fn ioremap(resource: &Resource) -> Result<Self> {
220        // Note: Some ioremap() implementations use types that depend on the CPU
221        // word width rather than the bus address width.
222        //
223        // TODO: Properly address this in the C code to avoid this `try_into`.
224        let size = resource.size().try_into()?;
225        if size == 0 {
226            return Err(EINVAL);
227        }
228
229        let res_start = resource.start();
230
231        let addr = if resource
232            .flags()
233            .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED)
234        {
235            // SAFETY:
236            // - `res_start` and `size` are read from a presumably valid `struct resource`.
237            // - `size` is known not to be zero at this point.
238            unsafe { bindings::ioremap_np(res_start, size) }
239        } else {
240            // SAFETY:
241            // - `res_start` and `size` are read from a presumably valid `struct resource`.
242            // - `size` is known not to be zero at this point.
243            unsafe { bindings::ioremap(res_start, size) }
244        };
245
246        if addr.is_null() {
247            return Err(ENOMEM);
248        }
249
250        let io = IoRaw::new(addr as usize, size)?;
251        let io = IoMem { io };
252
253        Ok(io)
254    }
255
256    /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`].
257    pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
258        let dev = io_request.device;
259        let res = io_request.resource;
260
261        Devres::new(dev, Self::ioremap(res))
262    }
263}
264
265impl<const SIZE: usize> Drop for IoMem<SIZE> {
266    fn drop(&mut self) {
267        // SAFETY: Safe as by the invariant of `Io`.
268        unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }
269    }
270}
271
272impl<const SIZE: usize> Deref for IoMem<SIZE> {
273    type Target = Io<SIZE>;
274
275    fn deref(&self) -> &Self::Target {
276        // SAFETY: Safe as by the invariant of `IoMem`.
277        unsafe { Io::from_raw(&self.io) }
278    }
279}