| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | #include <linux/errno.h> | 
|  | 2 | #include <linux/signal.h> | 
|  | 3 | #include <linux/sched.h> | 
|  | 4 | #include <linux/ioport.h> | 
|  | 5 | #include <linux/interrupt.h> | 
|  | 6 | #include <linux/slab.h> | 
|  | 7 | #include <linux/random.h> | 
|  | 8 | #include <linux/smp_lock.h> | 
|  | 9 | #include <linux/init.h> | 
|  | 10 | #include <linux/kernel_stat.h> | 
|  | 11 | #include <linux/sysdev.h> | 
|  | 12 | #include <linux/bitops.h> | 
|  | 13 |  | 
|  | 14 | #include <asm/8253pit.h> | 
|  | 15 | #include <asm/atomic.h> | 
|  | 16 | #include <asm/system.h> | 
|  | 17 | #include <asm/io.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | #include <asm/timer.h> | 
|  | 19 | #include <asm/pgtable.h> | 
|  | 20 | #include <asm/delay.h> | 
|  | 21 | #include <asm/desc.h> | 
|  | 22 | #include <asm/apic.h> | 
|  | 23 | #include <asm/arch_hooks.h> | 
|  | 24 | #include <asm/i8259.h> | 
|  | 25 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 26 | #include <io_ports.h> | 
|  | 27 |  | 
|  | 28 | /* | 
|  | 29 | * This is the 'legacy' 8259A Programmable Interrupt Controller, | 
|  | 30 | * present in the majority of PC/AT boxes. | 
|  | 31 | * plus some generic x86 specific things if generic specifics makes | 
|  | 32 | * any sense at all. | 
|  | 33 | * this file should become arch/i386/kernel/irq.c when the old irq.c | 
|  | 34 | * moves to arch independent land | 
|  | 35 | */ | 
|  | 36 |  | 
|  | 37 | DEFINE_SPINLOCK(i8259A_lock); | 
|  | 38 |  | 
|  | 39 | static void end_8259A_irq (unsigned int irq) | 
|  | 40 | { | 
|  | 41 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && | 
|  | 42 | irq_desc[irq].action) | 
|  | 43 | enable_8259A_irq(irq); | 
|  | 44 | } | 
|  | 45 |  | 
|  | 46 | #define shutdown_8259A_irq	disable_8259A_irq | 
|  | 47 |  | 
|  | 48 | static void mask_and_ack_8259A(unsigned int); | 
|  | 49 |  | 
|  | 50 | unsigned int startup_8259A_irq(unsigned int irq) | 
|  | 51 | { | 
|  | 52 | enable_8259A_irq(irq); | 
|  | 53 | return 0; /* never anything pending */ | 
|  | 54 | } | 
|  | 55 |  | 
|  | 56 | static struct hw_interrupt_type i8259A_irq_type = { | 
|  | 57 | .typename = "XT-PIC", | 
|  | 58 | .startup = startup_8259A_irq, | 
|  | 59 | .shutdown = shutdown_8259A_irq, | 
|  | 60 | .enable = enable_8259A_irq, | 
|  | 61 | .disable = disable_8259A_irq, | 
|  | 62 | .ack = mask_and_ack_8259A, | 
|  | 63 | .end = end_8259A_irq, | 
|  | 64 | }; | 
|  | 65 |  | 
|  | 66 | /* | 
|  | 67 | * 8259A PIC functions to handle ISA devices: | 
|  | 68 | */ | 
|  | 69 |  | 
|  | 70 | /* | 
|  | 71 | * This contains the irq mask for both 8259A irq controllers, | 
|  | 72 | */ | 
|  | 73 | unsigned int cached_irq_mask = 0xffff; | 
|  | 74 |  | 
|  | 75 | /* | 
|  | 76 | * Not all IRQs can be routed through the IO-APIC, eg. on certain (older) | 
|  | 77 | * boards the timer interrupt is not really connected to any IO-APIC pin, | 
|  | 78 | * it's fed to the master 8259A's IR0 line only. | 
|  | 79 | * | 
|  | 80 | * Any '1' bit in this mask means the IRQ is routed through the IO-APIC. | 
|  | 81 | * this 'mixed mode' IRQ handling costs nothing because it's only used | 
|  | 82 | * at IRQ setup time. | 
|  | 83 | */ | 
|  | 84 | unsigned long io_apic_irqs; | 
|  | 85 |  | 
|  | 86 | void disable_8259A_irq(unsigned int irq) | 
|  | 87 | { | 
|  | 88 | unsigned int mask = 1 << irq; | 
|  | 89 | unsigned long flags; | 
|  | 90 |  | 
|  | 91 | spin_lock_irqsave(&i8259A_lock, flags); | 
|  | 92 | cached_irq_mask |= mask; | 
|  | 93 | if (irq & 8) | 
|  | 94 | outb(cached_slave_mask, PIC_SLAVE_IMR); | 
|  | 95 | else | 
|  | 96 | outb(cached_master_mask, PIC_MASTER_IMR); | 
|  | 97 | spin_unlock_irqrestore(&i8259A_lock, flags); | 
|  | 98 | } | 
|  | 99 |  | 
|  | 100 | void enable_8259A_irq(unsigned int irq) | 
|  | 101 | { | 
|  | 102 | unsigned int mask = ~(1 << irq); | 
|  | 103 | unsigned long flags; | 
|  | 104 |  | 
|  | 105 | spin_lock_irqsave(&i8259A_lock, flags); | 
|  | 106 | cached_irq_mask &= mask; | 
|  | 107 | if (irq & 8) | 
|  | 108 | outb(cached_slave_mask, PIC_SLAVE_IMR); | 
|  | 109 | else | 
|  | 110 | outb(cached_master_mask, PIC_MASTER_IMR); | 
|  | 111 | spin_unlock_irqrestore(&i8259A_lock, flags); | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | int i8259A_irq_pending(unsigned int irq) | 
|  | 115 | { | 
|  | 116 | unsigned int mask = 1<<irq; | 
|  | 117 | unsigned long flags; | 
|  | 118 | int ret; | 
|  | 119 |  | 
|  | 120 | spin_lock_irqsave(&i8259A_lock, flags); | 
|  | 121 | if (irq < 8) | 
|  | 122 | ret = inb(PIC_MASTER_CMD) & mask; | 
|  | 123 | else | 
|  | 124 | ret = inb(PIC_SLAVE_CMD) & (mask >> 8); | 
|  | 125 | spin_unlock_irqrestore(&i8259A_lock, flags); | 
|  | 126 |  | 
|  | 127 | return ret; | 
|  | 128 | } | 
|  | 129 |  | 
|  | 130 | void make_8259A_irq(unsigned int irq) | 
|  | 131 | { | 
|  | 132 | disable_irq_nosync(irq); | 
|  | 133 | io_apic_irqs &= ~(1<<irq); | 
| Ingo Molnar | d1bef4e | 2006-06-29 02:24:36 -0700 | [diff] [blame] | 134 | irq_desc[irq].chip = &i8259A_irq_type; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 135 | enable_irq(irq); | 
|  | 136 | } | 
|  | 137 |  | 
|  | 138 | /* | 
|  | 139 | * This function assumes to be called rarely. Switching between | 
|  | 140 | * 8259A registers is slow. | 
|  | 141 | * This has to be protected by the irq controller spinlock | 
|  | 142 | * before being called. | 
|  | 143 | */ | 
|  | 144 | static inline int i8259A_irq_real(unsigned int irq) | 
|  | 145 | { | 
|  | 146 | int value; | 
|  | 147 | int irqmask = 1<<irq; | 
|  | 148 |  | 
|  | 149 | if (irq < 8) { | 
|  | 150 | outb(0x0B,PIC_MASTER_CMD);	/* ISR register */ | 
|  | 151 | value = inb(PIC_MASTER_CMD) & irqmask; | 
|  | 152 | outb(0x0A,PIC_MASTER_CMD);	/* back to the IRR register */ | 
|  | 153 | return value; | 
|  | 154 | } | 
|  | 155 | outb(0x0B,PIC_SLAVE_CMD);	/* ISR register */ | 
|  | 156 | value = inb(PIC_SLAVE_CMD) & (irqmask >> 8); | 
|  | 157 | outb(0x0A,PIC_SLAVE_CMD);	/* back to the IRR register */ | 
|  | 158 | return value; | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | /* | 
|  | 162 | * Careful! The 8259A is a fragile beast, it pretty | 
|  | 163 | * much _has_ to be done exactly like this (mask it | 
|  | 164 | * first, _then_ send the EOI, and the order of EOI | 
|  | 165 | * to the two 8259s is important! | 
|  | 166 | */ | 
|  | 167 | static void mask_and_ack_8259A(unsigned int irq) | 
|  | 168 | { | 
|  | 169 | unsigned int irqmask = 1 << irq; | 
|  | 170 | unsigned long flags; | 
|  | 171 |  | 
|  | 172 | spin_lock_irqsave(&i8259A_lock, flags); | 
|  | 173 | /* | 
|  | 174 | * Lightweight spurious IRQ detection. We do not want | 
|  | 175 | * to overdo spurious IRQ handling - it's usually a sign | 
|  | 176 | * of hardware problems, so we only do the checks we can | 
| Andreas Mohr | d6e05ed | 2006-06-26 18:35:02 +0200 | [diff] [blame] | 177 | * do without slowing down good hardware unnecessarily. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 178 | * | 
|  | 179 | * Note that IRQ7 and IRQ15 (the two spurious IRQs | 
|  | 180 | * usually resulting from the 8259A-1|2 PICs) occur | 
|  | 181 | * even if the IRQ is masked in the 8259A. Thus we | 
|  | 182 | * can check spurious 8259A IRQs without doing the | 
|  | 183 | * quite slow i8259A_irq_real() call for every IRQ. | 
|  | 184 | * This does not cover 100% of spurious interrupts, | 
|  | 185 | * but should be enough to warn the user that there | 
|  | 186 | * is something bad going on ... | 
|  | 187 | */ | 
|  | 188 | if (cached_irq_mask & irqmask) | 
|  | 189 | goto spurious_8259A_irq; | 
|  | 190 | cached_irq_mask |= irqmask; | 
|  | 191 |  | 
|  | 192 | handle_real_irq: | 
|  | 193 | if (irq & 8) { | 
|  | 194 | inb(PIC_SLAVE_IMR);	/* DUMMY - (do we need this?) */ | 
|  | 195 | outb(cached_slave_mask, PIC_SLAVE_IMR); | 
|  | 196 | outb(0x60+(irq&7),PIC_SLAVE_CMD);/* 'Specific EOI' to slave */ | 
|  | 197 | outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */ | 
|  | 198 | } else { | 
|  | 199 | inb(PIC_MASTER_IMR);	/* DUMMY - (do we need this?) */ | 
|  | 200 | outb(cached_master_mask, PIC_MASTER_IMR); | 
|  | 201 | outb(0x60+irq,PIC_MASTER_CMD);	/* 'Specific EOI to master */ | 
|  | 202 | } | 
|  | 203 | spin_unlock_irqrestore(&i8259A_lock, flags); | 
|  | 204 | return; | 
|  | 205 |  | 
|  | 206 | spurious_8259A_irq: | 
|  | 207 | /* | 
|  | 208 | * this is the slow path - should happen rarely. | 
|  | 209 | */ | 
|  | 210 | if (i8259A_irq_real(irq)) | 
|  | 211 | /* | 
|  | 212 | * oops, the IRQ _is_ in service according to the | 
|  | 213 | * 8259A - not spurious, go handle it. | 
|  | 214 | */ | 
|  | 215 | goto handle_real_irq; | 
|  | 216 |  | 
|  | 217 | { | 
|  | 218 | static int spurious_irq_mask; | 
|  | 219 | /* | 
|  | 220 | * At this point we can be sure the IRQ is spurious, | 
|  | 221 | * lets ACK and report it. [once per IRQ] | 
|  | 222 | */ | 
|  | 223 | if (!(spurious_irq_mask & irqmask)) { | 
|  | 224 | printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq); | 
|  | 225 | spurious_irq_mask |= irqmask; | 
|  | 226 | } | 
|  | 227 | atomic_inc(&irq_err_count); | 
|  | 228 | /* | 
|  | 229 | * Theoretically we do not have to handle this IRQ, | 
|  | 230 | * but in Linux this does not cause problems and is | 
|  | 231 | * simpler for us. | 
|  | 232 | */ | 
|  | 233 | goto handle_real_irq; | 
|  | 234 | } | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | static char irq_trigger[2]; | 
|  | 238 | /** | 
|  | 239 | * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ | 
|  | 240 | */ | 
|  | 241 | static void restore_ELCR(char *trigger) | 
|  | 242 | { | 
|  | 243 | outb(trigger[0], 0x4d0); | 
|  | 244 | outb(trigger[1], 0x4d1); | 
|  | 245 | } | 
|  | 246 |  | 
|  | 247 | static void save_ELCR(char *trigger) | 
|  | 248 | { | 
|  | 249 | /* IRQ 0,1,2,8,13 are marked as reserved */ | 
|  | 250 | trigger[0] = inb(0x4d0) & 0xF8; | 
|  | 251 | trigger[1] = inb(0x4d1) & 0xDE; | 
|  | 252 | } | 
|  | 253 |  | 
|  | 254 | static int i8259A_resume(struct sys_device *dev) | 
|  | 255 | { | 
|  | 256 | init_8259A(0); | 
|  | 257 | restore_ELCR(irq_trigger); | 
|  | 258 | return 0; | 
|  | 259 | } | 
|  | 260 |  | 
| Pavel Machek | 438510f | 2005-04-16 15:25:24 -0700 | [diff] [blame] | 261 | static int i8259A_suspend(struct sys_device *dev, pm_message_t state) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 262 | { | 
|  | 263 | save_ELCR(irq_trigger); | 
|  | 264 | return 0; | 
|  | 265 | } | 
|  | 266 |  | 
| Eric W. Biederman | cee5dab | 2005-06-25 14:57:43 -0700 | [diff] [blame] | 267 | static int i8259A_shutdown(struct sys_device *dev) | 
|  | 268 | { | 
|  | 269 | /* Put the i8259A into a quiescent state that | 
|  | 270 | * the kernel initialization code can get it | 
|  | 271 | * out of. | 
|  | 272 | */ | 
| Andreas Mohr | 110cb1d | 2006-06-23 02:04:28 -0700 | [diff] [blame] | 273 | outb(0xff, PIC_MASTER_IMR);	/* mask all of 8259A-1 */ | 
|  | 274 | outb(0xff, PIC_SLAVE_IMR);	/* mask all of 8259A-1 */ | 
| Eric W. Biederman | cee5dab | 2005-06-25 14:57:43 -0700 | [diff] [blame] | 275 | return 0; | 
|  | 276 | } | 
|  | 277 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 278 | static struct sysdev_class i8259_sysdev_class = { | 
|  | 279 | set_kset_name("i8259"), | 
|  | 280 | .suspend = i8259A_suspend, | 
|  | 281 | .resume = i8259A_resume, | 
| Eric W. Biederman | cee5dab | 2005-06-25 14:57:43 -0700 | [diff] [blame] | 282 | .shutdown = i8259A_shutdown, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 283 | }; | 
|  | 284 |  | 
|  | 285 | static struct sys_device device_i8259A = { | 
|  | 286 | .id	= 0, | 
|  | 287 | .cls	= &i8259_sysdev_class, | 
|  | 288 | }; | 
|  | 289 |  | 
|  | 290 | static int __init i8259A_init_sysfs(void) | 
|  | 291 | { | 
|  | 292 | int error = sysdev_class_register(&i8259_sysdev_class); | 
|  | 293 | if (!error) | 
|  | 294 | error = sysdev_register(&device_i8259A); | 
|  | 295 | return error; | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 | device_initcall(i8259A_init_sysfs); | 
|  | 299 |  | 
|  | 300 | void init_8259A(int auto_eoi) | 
|  | 301 | { | 
|  | 302 | unsigned long flags; | 
|  | 303 |  | 
|  | 304 | spin_lock_irqsave(&i8259A_lock, flags); | 
|  | 305 |  | 
|  | 306 | outb(0xff, PIC_MASTER_IMR);	/* mask all of 8259A-1 */ | 
|  | 307 | outb(0xff, PIC_SLAVE_IMR);	/* mask all of 8259A-2 */ | 
|  | 308 |  | 
|  | 309 | /* | 
|  | 310 | * outb_p - this has to work on a wide range of PC hardware. | 
|  | 311 | */ | 
|  | 312 | outb_p(0x11, PIC_MASTER_CMD);	/* ICW1: select 8259A-1 init */ | 
|  | 313 | outb_p(0x20 + 0, PIC_MASTER_IMR);	/* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */ | 
|  | 314 | outb_p(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);	/* 8259A-1 (the master) has a slave on IR2 */ | 
|  | 315 | if (auto_eoi)	/* master does Auto EOI */ | 
|  | 316 | outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR); | 
|  | 317 | else		/* master expects normal EOI */ | 
|  | 318 | outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR); | 
|  | 319 |  | 
|  | 320 | outb_p(0x11, PIC_SLAVE_CMD);	/* ICW1: select 8259A-2 init */ | 
|  | 321 | outb_p(0x20 + 8, PIC_SLAVE_IMR);	/* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */ | 
|  | 322 | outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR);	/* 8259A-2 is a slave on master's IR2 */ | 
|  | 323 | outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */ | 
|  | 324 | if (auto_eoi) | 
|  | 325 | /* | 
|  | 326 | * in AEOI mode we just have to mask the interrupt | 
|  | 327 | * when acking. | 
|  | 328 | */ | 
|  | 329 | i8259A_irq_type.ack = disable_8259A_irq; | 
|  | 330 | else | 
|  | 331 | i8259A_irq_type.ack = mask_and_ack_8259A; | 
|  | 332 |  | 
|  | 333 | udelay(100);		/* wait for 8259A to initialize */ | 
|  | 334 |  | 
|  | 335 | outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */ | 
|  | 336 | outb(cached_slave_mask, PIC_SLAVE_IMR);	  /* restore slave IRQ mask */ | 
|  | 337 |  | 
|  | 338 | spin_unlock_irqrestore(&i8259A_lock, flags); | 
|  | 339 | } | 
|  | 340 |  | 
|  | 341 | /* | 
|  | 342 | * Note that on a 486, we don't want to do a SIGFPE on an irq13 | 
|  | 343 | * as the irq is unreliable, and exception 16 works correctly | 
|  | 344 | * (ie as explained in the intel literature). On a 386, you | 
|  | 345 | * can't use exception 16 due to bad IBM design, so we have to | 
|  | 346 | * rely on the less exact irq13. | 
|  | 347 | * | 
|  | 348 | * Careful.. Not only is IRQ13 unreliable, but it is also | 
|  | 349 | * leads to races. IBM designers who came up with it should | 
|  | 350 | * be shot. | 
|  | 351 | */ | 
|  | 352 |  | 
|  | 353 |  | 
|  | 354 | static irqreturn_t math_error_irq(int cpl, void *dev_id, struct pt_regs *regs) | 
|  | 355 | { | 
|  | 356 | extern void math_error(void __user *); | 
|  | 357 | outb(0,0xF0); | 
|  | 358 | if (ignore_fpu_irq || !boot_cpu_data.hard_math) | 
|  | 359 | return IRQ_NONE; | 
|  | 360 | math_error((void __user *)regs->eip); | 
|  | 361 | return IRQ_HANDLED; | 
|  | 362 | } | 
|  | 363 |  | 
|  | 364 | /* | 
|  | 365 | * New motherboards sometimes make IRQ 13 be a PCI interrupt, | 
|  | 366 | * so allow interrupt sharing. | 
|  | 367 | */ | 
|  | 368 | static struct irqaction fpu_irq = { math_error_irq, 0, CPU_MASK_NONE, "fpu", NULL, NULL }; | 
|  | 369 |  | 
|  | 370 | void __init init_ISA_irqs (void) | 
|  | 371 | { | 
|  | 372 | int i; | 
|  | 373 |  | 
|  | 374 | #ifdef CONFIG_X86_LOCAL_APIC | 
|  | 375 | init_bsp_APIC(); | 
|  | 376 | #endif | 
|  | 377 | init_8259A(0); | 
|  | 378 |  | 
|  | 379 | for (i = 0; i < NR_IRQS; i++) { | 
|  | 380 | irq_desc[i].status = IRQ_DISABLED; | 
|  | 381 | irq_desc[i].action = NULL; | 
|  | 382 | irq_desc[i].depth = 1; | 
|  | 383 |  | 
|  | 384 | if (i < 16) { | 
|  | 385 | /* | 
|  | 386 | * 16 old-style INTA-cycle interrupts: | 
|  | 387 | */ | 
| Ingo Molnar | d1bef4e | 2006-06-29 02:24:36 -0700 | [diff] [blame] | 388 | irq_desc[i].chip = &i8259A_irq_type; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 389 | } else { | 
|  | 390 | /* | 
|  | 391 | * 'high' PCI IRQs filled in on demand | 
|  | 392 | */ | 
| Ingo Molnar | d1bef4e | 2006-06-29 02:24:36 -0700 | [diff] [blame] | 393 | irq_desc[i].chip = &no_irq_type; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 394 | } | 
|  | 395 | } | 
|  | 396 | } | 
|  | 397 |  | 
|  | 398 | void __init init_IRQ(void) | 
|  | 399 | { | 
|  | 400 | int i; | 
|  | 401 |  | 
|  | 402 | /* all the set up before the call gates are initialised */ | 
|  | 403 | pre_intr_init_hook(); | 
|  | 404 |  | 
|  | 405 | /* | 
|  | 406 | * Cover the whole vector space, no vector can escape | 
|  | 407 | * us. (some of these will be overridden and become | 
|  | 408 | * 'special' SMP interrupts) | 
|  | 409 | */ | 
|  | 410 | for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) { | 
|  | 411 | int vector = FIRST_EXTERNAL_VECTOR + i; | 
|  | 412 | if (i >= NR_IRQS) | 
|  | 413 | break; | 
|  | 414 | if (vector != SYSCALL_VECTOR) | 
|  | 415 | set_intr_gate(vector, interrupt[i]); | 
|  | 416 | } | 
|  | 417 |  | 
|  | 418 | /* setup after call gates are initialised (usually add in | 
|  | 419 | * the architecture specific gates) | 
|  | 420 | */ | 
|  | 421 | intr_init_hook(); | 
|  | 422 |  | 
|  | 423 | /* | 
|  | 424 | * Set the clock to HZ Hz, we already have a valid | 
|  | 425 | * vector now: | 
|  | 426 | */ | 
|  | 427 | setup_pit_timer(); | 
|  | 428 |  | 
|  | 429 | /* | 
|  | 430 | * External FPU? Set up irq13 if so, for | 
|  | 431 | * original braindamaged IBM FERR coupling. | 
|  | 432 | */ | 
|  | 433 | if (boot_cpu_data.hard_math && !cpu_has_fpu) | 
|  | 434 | setup_irq(FPU_IRQ, &fpu_irq); | 
|  | 435 |  | 
|  | 436 | irq_ctx_init(smp_processor_id()); | 
|  | 437 | } |