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, phys_addr_t phys_addr, pgprot_t prot) 17{ 18 pte_t *pte; 19 u64 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, phys_addr_t 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, phys_addr_t 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, phys_addr_t 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