forked from Minki/linux
c04e8e2fe5
Some drivers (ACPI notably) use ioremap_cache() to map an area which could either be outside of kernel RAM or in an already mapped reserved area of RAM. To avoid aliases with different caching attributes, ioremap() does not allow RAM to be remapped. But for ioremap_cache(), the existing kernel mapping may be used. Signed-off-by: Mark Salter <msalter@redhat.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
101 lines
2.7 KiB
C
101 lines
2.7 KiB
C
/*
|
|
* Based on arch/arm/mm/ioremap.c
|
|
*
|
|
* (C) Copyright 1995 1996 Linus Torvalds
|
|
* Hacked for ARM by Phil Blundell <philb@gnu.org>
|
|
* Hacked to allow all architectures to build, and various cleanups
|
|
* by Russell King
|
|
* Copyright (C) 2012 ARM Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/io.h>
|
|
|
|
static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size,
|
|
pgprot_t prot, void *caller)
|
|
{
|
|
unsigned long last_addr;
|
|
unsigned long offset = phys_addr & ~PAGE_MASK;
|
|
int err;
|
|
unsigned long addr;
|
|
struct vm_struct *area;
|
|
|
|
/*
|
|
* Page align the mapping address and size, taking account of any
|
|
* offset.
|
|
*/
|
|
phys_addr &= PAGE_MASK;
|
|
size = PAGE_ALIGN(size + offset);
|
|
|
|
/*
|
|
* Don't allow wraparound, zero size or outside PHYS_MASK.
|
|
*/
|
|
last_addr = phys_addr + size - 1;
|
|
if (!size || last_addr < phys_addr || (last_addr & ~PHYS_MASK))
|
|
return NULL;
|
|
|
|
/*
|
|
* Don't allow RAM to be mapped.
|
|
*/
|
|
if (WARN_ON(pfn_valid(__phys_to_pfn(phys_addr))))
|
|
return NULL;
|
|
|
|
area = get_vm_area_caller(size, VM_IOREMAP, caller);
|
|
if (!area)
|
|
return NULL;
|
|
addr = (unsigned long)area->addr;
|
|
|
|
err = ioremap_page_range(addr, addr + size, phys_addr, prot);
|
|
if (err) {
|
|
vunmap((void *)addr);
|
|
return NULL;
|
|
}
|
|
|
|
return (void __iomem *)(offset + addr);
|
|
}
|
|
|
|
void __iomem *__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot)
|
|
{
|
|
return __ioremap_caller(phys_addr, size, prot,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(__ioremap);
|
|
|
|
void __iounmap(volatile void __iomem *io_addr)
|
|
{
|
|
unsigned long addr = (unsigned long)io_addr & PAGE_MASK;
|
|
|
|
/*
|
|
* We could get an address outside vmalloc range in case
|
|
* of ioremap_cache() reusing a RAM mapping.
|
|
*/
|
|
if (VMALLOC_START <= addr && addr < VMALLOC_END)
|
|
vunmap((void *)addr);
|
|
}
|
|
EXPORT_SYMBOL(__iounmap);
|
|
|
|
void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size)
|
|
{
|
|
/* For normal memory we already have a cacheable mapping. */
|
|
if (pfn_valid(__phys_to_pfn(phys_addr)))
|
|
return (void __iomem *)__phys_to_virt(phys_addr);
|
|
|
|
return __ioremap_caller(phys_addr, size, __pgprot(PROT_NORMAL),
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(ioremap_cache);
|