[SPARC64]: Simplify TSB insert checks.
[safe/jmp/linux-2.6] / arch / sparc64 / kernel / irq.c
index c57b170..712b16c 100644 (file)
@@ -138,12 +138,48 @@ out_unlock:
        return 0;
 }
 
+extern unsigned long real_hard_smp_processor_id(void);
+
+static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid)
+{
+       unsigned int tid;
+
+       if (this_is_starfire) {
+               tid = starfire_translate(imap, cpuid);
+               tid <<= IMAP_TID_SHIFT;
+               tid &= IMAP_TID_UPA;
+       } else {
+               if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+                       unsigned long ver;
+
+                       __asm__ ("rdpr %%ver, %0" : "=r" (ver));
+                       if ((ver >> 32UL) == __JALAPENO_ID ||
+                           (ver >> 32UL) == __SERRANO_ID) {
+                               tid = cpuid << IMAP_TID_SHIFT;
+                               tid &= IMAP_TID_JBUS;
+                       } else {
+                               unsigned int a = cpuid & 0x1f;
+                               unsigned int n = (cpuid >> 5) & 0x1f;
+
+                               tid = ((a << IMAP_AID_SHIFT) |
+                                      (n << IMAP_NID_SHIFT));
+                               tid &= (IMAP_AID_SAFARI |
+                                       IMAP_NID_SAFARI);;
+                       }
+               } else {
+                       tid = cpuid << IMAP_TID_SHIFT;
+                       tid &= IMAP_TID_UPA;
+               }
+       }
+
+       return tid;
+}
+
 /* Now these are always passed a true fully specified sun4u INO. */
 void enable_irq(unsigned int irq)
 {
        struct ino_bucket *bucket = __bucket(irq);
-       unsigned long imap;
-       unsigned long tid;
+       unsigned long imap, cpuid;
 
        imap = bucket->imap;
        if (imap == 0UL)
@@ -151,45 +187,25 @@ void enable_irq(unsigned int irq)
 
        preempt_disable();
 
+       /* This gets the physical processor ID, even on uniprocessor,
+        * so we can always program the interrupt target correctly.
+        */
+       cpuid = real_hard_smp_processor_id();
+
        if (tlb_type == hypervisor) {
                unsigned int ino = __irq_ino(irq);
-               int cpu = hard_smp_processor_id();
-
-               sun4v_intr_settarget(ino, cpu);
-               sun4v_intr_setenabled(ino, HV_INTR_ENABLED);
+               int err;
+
+               err = sun4v_intr_settarget(ino, cpuid);
+               if (err != HV_EOK)
+                       printk("sun4v_intr_settarget(%x,%lu): err(%d)\n",
+                              ino, cpuid, err);
+               err = sun4v_intr_setenabled(ino, HV_INTR_ENABLED);
+               if (err != HV_EOK)
+                       printk("sun4v_intr_setenabled(%x): err(%d)\n",
+                              ino, err);
        } else {
-               if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-                       unsigned long ver;
-
-                       __asm__ ("rdpr %%ver, %0" : "=r" (ver));
-                       if ((ver >> 32) == __JALAPENO_ID ||
-                           (ver >> 32) == __SERRANO_ID) {
-                               /* We set it to our JBUS ID. */
-                               __asm__ __volatile__("ldxa [%%g0] %1, %0"
-                                                    : "=r" (tid)
-                                                    : "i" (ASI_JBUS_CONFIG));
-                               tid = ((tid & (0x1fUL<<17)) << 9);
-                               tid &= IMAP_TID_JBUS;
-                       } else {
-                               /* We set it to our Safari AID. */
-                               __asm__ __volatile__("ldxa [%%g0] %1, %0"
-                                                    : "=r" (tid)
-                                                    : "i"(ASI_SAFARI_CONFIG));
-                               tid = ((tid & (0x3ffUL<<17)) << 9);
-                               tid &= IMAP_AID_SAFARI;
-                       }
-               } else if (this_is_starfire == 0) {
-                       /* We set it to our UPA MID. */
-                       __asm__ __volatile__("ldxa [%%g0] %1, %0"
-                                            : "=r" (tid)
-                                            : "i" (ASI_UPA_CONFIG));
-                       tid = ((tid & UPA_CONFIG_MID) << 9);
-                       tid &= IMAP_TID_UPA;
-               } else {
-                       tid = (starfire_translate(imap,
-                                                 smp_processor_id()) << 26);
-                       tid &= IMAP_TID_UPA;
-               }
+               unsigned int tid = sun4u_compute_tid(imap, cpuid);
 
                /* NOTE NOTE NOTE, IGN and INO are read-only, IGN is a product
                 * of this SYSIO's preconfigured IGN in the SYSIO Control
@@ -216,8 +232,12 @@ void disable_irq(unsigned int irq)
        if (imap != 0UL) {
                if (tlb_type == hypervisor) {
                        unsigned int ino = __irq_ino(irq);
+                       int err;
 
-                       sun4v_intr_setenabled(ino, HV_INTR_DISABLED);
+                       err = sun4v_intr_setenabled(ino, HV_INTR_DISABLED);
+                       if (err != HV_EOK)
+                               printk("sun4v_intr_setenabled(%x): "
+                                      "err(%d)\n", ino, err);
                } else {
                        u32 tmp;
 
@@ -322,17 +342,17 @@ unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino, int pil, unsign
 
        sysino = sun4v_devino_to_sysino(devhandle, devino);
 
-       printk(KERN_INFO "sun4v_irq: Mapping (%x:%x) --> sysino[%lx]\n",
-              devhandle, devino, sysino);
-
        bucket = &ivector_table[sysino];
 
        /* Catch accidental accesses to these things.  IMAP/ICLR handling
         * is done by hypervisor calls on sun4v platforms, not by direct
         * register accesses.
+        *
+        * But we need to make them look unique for the disable_irq() logic
+        * in free_irq().
         */
-       bucket->imap = ~0UL;
-       bucket->iclr = ~0UL;
+       bucket->imap = ~0UL - sysino;
+       bucket->iclr = ~0UL - sysino;
 
        bucket->pil = pil;
        bucket->flags = flags;
@@ -535,7 +555,6 @@ void free_irq(unsigned int irq, void *dev_id)
        bucket = __bucket(irq);
        if (bucket != &pil0_dummy_bucket) {
                struct irq_desc *desc = bucket->irq_info;
-               unsigned long imap = bucket->imap;
                int ent, i;
 
                for (i = 0; i < MAX_IRQ_DESC_ACTION; i++) {
@@ -548,6 +567,8 @@ void free_irq(unsigned int irq, void *dev_id)
                }
 
                if (!desc->action_active_mask) {
+                       unsigned long imap = bucket->imap;
+
                        /* This unique interrupt source is now inactive. */
                        bucket->flags &= ~IBF_ACTIVE;
 
@@ -647,14 +668,19 @@ static void process_bucket(int irq, struct ino_bucket *bp, struct pt_regs *regs)
        if (bp->pil != 0) {
                if (tlb_type == hypervisor) {
                        unsigned int ino = __irq_ino(bp);
+                       int err;
 
-                       sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE);
+                       err = sun4v_intr_setstate(ino, HV_INTR_STATE_IDLE);
+                       if (err != HV_EOK)
+                               printk("sun4v_intr_setstate(%x): "
+                                      "err(%d)\n", ino, err);
                } else {
                        upa_writel(ICLR_IDLE, bp->iclr);
-                       /* Test and add entropy */
-                       if (random & SA_SAMPLE_RANDOM)
-                               add_interrupt_randomness(irq);
                }
+
+               /* Test and add entropy */
+               if (random & SA_SAMPLE_RANDOM)
+                       add_interrupt_randomness(irq);
        }
 out:
        bp->flags &= ~IBF_INPROGRESS;
@@ -786,7 +812,6 @@ EXPORT_SYMBOL(probe_irq_off);
 static int retarget_one_irq(struct irqaction *p, int goal_cpu)
 {
        struct ino_bucket *bucket = get_ino_in_irqaction(p) + ivector_table;
-       unsigned long imap = bucket->imap;
 
        while (!cpu_online(goal_cpu)) {
                if (++goal_cpu >= NR_CPUS)
@@ -799,18 +824,9 @@ static int retarget_one_irq(struct irqaction *p, int goal_cpu)
                sun4v_intr_settarget(ino, goal_cpu);
                sun4v_intr_setenabled(ino, HV_INTR_ENABLED);
        } else {
-               unsigned int tid;
+               unsigned long imap = bucket->imap;
+               unsigned int tid = sun4u_compute_tid(imap, goal_cpu);
 
-               if (tlb_type == cheetah || tlb_type == cheetah_plus) {
-                       tid = goal_cpu << 26;
-                       tid &= IMAP_AID_SAFARI;
-               } else if (this_is_starfire == 0) {
-                       tid = goal_cpu << 26;
-                       tid &= IMAP_TID_UPA;
-               } else {
-                       tid = (starfire_translate(imap, goal_cpu) << 26);
-                       tid &= IMAP_TID_UPA;
-               }
                upa_writel(tid | IMAP_VALID, imap);
        }
 
@@ -922,25 +938,13 @@ void init_irqwork_curcpu(void)
 
 static void __cpuinit register_one_mondo(unsigned long paddr, unsigned long type)
 {
-       register unsigned long func __asm__("%o5");
-       register unsigned long arg0 __asm__("%o0");
-       register unsigned long arg1 __asm__("%o1");
-       register unsigned long arg2 __asm__("%o2");
-
-       func = HV_FAST_CPU_QCONF;
-       arg0 = type;
-       arg1 = paddr;
-       arg2 = 128; /* XXX Implied by Niagara queue offsets. XXX */
-       __asm__ __volatile__("ta        %8"
-                            : "=&r" (func), "=&r" (arg0),
-                              "=&r" (arg1), "=&r" (arg2)
-                            : "0" (func), "1" (arg0),
-                              "2" (arg1), "3" (arg2),
-                              "i" (HV_FAST_TRAP));
-
-       if (arg0 != HV_EOK) {
-               prom_printf("SUN4V: cpu_qconf(%lu) failed with error %lu\n",
-                           type, func);
+       unsigned long num_entries = 128;
+       unsigned long status;
+
+       status = sun4v_cpu_qconf(type, paddr, num_entries);
+       if (status != HV_EOK) {
+               prom_printf("SUN4V: sun4v_cpu_qconf(%lu:%lx:%lu) failed, "
+                           "err %lu\n", type, paddr, num_entries, status);
                prom_halt();
        }
 }
@@ -1012,21 +1016,29 @@ static void __cpuinit init_cpu_send_mondo_info(struct trap_per_cpu *tb, int use_
 }
 
 /* Allocate and register the mondo and error queues for this cpu.  */
-void __cpuinit sun4v_init_mondo_queues(int use_bootmem)
+void __cpuinit sun4v_init_mondo_queues(int use_bootmem, int cpu, int alloc, int load)
 {
-       int cpu = hard_smp_processor_id();
        struct trap_per_cpu *tb = &trap_block[cpu];
 
-       alloc_one_mondo(&tb->cpu_mondo_pa, use_bootmem);
-       alloc_one_mondo(&tb->dev_mondo_pa, use_bootmem);
-       alloc_one_mondo(&tb->resum_mondo_pa, use_bootmem);
-       alloc_one_kbuf(&tb->resum_kernel_buf_pa, use_bootmem);
-       alloc_one_mondo(&tb->nonresum_mondo_pa, use_bootmem);
-       alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, use_bootmem);
+       if (alloc) {
+               alloc_one_mondo(&tb->cpu_mondo_pa, use_bootmem);
+               alloc_one_mondo(&tb->dev_mondo_pa, use_bootmem);
+               alloc_one_mondo(&tb->resum_mondo_pa, use_bootmem);
+               alloc_one_kbuf(&tb->resum_kernel_buf_pa, use_bootmem);
+               alloc_one_mondo(&tb->nonresum_mondo_pa, use_bootmem);
+               alloc_one_kbuf(&tb->nonresum_kernel_buf_pa, use_bootmem);
 
-       init_cpu_send_mondo_info(tb, use_bootmem);
+               init_cpu_send_mondo_info(tb, use_bootmem);
+       }
 
-       sun4v_register_mondo_queues(cpu);
+       if (load) {
+               if (cpu != hard_smp_processor_id()) {
+                       prom_printf("SUN4V: init mondo on cpu %d not %d\n",
+                                   cpu, hard_smp_processor_id());
+                       prom_halt();
+               }
+               sun4v_register_mondo_queues(cpu);
+       }
 }
 
 /* Only invoked on boot processor. */
@@ -1037,7 +1049,7 @@ void __init init_IRQ(void)
        memset(&ivector_table[0], 0, sizeof(ivector_table));
 
        if (tlb_type == hypervisor)
-               sun4v_init_mondo_queues(1);
+               sun4v_init_mondo_queues(1, hard_smp_processor_id(), 1, 1);
 
        /* We need to clear any IRQ's pending in the soft interrupt
         * registers, a spurious one could be left around from the