Add write-combining memory mapping support to the Rust iomem abstraction. This extends the existing IoMem and IoRequest abstractions to support write-combining cache policy, which is essential for framebuffer memory and other memory regions that benefit from write-combining semantics.
The implementation follows the same pattern as the existing ioremap and ioremap_np support: - Add rust_helper_ioremap_wc() in rust/helpers/io.c to wrap the C API - Add IoMem::ioremap_wc() to perform the actual mapping with write-combining - Add IoMem::new_wc() to create IoMem instances with write-combining policy - Add IoRequest::iomap_wc_sized() and IoRequest::iomap_wc() methods for compile-time and runtime-sized mappings respectively This enables Rust drivers, such as framebuffer drivers, to properly map memory regions with write-combining semantics. The API design is consistent with the existing iomap() methods, providing both sized and unsized variants to match the pattern established by the generic iomem abstraction. Signed-off-by: pengfuyuan <[email protected]> --- rust/helpers/io.c | 5 +++ rust/kernel/io/mem.rs | 71 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/rust/helpers/io.c b/rust/helpers/io.c index c475913c69e6..6c9edf7f2233 100644 --- a/rust/helpers/io.c +++ b/rust/helpers/io.c @@ -13,6 +13,11 @@ void __iomem *rust_helper_ioremap_np(phys_addr_t offset, size_t size) return ioremap_np(offset, size); } +void __iomem *rust_helper_ioremap_wc(phys_addr_t offset, size_t size) +{ + return ioremap_wc(offset, size); +} + void rust_helper_iounmap(void __iomem *addr) { iounmap(addr); diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index b03b82cd531b..94403d899bbd 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -149,6 +149,41 @@ pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a { pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a { Self::iomap_exclusive_sized::<0>(self) } + + /// Maps an [`IoRequest`] with write-combining cache policy where the size + /// is known at compile time. + /// + /// This uses the [`ioremap_wc()`] C API, which provides write-combining + /// semantics. This is useful for framebuffer memory and other memory + /// regions that benefit from write-combining, where multiple writes can + /// be combined and reordered for better performance. + /// + /// Unlike [`Self::iomap`], this method explicitly uses write-combining + /// mapping, which is typically needed for video framebuffers. + /// + /// [`ioremap_wc()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device + pub fn iomap_wc_sized<const SIZE: usize>( + self, + ) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a { + IoMem::new_wc(self) + } + + /// Maps an [`IoRequest`] with write-combining cache policy where the size + /// is not known at compile time. + /// + /// This uses the [`ioremap_wc()`] C API, which provides write-combining + /// semantics. This is useful for framebuffer memory and other memory + /// regions that benefit from write-combining. + /// + /// Unlike [`Self::iomap_wc_sized`], here the size of the memory region + /// is not known at compile time, so only the `try_read*` and `try_write*` + /// family of functions should be used, leading to runtime checks on every + /// access. + /// + /// [`ioremap_wc()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device + pub fn iomap_wc(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a { + Self::iomap_wc_sized::<0>(self) + } } /// An exclusive memory-mapped IO region. @@ -261,6 +296,33 @@ fn ioremap(resource: &Resource) -> Result<Self> { Ok(io) } + fn ioremap_wc(resource: &Resource) -> Result<Self> { + // Note: Some ioremap() implementations use types that depend on the CPU + // word width rather than the bus address width. + // + // TODO: Properly address this in the C code to avoid this `try_into`. + let size = resource.size().try_into()?; + if size == 0 { + return Err(EINVAL); + } + + let res_start = resource.start(); + + // SAFETY: + // - `res_start` and `size` are read from a presumably valid `struct resource`. + // - `size` is known not to be zero at this point. + let addr = unsafe { bindings::ioremap_wc(res_start, size) }; + + if addr.is_null() { + return Err(ENOMEM); + } + + let io = IoRaw::new(addr as usize, size)?; + let io = IoMem { io }; + + Ok(io) + } + /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`]. pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a { let dev = io_request.device; @@ -268,6 +330,15 @@ pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + Devres::new(dev, Self::ioremap(res)) } + + /// Creates a new `IoMem` instance with write-combining cache policy from + /// a previously acquired [`IoRequest`]. + pub fn new_wc<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a { + let dev = io_request.device; + let res = io_request.resource; + + Devres::new(dev, Self::ioremap_wc(res)) + } } impl<const SIZE: usize> Drop for IoMem<SIZE> { -- 2.25.1
