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 * 48*acf2c968SPaul Mundt ioremap_fixed(resource_size_t phys_addr, unsigned long offset, 49*acf2c968SPaul Mundt unsigned long size, pgprot_t prot) 504d35b93aSMatt Fleming { 514d35b93aSMatt Fleming enum fixed_addresses idx0, idx; 524d35b93aSMatt Fleming struct ioremap_map *map; 534d35b93aSMatt Fleming unsigned int nrpages; 544d35b93aSMatt Fleming int i, slot; 554d35b93aSMatt Fleming 564d35b93aSMatt Fleming slot = -1; 574d35b93aSMatt Fleming for (i = 0; i < FIX_N_IOREMAPS; i++) { 584d35b93aSMatt Fleming map = &ioremap_maps[i]; 594d35b93aSMatt Fleming if (!map->addr) { 604d35b93aSMatt Fleming map->size = size; 614d35b93aSMatt Fleming slot = i; 624d35b93aSMatt Fleming break; 634d35b93aSMatt Fleming } 644d35b93aSMatt Fleming } 654d35b93aSMatt Fleming 664d35b93aSMatt Fleming if (slot < 0) 674d35b93aSMatt Fleming return NULL; 684d35b93aSMatt Fleming 694d35b93aSMatt Fleming /* 704d35b93aSMatt Fleming * Mappings have to fit in the FIX_IOREMAP area. 714d35b93aSMatt Fleming */ 724d35b93aSMatt Fleming nrpages = size >> PAGE_SHIFT; 734d35b93aSMatt Fleming if (nrpages > FIX_N_IOREMAPS) 744d35b93aSMatt Fleming return NULL; 754d35b93aSMatt Fleming 764d35b93aSMatt Fleming /* 774d35b93aSMatt Fleming * Ok, go for it.. 784d35b93aSMatt Fleming */ 794d35b93aSMatt Fleming idx0 = FIX_IOREMAP_BEGIN + slot; 804d35b93aSMatt Fleming idx = idx0; 814d35b93aSMatt Fleming while (nrpages > 0) { 824d35b93aSMatt Fleming pgprot_val(prot) |= _PAGE_WIRED; 834d35b93aSMatt Fleming __set_fixmap(idx, phys_addr, prot); 844d35b93aSMatt Fleming phys_addr += PAGE_SIZE; 854d35b93aSMatt Fleming idx++; 864d35b93aSMatt Fleming --nrpages; 874d35b93aSMatt Fleming } 884d35b93aSMatt Fleming 894d35b93aSMatt Fleming map->addr = (void __iomem *)(offset + map->fixmap_addr); 904d35b93aSMatt Fleming return map->addr; 914d35b93aSMatt Fleming } 924d35b93aSMatt Fleming 934f744affSPaul Mundt int iounmap_fixed(void __iomem *addr) 944d35b93aSMatt Fleming { 954d35b93aSMatt Fleming enum fixed_addresses idx; 964d35b93aSMatt Fleming unsigned long virt_addr; 974d35b93aSMatt Fleming struct ioremap_map *map; 984d35b93aSMatt Fleming unsigned long offset; 994d35b93aSMatt Fleming unsigned int nrpages; 1004d35b93aSMatt Fleming int i, slot; 1014d35b93aSMatt Fleming 1024d35b93aSMatt Fleming slot = -1; 1034d35b93aSMatt Fleming for (i = 0; i < FIX_N_IOREMAPS; i++) { 1044d35b93aSMatt Fleming map = &ioremap_maps[i]; 1054d35b93aSMatt Fleming if (map->addr == addr) { 1064d35b93aSMatt Fleming slot = i; 1074d35b93aSMatt Fleming break; 1084d35b93aSMatt Fleming } 1094d35b93aSMatt Fleming } 1104d35b93aSMatt Fleming 1114f744affSPaul Mundt /* 1124f744affSPaul Mundt * If we don't match, it's not for us. 1134f744affSPaul Mundt */ 1144d35b93aSMatt Fleming if (slot < 0) 1154f744affSPaul Mundt return -EINVAL; 1164d35b93aSMatt Fleming 1174d35b93aSMatt Fleming virt_addr = (unsigned long)addr; 1184d35b93aSMatt Fleming 1194d35b93aSMatt Fleming offset = virt_addr & ~PAGE_MASK; 1204d35b93aSMatt Fleming nrpages = PAGE_ALIGN(offset + map->size - 1) >> PAGE_SHIFT; 1214d35b93aSMatt Fleming 1224d35b93aSMatt Fleming idx = FIX_IOREMAP_BEGIN + slot + nrpages; 1234d35b93aSMatt Fleming while (nrpages > 0) { 124*acf2c968SPaul Mundt __clear_fixmap(idx, __pgprot(_PAGE_WIRED)); 1254d35b93aSMatt Fleming --idx; 1264d35b93aSMatt Fleming --nrpages; 1274d35b93aSMatt Fleming } 1284d35b93aSMatt Fleming 1294d35b93aSMatt Fleming map->size = 0; 1304d35b93aSMatt Fleming map->addr = NULL; 1314f744affSPaul Mundt 1324f744affSPaul Mundt return 0; 1334d35b93aSMatt Fleming } 134