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 <asm/cacheflush.h>
  13#include <asm/pgtable.h>
  14
  15static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
  16                unsigned long end, unsigned long phys_addr, pgprot_t prot)
  17{
  18        pte_t *pte;
  19        unsigned long pfn;
  20
  21        pfn = phys_addr >> PAGE_SHIFT;
  22        pte = pte_alloc_kernel(pmd, addr);
  23        if (!pte)
  24                return -ENOMEM;
  25        do {
  26                BUG_ON(!pte_none(*pte));
  27                set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
  28                pfn++;
  29        } while (pte++, addr += PAGE_SIZE, addr != end);
  30        return 0;
  31}
  32
  33static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
  34                unsigned long end, unsigned long phys_addr, pgprot_t prot)
  35{
  36        pmd_t *pmd;
  37        unsigned long next;
  38
  39        phys_addr -= addr;
  40        pmd = pmd_alloc(&init_mm, pud, addr);
  41        if (!pmd)
  42                return -ENOMEM;
  43        do {
  44                next = pmd_addr_end(addr, end);
  45                if (ioremap_pte_range(pmd, addr, next, phys_addr + addr, prot))
  46                        return -ENOMEM;
  47        } while (pmd++, addr = next, addr != end);
  48        return 0;
  49}
  50
  51static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr,
  52                unsigned long end, unsigned long phys_addr, pgprot_t prot)
  53{
  54        pud_t *pud;
  55        unsigned long next;
  56
  57        phys_addr -= addr;
  58        pud = pud_alloc(&init_mm, pgd, addr);
  59        if (!pud)
  60                return -ENOMEM;
  61        do {
  62                next = pud_addr_end(addr, end);
  63                if (ioremap_pmd_range(pud, addr, next, phys_addr + addr, prot))
  64                        return -ENOMEM;
  65        } while (pud++, addr = next, addr != end);
  66        return 0;
  67}
  68
  69int ioremap_page_range(unsigned long addr,
  70                       unsigned long end, unsigned long phys_addr, pgprot_t prot)
  71{
  72        pgd_t *pgd;
  73        unsigned long start;
  74        unsigned long next;
  75        int err;
  76
  77        BUG_ON(addr >= end);
  78
  79        start = addr;
  80        phys_addr -= addr;
  81        pgd = pgd_offset_k(addr);
  82        do {
  83                next = pgd_addr_end(addr, end);
  84                err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot);
  85                if (err)
  86                        break;
  87        } while (pgd++, addr = next, addr != end);
  88
  89        flush_cache_vmap(start, end);
  90
  91        return err;
  92}
  93