#include <linux/cpu.h>
 #include <linux/msi.h>
 #include <linux/of.h>
+#include <linux/percpu.h>
 
 #include <asm/firmware.h>
 #include <asm/io.h>
  */
 #define IPI_PRIORITY           4
 
+/* The least favored priority */
+#define LOWEST_PRIORITY                0xFF
+
+/* The number of priorities defined above */
+#define MAX_NUM_PRIORITIES     3
+
 static unsigned int default_server = 0xFF;
 static unsigned int default_distrib_server = 0;
 static unsigned int interrupt_server_size = 8;
 static int ibm_int_on;
 static int ibm_int_off;
 
+struct xics_cppr {
+       unsigned char stack[MAX_NUM_PRIORITIES];
+       int index;
+};
+
+static DEFINE_PER_CPU(struct xics_cppr, xics_cppr);
 
 /* Direct hardware low level accessors */
 
        return xirr & 0x00ffffff;
 }
 
+static void push_cppr(unsigned int vec)
+{
+       struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
+
+       if (WARN_ON(os_cppr->index >= MAX_NUM_PRIORITIES - 1))
+               return;
+
+       if (vec == XICS_IPI)
+               os_cppr->stack[++os_cppr->index] = IPI_PRIORITY;
+       else
+               os_cppr->stack[++os_cppr->index] = DEFAULT_PRIORITY;
+}
+
 static unsigned int xics_get_irq_direct(void)
 {
        unsigned int xirr = direct_xirr_info_get();
                return NO_IRQ;
 
        irq = irq_radix_revmap_lookup(xics_host, vec);
-       if (likely(irq != NO_IRQ))
+       if (likely(irq != NO_IRQ)) {
+               push_cppr(vec);
                return irq;
+       }
 
        /* We don't have a linux mapping, so have rtas mask it. */
        xics_mask_unknown_vec(vec);
                return NO_IRQ;
 
        irq = irq_radix_revmap_lookup(xics_host, vec);
-       if (likely(irq != NO_IRQ))
+       if (likely(irq != NO_IRQ)) {
+               push_cppr(vec);
                return irq;
+       }
 
        /* We don't have a linux mapping, so have RTAS mask it. */
        xics_mask_unknown_vec(vec);
        return NO_IRQ;
 }
 
+static unsigned char pop_cppr(void)
+{
+       struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
+
+       if (WARN_ON(os_cppr->index < 1))
+               return LOWEST_PRIORITY;
+
+       return os_cppr->stack[--os_cppr->index];
+}
+
 static void xics_eoi_direct(unsigned int virq)
 {
        unsigned int irq = (unsigned int)irq_map[virq].hwirq;
 
        iosync();
-       direct_xirr_info_set((0xff << 24) | irq);
+       direct_xirr_info_set((pop_cppr() << 24) | irq);
 }
 
 static void xics_eoi_lpar(unsigned int virq)
        unsigned int irq = (unsigned int)irq_map[virq].hwirq;
 
        iosync();
-       lpar_xirr_info_set((0xff << 24) | irq);
+       lpar_xirr_info_set((pop_cppr() << 24) | irq);
 }
 
 static int xics_set_affinity(unsigned int virq, const struct cpumask *cpumask)
 
 static void xics_set_cpu_priority(unsigned char cppr)
 {
+       struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr);
+
+       BUG_ON(os_cppr->index != 0);
+
+       os_cppr->stack[os_cppr->index] = cppr;
+
        if (firmware_has_feature(FW_FEATURE_LPAR))
                lpar_cppr_info(cppr);
        else
 
 void xics_setup_cpu(void)
 {
-       xics_set_cpu_priority(0xff);
+       xics_set_cpu_priority(LOWEST_PRIORITY);
 
        xics_set_cpu_giq(default_distrib_server, 1);
 }