| 1 | |
| 2 | |
| 3 | * Alpha specific irq code.
|
| 4 |
|
| 5 |
|
| 6 | #include <linux/init.h>
|
| 7 | #include <linux/sched.h>
|
| 8 | #include <linux/irq.h>
|
| 9 | #include <linux/kernel_stat.h>
|
| 10 | #include <linux/module.h>
|
| 11 |
|
| 12 | #include <asm/machvec.h>
|
| 13 | #include <asm/dma.h>
|
| 14 | #include <asm/perf_event.h>
|
| 15 | #include <asm/mce.h>
|
| 16 |
|
| 17 | #include "proto.h"
|
| 18 | #include "irq_impl.h"
|
| 19 |
|
| 20 |
|
| 21 | #ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK
|
| 22 | int __min_ipl;
|
| 23 | EXPORT_SYMBOL(__min_ipl);
|
| 24 | #endif
|
| 25 |
|
| 26 | |
| 27 | * Performance counter hook. A module can override this to
|
| 28 | * do something useful.
|
| 29 |
|
| 30 | static void
|
| 31 | dummy_perf(unsigned long vector, struct pt_regs *regs)
|
| 32 | {
|
| 33 | irq_err_count++;
|
| 34 | printk(KERN_CRIT "Performance counter interrupt!\n");
|
| 35 | }
|
| 36 |
|
| 37 | void (*perf_irq)(unsigned long, struct pt_regs *) = dummy_perf;
|
| 38 | EXPORT_SYMBOL(perf_irq);
|
| 39 |
|
| 40 | |
| 41 | * The main interrupt entry point.
|
| 42 |
|
| 43 |
|
| 44 | asmlinkage void
|
| 45 | do_entInt(unsigned long type, unsigned long vector,
|
| 46 | unsigned long la_ptr, struct pt_regs *regs)
|
| 47 | {
|
| 48 | struct pt_regs *old_regs;
|
| 49 |
|
| 50 | |
| 51 | * Disable interrupts during IRQ handling.
|
| 52 | * Note that there is no matching local_irq_enable() due to
|
| 53 | * severe problems with RTI at IPL0 and some MILO PALcode
|
| 54 | * (namely LX164).
|
| 55 | *
|
| 56 | * PALcode has already raised PS.IPL to the level of the interrupt
|
| 57 | * being delivered. For an IPL 7 entry - a machine check or a system
|
| 58 | * event - that is IPL_MAX, which is what arch_irqs_disabled() tests
|
| 59 | * for, so local_irq_disable() would decide interrupts were already
|
| 60 | * off and skip trace_hardirqs_off(). lockdep would then spend the
|
| 61 | * whole handler believing interrupts are enabled. Drive the
|
| 62 | * annotation from lockdep's own state rather than the hardware IPL.
|
| 63 |
|
| 64 | raw_local_irq_disable();
|
| 65 | if (lockdep_hardirqs_enabled())
|
| 66 | trace_hardirqs_off();
|
| 67 |
|
| 68 | old_regs = set_irq_regs(regs);
|
| 69 |
|
| 70 | switch (type) {
|
| 71 | case 0:
|
| 72 | #ifdef CONFIG_SMP
|
| 73 | irq_enter();
|
| 74 | handle_ipi(regs);
|
| 75 | irq_exit();
|
| 76 | break;
|
| 77 | #else
|
| 78 | irq_err_count++;
|
| 79 | pr_crit("Interprocessor interrupt? You must be kidding!\n");
|
| 80 | break;
|
| 81 | #endif
|
| 82 | case 1:
|
| 83 |
|
| 84 | handle_irq(RTC_IRQ);
|
| 85 | break;
|
| 86 | case 2:
|
| 87 | irq_enter();
|
| 88 | alpha_mv.machine_check(vector, la_ptr);
|
| 89 | irq_exit();
|
| 90 | break;
|
| 91 | case 3:
|
| 92 | irq_enter();
|
| 93 | alpha_mv.device_interrupt(vector);
|
| 94 | irq_exit();
|
| 95 | break;
|
| 96 | case 4:
|
| 97 | irq_enter();
|
| 98 | perf_irq(la_ptr, regs);
|
| 99 | irq_exit();
|
| 100 | break;
|
| 101 | default:
|
| 102 | pr_crit("Hardware intr %lu %lx? Huh?\n", type, vector);
|
| 103 | pr_crit("PC = %016lx PS=%04lx\n", regs->pc, regs->ps);
|
| 104 | break;
|
| 105 | }
|
| 106 |
|
| 107 | set_irq_regs(old_regs);
|
| 108 |
|
| 109 | |
| 110 | * Intentionally no local_irq_enable(): Alpha historically avoids
|
| 111 | * enabling at IPL0 here due to PAL/RTI issues (LX164/MILO note).
|
| 112 |
|
| 113 | }
|
| 114 |
|
| 115 | void notrace lockdep_on_restore(unsigned long ps,
|
| 116 | unsigned long ip)
|
| 117 | {
|
| 118 | #ifdef CONFIG_PROVE_LOCKING
|
| 119 | |
| 120 | * If PAL_rti will restore IPL == 7, IRQs remain disabled.
|
| 121 | * There is no hardirqs-on transition to annotate.
|
| 122 |
|
| 123 | if ((ps & 7) == 7)
|
| 124 | return;
|
| 125 |
|
| 126 | |
| 127 | * This helper is meant to run before PAL_rti, after entry.S has
|
| 128 | * forced IPL to 7. If IRQs are already enabled, do not emit a
|
| 129 | * fake transition.
|
| 130 |
|
| 131 | if (!irqs_disabled())
|
| 132 | return;
|
| 133 |
|
| 134 | |
| 135 | * Only emit an ON transition if lockdep currently tracks hardirqs
|
| 136 | * as off.
|
| 137 |
|
| 138 | if (lockdep_hardirqs_enabled())
|
| 139 | return;
|
| 140 |
|
| 141 | lockdep_hardirqs_on_prepare();
|
| 142 | lockdep_hardirqs_on(ip);
|
| 143 | #endif
|
| 144 | }
|
| 145 |
|
| 146 | void __init
|
| 147 | common_init_isa_dma(void)
|
| 148 | {
|
| 149 | outb(0, DMA1_RESET_REG);
|
| 150 | outb(0, DMA2_RESET_REG);
|
| 151 | outb(0, DMA1_CLR_MASK_REG);
|
| 152 | outb(0, DMA2_CLR_MASK_REG);
|
| 153 | }
|
| 154 |
|
| 155 | |
| 156 | * The platforms wire up their interrupts by calling into the generic irq
|
| 157 | * code with fixed interrupt numbers, so the descriptors have to exist by
|
| 158 | * then. Preallocate the machine's full complement rather than take the
|
| 159 | * NR_IRQS_LEGACY default, which would leave everything above IRQ 15
|
| 160 | * without a descriptor and silently drop the handler setup.
|
| 161 |
|
| 162 | int __init
|
| 163 | arch_probe_nr_irqs(void)
|
| 164 | {
|
| 165 | return alpha_mv.nr_irqs;
|
| 166 | }
|
| 167 |
|
| 168 | void __init
|
| 169 | init_IRQ(void)
|
| 170 | {
|
| 171 | |
| 172 | (as is the case with RAWHIDE, at least).
|
| 173 | wrent(entInt, 0);
|
| 174 |
|
| 175 | alpha_mv.init_irq();
|
| 176 | }
|
| 177 |
|
| 178 | |
| 179 | * machine error checks
|
| 180 |
|
| 181 | #define MCHK_K_TPERR 0x0080
|
| 182 | #define MCHK_K_TCPERR 0x0082
|
| 183 | #define MCHK_K_HERR 0x0084
|
| 184 | #define MCHK_K_ECC_C 0x0086
|
| 185 | #define MCHK_K_ECC_NC 0x0088
|
| 186 | #define MCHK_K_OS_BUGCHECK 0x008A
|
| 187 | #define MCHK_K_PAL_BUGCHECK 0x0090
|
| 188 |
|
| 189 | #ifndef CONFIG_SMP
|
| 190 | struct mcheck_info __mcheck_info;
|
| 191 | #endif
|
| 192 |
|
| 193 | void
|
| 194 | process_mcheck_info(unsigned long vector, unsigned long la_ptr,
|
| 195 | const char *machine, int expected)
|
| 196 | {
|
| 197 | struct el_common *mchk_header;
|
| 198 | const char *reason;
|
| 199 |
|
| 200 | |
| 201 | * See if the machine check is due to a badaddr() and if so,
|
| 202 | * ignore it.
|
| 203 |
|
| 204 |
|
| 205 | #ifdef CONFIG_VERBOSE_MCHECK
|
| 206 | if (alpha_verbose_mcheck > 1) {
|
| 207 | printk(KERN_CRIT "%s machine check %s\n", machine,
|
| 208 | expected ? "expected." : "NOT expected!!!");
|
| 209 | }
|
| 210 | #endif
|
| 211 |
|
| 212 | if (expected) {
|
| 213 | int cpu = smp_processor_id();
|
| 214 | mcheck_expected(cpu) = 0;
|
| 215 | mcheck_taken(cpu) = 1;
|
| 216 | return;
|
| 217 | }
|
| 218 |
|
| 219 | mchk_header = (struct el_common *)la_ptr;
|
| 220 |
|
| 221 | printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%x\n",
|
| 222 | machine, vector, get_irq_regs()->pc, mchk_header->code);
|
| 223 |
|
| 224 | switch (mchk_header->code) {
|
| 225 |
|
| 226 | case 0x80: reason = "tag parity error"; break;
|
| 227 | case 0x82: reason = "tag control parity error"; break;
|
| 228 | case 0x84: reason = "generic hard error"; break;
|
| 229 | case 0x86: reason = "correctable ECC error"; break;
|
| 230 | case 0x88: reason = "uncorrectable ECC error"; break;
|
| 231 | case 0x8A: reason = "OS-specific PAL bugcheck"; break;
|
| 232 | case 0x90: reason = "callsys in kernel mode"; break;
|
| 233 | case 0x96: reason = "i-cache read retryable error"; break;
|
| 234 | case 0x98: reason = "processor detected hard error"; break;
|
| 235 |
|
| 236 |
|
| 237 | case 0x202: reason = "system detected hard error"; break;
|
| 238 | case 0x203: reason = "system detected uncorrectable ECC error"; break;
|
| 239 | case 0x204: reason = "SIO SERR occurred on PCI bus"; break;
|
| 240 | case 0x205: reason = "parity error detected by core logic"; break;
|
| 241 | case 0x206: reason = "SIO IOCHK occurred on ISA bus"; break;
|
| 242 | case 0x207: reason = "non-existent memory error"; break;
|
| 243 | case 0x208: reason = "MCHK_K_DCSR"; break;
|
| 244 | case 0x209: reason = "PCI SERR detected"; break;
|
| 245 | case 0x20b: reason = "PCI data parity error detected"; break;
|
| 246 | case 0x20d: reason = "PCI address parity error detected"; break;
|
| 247 | case 0x20f: reason = "PCI master abort error"; break;
|
| 248 | case 0x211: reason = "PCI target abort error"; break;
|
| 249 | case 0x213: reason = "scatter/gather PTE invalid error"; break;
|
| 250 | case 0x215: reason = "flash ROM write error"; break;
|
| 251 | case 0x217: reason = "IOA timeout detected"; break;
|
| 252 | case 0x219: reason = "IOCHK#, EISA add-in board parity or other catastrophic error"; break;
|
| 253 | case 0x21b: reason = "EISA fail-safe timer timeout"; break;
|
| 254 | case 0x21d: reason = "EISA bus time-out"; break;
|
| 255 | case 0x21f: reason = "EISA software generated NMI"; break;
|
| 256 | case 0x221: reason = "unexpected ev5 IRQ[3] interrupt"; break;
|
| 257 | default: reason = "unknown"; break;
|
| 258 | }
|
| 259 |
|
| 260 | printk(KERN_CRIT "machine check type: %s%s\n",
|
| 261 | reason, mchk_header->retry ? " (retryable)" : "");
|
| 262 |
|
| 263 | dik_show_regs(get_irq_regs(), NULL);
|
| 264 |
|
| 265 | #ifdef CONFIG_VERBOSE_MCHECK
|
| 266 | if (alpha_verbose_mcheck > 1) {
|
| 267 |
|
| 268 | unsigned long *ptr = (unsigned long *)la_ptr;
|
| 269 | long i;
|
| 270 | for (i = 0; i < mchk_header->size / sizeof(long); i += 2) {
|
| 271 | printk(KERN_CRIT " +%8lx %016lx %016lx\n",
|
| 272 | i*sizeof(long), ptr[i], ptr[i+1]);
|
| 273 | }
|
| 274 | }
|
| 275 | #endif
|
| 276 | }
|
| 277 |
|
| 278 | |
| 279 | * The special RTC interrupt type. The interrupt itself was
|
| 280 | * processed by PALcode, and comes in via entInt vector 1.
|
| 281 |
|
| 282 | void __init
|
| 283 | init_rtc_irq(irq_handler_t handler)
|
| 284 | {
|
| 285 | irq_set_chip_and_handler_name(RTC_IRQ, &dummy_irq_chip,
|
| 286 | handle_percpu_irq, "RTC");
|
| 287 | if (!handler)
|
| 288 | handler = rtc_timer_interrupt;
|
| 289 | if (request_irq(RTC_IRQ, handler, 0, "timer", NULL))
|
| 290 | pr_err("Failed to register timer interrupt\n");
|
| 291 | }
|