1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Generic memory-mapped IO. 4 5 use crate::{ 6 device::{ 7 Bound, 8 Device, // 9 }, 10 devres::DevresLt, 11 io::{ 12 self, 13 resource::{ 14 Region, 15 Resource, // 16 }, 17 IoBase, 18 Mmio, 19 MmioBackend, 20 MmioRaw, // 21 }, 22 prelude::*, 23 types::{ 24 CovariantForLt, 25 ForLt, // 26 }, 27 }; 28 29 /// An IO request for a specific device and resource. 30 pub struct IoRequest<'a> { 31 device: &'a Device<Bound>, 32 resource: &'a Resource, 33 } 34 35 impl<'a> IoRequest<'a> { 36 /// Creates a new [`IoRequest`] instance. 37 /// 38 /// # Safety 39 /// 40 /// Callers must ensure that `resource` is valid for `device` during the 41 /// lifetime `'a`. 42 pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self { 43 IoRequest { device, resource } 44 } 45 46 /// Maps an [`IoRequest`] where the size is known at compile time. 47 /// 48 /// This uses the [`ioremap()`] C API. 49 /// 50 /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device 51 /// 52 /// # Examples 53 /// 54 /// The following example uses a [`kernel::platform::Device`] for 55 /// illustration purposes. 56 /// 57 /// ```no_run 58 /// use kernel::{ 59 /// bindings, 60 /// device::Core, 61 /// io::Io, 62 /// of, 63 /// platform, 64 /// }; 65 /// struct SampleDriver; 66 /// 67 /// impl platform::Driver for SampleDriver { 68 /// # type IdInfo = (); 69 /// # type Data<'bound> = Self; 70 /// 71 /// fn probe<'bound>( 72 /// pdev: &'bound platform::Device<Core<'_>>, 73 /// info: Option<&'bound Self::IdInfo>, 74 /// ) -> impl PinInit<Self, Error> + 'bound { 75 /// let offset = 0; // Some offset. 76 /// 77 /// // If the size is known at compile time, use [`Self::iomap_sized`]. 78 /// // 79 /// // No runtime checks will apply when reading and writing. 80 /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; 81 /// let iomem = request.iomap_sized::<42>()?; 82 /// 83 /// // Read and write a 32-bit value at `offset`. 84 /// let data = iomem.read32(offset); 85 /// 86 /// iomem.write32(data, offset); 87 /// 88 /// # Ok(SampleDriver) 89 /// } 90 /// } 91 /// ``` 92 pub fn iomap_sized<const SIZE: usize>(self) -> Result<IoMem<'a, SIZE>> { 93 IoMem::ioremap(self.device, self.resource) 94 } 95 96 /// Same as [`Self::iomap_sized`] but with exclusive access to the 97 /// underlying region. 98 /// 99 /// This uses the [`ioremap()`] C API. 100 /// 101 /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device 102 pub fn iomap_exclusive_sized<const SIZE: usize>(self) -> Result<ExclusiveIoMem<'a, SIZE>> { 103 ExclusiveIoMem::ioremap(self.device, self.resource) 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 /// # type Data<'bound> = Self; 130 /// 131 /// fn probe<'bound>( 132 /// pdev: &'bound platform::Device<Core<'_>>, 133 /// info: Option<&'bound Self::IdInfo>, 134 /// ) -> impl PinInit<Self, Error> + 'bound { 135 /// let offset = 0; // Some offset. 136 /// 137 /// // Unlike [`Self::iomap_sized`], here the size of the memory region 138 /// // is not known at compile time, so only the `try_read*` and `try_write*` 139 /// // family of functions should be used, leading to runtime checks on every 140 /// // access. 141 /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; 142 /// let iomem = request.iomap()?; 143 /// 144 /// let data = iomem.try_read32(offset)?; 145 /// 146 /// iomem.try_write32(data, offset)?; 147 /// 148 /// # Ok(SampleDriver) 149 /// } 150 /// } 151 /// ``` 152 pub fn iomap(self) -> Result<IoMem<'a>> { 153 self.iomap_sized::<0>() 154 } 155 156 /// Same as [`Self::iomap`] but with exclusive access to the underlying 157 /// region. 158 pub fn iomap_exclusive(self) -> Result<ExclusiveIoMem<'a, 0>> { 159 self.iomap_exclusive_sized::<0>() 160 } 161 } 162 163 /// An exclusive memory-mapped IO region. 164 /// 165 /// # Invariants 166 /// 167 /// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`]. 168 pub struct ExclusiveIoMem<'a, const SIZE: usize> { 169 /// The underlying `IoMem` instance. 170 iomem: IoMem<'a, 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 179 impl<const SIZE: usize> ForLt for ExclusiveIoMem<'static, SIZE> { 180 type Of<'a> = ExclusiveIoMem<'a, SIZE>; 181 } 182 183 // SAFETY: `ExclusiveIoMem<'a, SIZE>` is covariant over `'a`; it holds an `IoMem<'a, SIZE>`, 184 // which holds `&'a Device<Bound>`, which is covariant. 185 unsafe impl<const SIZE: usize> CovariantForLt for ExclusiveIoMem<'static, SIZE> {} 186 187 /// A device-managed exclusive I/O memory region. 188 /// 189 /// See [`ExclusiveIoMem::into_devres`]. 190 pub type DevresExclusiveIoMem<const SIZE: usize> = DevresLt<ExclusiveIoMem<'static, SIZE>>; 191 192 impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> { 193 /// Creates a new `ExclusiveIoMem` instance. 194 fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> { 195 let start = resource.start(); 196 let size = resource.size(); 197 let name = resource.name().unwrap_or_default(); 198 199 let region = resource 200 .request_region( 201 start, 202 size, 203 name.to_cstring()?, 204 io::resource::Flags::IORESOURCE_MEM, 205 ) 206 .ok_or(EBUSY)?; 207 208 let iomem = IoMem::ioremap(dev, resource)?; 209 210 Ok(ExclusiveIoMem { 211 iomem, 212 _region: region, 213 }) 214 } 215 216 /// Consume the `ExclusiveIoMem` and register it as a device-managed resource. 217 /// 218 /// The returned [`DevresExclusiveIoMem`] can outlive the original borrow and be stored in 219 /// driver data. Access to the I/O memory is revoked automatically when the device is unbound. 220 pub fn into_devres(self) -> Result<DevresExclusiveIoMem<SIZE>> { 221 let dev = self.iomem.dev; 222 // SAFETY: `ExclusiveIoMem` only holds a device reference and an I/O mapping, both of 223 // which remain valid for the device's full bound scope, not just for `'a`. 224 unsafe { DevresLt::new(dev, self) } 225 } 226 } 227 228 impl<'a, const SIZE: usize> IoBase<'a> for &'a ExclusiveIoMem<'_, SIZE> { 229 type Backend = MmioBackend; 230 type Target = super::Region<SIZE>; 231 232 #[inline] 233 fn as_view(self) -> Mmio<'a, Self::Target> { 234 self.iomem.as_view() 235 } 236 } 237 238 /// A generic memory-mapped IO region. 239 /// 240 /// Accesses to the underlying region is checked either at compile time, if the 241 /// region's size is known at that point, or at runtime otherwise. 242 /// 243 /// # Invariants 244 /// 245 /// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the 246 /// start of the I/O memory mapped region. 247 pub struct IoMem<'a, const SIZE: usize = 0> { 248 dev: &'a Device<Bound>, 249 io: MmioRaw<super::Region<SIZE>>, 250 } 251 252 impl<const SIZE: usize> ForLt for IoMem<'static, SIZE> { 253 type Of<'a> = IoMem<'a, SIZE>; 254 } 255 256 // SAFETY: `IoMem<'a, SIZE>` is covariant over `'a`; it holds `&'a Device<Bound>`, 257 // which is covariant. 258 unsafe impl<const SIZE: usize> CovariantForLt for IoMem<'static, SIZE> {} 259 260 /// A device-managed I/O memory region. 261 /// 262 /// See [`IoMem::into_devres`]. 263 pub type DevresIoMem<const SIZE: usize = 0> = DevresLt<IoMem<'static, SIZE>>; 264 265 impl<'a, const SIZE: usize> IoMem<'a, SIZE> { 266 fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> { 267 // Note: Some ioremap() implementations use types that depend on the CPU 268 // word width rather than the bus address width. 269 // 270 // TODO: Properly address this in the C code to avoid this `try_into`. 271 let size = resource.size().try_into()?; 272 if size == 0 { 273 return Err(EINVAL); 274 } 275 276 let res_start = resource.start(); 277 278 let addr = if resource 279 .flags() 280 .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED) 281 { 282 // SAFETY: 283 // - `res_start` and `size` are read from a presumably valid `struct resource`. 284 // - `size` is known not to be zero at this point. 285 unsafe { bindings::ioremap_np(res_start, size) } 286 } else { 287 // SAFETY: 288 // - `res_start` and `size` are read from a presumably valid `struct resource`. 289 // - `size` is known not to be zero at this point. 290 unsafe { bindings::ioremap(res_start, size) } 291 }; 292 293 if addr.is_null() { 294 return Err(ENOMEM); 295 } 296 297 let io = MmioRaw::new_region(addr as usize, size)?; 298 Ok(IoMem { dev, io }) 299 } 300 301 /// Consume the `IoMem` and register it as a device-managed resource. 302 /// 303 /// The returned [`DevresIoMem`] can outlive the original borrow and be stored in driver data. 304 /// Access to the I/O memory is revoked automatically when the device is unbound. 305 pub fn into_devres(self) -> Result<DevresIoMem<SIZE>> { 306 let dev = self.dev; 307 // SAFETY: `IoMem` only holds a device reference and an I/O mapping, both of which 308 // remain valid for the device's full bound scope, not just for `'a`. 309 unsafe { DevresLt::new(dev, self) } 310 } 311 } 312 313 impl<const SIZE: usize> Drop for IoMem<'_, SIZE> { 314 fn drop(&mut self) { 315 // SAFETY: Safe as by the invariant of `Io`. 316 unsafe { bindings::iounmap(self.io.addr() as *mut c_void) } 317 } 318 } 319 320 impl<'a, const SIZE: usize> IoBase<'a> for &'a IoMem<'_, SIZE> { 321 type Backend = MmioBackend; 322 type Target = super::Region<SIZE>; 323 324 #[inline] 325 fn as_view(self) -> Mmio<'a, Self::Target> { 326 // SAFETY: Safe as by the invariant of `IoMem`. 327 unsafe { Mmio::from_raw(self.io) } 328 } 329 } 330