Skip to main content

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