#define PGALLOC_GFP    (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO)
 
+static inline void clean_pte_table(pte_t *pte)
+{
+       clean_dcache_area(pte + PTE_HWTABLE_PTRS, PTE_HWTABLE_SIZE);
+}
+
 /*
  * Allocate one PTE table.
  *
  * into one table thus:
  *
  *  +------------+
- *  |  h/w pt 0  |
- *  +------------+
- *  |  h/w pt 1  |
- *  +------------+
  *  | Linux pt 0 |
  *  +------------+
  *  | Linux pt 1 |
  *  +------------+
+ *  |  h/w pt 0  |
+ *  +------------+
+ *  |  h/w pt 1  |
+ *  +------------+
  */
 static inline pte_t *
 pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
        pte_t *pte;
 
        pte = (pte_t *)__get_free_page(PGALLOC_GFP);
-       if (pte) {
-               clean_dcache_area(pte, sizeof(pte_t) * PTRS_PER_PTE);
-               pte += PTRS_PER_PTE;
-       }
+       if (pte)
+               clean_pte_table(pte);
 
        return pte;
 }
        pte = alloc_pages(PGALLOC_GFP, 0);
 #endif
        if (pte) {
-               if (!PageHighMem(pte)) {
-                       void *page = page_address(pte);
-                       clean_dcache_area(page, sizeof(pte_t) * PTRS_PER_PTE);
-               }
+               if (!PageHighMem(pte))
+                       clean_pte_table(page_address(pte));
                pgtable_page_ctor(pte);
        }
 
  */
 static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
 {
-       if (pte) {
-               pte -= PTRS_PER_PTE;
+       if (pte)
                free_page((unsigned long)pte);
-       }
 }
 
 static inline void pte_free(struct mm_struct *mm, pgtable_t pte)
 static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte,
        unsigned long prot)
 {
-       unsigned long pmdval = pte | prot;
+       unsigned long pmdval = (pte + PTE_HWTABLE_OFF) | prot;
        pmdp[0] = __pmd(pmdval);
        pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));
        flush_pmd_entry(pmdp);
 static inline void
 pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep)
 {
-       unsigned long pte_ptr = (unsigned long)ptep;
-
        /*
-        * The pmd must be loaded with the physical
-        * address of the PTE table
+        * The pmd must be loaded with the physical address of the PTE table
         */
-       pte_ptr -= PTRS_PER_PTE * sizeof(void *);
-       __pmd_populate(pmdp, __pa(pte_ptr), _PAGE_KERNEL_TABLE);
+       __pmd_populate(pmdp, __pa(ptep), _PAGE_KERNEL_TABLE);
 }
 
 static inline void
 
  * Therefore, we tweak the implementation slightly - we tell Linux that we
  * have 2048 entries in the first level, each of which is 8 bytes (iow, two
  * hardware pointers to the second level.)  The second level contains two
- * hardware PTE tables arranged contiguously, followed by Linux versions
+ * hardware PTE tables arranged contiguously, preceded by Linux versions
  * which contain the state information Linux needs.  We, therefore, end up
  * with 512 entries in the "PTE" level.
  *
  *
  *    pgd             pte
  * |        |
- * +--------+ +0
- * |        |-----> +------------+ +0
+ * +--------+
+ * |        |       +------------+ +0
+ * +- - - - +       | Linux pt 0 |
+ * |        |       +------------+ +1024
+ * +--------+ +0    | Linux pt 1 |
+ * |        |-----> +------------+ +2048
  * +- - - - + +4    |  h/w pt 0  |
- * |        |-----> +------------+ +1024
+ * |        |-----> +------------+ +3072
  * +--------+ +8    |  h/w pt 1  |
- * |        |       +------------+ +2048
- * +- - - - +       | Linux pt 0 |
- * |        |       +------------+ +3072
- * +--------+       | Linux pt 1 |
  * |        |       +------------+ +4096
  *
  * See L_PTE_xxx below for definitions of bits in the "Linux pt", and
 #define PTRS_PER_PMD           1
 #define PTRS_PER_PGD           2048
 
+#define PTE_HWTABLE_PTRS       (PTRS_PER_PTE)
+#define PTE_HWTABLE_OFF                (PTE_HWTABLE_PTRS * sizeof(pte_t))
+#define PTE_HWTABLE_SIZE       (PTRS_PER_PTE * sizeof(u32))
+
 /*
  * PMD_SHIFT determines the size of the area a second-level page table can map
  * PGDIR_SHIFT determines what a third-level page table entry can map
 
 static inline pte_t *pmd_page_vaddr(pmd_t pmd)
 {
-       phys_addr_t ptr;
-
-       ptr = pmd_val(pmd) & ~(PTRS_PER_PTE * sizeof(void *) - 1);
-       ptr += PTRS_PER_PTE * sizeof(void *);
-
-       return __va(ptr);
+       return __va(pmd_val(pmd) & PAGE_MASK);
 }
 
 #define pmd_page(pmd)          pfn_to_page(__phys_to_pfn(pmd_val(pmd)))
 #define __pte_map(pmd)         pmd_page_vaddr(*(pmd))
 #define __pte_unmap(pte)       do { } while (0)
 #else
-#define __pte_map(pmd)         ((pte_t *)kmap_atomic(pmd_page(*(pmd))) + PTRS_PER_PTE)
-#define __pte_unmap(pte)       kunmap_atomic((pte - PTRS_PER_PTE))
+#define __pte_map(pmd)         (pte_t *)kmap_atomic(pmd_page(*(pmd)))
+#define __pte_unmap(pte)       kunmap_atomic(pte)
 #endif
 
 #define pte_index(addr)                (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
 
 
                pte = pte_offset_map(pmd, addr);
                printk(", *pte=%08lx", pte_val(*pte));
-               printk(", *ppte=%08lx", pte_val(pte[-PTRS_PER_PTE]));
+               printk(", *ppte=%08lx", pte_val(pte[PTE_HWTABLE_PTRS]));
                pte_unmap(pte);
        } while(0);
 
 
        .endm
 
        .macro  armv6_set_pte_ext pfx
-       str     r1, [r0], #-2048                @ linux version
+       str     r1, [r0], #2048                 @ linux version
 
        bic     r3, r1, #0x000003fc
        bic     r3, r3, #PTE_TYPE_MASK
  *  1111  0xff r/w     r/w
  */
        .macro  armv3_set_pte_ext wc_disable=1
-       str     r1, [r0], #-2048                @ linux version
+       str     r1, [r0], #2048                 @ linux version
 
        eor     r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
 
        bicne   r2, r2, #PTE_BUFFERABLE
 #endif
        .endif
-       str     r2, [r0]                        @ hardware version
+       str     r2, [r0]                @ hardware version
        .endm
 
 
  *  1111  11   r/w     r/w
  */
        .macro  xscale_set_pte_ext_prologue
-       str     r1, [r0], #-2048                @ linux version
+       str     r1, [r0]                        @ linux version
 
        eor     r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY
 
        tst     r3, #L_PTE_PRESENT | L_PTE_YOUNG        @ present and young?
        movne   r2, #0                          @ no -> fault
 
-       str     r2, [r0]                        @ hardware version
+       str     r2, [r0, #2048]!                @ hardware version
        mov     ip, #0
        mcr     p15, 0, r0, c7, c10, 1          @ clean L1 D line
        mcr     p15, 0, ip, c7, c10, 4          @ data write barrier
 
  *     Set a level 2 translation table entry.
  *
  *     - ptep  - pointer to level 2 translation table entry
- *               (hardware version is stored at -1024 bytes)
+ *               (hardware version is stored at +2048 bytes)
  *     - pte   - PTE value to store
  *     - ext   - value for extended PTE bits
  */
 ENTRY(cpu_v7_set_pte_ext)
 #ifdef CONFIG_MMU
- ARM(  str     r1, [r0], #-2048        )       @ linux version
- THUMB(        str     r1, [r0]                )       @ linux version
- THUMB(        sub     r0, r0, #2048           )
+       str     r1, [r0]                        @ linux version
 
        bic     r3, r1, #0x000003f0
        bic     r3, r3, #PTE_TYPE_MASK
        tstne   r1, #L_PTE_PRESENT
        moveq   r3, #0
 
-       str     r3, [r0]
+       str     r3, [r0, #2048]!
        mcr     p15, 0, r0, c7, c10, 1          @ flush_pte
 #endif
        mov     pc, lr