[POWERPC] bootwrapper: Modify *pp, not *p, in ft_shuffle().
[powerpc.git] / arch / powerpc / kernel / irq.c
index 5e37bf1..1009308 100644 (file)
@@ -64,8 +64,9 @@
 #include <asm/ptrace.h>
 #include <asm/machdep.h>
 #include <asm/udbg.h>
-#ifdef CONFIG_PPC_ISERIES
+#ifdef CONFIG_PPC64
 #include <asm/paca.h>
+#include <asm/firmware.h>
 #endif
 
 int __irq_offset_value;
@@ -95,6 +96,74 @@ extern atomic_t ipi_sent;
 EXPORT_SYMBOL(irq_desc);
 
 int distribute_irqs = 1;
+
+static inline unsigned long get_hard_enabled(void)
+{
+       unsigned long enabled;
+
+       __asm__ __volatile__("lbz %0,%1(13)"
+       : "=r" (enabled) : "i" (offsetof(struct paca_struct, hard_enabled)));
+
+       return enabled;
+}
+
+static inline void set_soft_enabled(unsigned long enable)
+{
+       __asm__ __volatile__("stb %0,%1(13)"
+       : : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled)));
+}
+
+void local_irq_restore(unsigned long en)
+{
+       /*
+        * get_paca()->soft_enabled = en;
+        * Is it ever valid to use local_irq_restore(0) when soft_enabled is 1?
+        * That was allowed before, and in such a case we do need to take care
+        * that gcc will set soft_enabled directly via r13, not choose to use
+        * an intermediate register, lest we're preempted to a different cpu.
+        */
+       set_soft_enabled(en);
+       if (!en)
+               return;
+
+       if (firmware_has_feature(FW_FEATURE_ISERIES)) {
+               /*
+                * Do we need to disable preemption here?  Not really: in the
+                * unlikely event that we're preempted to a different cpu in
+                * between getting r13, loading its lppaca_ptr, and loading
+                * its any_int, we might call iseries_handle_interrupts without
+                * an interrupt pending on the new cpu, but that's no disaster,
+                * is it?  And the business of preempting us off the old cpu
+                * would itself involve a local_irq_restore which handles the
+                * interrupt to that cpu.
+                *
+                * But use "local_paca->lppaca_ptr" instead of "get_lppaca()"
+                * to avoid any preemption checking added into get_paca().
+                */
+               if (local_paca->lppaca_ptr->int_dword.any_int)
+                       iseries_handle_interrupts();
+               return;
+       }
+
+       /*
+        * if (get_paca()->hard_enabled) return;
+        * But again we need to take care that gcc gets hard_enabled directly
+        * via r13, not choose to use an intermediate register, lest we're
+        * preempted to a different cpu in between the two instructions.
+        */
+       if (get_hard_enabled())
+               return;
+
+       /*
+        * Need to hard-enable interrupts here.  Since currently disabled,
+        * no need to take further asm precautions against preemption; but
+        * use local_paca instead of get_paca() to avoid preemption checking.
+        */
+       local_paca->hard_enabled = en;
+       if ((int)mfspr(SPRN_DEC) < 0)
+               mtspr(SPRN_DEC, 1);
+       hard_irq_enable();
+}
 #endif /* CONFIG_PPC64 */
 
 int show_interrupts(struct seq_file *p, void *v)
@@ -212,10 +281,10 @@ void do_IRQ(struct pt_regs *regs)
 
        /*
         * Every platform is required to implement ppc_md.get_irq.
-        * This function will either return an irq number or -1 to
+        * This function will either return an irq number or NO_IRQ to
         * indicate there are no more pending.
-        * The value -2 is for buggy hardware and means that this IRQ
-        * has already been handled. -- Tom
+        * The value NO_IRQ_IGNORE is for buggy hardware and means that this
+        * IRQ has already been handled. -- Tom
         */
        irq = ppc_md.get_irq();
 
@@ -246,7 +315,8 @@ void do_IRQ(struct pt_regs *regs)
        set_irq_regs(old_regs);
 
 #ifdef CONFIG_PPC_ISERIES
-       if (get_lppaca()->int_dword.fields.decr_int) {
+       if (firmware_has_feature(FW_FEATURE_ISERIES) &&
+                       get_lppaca()->int_dword.fields.decr_int) {
                get_lppaca()->int_dword.fields.decr_int = 0;
                /* Signal a fake decrementer interrupt */
                timer_interrupt(regs);
@@ -534,6 +604,8 @@ unsigned int irq_create_mapping(struct irq_host *host,
         */
        virq = irq_find_mapping(host, hwirq);
        if (virq != IRQ_NONE) {
+               if (host->ops->remap)
+                       host->ops->remap(host, virq, hwirq);
                pr_debug("irq: -> existing mapping on virq %d\n", virq);
                return virq;
        }
@@ -626,10 +698,14 @@ EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
 
 void irq_dispose_mapping(unsigned int virq)
 {
-       struct irq_host *host = irq_map[virq].host;
+       struct irq_host *host;
        irq_hw_number_t hwirq;
        unsigned long flags;
 
+       if (virq == NO_IRQ)
+               return;
+
+       host = irq_map[virq].host;
        WARN_ON (host == NULL);
        if (host == NULL)
                return;
@@ -892,7 +968,6 @@ void pci_scan_msi_device(struct pci_dev *dev) {}
 int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) {return -1;}
 void pci_disable_msix(struct pci_dev *dev) {}
 void msi_remove_pci_irq_vectors(struct pci_dev *dev) {}
-void disable_msi_mode(struct pci_dev *dev, int pos, int type) {}
 void pci_no_msi(void) {}
 EXPORT_SYMBOL(pci_enable_msix);
 EXPORT_SYMBOL(pci_disable_msix);