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