/*
         * Processor specific routine to flush tlbs.
         */
-       void            (*flush_tlb)(unsigned long inval_selector);
+       void            (*flush_tlb)(unsigned int action);
 
 };
 
 
 extern const char *powerpc_base_platform;
 
+/* TLB flush actions. Used as argument to cpu_spec.flush_tlb() hook */
+enum {
+       TLB_INVAL_SCOPE_GLOBAL = 0,     /* invalidate all TLBs */
+       TLB_INVAL_SCOPE_LPID = 1,       /* invalidate TLBs for current LPID */
+};
+
 #endif /* __ASSEMBLY__ */
 
 /* CPU kernel features */
 
 #define TLBIEL_INVAL_SET_SHIFT 12
 
 #define POWER7_TLB_SETS                128     /* # sets in POWER7 TLB */
+#define POWER8_TLB_SETS                512     /* # sets in POWER8 TLB */
 
 #ifndef __ASSEMBLY__
 
 
 /*
  * Clear the TLB using the specified IS form of tlbiel instruction
  * (invalidate by congruence class). P7 has 128 CCs., P8 has 512.
- *
- * r3 = IS field
  */
 __init_tlb_power7:
-       li      r3,0xc00        /* IS field = 0b11 */
-_GLOBAL(__flush_tlb_power7)
        li      r6,128
        mtctr   r6
-       mr      r7,r3           /* IS field */
+       li      r7,0xc00        /* IS field = 0b11 */
        ptesync
 2:     tlbiel  r7
        addi    r7,r7,0x1000
 1:     blr
 
 __init_tlb_power8:
-       li      r3,0xc00        /* IS field = 0b11 */
-_GLOBAL(__flush_tlb_power8)
        li      r6,512
        mtctr   r6
-       mr      r7,r3           /* IS field */
+       li      r7,0xc00        /* IS field = 0b11 */
        ptesync
 2:     tlbiel  r7
        addi    r7,r7,0x1000
 
 extern void __setup_cpu_power8(unsigned long offset, struct cpu_spec* spec);
 extern void __restore_cpu_power8(void);
 extern void __restore_cpu_a2(void);
-extern void __flush_tlb_power7(unsigned long inval_selector);
-extern void __flush_tlb_power8(unsigned long inval_selector);
+extern void __flush_tlb_power7(unsigned int action);
+extern void __flush_tlb_power8(unsigned int action);
 extern long __machine_check_early_realmode_p7(struct pt_regs *regs);
 extern long __machine_check_early_realmode_p8(struct pt_regs *regs);
 #endif /* CONFIG_PPC64 */
 
 #include <asm/mce.h>
 #include <asm/machdep.h>
 
+static void flush_tlb_206(unsigned int num_sets, unsigned int action)
+{
+       unsigned long rb;
+       unsigned int i;
+
+       switch (action) {
+       case TLB_INVAL_SCOPE_GLOBAL:
+               rb = TLBIEL_INVAL_SET;
+               break;
+       case TLB_INVAL_SCOPE_LPID:
+               rb = TLBIEL_INVAL_SET_LPID;
+               break;
+       default:
+               BUG();
+               break;
+       }
+
+       asm volatile("ptesync" : : : "memory");
+       for (i = 0; i < num_sets; i++) {
+               asm volatile("tlbiel %0" : : "r" (rb));
+               rb += 1 << TLBIEL_INVAL_SET_SHIFT;
+       }
+       asm volatile("ptesync" : : : "memory");
+}
+
+/*
+ * Generic routine to flush TLB on power7. This routine is used as
+ * flush_tlb hook in cpu_spec for Power7 processor.
+ *
+ * action => TLB_INVAL_SCOPE_GLOBAL:  Invalidate all TLBs.
+ *          TLB_INVAL_SCOPE_LPID: Invalidate TLB for current LPID.
+ */
+void __flush_tlb_power7(unsigned int action)
+{
+       flush_tlb_206(POWER7_TLB_SETS, action);
+}
+
+/*
+ * Generic routine to flush TLB on power8. This routine is used as
+ * flush_tlb hook in cpu_spec for power8 processor.
+ *
+ * action => TLB_INVAL_SCOPE_GLOBAL:  Invalidate all TLBs.
+ *          TLB_INVAL_SCOPE_LPID: Invalidate TLB for current LPID.
+ */
+void __flush_tlb_power8(unsigned int action)
+{
+       flush_tlb_206(POWER8_TLB_SETS, action);
+}
+
 /* flush SLBs and reload */
 static void flush_and_reload_slb(void)
 {
        }
        if (dsisr & P7_DSISR_MC_TLB_MULTIHIT_MFTLB) {
                if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
-                       cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET);
+                       cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_GLOBAL);
                /* reset error bits */
                dsisr &= ~P7_DSISR_MC_TLB_MULTIHIT_MFTLB;
        }
                break;
        case P7_SRR1_MC_IFETCH_TLB_MULTIHIT:
                if (cur_cpu_spec && cur_cpu_spec->flush_tlb) {
-                       cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET);
+                       cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_GLOBAL);
                        handled = 1;
                }
                break;
 
                }
                if (dsisr & DSISR_MC_TLB_MULTI) {
                        if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
-                               cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET_LPID);
+                               cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_LPID);
                        dsisr &= ~DSISR_MC_TLB_MULTI;
                }
                /* Any other errors we don't understand? */
                break;
        case SRR1_MC_IFETCH_TLBMULTI:
                if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
-                       cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET_LPID);
+                       cur_cpu_spec->flush_tlb(TLB_INVAL_SCOPE_LPID);
                break;
        default:
                handled = 0;