Module Name: src Committed By: riastradh Date: Thu Aug 28 13:45:59 UTC 2014
Modified Files: src/sys/external/bsd/drm2/include/linux: io-mapping.h Log Message: Make Linux io_mapping actually work. Can't use bus_space_map/bus_space_unmap in interrupt context, where Linux uses this io_mapping abstraction. Instead, do uvm_km_alloc up front and then use bus_space_mmap/pmap_kenter_pa to map it. XXX Should bus_space_reserve the region (but that's x86-only for now). XXX Should use direct map if possible. XXX Should set an MTRR WC too in case of direct map or no PAT. To generate a diff of this commit: cvs rdiff -u -r1.2 -r1.3 src/sys/external/bsd/drm2/include/linux/io-mapping.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/external/bsd/drm2/include/linux/io-mapping.h diff -u src/sys/external/bsd/drm2/include/linux/io-mapping.h:1.2 src/sys/external/bsd/drm2/include/linux/io-mapping.h:1.3 --- src/sys/external/bsd/drm2/include/linux/io-mapping.h:1.2 Tue Mar 18 18:20:43 2014 +++ src/sys/external/bsd/drm2/include/linux/io-mapping.h Thu Aug 28 13:45:59 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: io-mapping.h,v 1.2 2014/03/18 18:20:43 riastradh Exp $ */ +/* $NetBSD: io-mapping.h,v 1.3 2014/08/28 13:45:59 riastradh Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -32,40 +32,58 @@ #ifndef _LINUX_IO_MAPPING_H_ #define _LINUX_IO_MAPPING_H_ +#include <sys/param.h> #include <sys/bus.h> #include <sys/kmem.h> #include <sys/systm.h> +#include <uvm/uvm_extern.h> + struct io_mapping { bus_space_tag_t diom_bst; bus_addr_t diom_addr; bus_size_t diom_size; - int diom_flags; - bus_space_handle_t diom_bsh; - void *diom_vaddr; + vaddr_t diom_va; + bool diom_mapped; }; static inline struct io_mapping * bus_space_io_mapping_create_wc(bus_space_tag_t bst, bus_addr_t addr, bus_size_t size) { - struct io_mapping *const mapping = kmem_alloc(sizeof(*mapping), - KM_SLEEP); + struct io_mapping *mapping; + bus_size_t offset; + + KASSERT(PAGE_SIZE <= size); + KASSERT(0 == (size & (PAGE_SIZE - 1))); + KASSERT(__type_fit(off_t, size)); + + /* + * XXX For x86: Reserve the region (bus_space_reserve) and set + * an MTRR to make it write-combining. Doesn't matter if we + * have PAT and we use pmap_kenter_pa, but matters if we don't + * have PAT or if we later make this use direct map. + */ + + /* Make sure the region is mappable. */ + for (offset = 0; offset < size; offset += PAGE_SIZE) { + if (bus_space_mmap(bst, addr, offset, PROT_READ|PROT_WRITE, + BUS_SPACE_MAP_LINEAR|BUS_SPACE_MAP_PREFETCHABLE) + == (paddr_t)-1) + return NULL; + } + + /* Create a mapping record. */ + mapping = kmem_alloc(sizeof(*mapping), KM_SLEEP); mapping->diom_bst = bst; mapping->diom_addr = addr; mapping->diom_size = size; - mapping->diom_flags = 0; - mapping->diom_flags |= BUS_SPACE_MAP_LINEAR; - mapping->diom_flags |= BUS_SPACE_MAP_PREFETCHABLE; - mapping->diom_vaddr = NULL; - - bus_space_handle_t bsh; - if (bus_space_map(mapping->diom_bst, addr, PAGE_SIZE, - mapping->diom_flags, &bsh)) { - kmem_free(mapping, sizeof(*mapping)); - return NULL; - } - bus_space_unmap(mapping->diom_bst, bsh, PAGE_SIZE); + mapping->diom_mapped = false; + + /* Allocate kva for one page. */ + mapping->diom_va = uvm_km_alloc(kernel_map, PAGE_SIZE, PAGE_SIZE, + UVM_KMF_VAONLY | UVM_KMF_WAITVA); + KASSERT(mapping->diom_va != 0); return mapping; } @@ -74,44 +92,61 @@ static inline void io_mapping_free(struct io_mapping *mapping) { - KASSERT(mapping->diom_vaddr == NULL); + KASSERT(!mapping->diom_mapped); + + uvm_km_free(kernel_map, mapping->diom_va, PAGE_SIZE, UVM_KMF_VAONLY); kmem_free(mapping, sizeof(*mapping)); } static inline void * io_mapping_map_wc(struct io_mapping *mapping, unsigned long offset) { + paddr_t cookie; - KASSERT(mapping->diom_vaddr == NULL); - KASSERT(ISSET(mapping->diom_flags, BUS_SPACE_MAP_LINEAR)); - if (bus_space_map(mapping->diom_bst, (mapping->diom_addr + offset), - PAGE_SIZE, mapping->diom_flags, &mapping->diom_bsh)) - panic("Unable to make I/O mapping!"); /* XXX */ - mapping->diom_vaddr = bus_space_vaddr(mapping->diom_bst, - mapping->diom_bsh); + KASSERT(0 == (offset & (PAGE_SIZE - 1))); + KASSERT(PAGE_SIZE <= mapping->diom_size); + KASSERT(offset <= (mapping->diom_size - PAGE_SIZE)); + KASSERT(__type_fit(off_t, offset)); + KASSERT(!mapping->diom_mapped); + + cookie = bus_space_mmap(mapping->diom_bst, mapping->diom_addr, offset, + PROT_READ|PROT_WRITE, + BUS_SPACE_MAP_LINEAR|BUS_SPACE_MAP_PREFETCHABLE); + KASSERT(cookie != (paddr_t)-1); + + pmap_kenter_pa(mapping->diom_va, pmap_phys_address(cookie), + PROT_READ|PROT_WRITE, pmap_mmap_flags(cookie)); + pmap_update(pmap_kernel()); - return mapping->diom_vaddr; + mapping->diom_mapped = true; + return (void *)mapping->diom_va; } static inline void -io_mapping_unmap(struct io_mapping *mapping, void *vaddr __unused) +io_mapping_unmap(struct io_mapping *mapping, void *ptr __diagused) { - KASSERT(mapping->diom_vaddr == vaddr); - bus_space_unmap(mapping->diom_bst, mapping->diom_bsh, PAGE_SIZE); - mapping->diom_vaddr = NULL; + KASSERT(mapping->diom_mapped); + KASSERT(mapping->diom_va == (vaddr_t)ptr); + + pmap_kremove(mapping->diom_va, PAGE_SIZE); + pmap_update(pmap_kernel()); + + mapping->diom_mapped = false; } static inline void * io_mapping_map_atomic_wc(struct io_mapping *mapping, unsigned long offset) { + return io_mapping_map_wc(mapping, offset); } static inline void -io_mapping_unmap_atomic(struct io_mapping *mapping, void *vaddr __unused) +io_mapping_unmap_atomic(struct io_mapping *mapping, void *ptr) { - return io_mapping_unmap(mapping, vaddr); + + return io_mapping_unmap(mapping, ptr); } #endif /* _LINUX_IO_MAPPING_H_ */