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