Merge tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[linux-2.6-block.git] / lib / ioremap.c
index 517f5853ffed1726a462543f902450ddb7f9a9b4..063213685563c30c2575161f4fbc92dc1bb53d64 100644 (file)
@@ -76,83 +76,123 @@ static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
        return 0;
 }
 
+static int ioremap_try_huge_pmd(pmd_t *pmd, unsigned long addr,
+                               unsigned long end, phys_addr_t phys_addr,
+                               pgprot_t prot)
+{
+       if (!ioremap_pmd_enabled())
+               return 0;
+
+       if ((end - addr) != PMD_SIZE)
+               return 0;
+
+       if (!IS_ALIGNED(phys_addr, PMD_SIZE))
+               return 0;
+
+       if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
+               return 0;
+
+       return pmd_set_huge(pmd, phys_addr, prot);
+}
+
 static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
                unsigned long end, phys_addr_t 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_pmd_enabled() &&
-                   ((next - addr) == PMD_SIZE) &&
-                   IS_ALIGNED(phys_addr + addr, PMD_SIZE) &&
-                   pmd_free_pte_page(pmd, addr)) {
-                       if (pmd_set_huge(pmd, phys_addr + addr, prot))
-                               continue;
-               }
+               if (ioremap_try_huge_pmd(pmd, addr, next, phys_addr, prot))
+                       continue;
 
-               if (ioremap_pte_range(pmd, addr, next, phys_addr + addr, prot))
+               if (ioremap_pte_range(pmd, addr, next, phys_addr, prot))
                        return -ENOMEM;
-       } while (pmd++, addr = next, addr != end);
+       } while (pmd++, phys_addr += (next - addr), addr = next, addr != end);
        return 0;
 }
 
+static int ioremap_try_huge_pud(pud_t *pud, unsigned long addr,
+                               unsigned long end, phys_addr_t phys_addr,
+                               pgprot_t prot)
+{
+       if (!ioremap_pud_enabled())
+               return 0;
+
+       if ((end - addr) != PUD_SIZE)
+               return 0;
+
+       if (!IS_ALIGNED(phys_addr, PUD_SIZE))
+               return 0;
+
+       if (pud_present(*pud) && !pud_free_pmd_page(pud, addr))
+               return 0;
+
+       return pud_set_huge(pud, phys_addr, prot);
+}
+
 static inline int ioremap_pud_range(p4d_t *p4d, unsigned long addr,
                unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
 {
        pud_t *pud;
        unsigned long next;
 
-       phys_addr -= addr;
        pud = pud_alloc(&init_mm, p4d, addr);
        if (!pud)
                return -ENOMEM;
        do {
                next = pud_addr_end(addr, end);
 
-               if (ioremap_pud_enabled() &&
-                   ((next - addr) == PUD_SIZE) &&
-                   IS_ALIGNED(phys_addr + addr, PUD_SIZE) &&
-                   pud_free_pmd_page(pud, addr)) {
-                       if (pud_set_huge(pud, phys_addr + addr, prot))
-                               continue;
-               }
+               if (ioremap_try_huge_pud(pud, addr, next, phys_addr, prot))
+                       continue;
 
-               if (ioremap_pmd_range(pud, addr, next, phys_addr + addr, prot))
+               if (ioremap_pmd_range(pud, addr, next, phys_addr, prot))
                        return -ENOMEM;
-       } while (pud++, addr = next, addr != end);
+       } while (pud++, phys_addr += (next - addr), addr = next, addr != end);
        return 0;
 }
 
+static int ioremap_try_huge_p4d(p4d_t *p4d, unsigned long addr,
+                               unsigned long end, phys_addr_t phys_addr,
+                               pgprot_t prot)
+{
+       if (!ioremap_p4d_enabled())
+               return 0;
+
+       if ((end - addr) != P4D_SIZE)
+               return 0;
+
+       if (!IS_ALIGNED(phys_addr, P4D_SIZE))
+               return 0;
+
+       if (p4d_present(*p4d) && !p4d_free_pud_page(p4d, addr))
+               return 0;
+
+       return p4d_set_huge(p4d, phys_addr, prot);
+}
+
 static inline int ioremap_p4d_range(pgd_t *pgd, unsigned long addr,
                unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
 {
        p4d_t *p4d;
        unsigned long next;
 
-       phys_addr -= addr;
        p4d = p4d_alloc(&init_mm, pgd, addr);
        if (!p4d)
                return -ENOMEM;
        do {
                next = p4d_addr_end(addr, end);
 
-               if (ioremap_p4d_enabled() &&
-                   ((next - addr) == P4D_SIZE) &&
-                   IS_ALIGNED(phys_addr + addr, P4D_SIZE)) {
-                       if (p4d_set_huge(p4d, phys_addr + addr, prot))
-                               continue;
-               }
+               if (ioremap_try_huge_p4d(p4d, addr, next, phys_addr, prot))
+                       continue;
 
-               if (ioremap_pud_range(p4d, addr, next, phys_addr + addr, prot))
+               if (ioremap_pud_range(p4d, addr, next, phys_addr, prot))
                        return -ENOMEM;
-       } while (p4d++, addr = next, addr != end);
+       } while (p4d++, phys_addr += (next - addr), addr = next, addr != end);
        return 0;
 }
 
@@ -168,14 +208,13 @@ int ioremap_page_range(unsigned long addr,
        BUG_ON(addr >= end);
 
        start = addr;
-       phys_addr -= addr;
        pgd = pgd_offset_k(addr);
        do {
                next = pgd_addr_end(addr, end);
-               err = ioremap_p4d_range(pgd, addr, next, phys_addr+addr, prot);
+               err = ioremap_p4d_range(pgd, addr, next, phys_addr, prot);
                if (err)
                        break;
-       } while (pgd++, addr = next, addr != end);
+       } while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
 
        flush_cache_vmap(start, end);