1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Generic memory-mapped IO. 4 5 use core::ops::Deref; 6 7 use 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. 26 pub struct IoRequest<'a> { 27 device: &'a Device<Bound>, 28 resource: &'a Resource, 29 } 30 31 impl<'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`]. 164 pub 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 175 impl<'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 213 impl<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. 230 pub struct IoMem<'a, const SIZE: usize = 0> { 231 dev: &'a Device<Bound>, 232 io: MmioRaw<SIZE>, 233 } 234 235 impl<'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 287 impl<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 294 impl<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 } 302