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