xref: /linux/rust/kernel/io/mem.rs (revision 99676aed1fec109d62822e21a06760eb098dc5f4)
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<'bound> = Self;
66     ///
67     ///    fn probe<'bound>(
68     ///       pdev: &'bound platform::Device<Core<'_>>,
69     ///       info: Option<&'bound Self::IdInfo>,
70     ///    ) -> impl PinInit<Self, Error> + 'bound {
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     ///
79     ///       // Read and write a 32-bit value at `offset`.
80     ///       let data = iomem.read32(offset);
81     ///
82     ///       iomem.write32(data, offset);
83     ///
84     ///       # Ok(SampleDriver)
85     ///     }
86     /// }
87     /// ```
88     pub fn iomap_sized<const SIZE: usize>(self) -> Result<IoMem<'a, SIZE>> {
89         IoMem::ioremap(self.device, self.resource)
90     }
91 
92     /// Same as [`Self::iomap_sized`] but with exclusive access to the
93     /// underlying region.
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     pub fn iomap_exclusive_sized<const SIZE: usize>(self) -> Result<ExclusiveIoMem<'a, SIZE>> {
99         ExclusiveIoMem::ioremap(self.device, self.resource)
100     }
101 
102     /// Maps an [`IoRequest`] where the size is not known at compile time,
103     ///
104     /// This uses the [`ioremap()`] C API.
105     ///
106     /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
107     ///
108     /// # Examples
109     ///
110     /// The following example uses a [`kernel::platform::Device`] for
111     /// illustration purposes.
112     ///
113     /// ```no_run
114     /// use kernel::{
115     ///     bindings,
116     ///     device::Core,
117     ///     io::Io,
118     ///     of,
119     ///     platform,
120     /// };
121     /// struct SampleDriver;
122     ///
123     /// impl platform::Driver for SampleDriver {
124     ///    # type IdInfo = ();
125     ///    # type Data<'bound> = Self;
126     ///
127     ///    fn probe<'bound>(
128     ///       pdev: &'bound platform::Device<Core<'_>>,
129     ///       info: Option<&'bound Self::IdInfo>,
130     ///    ) -> impl PinInit<Self, Error> + 'bound {
131     ///       let offset = 0; // Some offset.
132     ///
133     ///       // Unlike [`Self::iomap_sized`], here the size of the memory region
134     ///       // is not known at compile time, so only the `try_read*` and `try_write*`
135     ///       // family of functions should be used, leading to runtime checks on every
136     ///       // access.
137     ///       let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
138     ///       let iomem = request.iomap()?;
139     ///
140     ///       let data = iomem.try_read32(offset)?;
141     ///
142     ///       iomem.try_write32(data, offset)?;
143     ///
144     ///       # Ok(SampleDriver)
145     ///     }
146     /// }
147     /// ```
148     pub fn iomap(self) -> Result<IoMem<'a>> {
149         self.iomap_sized::<0>()
150     }
151 
152     /// Same as [`Self::iomap`] but with exclusive access to the underlying
153     /// region.
154     pub fn iomap_exclusive(self) -> Result<ExclusiveIoMem<'a, 0>> {
155         self.iomap_exclusive_sized::<0>()
156     }
157 }
158 
159 /// An exclusive memory-mapped IO region.
160 ///
161 /// # Invariants
162 ///
163 /// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
164 pub struct ExclusiveIoMem<'a, const SIZE: usize> {
165     /// The underlying `IoMem` instance.
166     iomem: IoMem<'a, SIZE>,
167 
168     /// The region abstraction. This represents exclusive access to the
169     /// range represented by the underlying `iomem`.
170     ///
171     /// This field is needed for ownership of the region.
172     _region: Region,
173 }
174 
175 impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
176     /// Creates a new `ExclusiveIoMem` instance.
177     fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
178         let start = resource.start();
179         let size = resource.size();
180         let name = resource.name().unwrap_or_default();
181 
182         let region = resource
183             .request_region(
184                 start,
185                 size,
186                 name.to_cstring()?,
187                 io::resource::Flags::IORESOURCE_MEM,
188             )
189             .ok_or(EBUSY)?;
190 
191         let iomem = IoMem::ioremap(dev, resource)?;
192 
193         Ok(ExclusiveIoMem {
194             iomem,
195             _region: region,
196         })
197     }
198 
199     /// Consume the `ExclusiveIoMem` and register it as a device-managed resource.
200     ///
201     /// The returned `Devres<ExclusiveIoMem<'static, SIZE>>` can outlive the original lifetime
202     /// `'a`. Access to the I/O memory is revoked when the device is unbound.
203     pub fn into_devres(self) -> Result<Devres<ExclusiveIoMem<'static, SIZE>>> {
204         // SAFETY: Casting to `'static` is sound because `Devres` guarantees the
205         // `ExclusiveIoMem` does not actually outlive the device -- access is revoked and the
206         // resource is released when the device is unbound.
207         let iomem: ExclusiveIoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
208         let dev = iomem.iomem.dev;
209         Devres::new(dev, iomem)
210     }
211 }
212 
213 impl<const SIZE: usize> Deref for ExclusiveIoMem<'_, SIZE> {
214     type Target = Mmio<SIZE>;
215 
216     fn deref(&self) -> &Self::Target {
217         &self.iomem
218     }
219 }
220 
221 /// A generic memory-mapped IO region.
222 ///
223 /// Accesses to the underlying region is checked either at compile time, if the
224 /// region's size is known at that point, or at runtime otherwise.
225 ///
226 /// # Invariants
227 ///
228 /// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the
229 /// start of the I/O memory mapped region.
230 pub struct IoMem<'a, const SIZE: usize = 0> {
231     dev: &'a Device<Bound>,
232     io: MmioRaw<SIZE>,
233 }
234 
235 impl<'a, const SIZE: usize> IoMem<'a, SIZE> {
236     fn ioremap(dev: &'a Device<Bound>, 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 = MmioRaw::new(addr as usize, size)?;
268 
269         Ok(IoMem { dev, io })
270     }
271 
272     /// Consume the `IoMem` and register it as a device-managed resource.
273     ///
274     /// The returned `Devres<IoMem<'static, SIZE>>` can outlive the original
275     /// lifetime `'a`. Access to the I/O memory is revoked when the device
276     /// is unbound.
277     pub fn into_devres(self) -> Result<Devres<IoMem<'static, SIZE>>> {
278         // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `IoMem` does not
279         // actually outlive the device -- access is revoked and the resource is released when the
280         // device is unbound.
281         let iomem: IoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
282         let dev = iomem.dev;
283         Devres::new(dev, iomem)
284     }
285 }
286 
287 impl<const SIZE: usize> Drop for IoMem<'_, SIZE> {
288     fn drop(&mut self) {
289         // SAFETY: Safe as by the invariant of `Io`.
290         unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }
291     }
292 }
293 
294 impl<const SIZE: usize> Deref for IoMem<'_, SIZE> {
295     type Target = Mmio<SIZE>;
296 
297     fn deref(&self) -> &Self::Target {
298         // SAFETY: Safe as by the invariant of `IoMem`.
299         unsafe { Mmio::from_raw(&self.io) }
300     }
301 }
302