s390: convert to generic entry
[linux-2.6-microblaze.git] / arch / s390 / kernel / irq.c
index f8a8b94..c6d40bc 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/init.h>
 #include <linux/cpu.h>
 #include <linux/irq.h>
+#include <linux/entry-common.h>
 #include <asm/irq_regs.h>
 #include <asm/cputime.h>
 #include <asm/lowcore.h>
@@ -95,19 +96,97 @@ static const struct irq_class irqclass_sub_desc[] = {
        {.irq = CPU_RST,    .name = "RST", .desc = "[CPU] CPU Restart"},
 };
 
-void do_IRQ(struct pt_regs *regs, int irq)
+static void do_IRQ(struct pt_regs *regs, int irq)
 {
-       struct pt_regs *old_regs;
-
-       old_regs = set_irq_regs(regs);
-       irq_enter();
        if (tod_after_eq(S390_lowcore.int_clock,
                         S390_lowcore.clock_comparator))
                /* Serve timer interrupts first. */
                clock_comparator_work();
        generic_handle_irq(irq);
+}
+
+static int on_async_stack(void)
+{
+       unsigned long frame = current_frame_address();
+
+       return !!!((S390_lowcore.async_stack - frame) >> (PAGE_SHIFT + THREAD_SIZE_ORDER));
+}
+
+static void do_irq_async(struct pt_regs *regs, int irq)
+{
+       if (on_async_stack())
+               do_IRQ(regs, irq);
+       else
+               CALL_ON_STACK(do_IRQ, S390_lowcore.async_stack, 2, regs, irq);
+}
+
+static int irq_pending(struct pt_regs *regs)
+{
+       int cc;
+
+       asm volatile("tpi 0\n"
+                    "ipm %0" : "=d" (cc) : : "cc");
+       return cc >> 28;
+}
+
+void noinstr do_io_irq(struct pt_regs *regs)
+{
+       irqentry_state_t state = irqentry_enter(regs);
+       struct pt_regs *old_regs = set_irq_regs(regs);
+       int from_idle;
+
+       irq_enter();
+
+       if (user_mode(regs))
+               update_timer_sys();
+
+       from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit;
+       if (from_idle)
+               account_idle_time_irq();
+
+       do {
+               memcpy(&regs->int_code, &S390_lowcore.subchannel_id, 12);
+               if (S390_lowcore.io_int_word & BIT(31))
+                       do_irq_async(regs, THIN_INTERRUPT);
+               else
+                       do_irq_async(regs, IO_INTERRUPT);
+       } while (MACHINE_IS_LPAR && irq_pending(regs));
+
+       irq_exit();
+       set_irq_regs(old_regs);
+       irqentry_exit(regs, state);
+
+       if (from_idle)
+               regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
+}
+
+void noinstr do_ext_irq(struct pt_regs *regs)
+{
+       irqentry_state_t state = irqentry_enter(regs);
+       struct pt_regs *old_regs = set_irq_regs(regs);
+       int from_idle;
+
+       irq_enter();
+
+       if (user_mode(regs))
+               update_timer_sys();
+
+       memcpy(&regs->int_code, &S390_lowcore.ext_cpu_addr, 4);
+       regs->int_parm = S390_lowcore.ext_params;
+       regs->int_parm_long = *(unsigned long *)S390_lowcore.ext_params2;
+
+       from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit;
+       if (from_idle)
+               account_idle_time_irq();
+
+       do_irq_async(regs, EXT_INTERRUPT);
+
        irq_exit();
        set_irq_regs(old_regs);
+       irqentry_exit(regs, state);
+
+       if (from_idle)
+               regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
 }
 
 static void show_msi_interrupt(struct seq_file *p, int irq)