|
@@ -0,0 +1,94 @@
|
|
|
+/*
|
|
|
+ * Re-map IO memory to kernel address space so that we can access it.
|
|
|
+ * This is needed for high PCI addresses that aren't mapped in the
|
|
|
+ * 640k-1MB IO memory area on PC's
|
|
|
+ *
|
|
|
+ * (C) Copyright 1995 1996 Linus Torvalds
|
|
|
+ */
|
|
|
+#include <linux/io.h>
|
|
|
+#include <linux/vmalloc.h>
|
|
|
+#include <linux/mm.h>
|
|
|
+
|
|
|
+#include <asm/cacheflush.h>
|
|
|
+#include <asm/pgtable.h>
|
|
|
+
|
|
|
+static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
|
|
|
+ unsigned long end, unsigned long phys_addr, pgprot_t prot)
|
|
|
+{
|
|
|
+ pte_t *pte;
|
|
|
+ unsigned long pfn;
|
|
|
+
|
|
|
+ pfn = phys_addr >> PAGE_SHIFT;
|
|
|
+ pte = pte_alloc_kernel(pmd, addr);
|
|
|
+ if (!pte)
|
|
|
+ return -ENOMEM;
|
|
|
+ do {
|
|
|
+ BUG_ON(!pte_none(*pte));
|
|
|
+ set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
|
|
|
+ pfn++;
|
|
|
+ } while (pte++, addr += PAGE_SIZE, addr != end);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
|
|
|
+ unsigned long end, unsigned long phys_addr, pgprot_t prot)
|
|
|
+{
|
|
|
+ pmd_t *pmd;
|
|
|
+ unsigned long next;
|
|
|
+
|
|
|
+ phys_addr -= addr;
|
|
|
+ pmd = pmd_alloc(&init_mm, pud, addr);
|
|
|
+ if (!pmd)
|
|
|
+ return -ENOMEM;
|
|
|
+ do {
|
|
|
+ next = pmd_addr_end(addr, end);
|
|
|
+ if (ioremap_pte_range(pmd, addr, next, phys_addr + addr, prot))
|
|
|
+ return -ENOMEM;
|
|
|
+ } while (pmd++, addr = next, addr != end);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr,
|
|
|
+ unsigned long end, unsigned long phys_addr, pgprot_t prot)
|
|
|
+{
|
|
|
+ pud_t *pud;
|
|
|
+ unsigned long next;
|
|
|
+
|
|
|
+ phys_addr -= addr;
|
|
|
+ pud = pud_alloc(&init_mm, pgd, addr);
|
|
|
+ if (!pud)
|
|
|
+ return -ENOMEM;
|
|
|
+ do {
|
|
|
+ next = pud_addr_end(addr, end);
|
|
|
+ if (ioremap_pmd_range(pud, addr, next, phys_addr + addr, prot))
|
|
|
+ return -ENOMEM;
|
|
|
+ } while (pud++, addr = next, addr != end);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int ioremap_page_range(unsigned long addr,
|
|
|
+ unsigned long end, unsigned long phys_addr, pgprot_t prot)
|
|
|
+{
|
|
|
+ pgd_t *pgd;
|
|
|
+ unsigned long start;
|
|
|
+ unsigned long next;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ BUG_ON(addr >= end);
|
|
|
+
|
|
|
+ flush_cache_all();
|
|
|
+
|
|
|
+ start = addr;
|
|
|
+ phys_addr -= addr;
|
|
|
+ pgd = pgd_offset_k(addr);
|
|
|
+ do {
|
|
|
+ next = pgd_addr_end(addr, end);
|
|
|
+ err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot);
|
|
|
+ if (err)
|
|
|
+ break;
|
|
|
+ } while (pgd++, addr = next, addr != end);
|
|
|
+
|
|
|
+ flush_tlb_all();
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|