ioremap.c 2.1 KB

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