18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Provide common bits of early_ioremap() support for architectures needing 48c2ecf20Sopenharmony_ci * temporary mappings during boot before ioremap() is available. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This is mostly a direct copy of the x86 early_ioremap implementation. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * (C) Copyright 1995 1996, 2014 Linus Torvalds 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 188c2ecf20Sopenharmony_ci#include <asm/fixmap.h> 198c2ecf20Sopenharmony_ci#include <asm/early_ioremap.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 228c2ecf20Sopenharmony_cistatic int early_ioremap_debug __initdata; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int __init early_ioremap_debug_setup(char *str) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci early_ioremap_debug = 1; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci return 0; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ciearly_param("early_ioremap_debug", early_ioremap_debug_setup); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int after_paging_init __initdata; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cipgprot_t __init __weak early_memremap_pgprot_adjust(resource_size_t phys_addr, 358c2ecf20Sopenharmony_ci unsigned long size, 368c2ecf20Sopenharmony_ci pgprot_t prot) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return prot; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_civoid __init __weak early_ioremap_shutdown(void) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_civoid __init early_ioremap_reset(void) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci early_ioremap_shutdown(); 488c2ecf20Sopenharmony_ci after_paging_init = 1; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * Generally, ioremap() is available after paging_init() has been called. 538c2ecf20Sopenharmony_ci * Architectures wanting to allow early_ioremap after paging_init() can 548c2ecf20Sopenharmony_ci * define __late_set_fixmap and __late_clear_fixmap to do the right thing. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci#ifndef __late_set_fixmap 578c2ecf20Sopenharmony_cistatic inline void __init __late_set_fixmap(enum fixed_addresses idx, 588c2ecf20Sopenharmony_ci phys_addr_t phys, pgprot_t prot) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci BUG(); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci#endif 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#ifndef __late_clear_fixmap 658c2ecf20Sopenharmony_cistatic inline void __init __late_clear_fixmap(enum fixed_addresses idx) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci BUG(); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci#endif 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void __iomem *prev_map[FIX_BTMAPS_SLOTS] __initdata; 728c2ecf20Sopenharmony_cistatic unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata; 738c2ecf20Sopenharmony_cistatic unsigned long slot_virt[FIX_BTMAPS_SLOTS] __initdata; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_civoid __init early_ioremap_setup(void) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int i; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci for (i = 0; i < FIX_BTMAPS_SLOTS; i++) 808c2ecf20Sopenharmony_ci if (WARN_ON(prev_map[i])) 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci for (i = 0; i < FIX_BTMAPS_SLOTS; i++) 848c2ecf20Sopenharmony_ci slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int __init check_early_ioremap_leak(void) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci int count = 0; 908c2ecf20Sopenharmony_ci int i; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci for (i = 0; i < FIX_BTMAPS_SLOTS; i++) 938c2ecf20Sopenharmony_ci if (prev_map[i]) 948c2ecf20Sopenharmony_ci count++; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (WARN(count, KERN_WARNING 978c2ecf20Sopenharmony_ci "Debug warning: early ioremap leak of %d areas detected.\n" 988c2ecf20Sopenharmony_ci "please boot with early_ioremap_debug and report the dmesg.\n", 998c2ecf20Sopenharmony_ci count)) 1008c2ecf20Sopenharmony_ci return 1; 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_cilate_initcall(check_early_ioremap_leak); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void __init __iomem * 1068c2ecf20Sopenharmony_ci__early_ioremap(resource_size_t phys_addr, unsigned long size, pgprot_t prot) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci unsigned long offset; 1098c2ecf20Sopenharmony_ci resource_size_t last_addr; 1108c2ecf20Sopenharmony_ci unsigned int nrpages; 1118c2ecf20Sopenharmony_ci enum fixed_addresses idx; 1128c2ecf20Sopenharmony_ci int i, slot; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci WARN_ON(system_state >= SYSTEM_RUNNING); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci slot = -1; 1178c2ecf20Sopenharmony_ci for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { 1188c2ecf20Sopenharmony_ci if (!prev_map[i]) { 1198c2ecf20Sopenharmony_ci slot = i; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (WARN(slot < 0, "%s(%pa, %08lx) not found slot\n", 1258c2ecf20Sopenharmony_ci __func__, &phys_addr, size)) 1268c2ecf20Sopenharmony_ci return NULL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Don't allow wraparound or zero size */ 1298c2ecf20Sopenharmony_ci last_addr = phys_addr + size - 1; 1308c2ecf20Sopenharmony_ci if (WARN_ON(!size || last_addr < phys_addr)) 1318c2ecf20Sopenharmony_ci return NULL; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci prev_size[slot] = size; 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * Mappings have to be page-aligned 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci offset = offset_in_page(phys_addr); 1388c2ecf20Sopenharmony_ci phys_addr &= PAGE_MASK; 1398c2ecf20Sopenharmony_ci size = PAGE_ALIGN(last_addr + 1) - phys_addr; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* 1428c2ecf20Sopenharmony_ci * Mappings have to fit in the FIX_BTMAP area. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci nrpages = size >> PAGE_SHIFT; 1458c2ecf20Sopenharmony_ci if (WARN_ON(nrpages > NR_FIX_BTMAPS)) 1468c2ecf20Sopenharmony_ci return NULL; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * Ok, go for it.. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot; 1528c2ecf20Sopenharmony_ci while (nrpages > 0) { 1538c2ecf20Sopenharmony_ci if (after_paging_init) 1548c2ecf20Sopenharmony_ci __late_set_fixmap(idx, phys_addr, prot); 1558c2ecf20Sopenharmony_ci else 1568c2ecf20Sopenharmony_ci __early_set_fixmap(idx, phys_addr, prot); 1578c2ecf20Sopenharmony_ci phys_addr += PAGE_SIZE; 1588c2ecf20Sopenharmony_ci --idx; 1598c2ecf20Sopenharmony_ci --nrpages; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci WARN(early_ioremap_debug, "%s(%pa, %08lx) [%d] => %08lx + %08lx\n", 1628c2ecf20Sopenharmony_ci __func__, &phys_addr, size, slot, offset, slot_virt[slot]); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci prev_map[slot] = (void __iomem *)(offset + slot_virt[slot]); 1658c2ecf20Sopenharmony_ci return prev_map[slot]; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_civoid __init early_iounmap(void __iomem *addr, unsigned long size) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci unsigned long virt_addr; 1718c2ecf20Sopenharmony_ci unsigned long offset; 1728c2ecf20Sopenharmony_ci unsigned int nrpages; 1738c2ecf20Sopenharmony_ci enum fixed_addresses idx; 1748c2ecf20Sopenharmony_ci int i, slot; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci slot = -1; 1778c2ecf20Sopenharmony_ci for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { 1788c2ecf20Sopenharmony_ci if (prev_map[i] == addr) { 1798c2ecf20Sopenharmony_ci slot = i; 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (WARN(slot < 0, "early_iounmap(%p, %08lx) not found slot\n", 1858c2ecf20Sopenharmony_ci addr, size)) 1868c2ecf20Sopenharmony_ci return; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (WARN(prev_size[slot] != size, 1898c2ecf20Sopenharmony_ci "early_iounmap(%p, %08lx) [%d] size not consistent %08lx\n", 1908c2ecf20Sopenharmony_ci addr, size, slot, prev_size[slot])) 1918c2ecf20Sopenharmony_ci return; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci WARN(early_ioremap_debug, "early_iounmap(%p, %08lx) [%d]\n", 1948c2ecf20Sopenharmony_ci addr, size, slot); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci virt_addr = (unsigned long)addr; 1978c2ecf20Sopenharmony_ci if (WARN_ON(virt_addr < fix_to_virt(FIX_BTMAP_BEGIN))) 1988c2ecf20Sopenharmony_ci return; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci offset = offset_in_page(virt_addr); 2018c2ecf20Sopenharmony_ci nrpages = PAGE_ALIGN(offset + size) >> PAGE_SHIFT; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot; 2048c2ecf20Sopenharmony_ci while (nrpages > 0) { 2058c2ecf20Sopenharmony_ci if (after_paging_init) 2068c2ecf20Sopenharmony_ci __late_clear_fixmap(idx); 2078c2ecf20Sopenharmony_ci else 2088c2ecf20Sopenharmony_ci __early_set_fixmap(idx, 0, FIXMAP_PAGE_CLEAR); 2098c2ecf20Sopenharmony_ci --idx; 2108c2ecf20Sopenharmony_ci --nrpages; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci prev_map[slot] = NULL; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* Remap an IO device */ 2168c2ecf20Sopenharmony_civoid __init __iomem * 2178c2ecf20Sopenharmony_ciearly_ioremap(resource_size_t phys_addr, unsigned long size) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return __early_ioremap(phys_addr, size, FIXMAP_PAGE_IO); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/* Remap memory */ 2238c2ecf20Sopenharmony_civoid __init * 2248c2ecf20Sopenharmony_ciearly_memremap(resource_size_t phys_addr, unsigned long size) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci pgprot_t prot = early_memremap_pgprot_adjust(phys_addr, size, 2278c2ecf20Sopenharmony_ci FIXMAP_PAGE_NORMAL); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return (__force void *)__early_ioremap(phys_addr, size, prot); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci#ifdef FIXMAP_PAGE_RO 2328c2ecf20Sopenharmony_civoid __init * 2338c2ecf20Sopenharmony_ciearly_memremap_ro(resource_size_t phys_addr, unsigned long size) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci pgprot_t prot = early_memremap_pgprot_adjust(phys_addr, size, 2368c2ecf20Sopenharmony_ci FIXMAP_PAGE_RO); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return (__force void *)__early_ioremap(phys_addr, size, prot); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci#endif 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_USE_MEMREMAP_PROT 2438c2ecf20Sopenharmony_civoid __init * 2448c2ecf20Sopenharmony_ciearly_memremap_prot(resource_size_t phys_addr, unsigned long size, 2458c2ecf20Sopenharmony_ci unsigned long prot_val) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci return (__force void *)__early_ioremap(phys_addr, size, 2488c2ecf20Sopenharmony_ci __pgprot(prot_val)); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci#endif 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci#define MAX_MAP_CHUNK (NR_FIX_BTMAPS << PAGE_SHIFT) 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_civoid __init copy_from_early_mem(void *dest, phys_addr_t src, unsigned long size) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci unsigned long slop, clen; 2578c2ecf20Sopenharmony_ci char *p; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci while (size) { 2608c2ecf20Sopenharmony_ci slop = offset_in_page(src); 2618c2ecf20Sopenharmony_ci clen = size; 2628c2ecf20Sopenharmony_ci if (clen > MAX_MAP_CHUNK - slop) 2638c2ecf20Sopenharmony_ci clen = MAX_MAP_CHUNK - slop; 2648c2ecf20Sopenharmony_ci p = early_memremap(src & PAGE_MASK, clen + slop); 2658c2ecf20Sopenharmony_ci memcpy(dest, p + slop, clen); 2668c2ecf20Sopenharmony_ci early_memunmap(p, clen + slop); 2678c2ecf20Sopenharmony_ci dest += clen; 2688c2ecf20Sopenharmony_ci src += clen; 2698c2ecf20Sopenharmony_ci size -= clen; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci#else /* CONFIG_MMU */ 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_civoid __init __iomem * 2768c2ecf20Sopenharmony_ciearly_ioremap(resource_size_t phys_addr, unsigned long size) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci return (__force void __iomem *)phys_addr; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* Remap memory */ 2828c2ecf20Sopenharmony_civoid __init * 2838c2ecf20Sopenharmony_ciearly_memremap(resource_size_t phys_addr, unsigned long size) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci return (void *)phys_addr; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_civoid __init * 2888c2ecf20Sopenharmony_ciearly_memremap_ro(resource_size_t phys_addr, unsigned long size) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci return (void *)phys_addr; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_civoid __init early_iounmap(void __iomem *addr, unsigned long size) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */ 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_civoid __init early_memunmap(void *addr, unsigned long size) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci early_iounmap((__force void __iomem *)addr, size); 3038c2ecf20Sopenharmony_ci} 304