]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - arch/powerpc/kernel/irq.c
Merge branch 'linus' into tracing/mmiotrace
[linux-2.6.git] / arch / powerpc / kernel / irq.c
index a3351561d2834ebc320278302e35d4339ab71cec..dcc946e670991c7f972141ee162b814789fd4435 100644 (file)
@@ -52,6 +52,7 @@
 #include <linux/mutex.h>
 #include <linux/bootmem.h>
 #include <linux/pci.h>
+#include <linux/debugfs.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -97,7 +98,7 @@ EXPORT_SYMBOL(irq_desc);
 
 int distribute_irqs = 1;
 
-static inline unsigned long get_hard_enabled(void)
+static inline notrace unsigned long get_hard_enabled(void)
 {
        unsigned long enabled;
 
@@ -107,13 +108,13 @@ static inline unsigned long get_hard_enabled(void)
        return enabled;
 }
 
-static inline void set_soft_enabled(unsigned long enable)
+static inline notrace 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)
+notrace void raw_local_irq_restore(unsigned long en)
 {
        /*
         * get_paca()->soft_enabled = en;
@@ -142,7 +143,6 @@ void local_irq_restore(unsigned long en)
                 */
                if (local_paca->lppaca_ptr->int_dword.any_int)
                        iseries_handle_interrupts();
-               return;
        }
 
        /*
@@ -174,6 +174,7 @@ void local_irq_restore(unsigned long en)
 
        __hard_irq_enable();
 }
+EXPORT_SYMBOL(raw_local_irq_restore);
 #endif /* CONFIG_PPC64 */
 
 int show_interrupts(struct seq_file *p, void *v)
@@ -272,7 +273,7 @@ void do_IRQ(struct pt_regs *regs)
        struct thread_info *curtp, *irqtp;
 #endif
 
-        irq_enter();
+       irq_enter();
 
 #ifdef CONFIG_DEBUG_STACKOVERFLOW
        /* Debugging check for stack overflow: is there less than 2KB free? */
@@ -306,12 +307,29 @@ void do_IRQ(struct pt_regs *regs)
                if (curtp != irqtp) {
                        struct irq_desc *desc = irq_desc + irq;
                        void *handler = desc->handle_irq;
+                       unsigned long saved_sp_limit = current->thread.ksp_limit;
                        if (handler == NULL)
                                handler = &__do_IRQ;
                        irqtp->task = curtp->task;
                        irqtp->flags = 0;
+
+                       /* Copy the softirq bits in preempt_count so that the
+                        * softirq checks work in the hardirq context.
+                        */
+                       irqtp->preempt_count =
+                               (irqtp->preempt_count & ~SOFTIRQ_MASK) |
+                               (curtp->preempt_count & SOFTIRQ_MASK);
+
+                       current->thread.ksp_limit = (unsigned long)irqtp +
+                               _ALIGN_UP(sizeof(struct thread_info), 16);
                        call_handle_irq(irq, desc, irqtp, handler);
+                       current->thread.ksp_limit = saved_sp_limit;
                        irqtp->task = NULL;
+
+
+                       /* Set any flag that may have been set on the
+                        * alternate stack
+                        */
                        if (irqtp->flags)
                                set_bits(irqtp->flags, &curtp->flags);
                } else
@@ -321,7 +339,7 @@ void do_IRQ(struct pt_regs *regs)
                /* That's not SMP safe ... but who cares ? */
                ppc_spurious_interrupts++;
 
-        irq_exit();
+       irq_exit();
        set_irq_regs(old_regs);
 
 #ifdef CONFIG_PPC_ISERIES
@@ -336,10 +354,9 @@ void do_IRQ(struct pt_regs *regs)
 
 void __init init_IRQ(void)
 {
-       ppc_md.init_IRQ();
-#ifdef CONFIG_PPC64
+       if (ppc_md.init_IRQ)
+               ppc_md.init_IRQ();
        irq_ctx_init();
-#endif
 }
 
 
@@ -356,7 +373,7 @@ void irq_ctx_init(void)
                memset((void *)softirq_ctx[i], 0, THREAD_SIZE);
                tp = softirq_ctx[i];
                tp->cpu = i;
-               tp->preempt_count = SOFTIRQ_OFFSET;
+               tp->preempt_count = 0;
 
                memset((void *)hardirq_ctx[i], 0, THREAD_SIZE);
                tp = hardirq_ctx[i];
@@ -368,11 +385,15 @@ void irq_ctx_init(void)
 static inline void do_softirq_onstack(void)
 {
        struct thread_info *curtp, *irqtp;
+       unsigned long saved_sp_limit = current->thread.ksp_limit;
 
        curtp = current_thread_info();
        irqtp = softirq_ctx[smp_processor_id()];
        irqtp->task = curtp->task;
+       current->thread.ksp_limit = (unsigned long)irqtp +
+                                   _ALIGN_UP(sizeof(struct thread_info), 16);
        call_do_softirq(irqtp);
+       current->thread.ksp_limit = saved_sp_limit;
        irqtp->task = NULL;
 }
 
@@ -394,7 +415,6 @@ void do_softirq(void)
 
        local_irq_restore(flags);
 }
-EXPORT_SYMBOL(do_softirq);
 
 
 /*
@@ -417,7 +437,13 @@ irq_hw_number_t virq_to_hw(unsigned int virq)
 }
 EXPORT_SYMBOL_GPL(virq_to_hw);
 
-struct irq_host *irq_alloc_host(unsigned int revmap_type,
+static int default_irq_host_match(struct irq_host *h, struct device_node *np)
+{
+       return h->of_node != NULL && h->of_node == np;
+}
+
+struct irq_host *irq_alloc_host(struct device_node *of_node,
+                               unsigned int revmap_type,
                                unsigned int revmap_arg,
                                struct irq_host_ops *ops,
                                irq_hw_number_t inval_irq)
@@ -431,13 +457,7 @@ struct irq_host *irq_alloc_host(unsigned int revmap_type,
        /* Allocate structure and revmap table if using linear mapping */
        if (revmap_type == IRQ_HOST_MAP_LINEAR)
                size += revmap_arg * sizeof(unsigned int);
-       if (mem_init_done)
-               host = kzalloc(size, GFP_KERNEL);
-       else {
-               host = alloc_bootmem(size);
-               if (host)
-                       memset(host, 0, size);
-       }
+       host = zalloc_maybe_bootmem(size, GFP_KERNEL);
        if (host == NULL)
                return NULL;
 
@@ -445,6 +465,10 @@ struct irq_host *irq_alloc_host(unsigned int revmap_type,
        host->revmap_type = revmap_type;
        host->inval_irq = inval_irq;
        host->ops = ops;
+       host->of_node = of_node;
+
+       if (host->ops->match == NULL)
+               host->ops->match = default_irq_host_match;
 
        spin_lock_irqsave(&irq_big_lock, flags);
 
@@ -476,7 +500,7 @@ struct irq_host *irq_alloc_host(unsigned int revmap_type,
                host->inval_irq = 0;
                /* setup us as the host for all legacy interrupts */
                for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
-                       irq_map[i].hwirq = 0;
+                       irq_map[i].hwirq = i;
                        smp_wmb();
                        irq_map[i].host = host;
                        smp_wmb();
@@ -486,7 +510,7 @@ struct irq_host *irq_alloc_host(unsigned int revmap_type,
 
                        /* Legacy flags are left to default at this point,
                         * one can then use irq_create_mapping() to
-                        * explicitely change them
+                        * explicitly change them
                         */
                        ops->map(host, i, i);
                }
@@ -520,7 +544,7 @@ struct irq_host *irq_find_host(struct device_node *node)
         */
        spin_lock_irqsave(&irq_big_lock, flags);
        list_for_each_entry(h, &irq_hosts, link)
-               if (h->ops->match == NULL || h->ops->match(h, node)) {
+               if (h->ops->match(h, node)) {
                        found = h;
                        break;
                }
@@ -995,6 +1019,68 @@ static int irq_late_init(void)
 }
 arch_initcall(irq_late_init);
 
+#ifdef CONFIG_VIRQ_DEBUG
+static int virq_debug_show(struct seq_file *m, void *private)
+{
+       unsigned long flags;
+       irq_desc_t *desc;
+       const char *p;
+       char none[] = "none";
+       int i;
+
+       seq_printf(m, "%-5s  %-7s  %-15s  %s\n", "virq", "hwirq",
+                     "chip name", "host name");
+
+       for (i = 1; i < NR_IRQS; i++) {
+               desc = get_irq_desc(i);
+               spin_lock_irqsave(&desc->lock, flags);
+
+               if (desc->action && desc->action->handler) {
+                       seq_printf(m, "%5d  ", i);
+                       seq_printf(m, "0x%05lx  ", virq_to_hw(i));
+
+                       if (desc->chip && desc->chip->typename)
+                               p = desc->chip->typename;
+                       else
+                               p = none;
+                       seq_printf(m, "%-15s  ", p);
+
+                       if (irq_map[i].host && irq_map[i].host->of_node)
+                               p = irq_map[i].host->of_node->full_name;
+                       else
+                               p = none;
+                       seq_printf(m, "%s\n", p);
+               }
+
+               spin_unlock_irqrestore(&desc->lock, flags);
+       }
+
+       return 0;
+}
+
+static int virq_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, virq_debug_show, inode->i_private);
+}
+
+static const struct file_operations virq_debug_fops = {
+       .open = virq_debug_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+static int __init irq_debugfs_init(void)
+{
+       if (debugfs_create_file("virq_mapping", S_IRUGO, powerpc_debugfs_root,
+                                NULL, &virq_debug_fops) == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+__initcall(irq_debugfs_init);
+#endif /* CONFIG_VIRQ_DEBUG */
+
 #endif /* CONFIG_PPC_MERGE */
 
 #ifdef CONFIG_PPC64