1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * ip27-irq.c: Highlevel interrupt handling for IP27 architecture. 4 * 5 * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) 6 * Copyright (C) 1999, 2000 Silicon Graphics, Inc. 7 * Copyright (C) 1999 - 2001 Kanoj Sarcar 8 */ 9 10 #undef DEBUG 11 12 #include <linux/init.h> 13 #include <linux/irq.h> 14 #include <linux/errno.h> 15 #include <linux/signal.h> 16 #include <linux/sched.h> 17 #include <linux/types.h> 18 #include <linux/interrupt.h> 19 #include <linux/ioport.h> 20 #include <linux/timex.h> 21 #include <linux/smp.h> 22 #include <linux/random.h> 23 #include <linux/kernel.h> 24 #include <linux/kernel_stat.h> 25 #include <linux/delay.h> 26 #include <linux/bitops.h> 27 28 #include <asm/bootinfo.h> 29 #include <asm/io.h> 30 #include <asm/mipsregs.h> 31 32 #include <asm/processor.h> 33 #include <asm/sn/addrs.h> 34 #include <asm/sn/agent.h> 35 #include <asm/sn/arch.h> 36 #include <asm/sn/hub.h> 37 #include <asm/sn/intr.h> 38 39 /* 40 * Linux has a controller-independent x86 interrupt architecture. 41 * every controller has a 'controller-template', that is used 42 * by the main code to do the right thing. Each driver-visible 43 * interrupt source is transparently wired to the appropriate 44 * controller. Thus drivers need not be aware of the 45 * interrupt-controller. 46 * 47 * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, 48 * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. 49 * (IO-APICs assumed to be messaging to Pentium local-APICs) 50 * 51 * the code is designed to be easily extended with new/different 52 * interrupt controllers, without having to do assembly magic. 53 */ 54 55 extern asmlinkage void ip27_irq(void); 56 57 /* 58 * Find first bit set 59 */ 60 static int ms1bit(unsigned long x) 61 { 62 int b = 0, s; 63 64 s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s; 65 s = 8; if (x >> 8 == 0) s = 0; b += s; x >>= s; 66 s = 4; if (x >> 4 == 0) s = 0; b += s; x >>= s; 67 s = 2; if (x >> 2 == 0) s = 0; b += s; x >>= s; 68 s = 1; if (x >> 1 == 0) s = 0; b += s; 69 70 return b; 71 } 72 73 /* 74 * This code is unnecessarily complex, because we do 75 * intr enabling. Basically, once we grab the set of intrs we need 76 * to service, we must mask _all_ these interrupts; firstly, to make 77 * sure the same intr does not intr again, causing recursion that 78 * can lead to stack overflow. Secondly, we can not just mask the 79 * one intr we are do_IRQing, because the non-masked intrs in the 80 * first set might intr again, causing multiple servicings of the 81 * same intr. This effect is mostly seen for intercpu intrs. 82 * Kanoj 05.13.00 83 */ 84 85 static void ip27_do_irq_mask0(void) 86 { 87 int irq, swlevel; 88 hubreg_t pend0, mask0; 89 cpuid_t cpu = smp_processor_id(); 90 int pi_int_mask0 = 91 (cputoslice(cpu) == 0) ? PI_INT_MASK0_A : PI_INT_MASK0_B; 92 93 /* copied from Irix intpend0() */ 94 pend0 = LOCAL_HUB_L(PI_INT_PEND0); 95 mask0 = LOCAL_HUB_L(pi_int_mask0); 96 97 pend0 &= mask0; /* Pick intrs we should look at */ 98 if (!pend0) 99 return; 100 101 swlevel = ms1bit(pend0); 102 #ifdef CONFIG_SMP 103 if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) { 104 LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ); 105 scheduler_ipi(); 106 } else if (pend0 & (1UL << CPU_RESCHED_B_IRQ)) { 107 LOCAL_HUB_CLR_INTR(CPU_RESCHED_B_IRQ); 108 scheduler_ipi(); 109 } else if (pend0 & (1UL << CPU_CALL_A_IRQ)) { 110 LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ); 111 irq_enter(); 112 generic_smp_call_function_interrupt(); 113 irq_exit(); 114 } else if (pend0 & (1UL << CPU_CALL_B_IRQ)) { 115 LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ); 116 irq_enter(); 117 generic_smp_call_function_interrupt(); 118 irq_exit(); 119 } else 120 #endif 121 { 122 /* "map" swlevel to irq */ 123 struct slice_data *si = cpu_data[cpu].data; 124 125 irq = si->level_to_irq[swlevel]; 126 do_IRQ(irq); 127 } 128 129 LOCAL_HUB_L(PI_INT_PEND0); 130 } 131 132 static void ip27_do_irq_mask1(void) 133 { 134 int irq, swlevel; 135 hubreg_t pend1, mask1; 136 cpuid_t cpu = smp_processor_id(); 137 int pi_int_mask1 = (cputoslice(cpu) == 0) ? PI_INT_MASK1_A : PI_INT_MASK1_B; 138 struct slice_data *si = cpu_data[cpu].data; 139 140 /* copied from Irix intpend0() */ 141 pend1 = LOCAL_HUB_L(PI_INT_PEND1); 142 mask1 = LOCAL_HUB_L(pi_int_mask1); 143 144 pend1 &= mask1; /* Pick intrs we should look at */ 145 if (!pend1) 146 return; 147 148 swlevel = ms1bit(pend1); 149 /* "map" swlevel to irq */ 150 irq = si->level_to_irq[swlevel]; 151 LOCAL_HUB_CLR_INTR(swlevel); 152 do_IRQ(irq); 153 154 LOCAL_HUB_L(PI_INT_PEND1); 155 } 156 157 static void ip27_prof_timer(void) 158 { 159 panic("CPU %d got a profiling interrupt", smp_processor_id()); 160 } 161 162 static void ip27_hub_error(void) 163 { 164 panic("CPU %d got a hub error interrupt", smp_processor_id()); 165 } 166 167 asmlinkage void plat_irq_dispatch(void) 168 { 169 unsigned long pending = read_c0_cause() & read_c0_status(); 170 extern unsigned int rt_timer_irq; 171 172 if (pending & CAUSEF_IP4) 173 do_IRQ(rt_timer_irq); 174 else if (pending & CAUSEF_IP2) /* PI_INT_PEND_0 or CC_PEND_{A|B} */ 175 ip27_do_irq_mask0(); 176 else if (pending & CAUSEF_IP3) /* PI_INT_PEND_1 */ 177 ip27_do_irq_mask1(); 178 else if (pending & CAUSEF_IP5) 179 ip27_prof_timer(); 180 else if (pending & CAUSEF_IP6) 181 ip27_hub_error(); 182 } 183 184 void __init arch_init_irq(void) 185 { 186 } 187 188 void install_ipi(void) 189 { 190 int slice = LOCAL_HUB_L(PI_CPU_NUM); 191 int cpu = smp_processor_id(); 192 struct slice_data *si = cpu_data[cpu].data; 193 struct hub_data *hub = hub_data(cpu_to_node(cpu)); 194 int resched, call; 195 196 resched = CPU_RESCHED_A_IRQ + slice; 197 __set_bit(resched, hub->irq_alloc_mask); 198 __set_bit(resched, si->irq_enable_mask); 199 LOCAL_HUB_CLR_INTR(resched); 200 201 call = CPU_CALL_A_IRQ + slice; 202 __set_bit(call, hub->irq_alloc_mask); 203 __set_bit(call, si->irq_enable_mask); 204 LOCAL_HUB_CLR_INTR(call); 205 206 if (slice == 0) { 207 LOCAL_HUB_S(PI_INT_MASK0_A, si->irq_enable_mask[0]); 208 LOCAL_HUB_S(PI_INT_MASK1_A, si->irq_enable_mask[1]); 209 } else { 210 LOCAL_HUB_S(PI_INT_MASK0_B, si->irq_enable_mask[0]); 211 LOCAL_HUB_S(PI_INT_MASK1_B, si->irq_enable_mask[1]); 212 } 213 } 214