linux/arch/avr32/mm/ioremap.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2004-2006 Atmel Corporation
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8#include <linux/vmalloc.h>
   9#include <linux/mm.h>
  10#include <linux/module.h>
  11#include <linux/io.h>
  12#include <linux/slab.h>
  13
  14#include <asm/pgtable.h>
  15#include <asm/addrspace.h>
  16
  17/*
  18 * Re-map an arbitrary physical address space into the kernel virtual
  19 * address space. Needed when the kernel wants to access physical
  20 * memory directly.
  21 */
  22void __iomem *__ioremap(unsigned long phys_addr, size_t size,
  23                        unsigned long flags)
  24{
  25        unsigned long addr;
  26        struct vm_struct *area;
  27        unsigned long offset, last_addr;
  28        pgprot_t prot;
  29
  30        /*
  31         * Check if we can simply use the P4 segment. This area is
  32         * uncacheable, so if caching/buffering is requested, we can't
  33         * use it.
  34         */
  35        if ((phys_addr >= P4SEG) && (flags == 0))
  36                return (void __iomem *)phys_addr;
  37
  38        /* Don't allow wraparound or zero size */
  39        last_addr = phys_addr + size - 1;
  40        if (!size || last_addr < phys_addr)
  41                return NULL;
  42
  43        /*
  44         * XXX: When mapping regular RAM, we'd better make damn sure
  45         * it's never used for anything else.  But this is really the
  46         * caller's responsibility...
  47         */
  48        if (PHYSADDR(P2SEGADDR(phys_addr)) == phys_addr)
  49                return (void __iomem *)P2SEGADDR(phys_addr);
  50
  51        /* Mappings have to be page-aligned */
  52        offset = phys_addr & ~PAGE_MASK;
  53        phys_addr &= PAGE_MASK;
  54        size = PAGE_ALIGN(last_addr + 1) - phys_addr;
  55
  56        prot = __pgprot(_PAGE_PRESENT | _PAGE_GLOBAL | _PAGE_RW | _PAGE_DIRTY
  57                        | _PAGE_ACCESSED | _PAGE_TYPE_SMALL | flags);
  58
  59        /*
  60         * Ok, go for it..
  61         */
  62        area = get_vm_area(size, VM_IOREMAP);
  63        if (!area)
  64                return NULL;
  65        area->phys_addr = phys_addr;
  66        addr = (unsigned long )area->addr;
  67        if (ioremap_page_range(addr, addr + size, phys_addr, prot)) {
  68                vunmap((void *)addr);
  69                return NULL;
  70        }
  71
  72        return (void __iomem *)(offset + (char *)addr);
  73}
  74EXPORT_SYMBOL(__ioremap);
  75
  76void __iounmap(void __iomem *addr)
  77{
  78        struct vm_struct *p;
  79
  80        if ((unsigned long)addr >= P4SEG)
  81                return;
  82        if (PXSEG(addr) == P2SEG)
  83                return;
  84
  85        p = remove_vm_area((void *)(PAGE_MASK & (unsigned long __force)addr));
  86        if (unlikely(!p)) {
  87                printk (KERN_ERR "iounmap: bad address %p\n", addr);
  88                return;
  89        }
  90
  91        kfree (p);
  92}
  93EXPORT_SYMBOL(__iounmap);
  94