xref: /linux/rust/kernel/io/mem.rs (revision 40286d6379aacfcc053253ef78dc78b09addffda)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Generic memory-mapped IO.
4 
5 use core::ops::Deref;
6 
7 use 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.
26 pub struct IoRequest<'a> {
27     device: &'a Device<Bound>,
28     resource: &'a Resource,
29 }
30 
31 impl<'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`].
170 pub 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 
181 impl<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 
216 impl<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.
233 pub struct IoMem<const SIZE: usize = 0> {
234     io: MmioRaw<SIZE>,
235 }
236 
237 impl<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 
284 impl<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 
291 impl<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 }
299