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