14d35b93aSMatt Fleming /* 24d35b93aSMatt Fleming * Re-map IO memory to kernel address space so that we can access it. 34d35b93aSMatt Fleming * 44d35b93aSMatt Fleming * These functions should only be used when it is necessary to map a 54d35b93aSMatt Fleming * physical address space into the kernel address space before ioremap() 64d35b93aSMatt Fleming * can be used, e.g. early in boot before paging_init(). 74d35b93aSMatt Fleming * 84d35b93aSMatt Fleming * Copyright (C) 2009 Matt Fleming 94d35b93aSMatt Fleming */ 104d35b93aSMatt Fleming 114d35b93aSMatt Fleming #include <linux/vmalloc.h> 124d35b93aSMatt Fleming #include <linux/ioport.h> 134d35b93aSMatt Fleming #include <linux/module.h> 144d35b93aSMatt Fleming #include <linux/mm.h> 154d35b93aSMatt Fleming #include <linux/io.h> 164d35b93aSMatt Fleming #include <linux/bootmem.h> 174d35b93aSMatt Fleming #include <linux/proc_fs.h> 184d35b93aSMatt Fleming #include <linux/slab.h> 194d35b93aSMatt Fleming #include <asm/fixmap.h> 204d35b93aSMatt Fleming #include <asm/page.h> 214d35b93aSMatt Fleming #include <asm/pgalloc.h> 224d35b93aSMatt Fleming #include <asm/addrspace.h> 234d35b93aSMatt Fleming #include <asm/cacheflush.h> 244d35b93aSMatt Fleming #include <asm/tlbflush.h> 254d35b93aSMatt Fleming #include <asm/mmu.h> 264d35b93aSMatt Fleming #include <asm/mmu_context.h> 274d35b93aSMatt Fleming 284d35b93aSMatt Fleming struct ioremap_map { 294d35b93aSMatt Fleming void __iomem *addr; 304d35b93aSMatt Fleming unsigned long size; 314d35b93aSMatt Fleming unsigned long fixmap_addr; 324d35b93aSMatt Fleming }; 334d35b93aSMatt Fleming 344d35b93aSMatt Fleming static struct ioremap_map ioremap_maps[FIX_N_IOREMAPS]; 354d35b93aSMatt Fleming 364d35b93aSMatt Fleming void __init ioremap_fixed_init(void) 374d35b93aSMatt Fleming { 384d35b93aSMatt Fleming struct ioremap_map *map; 394d35b93aSMatt Fleming int i; 404d35b93aSMatt Fleming 414d35b93aSMatt Fleming for (i = 0; i < FIX_N_IOREMAPS; i++) { 424d35b93aSMatt Fleming map = &ioremap_maps[i]; 434d35b93aSMatt Fleming map->fixmap_addr = __fix_to_virt(FIX_IOREMAP_BEGIN + i); 444d35b93aSMatt Fleming } 454d35b93aSMatt Fleming } 464d35b93aSMatt Fleming 474d35b93aSMatt Fleming void __init __iomem * 484d35b93aSMatt Fleming ioremap_fixed(resource_size_t phys_addr, unsigned long size, pgprot_t prot) 494d35b93aSMatt Fleming { 504d35b93aSMatt Fleming enum fixed_addresses idx0, idx; 514d35b93aSMatt Fleming resource_size_t last_addr; 524d35b93aSMatt Fleming struct ioremap_map *map; 534d35b93aSMatt Fleming unsigned long offset; 544d35b93aSMatt Fleming unsigned int nrpages; 554d35b93aSMatt Fleming int i, slot; 564d35b93aSMatt Fleming 574d35b93aSMatt Fleming slot = -1; 584d35b93aSMatt Fleming for (i = 0; i < FIX_N_IOREMAPS; i++) { 594d35b93aSMatt Fleming map = &ioremap_maps[i]; 604d35b93aSMatt Fleming if (!map->addr) { 614d35b93aSMatt Fleming map->size = size; 624d35b93aSMatt Fleming slot = i; 634d35b93aSMatt Fleming break; 644d35b93aSMatt Fleming } 654d35b93aSMatt Fleming } 664d35b93aSMatt Fleming 674d35b93aSMatt Fleming if (slot < 0) 684d35b93aSMatt Fleming return NULL; 694d35b93aSMatt Fleming 704d35b93aSMatt Fleming /* Don't allow wraparound or zero size */ 714d35b93aSMatt Fleming last_addr = phys_addr + size - 1; 724d35b93aSMatt Fleming if (!size || last_addr < phys_addr) 734d35b93aSMatt Fleming return NULL; 744d35b93aSMatt Fleming 754d35b93aSMatt Fleming /* 764d35b93aSMatt Fleming * Fixmap mappings have to be page-aligned 774d35b93aSMatt Fleming */ 784d35b93aSMatt Fleming offset = phys_addr & ~PAGE_MASK; 794d35b93aSMatt Fleming phys_addr &= PAGE_MASK; 804d35b93aSMatt Fleming size = PAGE_ALIGN(last_addr + 1) - phys_addr; 814d35b93aSMatt Fleming 824d35b93aSMatt Fleming /* 834d35b93aSMatt Fleming * Mappings have to fit in the FIX_IOREMAP area. 844d35b93aSMatt Fleming */ 854d35b93aSMatt Fleming nrpages = size >> PAGE_SHIFT; 864d35b93aSMatt Fleming if (nrpages > FIX_N_IOREMAPS) 874d35b93aSMatt Fleming return NULL; 884d35b93aSMatt Fleming 894d35b93aSMatt Fleming /* 904d35b93aSMatt Fleming * Ok, go for it.. 914d35b93aSMatt Fleming */ 924d35b93aSMatt Fleming idx0 = FIX_IOREMAP_BEGIN + slot; 934d35b93aSMatt Fleming idx = idx0; 944d35b93aSMatt Fleming while (nrpages > 0) { 954d35b93aSMatt Fleming pgprot_val(prot) |= _PAGE_WIRED; 964d35b93aSMatt Fleming __set_fixmap(idx, phys_addr, prot); 974d35b93aSMatt Fleming phys_addr += PAGE_SIZE; 984d35b93aSMatt Fleming idx++; 994d35b93aSMatt Fleming --nrpages; 1004d35b93aSMatt Fleming } 1014d35b93aSMatt Fleming 1024d35b93aSMatt Fleming map->addr = (void __iomem *)(offset + map->fixmap_addr); 1034d35b93aSMatt Fleming return map->addr; 1044d35b93aSMatt Fleming } 1054d35b93aSMatt Fleming 106*4f744affSPaul Mundt int iounmap_fixed(void __iomem *addr) 1074d35b93aSMatt Fleming { 1084d35b93aSMatt Fleming enum fixed_addresses idx; 1094d35b93aSMatt Fleming unsigned long virt_addr; 1104d35b93aSMatt Fleming struct ioremap_map *map; 1114d35b93aSMatt Fleming unsigned long offset; 1124d35b93aSMatt Fleming unsigned int nrpages; 1134d35b93aSMatt Fleming int i, slot; 1144d35b93aSMatt Fleming pgprot_t prot; 1154d35b93aSMatt Fleming 1164d35b93aSMatt Fleming slot = -1; 1174d35b93aSMatt Fleming for (i = 0; i < FIX_N_IOREMAPS; i++) { 1184d35b93aSMatt Fleming map = &ioremap_maps[i]; 1194d35b93aSMatt Fleming if (map->addr == addr) { 1204d35b93aSMatt Fleming slot = i; 1214d35b93aSMatt Fleming break; 1224d35b93aSMatt Fleming } 1234d35b93aSMatt Fleming } 1244d35b93aSMatt Fleming 125*4f744affSPaul Mundt /* 126*4f744affSPaul Mundt * If we don't match, it's not for us. 127*4f744affSPaul Mundt */ 1284d35b93aSMatt Fleming if (slot < 0) 129*4f744affSPaul Mundt return -EINVAL; 1304d35b93aSMatt Fleming 1314d35b93aSMatt Fleming virt_addr = (unsigned long)addr; 1324d35b93aSMatt Fleming 1334d35b93aSMatt Fleming offset = virt_addr & ~PAGE_MASK; 1344d35b93aSMatt Fleming nrpages = PAGE_ALIGN(offset + map->size - 1) >> PAGE_SHIFT; 1354d35b93aSMatt Fleming 1364d35b93aSMatt Fleming pgprot_val(prot) = _PAGE_WIRED; 1374d35b93aSMatt Fleming 1384d35b93aSMatt Fleming idx = FIX_IOREMAP_BEGIN + slot + nrpages; 1394d35b93aSMatt Fleming while (nrpages > 0) { 1404d35b93aSMatt Fleming __clear_fixmap(idx, prot); 1414d35b93aSMatt Fleming --idx; 1424d35b93aSMatt Fleming --nrpages; 1434d35b93aSMatt Fleming } 1444d35b93aSMatt Fleming 1454d35b93aSMatt Fleming map->size = 0; 1464d35b93aSMatt Fleming map->addr = NULL; 147*4f744affSPaul Mundt 148*4f744affSPaul Mundt return 0; 1494d35b93aSMatt Fleming } 150