at v3.1 2.2 kB view raw
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);