xref: /openbmc/linux/arch/sh/mm/ioremap_fixed.c (revision 4f744affc345f8b158615e0cdd01d1f4985837c3)
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