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 Mmio,
20 MmioRaw, //
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 /// 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`].
170pub 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
181impl<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
216impl<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.
233pub struct IoMem<const SIZE: usize = 0> {
234 io: MmioRaw<SIZE>,
235}
236
237impl<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
284impl<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
291impl<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}