ioremap.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. * arch/s390/mm/ioremap.c
  3. *
  4. * S390 version
  5. * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
  6. * Author(s): Hartmut Penner (hp@de.ibm.com)
  7. *
  8. * Derived from "arch/i386/mm/extable.c"
  9. * (C) Copyright 1995 1996 Linus Torvalds
  10. *
  11. * Re-map IO memory to kernel address space so that we can access it.
  12. * This is needed for high PCI addresses that aren't mapped in the
  13. * 640k-1MB IO memory area on PC's
  14. */
  15. #include <linux/vmalloc.h>
  16. #include <linux/mm.h>
  17. #include <asm/io.h>
  18. #include <asm/pgalloc.h>
  19. #include <asm/cacheflush.h>
  20. #include <asm/tlbflush.h>
  21. static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
  22. unsigned long phys_addr, unsigned long flags)
  23. {
  24. unsigned long end;
  25. unsigned long pfn;
  26. address &= ~PMD_MASK;
  27. end = address + size;
  28. if (end > PMD_SIZE)
  29. end = PMD_SIZE;
  30. if (address >= end)
  31. BUG();
  32. pfn = phys_addr >> PAGE_SHIFT;
  33. do {
  34. if (!pte_none(*pte)) {
  35. printk("remap_area_pte: page already exists\n");
  36. BUG();
  37. }
  38. set_pte(pte, pfn_pte(pfn, __pgprot(flags)));
  39. address += PAGE_SIZE;
  40. pfn++;
  41. pte++;
  42. } while (address && (address < end));
  43. }
  44. static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
  45. unsigned long phys_addr, unsigned long flags)
  46. {
  47. unsigned long end;
  48. address &= ~PGDIR_MASK;
  49. end = address + size;
  50. if (end > PGDIR_SIZE)
  51. end = PGDIR_SIZE;
  52. phys_addr -= address;
  53. if (address >= end)
  54. BUG();
  55. do {
  56. pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address);
  57. if (!pte)
  58. return -ENOMEM;
  59. remap_area_pte(pte, address, end - address, address + phys_addr, flags);
  60. address = (address + PMD_SIZE) & PMD_MASK;
  61. pmd++;
  62. } while (address && (address < end));
  63. return 0;
  64. }
  65. static int remap_area_pages(unsigned long address, unsigned long phys_addr,
  66. unsigned long size, unsigned long flags)
  67. {
  68. int error;
  69. pgd_t * dir;
  70. unsigned long end = address + size;
  71. phys_addr -= address;
  72. dir = pgd_offset(&init_mm, address);
  73. flush_cache_all();
  74. if (address >= end)
  75. BUG();
  76. spin_lock(&init_mm.page_table_lock);
  77. do {
  78. pmd_t *pmd;
  79. pmd = pmd_alloc(&init_mm, dir, address);
  80. error = -ENOMEM;
  81. if (!pmd)
  82. break;
  83. if (remap_area_pmd(pmd, address, end - address,
  84. phys_addr + address, flags))
  85. break;
  86. error = 0;
  87. address = (address + PGDIR_SIZE) & PGDIR_MASK;
  88. dir++;
  89. } while (address && (address < end));
  90. spin_unlock(&init_mm.page_table_lock);
  91. flush_tlb_all();
  92. return 0;
  93. }
  94. /*
  95. * Generic mapping function (not visible outside):
  96. */
  97. /*
  98. * Remap an arbitrary physical address space into the kernel virtual
  99. * address space. Needed when the kernel wants to access high addresses
  100. * directly.
  101. */
  102. void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
  103. {
  104. void * addr;
  105. struct vm_struct * area;
  106. if (phys_addr < virt_to_phys(high_memory))
  107. return phys_to_virt(phys_addr);
  108. if (phys_addr & ~PAGE_MASK)
  109. return NULL;
  110. size = PAGE_ALIGN(size);
  111. if (!size || size > phys_addr + size)
  112. return NULL;
  113. area = get_vm_area(size, VM_IOREMAP);
  114. if (!area)
  115. return NULL;
  116. addr = area->addr;
  117. if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) {
  118. vfree(addr);
  119. return NULL;
  120. }
  121. return addr;
  122. }
  123. void iounmap(void *addr)
  124. {
  125. if (addr > high_memory)
  126. vfree(addr);
  127. }