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 /// # 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`].
164pub 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
175impl<'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
213impl<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.
230pub struct IoMem<'a, const SIZE: usize = 0> {
231 dev: &'a Device<Bound>,
232 io: MmioRaw<SIZE>,
233}
234
235impl<'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
287impl<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
294impl<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}