diff options
Diffstat (limited to 'arch/x86_64/kernel/apic.c')
-rw-r--r-- | arch/x86_64/kernel/apic.c | 229 |
1 files changed, 111 insertions, 118 deletions
diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c index 2b8cef037a65..135ff25e6b44 100644 --- a/arch/x86_64/kernel/apic.c +++ b/arch/x86_64/kernel/apic.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/kernel_stat.h> | 25 | #include <linux/kernel_stat.h> |
26 | #include <linux/sysdev.h> | 26 | #include <linux/sysdev.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/ioport.h> | ||
28 | 29 | ||
29 | #include <asm/atomic.h> | 30 | #include <asm/atomic.h> |
30 | #include <asm/smp.h> | 31 | #include <asm/smp.h> |
@@ -36,13 +37,20 @@ | |||
36 | #include <asm/idle.h> | 37 | #include <asm/idle.h> |
37 | #include <asm/proto.h> | 38 | #include <asm/proto.h> |
38 | #include <asm/timex.h> | 39 | #include <asm/timex.h> |
40 | #include <asm/apic.h> | ||
39 | 41 | ||
42 | int apic_mapped; | ||
40 | int apic_verbosity; | 43 | int apic_verbosity; |
41 | int apic_runs_main_timer; | 44 | int apic_runs_main_timer; |
42 | int apic_calibrate_pmtmr __initdata; | 45 | int apic_calibrate_pmtmr __initdata; |
43 | 46 | ||
44 | int disable_apic_timer __initdata; | 47 | int disable_apic_timer __initdata; |
45 | 48 | ||
49 | static struct resource lapic_resource = { | ||
50 | .name = "Local APIC", | ||
51 | .flags = IORESOURCE_MEM | IORESOURCE_BUSY, | ||
52 | }; | ||
53 | |||
46 | /* | 54 | /* |
47 | * cpu_mask that denotes the CPUs that needs timer interrupt coming in as | 55 | * cpu_mask that denotes the CPUs that needs timer interrupt coming in as |
48 | * IPIs in place of local APIC timers | 56 | * IPIs in place of local APIC timers |
@@ -136,72 +144,40 @@ void clear_local_APIC(void) | |||
136 | apic_read(APIC_ESR); | 144 | apic_read(APIC_ESR); |
137 | } | 145 | } |
138 | 146 | ||
139 | void __init connect_bsp_APIC(void) | ||
140 | { | ||
141 | if (pic_mode) { | ||
142 | /* | ||
143 | * Do not trust the local APIC being empty at bootup. | ||
144 | */ | ||
145 | clear_local_APIC(); | ||
146 | /* | ||
147 | * PIC mode, enable APIC mode in the IMCR, i.e. | ||
148 | * connect BSP's local APIC to INT and NMI lines. | ||
149 | */ | ||
150 | apic_printk(APIC_VERBOSE, "leaving PIC mode, enabling APIC mode.\n"); | ||
151 | outb(0x70, 0x22); | ||
152 | outb(0x01, 0x23); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | void disconnect_bsp_APIC(int virt_wire_setup) | 147 | void disconnect_bsp_APIC(int virt_wire_setup) |
157 | { | 148 | { |
158 | if (pic_mode) { | 149 | /* Go back to Virtual Wire compatibility mode */ |
159 | /* | 150 | unsigned long value; |
160 | * Put the board back into PIC mode (has an effect | 151 | |
161 | * only on certain older boards). Note that APIC | 152 | /* For the spurious interrupt use vector F, and enable it */ |
162 | * interrupts, including IPIs, won't work beyond | 153 | value = apic_read(APIC_SPIV); |
163 | * this point! The only exception are INIT IPIs. | 154 | value &= ~APIC_VECTOR_MASK; |
164 | */ | 155 | value |= APIC_SPIV_APIC_ENABLED; |
165 | apic_printk(APIC_QUIET, "disabling APIC mode, entering PIC mode.\n"); | 156 | value |= 0xf; |
166 | outb(0x70, 0x22); | 157 | apic_write(APIC_SPIV, value); |
167 | outb(0x00, 0x23); | ||
168 | } | ||
169 | else { | ||
170 | /* Go back to Virtual Wire compatibility mode */ | ||
171 | unsigned long value; | ||
172 | |||
173 | /* For the spurious interrupt use vector F, and enable it */ | ||
174 | value = apic_read(APIC_SPIV); | ||
175 | value &= ~APIC_VECTOR_MASK; | ||
176 | value |= APIC_SPIV_APIC_ENABLED; | ||
177 | value |= 0xf; | ||
178 | apic_write(APIC_SPIV, value); | ||
179 | |||
180 | if (!virt_wire_setup) { | ||
181 | /* For LVT0 make it edge triggered, active high, external and enabled */ | ||
182 | value = apic_read(APIC_LVT0); | ||
183 | value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | | ||
184 | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | | ||
185 | APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED ); | ||
186 | value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; | ||
187 | value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXTINT); | ||
188 | apic_write(APIC_LVT0, value); | ||
189 | } | ||
190 | else { | ||
191 | /* Disable LVT0 */ | ||
192 | apic_write(APIC_LVT0, APIC_LVT_MASKED); | ||
193 | } | ||
194 | 158 | ||
195 | /* For LVT1 make it edge triggered, active high, nmi and enabled */ | 159 | if (!virt_wire_setup) { |
196 | value = apic_read(APIC_LVT1); | 160 | /* For LVT0 make it edge triggered, active high, external and enabled */ |
197 | value &= ~( | 161 | value = apic_read(APIC_LVT0); |
198 | APIC_MODE_MASK | APIC_SEND_PENDING | | 162 | value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | |
199 | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | | 163 | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | |
200 | APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); | 164 | APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED ); |
201 | value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; | 165 | value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; |
202 | value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI); | 166 | value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXTINT); |
203 | apic_write(APIC_LVT1, value); | 167 | apic_write(APIC_LVT0, value); |
168 | } else { | ||
169 | /* Disable LVT0 */ | ||
170 | apic_write(APIC_LVT0, APIC_LVT_MASKED); | ||
204 | } | 171 | } |
172 | |||
173 | /* For LVT1 make it edge triggered, active high, nmi and enabled */ | ||
174 | value = apic_read(APIC_LVT1); | ||
175 | value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | | ||
176 | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | | ||
177 | APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); | ||
178 | value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; | ||
179 | value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI); | ||
180 | apic_write(APIC_LVT1, value); | ||
205 | } | 181 | } |
206 | 182 | ||
207 | void disable_local_APIC(void) | 183 | void disable_local_APIC(void) |
@@ -297,8 +273,6 @@ void __init sync_Arb_IDs(void) | |||
297 | | APIC_DM_INIT); | 273 | | APIC_DM_INIT); |
298 | } | 274 | } |
299 | 275 | ||
300 | extern void __error_in_apic_c (void); | ||
301 | |||
302 | /* | 276 | /* |
303 | * An initial setup of the virtual wire mode. | 277 | * An initial setup of the virtual wire mode. |
304 | */ | 278 | */ |
@@ -345,8 +319,7 @@ void __cpuinit setup_local_APIC (void) | |||
345 | 319 | ||
346 | value = apic_read(APIC_LVR); | 320 | value = apic_read(APIC_LVR); |
347 | 321 | ||
348 | if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f) | 322 | BUILD_BUG_ON((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f); |
349 | __error_in_apic_c(); | ||
350 | 323 | ||
351 | /* | 324 | /* |
352 | * Double-check whether this APIC is really registered. | 325 | * Double-check whether this APIC is really registered. |
@@ -399,32 +372,8 @@ void __cpuinit setup_local_APIC (void) | |||
399 | */ | 372 | */ |
400 | value |= APIC_SPIV_APIC_ENABLED; | 373 | value |= APIC_SPIV_APIC_ENABLED; |
401 | 374 | ||
402 | /* | 375 | /* We always use processor focus */ |
403 | * Some unknown Intel IO/APIC (or APIC) errata is biting us with | 376 | |
404 | * certain networking cards. If high frequency interrupts are | ||
405 | * happening on a particular IOAPIC pin, plus the IOAPIC routing | ||
406 | * entry is masked/unmasked at a high rate as well then sooner or | ||
407 | * later IOAPIC line gets 'stuck', no more interrupts are received | ||
408 | * from the device. If focus CPU is disabled then the hang goes | ||
409 | * away, oh well :-( | ||
410 | * | ||
411 | * [ This bug can be reproduced easily with a level-triggered | ||
412 | * PCI Ne2000 networking cards and PII/PIII processors, dual | ||
413 | * BX chipset. ] | ||
414 | */ | ||
415 | /* | ||
416 | * Actually disabling the focus CPU check just makes the hang less | ||
417 | * frequent as it makes the interrupt distributon model be more | ||
418 | * like LRU than MRU (the short-term load is more even across CPUs). | ||
419 | * See also the comment in end_level_ioapic_irq(). --macro | ||
420 | */ | ||
421 | #if 1 | ||
422 | /* Enable focus processor (bit==0) */ | ||
423 | value &= ~APIC_SPIV_FOCUS_DISABLED; | ||
424 | #else | ||
425 | /* Disable focus processor (bit==1) */ | ||
426 | value |= APIC_SPIV_FOCUS_DISABLED; | ||
427 | #endif | ||
428 | /* | 377 | /* |
429 | * Set spurious IRQ vector | 378 | * Set spurious IRQ vector |
430 | */ | 379 | */ |
@@ -442,7 +391,7 @@ void __cpuinit setup_local_APIC (void) | |||
442 | * TODO: set up through-local-APIC from through-I/O-APIC? --macro | 391 | * TODO: set up through-local-APIC from through-I/O-APIC? --macro |
443 | */ | 392 | */ |
444 | value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; | 393 | value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; |
445 | if (!smp_processor_id() && (pic_mode || !value)) { | 394 | if (!smp_processor_id() && !value) { |
446 | value = APIC_DM_EXTINT; | 395 | value = APIC_DM_EXTINT; |
447 | apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d\n", smp_processor_id()); | 396 | apic_printk(APIC_VERBOSE, "enabled ExtINT on CPU#%d\n", smp_processor_id()); |
448 | } else { | 397 | } else { |
@@ -479,8 +428,7 @@ void __cpuinit setup_local_APIC (void) | |||
479 | } | 428 | } |
480 | 429 | ||
481 | nmi_watchdog_default(); | 430 | nmi_watchdog_default(); |
482 | if (nmi_watchdog == NMI_LOCAL_APIC) | 431 | setup_apic_nmi_watchdog(NULL); |
483 | setup_apic_nmi_watchdog(); | ||
484 | apic_pm_activate(); | 432 | apic_pm_activate(); |
485 | } | 433 | } |
486 | 434 | ||
@@ -527,8 +475,7 @@ static int lapic_suspend(struct sys_device *dev, pm_message_t state) | |||
527 | apic_pm_state.apic_tmict = apic_read(APIC_TMICT); | 475 | apic_pm_state.apic_tmict = apic_read(APIC_TMICT); |
528 | apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); | 476 | apic_pm_state.apic_tdcr = apic_read(APIC_TDCR); |
529 | apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR); | 477 | apic_pm_state.apic_thmr = apic_read(APIC_LVTTHMR); |
530 | local_save_flags(flags); | 478 | local_irq_save(flags); |
531 | local_irq_disable(); | ||
532 | disable_local_APIC(); | 479 | disable_local_APIC(); |
533 | local_irq_restore(flags); | 480 | local_irq_restore(flags); |
534 | return 0; | 481 | return 0; |
@@ -606,18 +553,24 @@ static void apic_pm_activate(void) { } | |||
606 | 553 | ||
607 | static int __init apic_set_verbosity(char *str) | 554 | static int __init apic_set_verbosity(char *str) |
608 | { | 555 | { |
556 | if (str == NULL) { | ||
557 | skip_ioapic_setup = 0; | ||
558 | ioapic_force = 1; | ||
559 | return 0; | ||
560 | } | ||
609 | if (strcmp("debug", str) == 0) | 561 | if (strcmp("debug", str) == 0) |
610 | apic_verbosity = APIC_DEBUG; | 562 | apic_verbosity = APIC_DEBUG; |
611 | else if (strcmp("verbose", str) == 0) | 563 | else if (strcmp("verbose", str) == 0) |
612 | apic_verbosity = APIC_VERBOSE; | 564 | apic_verbosity = APIC_VERBOSE; |
613 | else | 565 | else { |
614 | printk(KERN_WARNING "APIC Verbosity level %s not recognised" | 566 | printk(KERN_WARNING "APIC Verbosity level %s not recognised" |
615 | " use apic=verbose or apic=debug", str); | 567 | " use apic=verbose or apic=debug\n", str); |
568 | return -EINVAL; | ||
569 | } | ||
616 | 570 | ||
617 | return 1; | 571 | return 0; |
618 | } | 572 | } |
619 | 573 | early_param("apic", apic_set_verbosity); | |
620 | __setup("apic=", apic_set_verbosity); | ||
621 | 574 | ||
622 | /* | 575 | /* |
623 | * Detect and enable local APICs on non-SMP boards. | 576 | * Detect and enable local APICs on non-SMP boards. |
@@ -638,6 +591,40 @@ static int __init detect_init_APIC (void) | |||
638 | return 0; | 591 | return 0; |
639 | } | 592 | } |
640 | 593 | ||
594 | #ifdef CONFIG_X86_IO_APIC | ||
595 | static struct resource * __init ioapic_setup_resources(void) | ||
596 | { | ||
597 | #define IOAPIC_RESOURCE_NAME_SIZE 11 | ||
598 | unsigned long n; | ||
599 | struct resource *res; | ||
600 | char *mem; | ||
601 | int i; | ||
602 | |||
603 | if (nr_ioapics <= 0) | ||
604 | return NULL; | ||
605 | |||
606 | n = IOAPIC_RESOURCE_NAME_SIZE + sizeof(struct resource); | ||
607 | n *= nr_ioapics; | ||
608 | |||
609 | res = alloc_bootmem(n); | ||
610 | |||
611 | if (!res) | ||
612 | return NULL; | ||
613 | |||
614 | memset(res, 0, n); | ||
615 | mem = (void *)&res[nr_ioapics]; | ||
616 | |||
617 | for (i = 0; i < nr_ioapics; i++) { | ||
618 | res[i].name = mem; | ||
619 | res[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY; | ||
620 | snprintf(mem, IOAPIC_RESOURCE_NAME_SIZE, "IOAPIC %u", i); | ||
621 | mem += IOAPIC_RESOURCE_NAME_SIZE; | ||
622 | } | ||
623 | |||
624 | return res; | ||
625 | } | ||
626 | #endif | ||
627 | |||
641 | void __init init_apic_mappings(void) | 628 | void __init init_apic_mappings(void) |
642 | { | 629 | { |
643 | unsigned long apic_phys; | 630 | unsigned long apic_phys; |
@@ -654,19 +641,26 @@ void __init init_apic_mappings(void) | |||
654 | apic_phys = mp_lapic_addr; | 641 | apic_phys = mp_lapic_addr; |
655 | 642 | ||
656 | set_fixmap_nocache(FIX_APIC_BASE, apic_phys); | 643 | set_fixmap_nocache(FIX_APIC_BASE, apic_phys); |
644 | apic_mapped = 1; | ||
657 | apic_printk(APIC_VERBOSE,"mapped APIC to %16lx (%16lx)\n", APIC_BASE, apic_phys); | 645 | apic_printk(APIC_VERBOSE,"mapped APIC to %16lx (%16lx)\n", APIC_BASE, apic_phys); |
658 | 646 | ||
647 | /* Put local APIC into the resource map. */ | ||
648 | lapic_resource.start = apic_phys; | ||
649 | lapic_resource.end = lapic_resource.start + PAGE_SIZE - 1; | ||
650 | insert_resource(&iomem_resource, &lapic_resource); | ||
651 | |||
659 | /* | 652 | /* |
660 | * Fetch the APIC ID of the BSP in case we have a | 653 | * Fetch the APIC ID of the BSP in case we have a |
661 | * default configuration (or the MP table is broken). | 654 | * default configuration (or the MP table is broken). |
662 | */ | 655 | */ |
663 | boot_cpu_id = GET_APIC_ID(apic_read(APIC_ID)); | 656 | boot_cpu_id = GET_APIC_ID(apic_read(APIC_ID)); |
664 | 657 | ||
665 | #ifdef CONFIG_X86_IO_APIC | ||
666 | { | 658 | { |
667 | unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; | 659 | unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; |
668 | int i; | 660 | int i; |
661 | struct resource *ioapic_res; | ||
669 | 662 | ||
663 | ioapic_res = ioapic_setup_resources(); | ||
670 | for (i = 0; i < nr_ioapics; i++) { | 664 | for (i = 0; i < nr_ioapics; i++) { |
671 | if (smp_found_config) { | 665 | if (smp_found_config) { |
672 | ioapic_phys = mp_ioapics[i].mpc_apicaddr; | 666 | ioapic_phys = mp_ioapics[i].mpc_apicaddr; |
@@ -678,9 +672,15 @@ void __init init_apic_mappings(void) | |||
678 | apic_printk(APIC_VERBOSE,"mapped IOAPIC to %016lx (%016lx)\n", | 672 | apic_printk(APIC_VERBOSE,"mapped IOAPIC to %016lx (%016lx)\n", |
679 | __fix_to_virt(idx), ioapic_phys); | 673 | __fix_to_virt(idx), ioapic_phys); |
680 | idx++; | 674 | idx++; |
675 | |||
676 | if (ioapic_res) { | ||
677 | ioapic_res->start = ioapic_phys; | ||
678 | ioapic_res->end = ioapic_phys + (4 * 1024) - 1; | ||
679 | insert_resource(&iomem_resource, ioapic_res); | ||
680 | ioapic_res++; | ||
681 | } | ||
681 | } | 682 | } |
682 | } | 683 | } |
683 | #endif | ||
684 | } | 684 | } |
685 | 685 | ||
686 | /* | 686 | /* |
@@ -951,7 +951,7 @@ void smp_local_timer_interrupt(struct pt_regs *regs) | |||
951 | * We take the 'long' return path, and there every subsystem | 951 | * We take the 'long' return path, and there every subsystem |
952 | * grabs the appropriate locks (kernel lock/ irq lock). | 952 | * grabs the appropriate locks (kernel lock/ irq lock). |
953 | * | 953 | * |
954 | * we might want to decouple profiling from the 'long path', | 954 | * We might want to decouple profiling from the 'long path', |
955 | * and do the profiling totally in assembly. | 955 | * and do the profiling totally in assembly. |
956 | * | 956 | * |
957 | * Currently this isn't too much of an issue (performance wise), | 957 | * Currently this isn't too much of an issue (performance wise), |
@@ -1123,19 +1123,15 @@ int __init APIC_init_uniprocessor (void) | |||
1123 | 1123 | ||
1124 | verify_local_APIC(); | 1124 | verify_local_APIC(); |
1125 | 1125 | ||
1126 | connect_bsp_APIC(); | ||
1127 | |||
1128 | phys_cpu_present_map = physid_mask_of_physid(boot_cpu_id); | 1126 | phys_cpu_present_map = physid_mask_of_physid(boot_cpu_id); |
1129 | apic_write(APIC_ID, SET_APIC_ID(boot_cpu_id)); | 1127 | apic_write(APIC_ID, SET_APIC_ID(boot_cpu_id)); |
1130 | 1128 | ||
1131 | setup_local_APIC(); | 1129 | setup_local_APIC(); |
1132 | 1130 | ||
1133 | #ifdef CONFIG_X86_IO_APIC | ||
1134 | if (smp_found_config && !skip_ioapic_setup && nr_ioapics) | 1131 | if (smp_found_config && !skip_ioapic_setup && nr_ioapics) |
1135 | setup_IO_APIC(); | 1132 | setup_IO_APIC(); |
1136 | else | 1133 | else |
1137 | nr_ioapics = 0; | 1134 | nr_ioapics = 0; |
1138 | #endif | ||
1139 | setup_boot_APIC_clock(); | 1135 | setup_boot_APIC_clock(); |
1140 | check_nmi_watchdog(); | 1136 | check_nmi_watchdog(); |
1141 | return 0; | 1137 | return 0; |
@@ -1144,14 +1140,17 @@ int __init APIC_init_uniprocessor (void) | |||
1144 | static __init int setup_disableapic(char *str) | 1140 | static __init int setup_disableapic(char *str) |
1145 | { | 1141 | { |
1146 | disable_apic = 1; | 1142 | disable_apic = 1; |
1147 | return 1; | 1143 | clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability); |
1148 | } | 1144 | return 0; |
1145 | } | ||
1146 | early_param("disableapic", setup_disableapic); | ||
1149 | 1147 | ||
1148 | /* same as disableapic, for compatibility */ | ||
1150 | static __init int setup_nolapic(char *str) | 1149 | static __init int setup_nolapic(char *str) |
1151 | { | 1150 | { |
1152 | disable_apic = 1; | 1151 | return setup_disableapic(str); |
1153 | return 1; | ||
1154 | } | 1152 | } |
1153 | early_param("nolapic", setup_nolapic); | ||
1155 | 1154 | ||
1156 | static __init int setup_noapictimer(char *str) | 1155 | static __init int setup_noapictimer(char *str) |
1157 | { | 1156 | { |
@@ -1184,11 +1183,5 @@ static __init int setup_apicpmtimer(char *s) | |||
1184 | } | 1183 | } |
1185 | __setup("apicpmtimer", setup_apicpmtimer); | 1184 | __setup("apicpmtimer", setup_apicpmtimer); |
1186 | 1185 | ||
1187 | /* dummy parsing: see setup.c */ | ||
1188 | |||
1189 | __setup("disableapic", setup_disableapic); | ||
1190 | __setup("nolapic", setup_nolapic); /* same as disableapic, for compatibility */ | ||
1191 | |||
1192 | __setup("noapictimer", setup_noapictimer); | 1186 | __setup("noapictimer", setup_noapictimer); |
1193 | 1187 | ||
1194 | /* no "lapic" flag - we only use the lapic when the BIOS tells us so. */ | ||