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 /// 66 /// fn probe( 67 /// pdev: &platform::Device<Core>, 68 /// info: Option<&Self::IdInfo>, 69 /// ) -> impl PinInit<Self, Error> { 70 /// let offset = 0; // Some offset. 71 /// 72 /// // If the size is known at compile time, use [`Self::iomap_sized`]. 73 /// // 74 /// // No runtime checks will apply when reading and writing. 75 /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; 76 /// let iomem = request.iomap_sized::<42>(); 77 /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; 78 /// 79 /// let io = iomem.access(pdev.as_ref())?; 80 /// 81 /// // Read and write a 32-bit value at `offset`. 82 /// let data = io.read32(offset); 83 /// 84 /// io.write32(data, offset); 85 /// 86 /// # Ok(SampleDriver) 87 /// } 88 /// } 89 /// ``` 90 pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a { 91 IoMem::new(self) 92 } 93 94 /// Same as [`Self::iomap_sized`] but with exclusive access to the 95 /// underlying region. 96 /// 97 /// This uses the [`ioremap()`] C API. 98 /// 99 /// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device 100 pub fn iomap_exclusive_sized<const SIZE: usize>( 101 self, 102 ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a { 103 ExclusiveIoMem::new(self) 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 /// 130 /// fn probe( 131 /// pdev: &platform::Device<Core>, 132 /// info: Option<&Self::IdInfo>, 133 /// ) -> impl PinInit<Self, Error> { 134 /// let offset = 0; // Some offset. 135 /// 136 /// // Unlike [`Self::iomap_sized`], here the size of the memory region 137 /// // is not known at compile time, so only the `try_read*` and `try_write*` 138 /// // family of functions should be used, leading to runtime checks on every 139 /// // access. 140 /// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?; 141 /// let iomem = request.iomap(); 142 /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?; 143 /// 144 /// let io = iomem.access(pdev.as_ref())?; 145 /// 146 /// let data = io.try_read32(offset)?; 147 /// 148 /// io.try_write32(data, offset)?; 149 /// 150 /// # Ok(SampleDriver) 151 /// } 152 /// } 153 /// ``` 154 pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a { 155 Self::iomap_sized::<0>(self) 156 } 157 158 /// Same as [`Self::iomap`] but with exclusive access to the underlying 159 /// region. 160 pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a { 161 Self::iomap_exclusive_sized::<0>(self) 162 } 163 } 164 165 /// An exclusive memory-mapped IO region. 166 /// 167 /// # Invariants 168 /// 169 /// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`]. 170 pub struct ExclusiveIoMem<const SIZE: usize> { 171 /// The underlying `IoMem` instance. 172 iomem: IoMem<SIZE>, 173 174 /// The region abstraction. This represents exclusive access to the 175 /// range represented by the underlying `iomem`. 176 /// 177 /// This field is needed for ownership of the region. 178 _region: Region, 179 } 180 181 impl<const SIZE: usize> ExclusiveIoMem<SIZE> { 182 /// Creates a new `ExclusiveIoMem` instance. 183 fn ioremap(resource: &Resource) -> Result<Self> { 184 let start = resource.start(); 185 let size = resource.size(); 186 let name = resource.name().unwrap_or_default(); 187 188 let region = resource 189 .request_region( 190 start, 191 size, 192 name.to_cstring()?, 193 io::resource::Flags::IORESOURCE_MEM, 194 ) 195 .ok_or(EBUSY)?; 196 197 let iomem = IoMem::ioremap(resource)?; 198 199 let iomem = ExclusiveIoMem { 200 iomem, 201 _region: region, 202 }; 203 204 Ok(iomem) 205 } 206 207 /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`]. 208 pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a { 209 let dev = io_request.device; 210 let res = io_request.resource; 211 212 Devres::new(dev, Self::ioremap(res)) 213 } 214 } 215 216 impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> { 217 type Target = Mmio<SIZE>; 218 219 fn deref(&self) -> &Self::Target { 220 &self.iomem 221 } 222 } 223 224 /// A generic memory-mapped IO region. 225 /// 226 /// Accesses to the underlying region is checked either at compile time, if the 227 /// region's size is known at that point, or at runtime otherwise. 228 /// 229 /// # Invariants 230 /// 231 /// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the 232 /// start of the I/O memory mapped region. 233 pub struct IoMem<const SIZE: usize = 0> { 234 io: MmioRaw<SIZE>, 235 } 236 237 impl<const SIZE: usize> IoMem<SIZE> { 238 fn ioremap(resource: &Resource) -> Result<Self> { 239 // Note: Some ioremap() implementations use types that depend on the CPU 240 // word width rather than the bus address width. 241 // 242 // TODO: Properly address this in the C code to avoid this `try_into`. 243 let size = resource.size().try_into()?; 244 if size == 0 { 245 return Err(EINVAL); 246 } 247 248 let res_start = resource.start(); 249 250 let addr = if resource 251 .flags() 252 .contains(io::resource::Flags::IORESOURCE_MEM_NONPOSTED) 253 { 254 // SAFETY: 255 // - `res_start` and `size` are read from a presumably valid `struct resource`. 256 // - `size` is known not to be zero at this point. 257 unsafe { bindings::ioremap_np(res_start, size) } 258 } else { 259 // SAFETY: 260 // - `res_start` and `size` are read from a presumably valid `struct resource`. 261 // - `size` is known not to be zero at this point. 262 unsafe { bindings::ioremap(res_start, size) } 263 }; 264 265 if addr.is_null() { 266 return Err(ENOMEM); 267 } 268 269 let io = MmioRaw::new(addr as usize, size)?; 270 let io = IoMem { io }; 271 272 Ok(io) 273 } 274 275 /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`]. 276 pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a { 277 let dev = io_request.device; 278 let res = io_request.resource; 279 280 Devres::new(dev, Self::ioremap(res)) 281 } 282 } 283 284 impl<const SIZE: usize> Drop for IoMem<SIZE> { 285 fn drop(&mut self) { 286 // SAFETY: Safe as by the invariant of `Io`. 287 unsafe { bindings::iounmap(self.io.addr() as *mut c_void) } 288 } 289 } 290 291 impl<const SIZE: usize> Deref for IoMem<SIZE> { 292 type Target = Mmio<SIZE>; 293 294 fn deref(&self) -> &Self::Target { 295 // SAFETY: Safe as by the invariant of `IoMem`. 296 unsafe { Mmio::from_raw(&self.io) } 297 } 298 } 299