diff options
Diffstat (limited to 'arch/x86_64/kernel/io_apic.c')
-rw-r--r-- | arch/x86_64/kernel/io_apic.c | 258 |
1 files changed, 138 insertions, 120 deletions
diff --git a/arch/x86_64/kernel/io_apic.c b/arch/x86_64/kernel/io_apic.c index c80081a6ba41..2a1dcd5f69c2 100644 --- a/arch/x86_64/kernel/io_apic.c +++ b/arch/x86_64/kernel/io_apic.c | |||
@@ -55,10 +55,6 @@ int sis_apic_bug; /* not actually supported, dummy for compile */ | |||
55 | 55 | ||
56 | static int no_timer_check; | 56 | static int no_timer_check; |
57 | 57 | ||
58 | static int disable_timer_pin_1 __initdata; | ||
59 | |||
60 | int timer_over_8254 __initdata = 1; | ||
61 | |||
62 | /* Where if anywhere is the i8259 connect in external int mode */ | 58 | /* Where if anywhere is the i8259 connect in external int mode */ |
63 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; | 59 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; |
64 | 60 | ||
@@ -178,14 +174,20 @@ static struct IO_APIC_route_entry ioapic_read_entry(int apic, int pin) | |||
178 | * the interrupt, and we need to make sure the entry is fully populated | 174 | * the interrupt, and we need to make sure the entry is fully populated |
179 | * before that happens. | 175 | * before that happens. |
180 | */ | 176 | */ |
181 | static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) | 177 | static void |
178 | __ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) | ||
182 | { | 179 | { |
183 | unsigned long flags; | ||
184 | union entry_union eu; | 180 | union entry_union eu; |
185 | eu.entry = e; | 181 | eu.entry = e; |
186 | spin_lock_irqsave(&ioapic_lock, flags); | ||
187 | io_apic_write(apic, 0x11 + 2*pin, eu.w2); | 182 | io_apic_write(apic, 0x11 + 2*pin, eu.w2); |
188 | io_apic_write(apic, 0x10 + 2*pin, eu.w1); | 183 | io_apic_write(apic, 0x10 + 2*pin, eu.w1); |
184 | } | ||
185 | |||
186 | static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) | ||
187 | { | ||
188 | unsigned long flags; | ||
189 | spin_lock_irqsave(&ioapic_lock, flags); | ||
190 | __ioapic_write_entry(apic, pin, e); | ||
189 | spin_unlock_irqrestore(&ioapic_lock, flags); | 191 | spin_unlock_irqrestore(&ioapic_lock, flags); |
190 | } | 192 | } |
191 | 193 | ||
@@ -348,29 +350,6 @@ static int __init disable_ioapic_setup(char *str) | |||
348 | } | 350 | } |
349 | early_param("noapic", disable_ioapic_setup); | 351 | early_param("noapic", disable_ioapic_setup); |
350 | 352 | ||
351 | /* Actually the next is obsolete, but keep it for paranoid reasons -AK */ | ||
352 | static int __init disable_timer_pin_setup(char *arg) | ||
353 | { | ||
354 | disable_timer_pin_1 = 1; | ||
355 | return 1; | ||
356 | } | ||
357 | __setup("disable_timer_pin_1", disable_timer_pin_setup); | ||
358 | |||
359 | static int __init setup_disable_8254_timer(char *s) | ||
360 | { | ||
361 | timer_over_8254 = -1; | ||
362 | return 1; | ||
363 | } | ||
364 | static int __init setup_enable_8254_timer(char *s) | ||
365 | { | ||
366 | timer_over_8254 = 2; | ||
367 | return 1; | ||
368 | } | ||
369 | |||
370 | __setup("disable_8254_timer", setup_disable_8254_timer); | ||
371 | __setup("enable_8254_timer", setup_enable_8254_timer); | ||
372 | |||
373 | |||
374 | /* | 353 | /* |
375 | * Find the IRQ entry number of a certain pin. | 354 | * Find the IRQ entry number of a certain pin. |
376 | */ | 355 | */ |
@@ -750,6 +729,22 @@ static int assign_irq_vector(int irq, cpumask_t mask, cpumask_t *result) | |||
750 | return vector; | 729 | return vector; |
751 | } | 730 | } |
752 | 731 | ||
732 | static void __clear_irq_vector(int irq) | ||
733 | { | ||
734 | cpumask_t mask; | ||
735 | int cpu, vector; | ||
736 | |||
737 | BUG_ON(!irq_vector[irq]); | ||
738 | |||
739 | vector = irq_vector[irq]; | ||
740 | cpus_and(mask, irq_domain[irq], cpu_online_map); | ||
741 | for_each_cpu_mask(cpu, mask) | ||
742 | per_cpu(vector_irq, cpu)[vector] = -1; | ||
743 | |||
744 | irq_vector[irq] = 0; | ||
745 | irq_domain[irq] = CPU_MASK_NONE; | ||
746 | } | ||
747 | |||
753 | void __setup_vector_irq(int cpu) | 748 | void __setup_vector_irq(int cpu) |
754 | { | 749 | { |
755 | /* Initialize vector_irq on a new cpu */ | 750 | /* Initialize vector_irq on a new cpu */ |
@@ -794,27 +789,65 @@ static void ioapic_register_intr(int irq, int vector, unsigned long trigger) | |||
794 | handle_edge_irq, "edge"); | 789 | handle_edge_irq, "edge"); |
795 | } | 790 | } |
796 | } | 791 | } |
797 | 792 | static void __init setup_IO_APIC_irq(int apic, int pin, int idx, int irq) | |
798 | static void __init setup_IO_APIC_irqs(void) | ||
799 | { | 793 | { |
800 | struct IO_APIC_route_entry entry; | 794 | struct IO_APIC_route_entry entry; |
801 | int apic, pin, idx, irq, first_notcon = 1, vector; | 795 | int vector; |
802 | unsigned long flags; | 796 | unsigned long flags; |
803 | 797 | ||
804 | apic_printk(APIC_VERBOSE, KERN_DEBUG "init IO_APIC IRQs\n"); | ||
805 | 798 | ||
806 | for (apic = 0; apic < nr_ioapics; apic++) { | 799 | /* |
807 | for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { | 800 | * add it to the IO-APIC irq-routing table: |
801 | */ | ||
802 | memset(&entry,0,sizeof(entry)); | ||
808 | 803 | ||
809 | /* | 804 | entry.delivery_mode = INT_DELIVERY_MODE; |
810 | * add it to the IO-APIC irq-routing table: | 805 | entry.dest_mode = INT_DEST_MODE; |
811 | */ | 806 | entry.mask = 0; /* enable IRQ */ |
812 | memset(&entry,0,sizeof(entry)); | 807 | entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); |
808 | |||
809 | entry.trigger = irq_trigger(idx); | ||
810 | entry.polarity = irq_polarity(idx); | ||
813 | 811 | ||
814 | entry.delivery_mode = INT_DELIVERY_MODE; | 812 | if (irq_trigger(idx)) { |
815 | entry.dest_mode = INT_DEST_MODE; | 813 | entry.trigger = 1; |
816 | entry.mask = 0; /* enable IRQ */ | 814 | entry.mask = 1; |
817 | entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); | 815 | entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); |
816 | } | ||
817 | |||
818 | if (!apic && !IO_APIC_IRQ(irq)) | ||
819 | return; | ||
820 | |||
821 | if (IO_APIC_IRQ(irq)) { | ||
822 | cpumask_t mask; | ||
823 | vector = assign_irq_vector(irq, TARGET_CPUS, &mask); | ||
824 | if (vector < 0) | ||
825 | return; | ||
826 | |||
827 | entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask); | ||
828 | entry.vector = vector; | ||
829 | |||
830 | ioapic_register_intr(irq, vector, IOAPIC_AUTO); | ||
831 | if (!apic && (irq < 16)) | ||
832 | disable_8259A_irq(irq); | ||
833 | } | ||
834 | |||
835 | ioapic_write_entry(apic, pin, entry); | ||
836 | |||
837 | spin_lock_irqsave(&ioapic_lock, flags); | ||
838 | set_native_irq_info(irq, TARGET_CPUS); | ||
839 | spin_unlock_irqrestore(&ioapic_lock, flags); | ||
840 | |||
841 | } | ||
842 | |||
843 | static void __init setup_IO_APIC_irqs(void) | ||
844 | { | ||
845 | int apic, pin, idx, irq, first_notcon = 1; | ||
846 | |||
847 | apic_printk(APIC_VERBOSE, KERN_DEBUG "init IO_APIC IRQs\n"); | ||
848 | |||
849 | for (apic = 0; apic < nr_ioapics; apic++) { | ||
850 | for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { | ||
818 | 851 | ||
819 | idx = find_irq_entry(apic,pin,mp_INT); | 852 | idx = find_irq_entry(apic,pin,mp_INT); |
820 | if (idx == -1) { | 853 | if (idx == -1) { |
@@ -826,39 +859,11 @@ static void __init setup_IO_APIC_irqs(void) | |||
826 | continue; | 859 | continue; |
827 | } | 860 | } |
828 | 861 | ||
829 | entry.trigger = irq_trigger(idx); | ||
830 | entry.polarity = irq_polarity(idx); | ||
831 | |||
832 | if (irq_trigger(idx)) { | ||
833 | entry.trigger = 1; | ||
834 | entry.mask = 1; | ||
835 | entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); | ||
836 | } | ||
837 | |||
838 | irq = pin_2_irq(idx, apic, pin); | 862 | irq = pin_2_irq(idx, apic, pin); |
839 | add_pin_to_irq(irq, apic, pin); | 863 | add_pin_to_irq(irq, apic, pin); |
840 | 864 | ||
841 | if (!apic && !IO_APIC_IRQ(irq)) | 865 | setup_IO_APIC_irq(apic, pin, idx, irq); |
842 | continue; | ||
843 | |||
844 | if (IO_APIC_IRQ(irq)) { | ||
845 | cpumask_t mask; | ||
846 | vector = assign_irq_vector(irq, TARGET_CPUS, &mask); | ||
847 | if (vector < 0) | ||
848 | continue; | ||
849 | |||
850 | entry.dest.logical.logical_dest = cpu_mask_to_apicid(mask); | ||
851 | entry.vector = vector; | ||
852 | |||
853 | ioapic_register_intr(irq, vector, IOAPIC_AUTO); | ||
854 | if (!apic && (irq < 16)) | ||
855 | disable_8259A_irq(irq); | ||
856 | } | ||
857 | ioapic_write_entry(apic, pin, entry); | ||
858 | 866 | ||
859 | spin_lock_irqsave(&ioapic_lock, flags); | ||
860 | set_native_irq_info(irq, TARGET_CPUS); | ||
861 | spin_unlock_irqrestore(&ioapic_lock, flags); | ||
862 | } | 867 | } |
863 | } | 868 | } |
864 | 869 | ||
@@ -1563,10 +1568,33 @@ static inline void unlock_ExtINT_logic(void) | |||
1563 | * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ | 1568 | * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ |
1564 | * is so screwy. Thanks to Brian Perkins for testing/hacking this beast | 1569 | * is so screwy. Thanks to Brian Perkins for testing/hacking this beast |
1565 | * fanatically on his truly buggy board. | 1570 | * fanatically on his truly buggy board. |
1566 | * | ||
1567 | * FIXME: really need to revamp this for modern platforms only. | ||
1568 | */ | 1571 | */ |
1569 | static inline void check_timer(void) | 1572 | |
1573 | static int try_apic_pin(int apic, int pin, char *msg) | ||
1574 | { | ||
1575 | apic_printk(APIC_VERBOSE, KERN_INFO | ||
1576 | "..TIMER: trying IO-APIC=%d PIN=%d %s", | ||
1577 | apic, pin, msg); | ||
1578 | |||
1579 | /* | ||
1580 | * Ok, does IRQ0 through the IOAPIC work? | ||
1581 | */ | ||
1582 | if (!no_timer_check && timer_irq_works()) { | ||
1583 | nmi_watchdog_default(); | ||
1584 | if (nmi_watchdog == NMI_IO_APIC) { | ||
1585 | disable_8259A_irq(0); | ||
1586 | setup_nmi(); | ||
1587 | enable_8259A_irq(0); | ||
1588 | } | ||
1589 | return 1; | ||
1590 | } | ||
1591 | clear_IO_APIC_pin(apic, pin); | ||
1592 | apic_printk(APIC_QUIET, KERN_ERR " .. failed\n"); | ||
1593 | return 0; | ||
1594 | } | ||
1595 | |||
1596 | /* The function from hell */ | ||
1597 | static void check_timer(void) | ||
1570 | { | 1598 | { |
1571 | int apic1, pin1, apic2, pin2; | 1599 | int apic1, pin1, apic2, pin2; |
1572 | int vector; | 1600 | int vector; |
@@ -1587,61 +1615,43 @@ static inline void check_timer(void) | |||
1587 | */ | 1615 | */ |
1588 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); | 1616 | apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); |
1589 | init_8259A(1); | 1617 | init_8259A(1); |
1590 | if (timer_over_8254 > 0) | ||
1591 | enable_8259A_irq(0); | ||
1592 | 1618 | ||
1593 | pin1 = find_isa_irq_pin(0, mp_INT); | 1619 | pin1 = find_isa_irq_pin(0, mp_INT); |
1594 | apic1 = find_isa_irq_apic(0, mp_INT); | 1620 | apic1 = find_isa_irq_apic(0, mp_INT); |
1595 | pin2 = ioapic_i8259.pin; | 1621 | pin2 = ioapic_i8259.pin; |
1596 | apic2 = ioapic_i8259.apic; | 1622 | apic2 = ioapic_i8259.apic; |
1597 | 1623 | ||
1598 | apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", | 1624 | /* Do this first, otherwise we get double interrupts on ATI boards */ |
1599 | vector, apic1, pin1, apic2, pin2); | 1625 | if ((pin1 != -1) && try_apic_pin(apic1, pin1,"with 8259 IRQ0 disabled")) |
1626 | return; | ||
1600 | 1627 | ||
1601 | if (pin1 != -1) { | 1628 | /* Now try again with IRQ0 8259A enabled. |
1602 | /* | 1629 | Assumes timer is on IO-APIC 0 ?!? */ |
1603 | * Ok, does IRQ0 through the IOAPIC work? | 1630 | enable_8259A_irq(0); |
1604 | */ | 1631 | unmask_IO_APIC_irq(0); |
1605 | unmask_IO_APIC_irq(0); | 1632 | if (try_apic_pin(apic1, pin1, "with 8259 IRQ0 enabled")) |
1606 | if (!no_timer_check && timer_irq_works()) { | 1633 | return; |
1607 | nmi_watchdog_default(); | 1634 | disable_8259A_irq(0); |
1608 | if (nmi_watchdog == NMI_IO_APIC) { | 1635 | |
1609 | disable_8259A_irq(0); | 1636 | /* Always try pin0 and pin2 on APIC 0 to handle buggy timer overrides |
1610 | setup_nmi(); | 1637 | on Nvidia boards */ |
1611 | enable_8259A_irq(0); | 1638 | if (!(apic1 == 0 && pin1 == 0) && |
1612 | } | 1639 | try_apic_pin(0, 0, "fallback with 8259 IRQ0 disabled")) |
1613 | if (disable_timer_pin_1 > 0) | 1640 | return; |
1614 | clear_IO_APIC_pin(0, pin1); | 1641 | if (!(apic1 == 0 && pin1 == 2) && |
1615 | return; | 1642 | try_apic_pin(0, 2, "fallback with 8259 IRQ0 disabled")) |
1616 | } | 1643 | return; |
1617 | clear_IO_APIC_pin(apic1, pin1); | ||
1618 | apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not " | ||
1619 | "connected to IO-APIC\n"); | ||
1620 | } | ||
1621 | 1644 | ||
1622 | apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) " | 1645 | /* Then try pure 8259A routing on the 8259 as reported by BIOS*/ |
1623 | "through the 8259A ... "); | 1646 | enable_8259A_irq(0); |
1624 | if (pin2 != -1) { | 1647 | if (pin2 != -1) { |
1625 | apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...", | ||
1626 | apic2, pin2); | ||
1627 | /* | ||
1628 | * legacy devices should be connected to IO APIC #0 | ||
1629 | */ | ||
1630 | setup_ExtINT_IRQ0_pin(apic2, pin2, vector); | 1648 | setup_ExtINT_IRQ0_pin(apic2, pin2, vector); |
1631 | if (timer_irq_works()) { | 1649 | if (try_apic_pin(apic2,pin2,"8259A broadcast ExtINT from BIOS")) |
1632 | apic_printk(APIC_VERBOSE," works.\n"); | ||
1633 | nmi_watchdog_default(); | ||
1634 | if (nmi_watchdog == NMI_IO_APIC) { | ||
1635 | setup_nmi(); | ||
1636 | } | ||
1637 | return; | 1650 | return; |
1638 | } | ||
1639 | /* | ||
1640 | * Cleanup, just in case ... | ||
1641 | */ | ||
1642 | clear_IO_APIC_pin(apic2, pin2); | ||
1643 | } | 1651 | } |
1644 | apic_printk(APIC_VERBOSE," failed.\n"); | 1652 | |
1653 | /* Tried all possibilities to go through the IO-APIC. Now come the | ||
1654 | really cheesy fallbacks. */ | ||
1645 | 1655 | ||
1646 | if (nmi_watchdog == NMI_IO_APIC) { | 1656 | if (nmi_watchdog == NMI_IO_APIC) { |
1647 | printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); | 1657 | printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); |
@@ -1837,7 +1847,7 @@ void destroy_irq(unsigned int irq) | |||
1837 | dynamic_irq_cleanup(irq); | 1847 | dynamic_irq_cleanup(irq); |
1838 | 1848 | ||
1839 | spin_lock_irqsave(&vector_lock, flags); | 1849 | spin_lock_irqsave(&vector_lock, flags); |
1840 | irq_vector[irq] = 0; | 1850 | __clear_irq_vector(irq); |
1841 | spin_unlock_irqrestore(&vector_lock, flags); | 1851 | spin_unlock_irqrestore(&vector_lock, flags); |
1842 | } | 1852 | } |
1843 | 1853 | ||
@@ -2139,7 +2149,15 @@ void __init setup_ioapic_dest(void) | |||
2139 | if (irq_entry == -1) | 2149 | if (irq_entry == -1) |
2140 | continue; | 2150 | continue; |
2141 | irq = pin_2_irq(irq_entry, ioapic, pin); | 2151 | irq = pin_2_irq(irq_entry, ioapic, pin); |
2142 | set_ioapic_affinity_irq(irq, TARGET_CPUS); | 2152 | |
2153 | /* setup_IO_APIC_irqs could fail to get vector for some device | ||
2154 | * when you have too many devices, because at that time only boot | ||
2155 | * cpu is online. | ||
2156 | */ | ||
2157 | if(!irq_vector[irq]) | ||
2158 | setup_IO_APIC_irq(ioapic, pin, irq_entry, irq); | ||
2159 | else | ||
2160 | set_ioapic_affinity_irq(irq, TARGET_CPUS); | ||
2143 | } | 2161 | } |
2144 | 2162 | ||
2145 | } | 2163 | } |