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