18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2008 Ingo Molnar 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <asm/iomap.h> 78c2ecf20Sopenharmony_ci#include <asm/memtype.h> 88c2ecf20Sopenharmony_ci#include <linux/export.h> 98c2ecf20Sopenharmony_ci#include <linux/highmem.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic int is_io_mapping_possible(resource_size_t base, unsigned long size) 128c2ecf20Sopenharmony_ci{ 138c2ecf20Sopenharmony_ci#if !defined(CONFIG_X86_PAE) && defined(CONFIG_PHYS_ADDR_T_64BIT) 148c2ecf20Sopenharmony_ci /* There is no way to map greater than 1 << 32 address without PAE */ 158c2ecf20Sopenharmony_ci if (base + size > 0x100000000ULL) 168c2ecf20Sopenharmony_ci return 0; 178c2ecf20Sopenharmony_ci#endif 188c2ecf20Sopenharmony_ci return 1; 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciint iomap_create_wc(resource_size_t base, unsigned long size, pgprot_t *prot) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci enum page_cache_mode pcm = _PAGE_CACHE_MODE_WC; 248c2ecf20Sopenharmony_ci int ret; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (!is_io_mapping_possible(base, size)) 278c2ecf20Sopenharmony_ci return -EINVAL; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci ret = memtype_reserve_io(base, base + size, &pcm); 308c2ecf20Sopenharmony_ci if (ret) 318c2ecf20Sopenharmony_ci return ret; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci *prot = __pgprot(__PAGE_KERNEL | cachemode2protval(pcm)); 348c2ecf20Sopenharmony_ci /* Filter out unsupported __PAGE_KERNEL* bits: */ 358c2ecf20Sopenharmony_ci pgprot_val(*prot) &= __default_kernel_pte_mask; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iomap_create_wc); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_civoid iomap_free(resource_size_t base, unsigned long size) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci memtype_free_io(base, base + size); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iomap_free); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_civoid *kmap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci unsigned long vaddr; 508c2ecf20Sopenharmony_ci int idx, type; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci preempt_disable(); 538c2ecf20Sopenharmony_ci pagefault_disable(); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci type = kmap_atomic_idx_push(); 568c2ecf20Sopenharmony_ci idx = type + KM_TYPE_NR * smp_processor_id(); 578c2ecf20Sopenharmony_ci vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); 588c2ecf20Sopenharmony_ci set_pte(kmap_pte - idx, pfn_pte(pfn, prot)); 598c2ecf20Sopenharmony_ci arch_flush_lazy_mmu_mode(); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci return (void *)vaddr; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Map 'pfn' using protections 'prot' 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_civoid __iomem * 688c2ecf20Sopenharmony_ciiomap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * For non-PAT systems, translate non-WB request to UC- just in 728c2ecf20Sopenharmony_ci * case the caller set the PWT bit to prot directly without using 738c2ecf20Sopenharmony_ci * pgprot_writecombine(). UC- translates to uncached if the MTRR 748c2ecf20Sopenharmony_ci * is UC or WC. UC- gets the real intention, of the user, which is 758c2ecf20Sopenharmony_ci * "WC if the MTRR is WC, UC if you can't do that." 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci if (!pat_enabled() && pgprot2cachemode(prot) != _PAGE_CACHE_MODE_WB) 788c2ecf20Sopenharmony_ci prot = __pgprot(__PAGE_KERNEL | 798c2ecf20Sopenharmony_ci cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS)); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Filter out unsupported __PAGE_KERNEL* bits: */ 828c2ecf20Sopenharmony_ci pgprot_val(prot) &= __default_kernel_pte_mask; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return (void __force __iomem *) kmap_atomic_prot_pfn(pfn, prot); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iomap_atomic_prot_pfn); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_civoid 898c2ecf20Sopenharmony_ciiounmap_atomic(void __iomem *kvaddr) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (vaddr >= __fix_to_virt(FIX_KMAP_END) && 948c2ecf20Sopenharmony_ci vaddr <= __fix_to_virt(FIX_KMAP_BEGIN)) { 958c2ecf20Sopenharmony_ci int idx, type; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci type = kmap_atomic_idx(); 988c2ecf20Sopenharmony_ci idx = type + KM_TYPE_NR * smp_processor_id(); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_HIGHMEM 1018c2ecf20Sopenharmony_ci WARN_ON_ONCE(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx)); 1028c2ecf20Sopenharmony_ci#endif 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * Force other mappings to Oops if they'll try to access this 1058c2ecf20Sopenharmony_ci * pte without first remap it. Keeping stale mappings around 1068c2ecf20Sopenharmony_ci * is a bad idea also, in case the page changes cacheability 1078c2ecf20Sopenharmony_ci * attributes or becomes a protected page in a hypervisor. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci kpte_clear_flush(kmap_pte-idx, vaddr); 1108c2ecf20Sopenharmony_ci kmap_atomic_idx_pop(); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci pagefault_enable(); 1148c2ecf20Sopenharmony_ci preempt_enable(); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iounmap_atomic); 117