![Vincent Chen](/assets/img/avatar_default.png)
For 32bit, the upper 32-bit of phys_addr_t will be flushed to zero after AND with PAGE_MASK because the data type of PAGE_MASK is unsigned long. To fix this problem, the page alignment is done by subtracting the page offset instead of AND with PAGE_MASK. Signed-off-by: Vincent Chen <vincentc@andestech.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
93 lines
2.6 KiB
C
93 lines
2.6 KiB
C
/*
|
|
* (C) Copyright 1995 1996 Linus Torvalds
|
|
* (C) Copyright 2012 Regents of the University of California
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation, version 2.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/io.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
/*
|
|
* Remap an arbitrary physical address space into the kernel virtual
|
|
* address space. Needed when the kernel wants to access high addresses
|
|
* directly.
|
|
*
|
|
* NOTE! We need to allow non-page-aligned mappings too: we will obviously
|
|
* have to convert them into an offset in a page-aligned mapping, but the
|
|
* caller shouldn't need to know that small detail.
|
|
*/
|
|
static void __iomem *__ioremap_caller(phys_addr_t addr, size_t size,
|
|
pgprot_t prot, void *caller)
|
|
{
|
|
phys_addr_t last_addr;
|
|
unsigned long offset, vaddr;
|
|
struct vm_struct *area;
|
|
|
|
/* Disallow wrap-around or zero size */
|
|
last_addr = addr + size - 1;
|
|
if (!size || last_addr < addr)
|
|
return NULL;
|
|
|
|
/* Page-align mappings */
|
|
offset = addr & (~PAGE_MASK);
|
|
addr -= offset;
|
|
size = PAGE_ALIGN(size + offset);
|
|
|
|
area = get_vm_area_caller(size, VM_IOREMAP, caller);
|
|
if (!area)
|
|
return NULL;
|
|
vaddr = (unsigned long)area->addr;
|
|
|
|
if (ioremap_page_range(vaddr, vaddr + size, addr, prot)) {
|
|
free_vm_area(area);
|
|
return NULL;
|
|
}
|
|
|
|
return (void __iomem *)(vaddr + offset);
|
|
}
|
|
|
|
/*
|
|
* ioremap - map bus memory into CPU space
|
|
* @offset: bus address of the memory
|
|
* @size: size of the resource to map
|
|
*
|
|
* ioremap performs a platform specific sequence of operations to
|
|
* make bus memory CPU accessible via the readb/readw/readl/writeb/
|
|
* writew/writel functions and the other mmio helpers. The returned
|
|
* address is not guaranteed to be usable directly as a virtual
|
|
* address.
|
|
*
|
|
* Must be freed with iounmap.
|
|
*/
|
|
void __iomem *ioremap(phys_addr_t offset, unsigned long size)
|
|
{
|
|
return __ioremap_caller(offset, size, PAGE_KERNEL,
|
|
__builtin_return_address(0));
|
|
}
|
|
EXPORT_SYMBOL(ioremap);
|
|
|
|
|
|
/**
|
|
* iounmap - Free a IO remapping
|
|
* @addr: virtual address from ioremap_*
|
|
*
|
|
* Caller must ensure there is only one unmapping for the same pointer.
|
|
*/
|
|
void iounmap(volatile void __iomem *addr)
|
|
{
|
|
vunmap((void *)((unsigned long)addr & PAGE_MASK));
|
|
}
|
|
EXPORT_SYMBOL(iounmap);
|