diff options
Diffstat (limited to 'arch/x86/kernel/io_apic_64.c')
-rw-r--r-- | arch/x86/kernel/io_apic_64.c | 132 |
1 files changed, 80 insertions, 52 deletions
diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c index ef1a8dfcc529..e5ef60303562 100644 --- a/arch/x86/kernel/io_apic_64.c +++ b/arch/x86/kernel/io_apic_64.c | |||
@@ -61,7 +61,7 @@ struct irq_cfg { | |||
61 | }; | 61 | }; |
62 | 62 | ||
63 | /* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */ | 63 | /* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */ |
64 | struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = { | 64 | static struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = { |
65 | [0] = { .domain = CPU_MASK_ALL, .vector = IRQ0_VECTOR, }, | 65 | [0] = { .domain = CPU_MASK_ALL, .vector = IRQ0_VECTOR, }, |
66 | [1] = { .domain = CPU_MASK_ALL, .vector = IRQ1_VECTOR, }, | 66 | [1] = { .domain = CPU_MASK_ALL, .vector = IRQ1_VECTOR, }, |
67 | [2] = { .domain = CPU_MASK_ALL, .vector = IRQ2_VECTOR, }, | 67 | [2] = { .domain = CPU_MASK_ALL, .vector = IRQ2_VECTOR, }, |
@@ -90,7 +90,7 @@ static int no_timer_check; | |||
90 | 90 | ||
91 | static int disable_timer_pin_1 __initdata; | 91 | static int disable_timer_pin_1 __initdata; |
92 | 92 | ||
93 | int timer_over_8254 __initdata = 1; | 93 | int timer_through_8259 __initdata; |
94 | 94 | ||
95 | /* Where if anywhere is the i8259 connect in external int mode */ | 95 | /* Where if anywhere is the i8259 connect in external int mode */ |
96 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; | 96 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; |
@@ -183,7 +183,7 @@ static bool io_apic_level_ack_pending(unsigned int irq) | |||
183 | break; | 183 | break; |
184 | reg = io_apic_read(entry->apic, 0x10 + pin*2); | 184 | reg = io_apic_read(entry->apic, 0x10 + pin*2); |
185 | /* Is the remote IRR bit set? */ | 185 | /* Is the remote IRR bit set? */ |
186 | if ((reg >> 14) & 1) { | 186 | if (reg & IO_APIC_REDIR_REMOTE_IRR) { |
187 | spin_unlock_irqrestore(&ioapic_lock, flags); | 187 | spin_unlock_irqrestore(&ioapic_lock, flags); |
188 | return true; | 188 | return true; |
189 | } | 189 | } |
@@ -298,7 +298,7 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector) | |||
298 | break; | 298 | break; |
299 | io_apic_write(apic, 0x11 + pin*2, dest); | 299 | io_apic_write(apic, 0x11 + pin*2, dest); |
300 | reg = io_apic_read(apic, 0x10 + pin*2); | 300 | reg = io_apic_read(apic, 0x10 + pin*2); |
301 | reg &= ~0x000000ff; | 301 | reg &= ~IO_APIC_REDIR_VECTOR_MASK; |
302 | reg |= vector; | 302 | reg |= vector; |
303 | io_apic_modify(apic, reg); | 303 | io_apic_modify(apic, reg); |
304 | if (!entry->next) | 304 | if (!entry->next) |
@@ -360,16 +360,37 @@ static void add_pin_to_irq(unsigned int irq, int apic, int pin) | |||
360 | entry->pin = pin; | 360 | entry->pin = pin; |
361 | } | 361 | } |
362 | 362 | ||
363 | /* | ||
364 | * Reroute an IRQ to a different pin. | ||
365 | */ | ||
366 | static void __init replace_pin_at_irq(unsigned int irq, | ||
367 | int oldapic, int oldpin, | ||
368 | int newapic, int newpin) | ||
369 | { | ||
370 | struct irq_pin_list *entry = irq_2_pin + irq; | ||
371 | |||
372 | while (1) { | ||
373 | if (entry->apic == oldapic && entry->pin == oldpin) { | ||
374 | entry->apic = newapic; | ||
375 | entry->pin = newpin; | ||
376 | } | ||
377 | if (!entry->next) | ||
378 | break; | ||
379 | entry = irq_2_pin + entry->next; | ||
380 | } | ||
381 | } | ||
382 | |||
363 | 383 | ||
364 | #define DO_ACTION(name,R,ACTION, FINAL) \ | 384 | #define DO_ACTION(name,R,ACTION, FINAL) \ |
365 | \ | 385 | \ |
366 | static void name##_IO_APIC_irq (unsigned int irq) \ | 386 | static void name##_IO_APIC_irq (unsigned int irq) \ |
367 | __DO_ACTION(R, ACTION, FINAL) | 387 | __DO_ACTION(R, ACTION, FINAL) |
368 | 388 | ||
369 | DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic) ) | 389 | /* mask = 1 */ |
370 | /* mask = 1 */ | 390 | DO_ACTION(__mask, 0, |= IO_APIC_REDIR_MASKED, io_apic_sync(entry->apic)) |
371 | DO_ACTION( __unmask, 0, &= 0xfffeffff, ) | 391 | |
372 | /* mask = 0 */ | 392 | /* mask = 0 */ |
393 | DO_ACTION(__unmask, 0, &= ~IO_APIC_REDIR_MASKED, ) | ||
373 | 394 | ||
374 | static void mask_IO_APIC_irq (unsigned int irq) | 395 | static void mask_IO_APIC_irq (unsigned int irq) |
375 | { | 396 | { |
@@ -430,20 +451,6 @@ static int __init disable_timer_pin_setup(char *arg) | |||
430 | } | 451 | } |
431 | __setup("disable_timer_pin_1", disable_timer_pin_setup); | 452 | __setup("disable_timer_pin_1", disable_timer_pin_setup); |
432 | 453 | ||
433 | static int __init setup_disable_8254_timer(char *s) | ||
434 | { | ||
435 | timer_over_8254 = -1; | ||
436 | return 1; | ||
437 | } | ||
438 | static int __init setup_enable_8254_timer(char *s) | ||
439 | { | ||
440 | timer_over_8254 = 2; | ||
441 | return 1; | ||
442 | } | ||
443 | |||
444 | __setup("disable_8254_timer", setup_disable_8254_timer); | ||
445 | __setup("enable_8254_timer", setup_enable_8254_timer); | ||
446 | |||
447 | 454 | ||
448 | /* | 455 | /* |
449 | * Find the IRQ entry number of a certain pin. | 456 | * Find the IRQ entry number of a certain pin. |
@@ -911,26 +918,21 @@ static void __init setup_IO_APIC_irqs(void) | |||
911 | } | 918 | } |
912 | 919 | ||
913 | /* | 920 | /* |
914 | * Set up the 8259A-master output pin as broadcast to all | 921 | * Set up the timer pin, possibly with the 8259A-master behind. |
915 | * CPUs. | ||
916 | */ | 922 | */ |
917 | static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, int vector) | 923 | static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin, |
924 | int vector) | ||
918 | { | 925 | { |
919 | struct IO_APIC_route_entry entry; | 926 | struct IO_APIC_route_entry entry; |
920 | 927 | ||
921 | memset(&entry, 0, sizeof(entry)); | 928 | memset(&entry, 0, sizeof(entry)); |
922 | 929 | ||
923 | disable_8259A_irq(0); | ||
924 | |||
925 | /* mask LVT0 */ | ||
926 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); | ||
927 | |||
928 | /* | 930 | /* |
929 | * We use logical delivery to get the timer IRQ | 931 | * We use logical delivery to get the timer IRQ |
930 | * to the first CPU. | 932 | * to the first CPU. |
931 | */ | 933 | */ |
932 | entry.dest_mode = INT_DEST_MODE; | 934 | entry.dest_mode = INT_DEST_MODE; |
933 | entry.mask = 0; /* unmask IRQ now */ | 935 | entry.mask = 1; /* mask IRQ now */ |
934 | entry.dest = cpu_mask_to_apicid(TARGET_CPUS); | 936 | entry.dest = cpu_mask_to_apicid(TARGET_CPUS); |
935 | entry.delivery_mode = INT_DELIVERY_MODE; | 937 | entry.delivery_mode = INT_DELIVERY_MODE; |
936 | entry.polarity = 0; | 938 | entry.polarity = 0; |
@@ -939,7 +941,7 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in | |||
939 | 941 | ||
940 | /* | 942 | /* |
941 | * The timer IRQ doesn't have to know that behind the | 943 | * The timer IRQ doesn't have to know that behind the |
942 | * scene we have a 8259A-master in AEOI mode ... | 944 | * scene we may have a 8259A-master in AEOI mode ... |
943 | */ | 945 | */ |
944 | set_irq_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq, "edge"); | 946 | set_irq_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq, "edge"); |
945 | 947 | ||
@@ -947,8 +949,6 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in | |||
947 | * Add it to the IO-APIC irq-routing table: | 949 | * Add it to the IO-APIC irq-routing table: |
948 | */ | 950 | */ |
949 | ioapic_write_entry(apic, pin, entry); | 951 | ioapic_write_entry(apic, pin, entry); |
950 | |||
951 | enable_8259A_irq(0); | ||
952 | } | 952 | } |
953 | 953 | ||
954 | void __apicdebuginit print_IO_APIC(void) | 954 | void __apicdebuginit print_IO_APIC(void) |
@@ -1077,6 +1077,7 @@ void __apicdebuginit print_local_APIC(void * dummy) | |||
1077 | 1077 | ||
1078 | printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n", | 1078 | printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n", |
1079 | smp_processor_id(), hard_smp_processor_id()); | 1079 | smp_processor_id(), hard_smp_processor_id()); |
1080 | v = apic_read(APIC_ID); | ||
1080 | printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, GET_APIC_ID(read_apic_id())); | 1081 | printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, GET_APIC_ID(read_apic_id())); |
1081 | v = apic_read(APIC_LVR); | 1082 | v = apic_read(APIC_LVR); |
1082 | printk(KERN_INFO "... APIC VERSION: %08x\n", v); | 1083 | printk(KERN_INFO "... APIC VERSION: %08x\n", v); |
@@ -1659,6 +1660,7 @@ static inline void __init check_timer(void) | |||
1659 | struct irq_cfg *cfg = irq_cfg + 0; | 1660 | struct irq_cfg *cfg = irq_cfg + 0; |
1660 | int apic1, pin1, apic2, pin2; | 1661 | int apic1, pin1, apic2, pin2; |
1661 | unsigned long flags; | 1662 | unsigned long flags; |
1663 | int no_pin1 = 0; | ||
1662 | 1664 | ||
1663 | local_irq_save(flags); | 1665 | local_irq_save(flags); |
1664 | 1666 | ||
@@ -1669,16 +1671,11 @@ static inline void __init check_timer(void) | |||
1669 | assign_irq_vector(0, TARGET_CPUS); | 1671 | assign_irq_vector(0, TARGET_CPUS); |
1670 | 1672 | ||
1671 | /* | 1673 | /* |
1672 | * Subtle, code in do_timer_interrupt() expects an AEOI | 1674 | * As IRQ0 is to be enabled in the 8259A, the virtual |
1673 | * mode for the 8259A whenever interrupts are routed | 1675 | * wire has to be disabled in the local APIC. |
1674 | * through I/O APICs. Also IRQ0 has to be enabled in | ||
1675 | * the 8259A which implies the virtual wire has to be | ||
1676 | * disabled in the local APIC. | ||
1677 | */ | 1676 | */ |
1678 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); | 1677 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); |
1679 | init_8259A(1); | 1678 | init_8259A(1); |
1680 | if (timer_over_8254 > 0) | ||
1681 | enable_8259A_irq(0); | ||
1682 | 1679 | ||
1683 | pin1 = find_isa_irq_pin(0, mp_INT); | 1680 | pin1 = find_isa_irq_pin(0, mp_INT); |
1684 | apic1 = find_isa_irq_apic(0, mp_INT); | 1681 | apic1 = find_isa_irq_apic(0, mp_INT); |
@@ -1688,15 +1685,39 @@ static inline void __init check_timer(void) | |||
1688 | apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", | 1685 | apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", |
1689 | cfg->vector, apic1, pin1, apic2, pin2); | 1686 | cfg->vector, apic1, pin1, apic2, pin2); |
1690 | 1687 | ||
1688 | /* | ||
1689 | * Some BIOS writers are clueless and report the ExtINTA | ||
1690 | * I/O APIC input from the cascaded 8259A as the timer | ||
1691 | * interrupt input. So just in case, if only one pin | ||
1692 | * was found above, try it both directly and through the | ||
1693 | * 8259A. | ||
1694 | */ | ||
1695 | if (pin1 == -1) { | ||
1696 | pin1 = pin2; | ||
1697 | apic1 = apic2; | ||
1698 | no_pin1 = 1; | ||
1699 | } else if (pin2 == -1) { | ||
1700 | pin2 = pin1; | ||
1701 | apic2 = apic1; | ||
1702 | } | ||
1703 | |||
1704 | replace_pin_at_irq(0, 0, 0, apic1, pin1); | ||
1705 | apic1 = 0; | ||
1706 | pin1 = 0; | ||
1707 | setup_timer_IRQ0_pin(apic1, pin1, cfg->vector); | ||
1708 | |||
1691 | if (pin1 != -1) { | 1709 | if (pin1 != -1) { |
1692 | /* | 1710 | /* |
1693 | * Ok, does IRQ0 through the IOAPIC work? | 1711 | * Ok, does IRQ0 through the IOAPIC work? |
1694 | */ | 1712 | */ |
1713 | if (no_pin1) { | ||
1714 | add_pin_to_irq(0, apic1, pin1); | ||
1715 | setup_timer_IRQ0_pin(apic1, pin1, cfg->vector); | ||
1716 | } | ||
1695 | unmask_IO_APIC_irq(0); | 1717 | unmask_IO_APIC_irq(0); |
1696 | if (!no_timer_check && timer_irq_works()) { | 1718 | if (!no_timer_check && timer_irq_works()) { |
1697 | nmi_watchdog_default(); | 1719 | nmi_watchdog_default(); |
1698 | if (nmi_watchdog == NMI_IO_APIC) { | 1720 | if (nmi_watchdog == NMI_IO_APIC) { |
1699 | disable_8259A_irq(0); | ||
1700 | setup_nmi(); | 1721 | setup_nmi(); |
1701 | enable_8259A_irq(0); | 1722 | enable_8259A_irq(0); |
1702 | } | 1723 | } |
@@ -1705,42 +1726,48 @@ static inline void __init check_timer(void) | |||
1705 | goto out; | 1726 | goto out; |
1706 | } | 1727 | } |
1707 | clear_IO_APIC_pin(apic1, pin1); | 1728 | clear_IO_APIC_pin(apic1, pin1); |
1708 | apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not " | 1729 | if (!no_pin1) |
1709 | "connected to IO-APIC\n"); | 1730 | apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: " |
1710 | } | 1731 | "8254 timer not connected to IO-APIC\n"); |
1711 | 1732 | ||
1712 | apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) " | 1733 | apic_printk(APIC_VERBOSE,KERN_INFO |
1713 | "through the 8259A ... "); | 1734 | "...trying to set up timer (IRQ0) " |
1714 | if (pin2 != -1) { | 1735 | "through the 8259A ... "); |
1715 | apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...", | 1736 | apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...", |
1716 | apic2, pin2); | 1737 | apic2, pin2); |
1717 | /* | 1738 | /* |
1718 | * legacy devices should be connected to IO APIC #0 | 1739 | * legacy devices should be connected to IO APIC #0 |
1719 | */ | 1740 | */ |
1720 | setup_ExtINT_IRQ0_pin(apic2, pin2, cfg->vector); | 1741 | replace_pin_at_irq(0, apic1, pin1, apic2, pin2); |
1742 | setup_timer_IRQ0_pin(apic2, pin2, cfg->vector); | ||
1743 | unmask_IO_APIC_irq(0); | ||
1744 | enable_8259A_irq(0); | ||
1721 | if (timer_irq_works()) { | 1745 | if (timer_irq_works()) { |
1722 | apic_printk(APIC_VERBOSE," works.\n"); | 1746 | apic_printk(APIC_VERBOSE," works.\n"); |
1747 | timer_through_8259 = 1; | ||
1723 | nmi_watchdog_default(); | 1748 | nmi_watchdog_default(); |
1724 | if (nmi_watchdog == NMI_IO_APIC) { | 1749 | if (nmi_watchdog == NMI_IO_APIC) { |
1750 | disable_8259A_irq(0); | ||
1725 | setup_nmi(); | 1751 | setup_nmi(); |
1752 | enable_8259A_irq(0); | ||
1726 | } | 1753 | } |
1727 | goto out; | 1754 | goto out; |
1728 | } | 1755 | } |
1729 | /* | 1756 | /* |
1730 | * Cleanup, just in case ... | 1757 | * Cleanup, just in case ... |
1731 | */ | 1758 | */ |
1759 | disable_8259A_irq(0); | ||
1732 | clear_IO_APIC_pin(apic2, pin2); | 1760 | clear_IO_APIC_pin(apic2, pin2); |
1761 | apic_printk(APIC_VERBOSE," failed.\n"); | ||
1733 | } | 1762 | } |
1734 | apic_printk(APIC_VERBOSE," failed.\n"); | ||
1735 | 1763 | ||
1736 | if (nmi_watchdog == NMI_IO_APIC) { | 1764 | if (nmi_watchdog == NMI_IO_APIC) { |
1737 | printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); | 1765 | printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); |
1738 | nmi_watchdog = 0; | 1766 | nmi_watchdog = NMI_NONE; |
1739 | } | 1767 | } |
1740 | 1768 | ||
1741 | apic_printk(APIC_VERBOSE, KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); | 1769 | apic_printk(APIC_VERBOSE, KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); |
1742 | 1770 | ||
1743 | disable_8259A_irq(0); | ||
1744 | irq_desc[0].chip = &lapic_irq_type; | 1771 | irq_desc[0].chip = &lapic_irq_type; |
1745 | apic_write(APIC_LVT0, APIC_DM_FIXED | cfg->vector); /* Fixed mode */ | 1772 | apic_write(APIC_LVT0, APIC_DM_FIXED | cfg->vector); /* Fixed mode */ |
1746 | enable_8259A_irq(0); | 1773 | enable_8259A_irq(0); |
@@ -1749,6 +1776,7 @@ static inline void __init check_timer(void) | |||
1749 | apic_printk(APIC_VERBOSE," works.\n"); | 1776 | apic_printk(APIC_VERBOSE," works.\n"); |
1750 | goto out; | 1777 | goto out; |
1751 | } | 1778 | } |
1779 | disable_8259A_irq(0); | ||
1752 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector); | 1780 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector); |
1753 | apic_printk(APIC_VERBOSE," failed.\n"); | 1781 | apic_printk(APIC_VERBOSE," failed.\n"); |
1754 | 1782 | ||