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