diff options
Diffstat (limited to 'include/linux/io-mapping.h')
-rw-r--r-- | include/linux/io-mapping.h | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/include/linux/io-mapping.h b/include/linux/io-mapping.h new file mode 100644 index 000000000..66a774d27 --- /dev/null +++ b/include/linux/io-mapping.h @@ -0,0 +1,219 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright © 2008 Keith Packard <keithp@keithp.com> + */ + +#ifndef _LINUX_IO_MAPPING_H +#define _LINUX_IO_MAPPING_H + +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/bug.h> +#include <linux/io.h> +#include <linux/pgtable.h> +#include <asm/page.h> + +/* + * The io_mapping mechanism provides an abstraction for mapping + * individual pages from an io device to the CPU in an efficient fashion. + * + * See Documentation/driver-api/io-mapping.rst + */ + +struct io_mapping { + resource_size_t base; + unsigned long size; + pgprot_t prot; + void __iomem *iomem; +}; + +#ifdef CONFIG_HAVE_ATOMIC_IOMAP + +#include <linux/pfn.h> +#include <asm/iomap.h> +/* + * For small address space machines, mapping large objects + * into the kernel virtual space isn't practical. Where + * available, use fixmap support to dynamically map pages + * of the object at run time. + */ + +static inline struct io_mapping * +io_mapping_init_wc(struct io_mapping *iomap, + resource_size_t base, + unsigned long size) +{ + pgprot_t prot; + + if (iomap_create_wc(base, size, &prot)) + return NULL; + + iomap->base = base; + iomap->size = size; + iomap->prot = prot; + return iomap; +} + +static inline void +io_mapping_fini(struct io_mapping *mapping) +{ + iomap_free(mapping->base, mapping->size); +} + +/* Atomic map/unmap */ +static inline void __iomem * +io_mapping_map_atomic_wc(struct io_mapping *mapping, + unsigned long offset) +{ + resource_size_t phys_addr; + + BUG_ON(offset >= mapping->size); + phys_addr = mapping->base + offset; + preempt_disable(); + pagefault_disable(); + return __iomap_local_pfn_prot(PHYS_PFN(phys_addr), mapping->prot); +} + +static inline void +io_mapping_unmap_atomic(void __iomem *vaddr) +{ + kunmap_local_indexed((void __force *)vaddr); + pagefault_enable(); + preempt_enable(); +} + +static inline void __iomem * +io_mapping_map_local_wc(struct io_mapping *mapping, unsigned long offset) +{ + resource_size_t phys_addr; + + BUG_ON(offset >= mapping->size); + phys_addr = mapping->base + offset; + return __iomap_local_pfn_prot(PHYS_PFN(phys_addr), mapping->prot); +} + +static inline void io_mapping_unmap_local(void __iomem *vaddr) +{ + kunmap_local_indexed((void __force *)vaddr); +} + +static inline void __iomem * +io_mapping_map_wc(struct io_mapping *mapping, + unsigned long offset, + unsigned long size) +{ + resource_size_t phys_addr; + + BUG_ON(offset >= mapping->size); + phys_addr = mapping->base + offset; + + return ioremap_wc(phys_addr, size); +} + +static inline void +io_mapping_unmap(void __iomem *vaddr) +{ + iounmap(vaddr); +} + +#else /* HAVE_ATOMIC_IOMAP */ + +#include <linux/uaccess.h> + +/* Create the io_mapping object*/ +static inline struct io_mapping * +io_mapping_init_wc(struct io_mapping *iomap, + resource_size_t base, + unsigned long size) +{ + iomap->iomem = ioremap_wc(base, size); + if (!iomap->iomem) + return NULL; + + iomap->base = base; + iomap->size = size; + iomap->prot = pgprot_writecombine(PAGE_KERNEL); + + return iomap; +} + +static inline void +io_mapping_fini(struct io_mapping *mapping) +{ + iounmap(mapping->iomem); +} + +/* Non-atomic map/unmap */ +static inline void __iomem * +io_mapping_map_wc(struct io_mapping *mapping, + unsigned long offset, + unsigned long size) +{ + return mapping->iomem + offset; +} + +static inline void +io_mapping_unmap(void __iomem *vaddr) +{ +} + +/* Atomic map/unmap */ +static inline void __iomem * +io_mapping_map_atomic_wc(struct io_mapping *mapping, + unsigned long offset) +{ + preempt_disable(); + pagefault_disable(); + return io_mapping_map_wc(mapping, offset, PAGE_SIZE); +} + +static inline void +io_mapping_unmap_atomic(void __iomem *vaddr) +{ + io_mapping_unmap(vaddr); + pagefault_enable(); + preempt_enable(); +} + +static inline void __iomem * +io_mapping_map_local_wc(struct io_mapping *mapping, unsigned long offset) +{ + return io_mapping_map_wc(mapping, offset, PAGE_SIZE); +} + +static inline void io_mapping_unmap_local(void __iomem *vaddr) +{ + io_mapping_unmap(vaddr); +} + +#endif /* !HAVE_ATOMIC_IOMAP */ + +static inline struct io_mapping * +io_mapping_create_wc(resource_size_t base, + unsigned long size) +{ + struct io_mapping *iomap; + + iomap = kmalloc(sizeof(*iomap), GFP_KERNEL); + if (!iomap) + return NULL; + + if (!io_mapping_init_wc(iomap, base, size)) { + kfree(iomap); + return NULL; + } + + return iomap; +} + +static inline void +io_mapping_free(struct io_mapping *iomap) +{ + io_mapping_fini(iomap); + kfree(iomap); +} + +#endif /* _LINUX_IO_MAPPING_H */ + +int io_mapping_map_user(struct io_mapping *iomap, struct vm_area_struct *vma, + unsigned long addr, unsigned long pfn, unsigned long size); |