linux/lib/ioremap.c
<<
>>
Prefs
   1/*
   2 * Re-map IO memory to kernel address space so that we can access it.
   3 * This is needed for high PCI addresses that aren't mapped in the
   4 * 640k-1MB IO memory area on PC's
   5 *
   6 * (C) Copyright 1995 1996 Linus Torvalds
   7 */
   8#include <linux/vmalloc.h>
   9#include <linux/mm.h>
  10#include <linux/sched.h>
  11#include <linux/io.h>
  12#include <linux/module.h>
  13#include <asm/cacheflush.h>
  14#include <asm/pgtable.h>
  15
  16static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
  17                unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
  18{
  19        pte_t *pte;
  20        u64 pfn;
  21
  22        pfn = phys_addr >> PAGE_SHIFT;
  23        pte = pte_alloc_kernel(pmd, addr);
  24        if (!pte)
  25                return -ENOMEM;
  26        do {
  27                BUG_ON(!pte_none(*pte));
  28                set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
  29                pfn++;
  30        } while (pte++, addr += PAGE_SIZE, addr != end);
  31        return 0;
  32}
  33
  34static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
  35                unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
  36{
  37        pmd_t *pmd;
  38        unsigned long next;
  39
  40        phys_addr -= addr;
  41        pmd = pmd_alloc(&init_mm, pud, addr);
  42        if (!pmd)
  43                return -ENOMEM;
  44        do {
  45                next = pmd_addr_end(addr, end);
  46                if (ioremap_pte_range(pmd, addr, next, phys_addr + addr, prot))
  47                        return -ENOMEM;
  48        } while (pmd++, addr = next, addr != end);
  49        return 0;
  50}
  51
  52static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr,
  53                unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
  54{
  55        pud_t *pud;
  56        unsigned long next;
  57
  58        phys_addr -= addr;
  59        pud = pud_alloc(&init_mm, pgd, addr);
  60        if (!pud)
  61                return -ENOMEM;
  62        do {
  63                next = pud_addr_end(addr, end);
  64                if (ioremap_pmd_range(pud, addr, next, phys_addr + addr, prot))
  65                        return -ENOMEM;
  66        } while (pud++, addr = next, addr != end);
  67        return 0;
  68}
  69
  70int ioremap_page_range(unsigned long addr,
  71                       unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
  72{
  73        pgd_t *pgd;
  74        unsigned long start;
  75        unsigned long next;
  76        int err;
  77
  78        BUG_ON(addr >= end);
  79
  80        start = addr;
  81        phys_addr -= addr;
  82        pgd = pgd_offset_k(addr);
  83        do {
  84                next = pgd_addr_end(addr, end);
  85                err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot);
  86                if (err)
  87                        break;
  88        } while (pgd++, addr = next, addr != end);
  89
  90        flush_cache_vmap(start, end);
  91
  92        return err;
  93}
  94EXPORT_SYMBOL_GPL(ioremap_page_range);
  95