]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/powerpc/mm/hash_native_64.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6-omap-h63xx.git] / arch / powerpc / mm / hash_native_64.c
index 79aedaf36f2bd3cc91a893ce862bf8c00056a488..6ba9b47e55afbe746734761c672761dcea07e9f0 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/tlb.h>
 #include <asm/cputable.h>
 #include <asm/udbg.h>
+#include <asm/kexec.h>
 
 #ifdef DEBUG_LOW
 #define DBG_LOW(fmt...) udbg_printf(fmt)
@@ -103,7 +104,7 @@ static inline void tlbie(unsigned long va, int psize, int local)
                spin_unlock(&native_tlbie_lock);
 }
 
-static inline void native_lock_hpte(hpte_t *hptep)
+static inline void native_lock_hpte(struct hash_pte *hptep)
 {
        unsigned long *word = &hptep->v;
 
@@ -115,7 +116,7 @@ static inline void native_lock_hpte(hpte_t *hptep)
        }
 }
 
-static inline void native_unlock_hpte(hpte_t *hptep)
+static inline void native_unlock_hpte(struct hash_pte *hptep)
 {
        unsigned long *word = &hptep->v;
 
@@ -127,7 +128,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va,
                        unsigned long pa, unsigned long rflags,
                        unsigned long vflags, int psize)
 {
-       hpte_t *hptep = htab_address + hpte_group;
+       struct hash_pte *hptep = htab_address + hpte_group;
        unsigned long hpte_v, hpte_r;
        int i;
 
@@ -162,7 +163,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va,
 
        hptep->r = hpte_r;
        /* Guarantee the second dword is visible before the valid bit */
-       __asm__ __volatile__ ("eieio" : : : "memory");
+       eieio();
        /*
         * Now set the first dword including the valid bit
         * NOTE: this also unlocks the hpte
@@ -176,7 +177,7 @@ static long native_hpte_insert(unsigned long hpte_group, unsigned long va,
 
 static long native_hpte_remove(unsigned long hpte_group)
 {
-       hpte_t *hptep;
+       struct hash_pte *hptep;
        int i;
        int slot_offset;
        unsigned long hpte_v;
@@ -216,7 +217,7 @@ static long native_hpte_remove(unsigned long hpte_group)
 static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
                                 unsigned long va, int psize, int local)
 {
-       hpte_t *hptep = htab_address + slot;
+       struct hash_pte *hptep = htab_address + slot;
        unsigned long hpte_v, want_v;
        int ret = 0;
 
@@ -232,15 +233,14 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
        /* Even if we miss, we need to invalidate the TLB */
        if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) {
                DBG_LOW(" -> miss\n");
-               native_unlock_hpte(hptep);
                ret = -1;
        } else {
                DBG_LOW(" -> hit\n");
                /* Update the HPTE */
                hptep->r = (hptep->r & ~(HPTE_R_PP | HPTE_R_N)) |
                        (newpp & (HPTE_R_PP | HPTE_R_N | HPTE_R_C));
-               native_unlock_hpte(hptep);
        }
+       native_unlock_hpte(hptep);
 
        /* Ensure it is out of the tlb too. */
        tlbie(va, psize, local);
@@ -250,7 +250,7 @@ static long native_hpte_updatepp(unsigned long slot, unsigned long newpp,
 
 static long native_hpte_find(unsigned long va, int psize)
 {
-       hpte_t *hptep;
+       struct hash_pte *hptep;
        unsigned long hash;
        unsigned long i, j;
        long slot;
@@ -293,7 +293,7 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
 {
        unsigned long vsid, va;
        long slot;
-       hpte_t *hptep;
+       struct hash_pte *hptep;
 
        vsid = get_kernel_vsid(ea);
        va = (vsid << 28) | (ea & 0x0fffffff);
@@ -314,7 +314,7 @@ static void native_hpte_updateboltedpp(unsigned long newpp, unsigned long ea,
 static void native_hpte_invalidate(unsigned long slot, unsigned long va,
                                   int psize, int local)
 {
-       hpte_t *hptep = htab_address + slot;
+       struct hash_pte *hptep = htab_address + slot;
        unsigned long hpte_v;
        unsigned long want_v;
        unsigned long flags;
@@ -340,31 +340,67 @@ static void native_hpte_invalidate(unsigned long slot, unsigned long va,
        local_irq_restore(flags);
 }
 
-/*
- * XXX This need fixing based on page size. It's only used by
- * native_hpte_clear() for now which needs fixing too so they
- * make a good pair...
- */
-static unsigned long slot2va(unsigned long hpte_v, unsigned long slot)
+#define LP_SHIFT       12
+#define LP_BITS                8
+#define LP_MASK(i)     ((0xFF >> (i)) << LP_SHIFT)
+
+static void hpte_decode(struct hash_pte *hpte, unsigned long slot,
+                       int *psize, unsigned long *va)
 {
-       unsigned long avpn = HPTE_V_AVPN_VAL(hpte_v);
-       unsigned long va;
+       unsigned long hpte_r = hpte->r;
+       unsigned long hpte_v = hpte->v;
+       unsigned long avpn;
+       int i, size, shift, penc;
+
+       if (!(hpte_v & HPTE_V_LARGE))
+               size = MMU_PAGE_4K;
+       else {
+               for (i = 0; i < LP_BITS; i++) {
+                       if ((hpte_r & LP_MASK(i+1)) == LP_MASK(i+1))
+                               break;
+               }
+               penc = LP_MASK(i+1) >> LP_SHIFT;
+               for (size = 0; size < MMU_PAGE_COUNT; size++) {
+
+                       /* 4K pages are not represented by LP */
+                       if (size == MMU_PAGE_4K)
+                               continue;
+
+                       /* valid entries have a shift value */
+                       if (!mmu_psize_defs[size].shift)
+                               continue;
+
+                       if (penc == mmu_psize_defs[size].penc)
+                               break;
+               }
+       }
 
-       va = avpn << 23;
+       /* This works for all page sizes, and for 256M and 1T segments */
+       shift = mmu_psize_defs[size].shift;
+       avpn = (HPTE_V_AVPN_VAL(hpte_v) & ~mmu_psize_defs[size].avpnm) << 23;
 
-       if (! (hpte_v & HPTE_V_LARGE)) {
-               unsigned long vpi, pteg;
+       if (shift < 23) {
+               unsigned long vpi, vsid, pteg;
 
                pteg = slot / HPTES_PER_GROUP;
                if (hpte_v & HPTE_V_SECONDARY)
                        pteg = ~pteg;
-
-               vpi = ((va >> 28) ^ pteg) & htab_hash_mask;
-
-               va |= vpi << PAGE_SHIFT;
+               switch (hpte_v >> HPTE_V_SSIZE_SHIFT) {
+               case MMU_SEGSIZE_256M:
+                       vpi = ((avpn >> 28) ^ pteg) & htab_hash_mask;
+                       break;
+               case MMU_SEGSIZE_1T:
+                       vsid = avpn >> 40;
+                       vpi = (vsid ^ (vsid << 25) ^ pteg) & htab_hash_mask;
+                       break;
+               default:
+                       avpn = vpi = size = 0;
+               }
+               avpn |= (vpi << mmu_psize_defs[size].shift);
        }
 
-       return va;
+       *va = avpn;
+       *psize = size;
 }
 
 /*
@@ -374,15 +410,14 @@ static unsigned long slot2va(unsigned long hpte_v, unsigned long slot)
  *
  * TODO: add batching support when enabled.  remember, no dynamic memory here,
  * athough there is the control page available...
- *
- * XXX FIXME: 4k only for now !
  */
 static void native_hpte_clear(void)
 {
        unsigned long slot, slots, flags;
-       hpte_t *hptep = htab_address;
-       unsigned long hpte_v;
+       struct hash_pte *hptep = htab_address;
+       unsigned long hpte_v, va;
        unsigned long pteg_count;
+       int psize;
 
        pteg_count = htab_hash_mask + 1;
 
@@ -408,8 +443,9 @@ static void native_hpte_clear(void)
                 * already hold the native_tlbie_lock.
                 */
                if (hpte_v & HPTE_V_VALID) {
+                       hpte_decode(hptep, slot, &psize, &va);
                        hptep->v = 0;
-                       __tlbie(slot2va(hpte_v, slot), MMU_PAGE_4K);
+                       __tlbie(va, psize);
                }
        }
 
@@ -425,7 +461,7 @@ static void native_hpte_clear(void)
 static void native_flush_hash_range(unsigned long number, int local)
 {
        unsigned long va, hash, index, hidx, shift, slot;
-       hpte_t *hptep;
+       struct hash_pte *hptep;
        unsigned long hpte_v;
        unsigned long want_v;
        unsigned long flags;