diff options
Diffstat (limited to 'arch/i386/kernel/io_apic.c')
-rw-r--r-- | arch/i386/kernel/io_apic.c | 153 |
1 files changed, 117 insertions, 36 deletions
diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index fb3991e8229e..5a77c52b20a9 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c | |||
@@ -46,6 +46,9 @@ | |||
46 | int (*ioapic_renumber_irq)(int ioapic, int irq); | 46 | int (*ioapic_renumber_irq)(int ioapic, int irq); |
47 | atomic_t irq_mis_count; | 47 | atomic_t irq_mis_count; |
48 | 48 | ||
49 | /* Where if anywhere is the i8259 connect in external int mode */ | ||
50 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; | ||
51 | |||
49 | static DEFINE_SPINLOCK(ioapic_lock); | 52 | static DEFINE_SPINLOCK(ioapic_lock); |
50 | 53 | ||
51 | /* | 54 | /* |
@@ -738,7 +741,7 @@ static int find_irq_entry(int apic, int pin, int type) | |||
738 | /* | 741 | /* |
739 | * Find the pin to which IRQ[irq] (ISA) is connected | 742 | * Find the pin to which IRQ[irq] (ISA) is connected |
740 | */ | 743 | */ |
741 | static int find_isa_irq_pin(int irq, int type) | 744 | static int __init find_isa_irq_pin(int irq, int type) |
742 | { | 745 | { |
743 | int i; | 746 | int i; |
744 | 747 | ||
@@ -758,6 +761,33 @@ static int find_isa_irq_pin(int irq, int type) | |||
758 | return -1; | 761 | return -1; |
759 | } | 762 | } |
760 | 763 | ||
764 | static int __init find_isa_irq_apic(int irq, int type) | ||
765 | { | ||
766 | int i; | ||
767 | |||
768 | for (i = 0; i < mp_irq_entries; i++) { | ||
769 | int lbus = mp_irqs[i].mpc_srcbus; | ||
770 | |||
771 | if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || | ||
772 | mp_bus_id_to_type[lbus] == MP_BUS_EISA || | ||
773 | mp_bus_id_to_type[lbus] == MP_BUS_MCA || | ||
774 | mp_bus_id_to_type[lbus] == MP_BUS_NEC98 | ||
775 | ) && | ||
776 | (mp_irqs[i].mpc_irqtype == type) && | ||
777 | (mp_irqs[i].mpc_srcbusirq == irq)) | ||
778 | break; | ||
779 | } | ||
780 | if (i < mp_irq_entries) { | ||
781 | int apic; | ||
782 | for(apic = 0; apic < nr_ioapics; apic++) { | ||
783 | if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic) | ||
784 | return apic; | ||
785 | } | ||
786 | } | ||
787 | |||
788 | return -1; | ||
789 | } | ||
790 | |||
761 | /* | 791 | /* |
762 | * Find a specific PCI IRQ entry. | 792 | * Find a specific PCI IRQ entry. |
763 | * Not an __init, possibly needed by modules | 793 | * Not an __init, possibly needed by modules |
@@ -1253,7 +1283,7 @@ static void __init setup_IO_APIC_irqs(void) | |||
1253 | /* | 1283 | /* |
1254 | * Set up the 8259A-master output pin: | 1284 | * Set up the 8259A-master output pin: |
1255 | */ | 1285 | */ |
1256 | static void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) | 1286 | static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, int vector) |
1257 | { | 1287 | { |
1258 | struct IO_APIC_route_entry entry; | 1288 | struct IO_APIC_route_entry entry; |
1259 | unsigned long flags; | 1289 | unsigned long flags; |
@@ -1287,8 +1317,8 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) | |||
1287 | * Add it to the IO-APIC irq-routing table: | 1317 | * Add it to the IO-APIC irq-routing table: |
1288 | */ | 1318 | */ |
1289 | spin_lock_irqsave(&ioapic_lock, flags); | 1319 | spin_lock_irqsave(&ioapic_lock, flags); |
1290 | io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); | 1320 | io_apic_write(apic, 0x11+2*pin, *(((int *)&entry)+1)); |
1291 | io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); | 1321 | io_apic_write(apic, 0x10+2*pin, *(((int *)&entry)+0)); |
1292 | spin_unlock_irqrestore(&ioapic_lock, flags); | 1322 | spin_unlock_irqrestore(&ioapic_lock, flags); |
1293 | 1323 | ||
1294 | enable_8259A_irq(0); | 1324 | enable_8259A_irq(0); |
@@ -1595,7 +1625,8 @@ void /*__init*/ print_PIC(void) | |||
1595 | static void __init enable_IO_APIC(void) | 1625 | static void __init enable_IO_APIC(void) |
1596 | { | 1626 | { |
1597 | union IO_APIC_reg_01 reg_01; | 1627 | union IO_APIC_reg_01 reg_01; |
1598 | int i; | 1628 | int i8259_apic, i8259_pin; |
1629 | int i, apic; | ||
1599 | unsigned long flags; | 1630 | unsigned long flags; |
1600 | 1631 | ||
1601 | for (i = 0; i < PIN_MAP_SIZE; i++) { | 1632 | for (i = 0; i < PIN_MAP_SIZE; i++) { |
@@ -1609,11 +1640,52 @@ static void __init enable_IO_APIC(void) | |||
1609 | /* | 1640 | /* |
1610 | * The number of IO-APIC IRQ registers (== #pins): | 1641 | * The number of IO-APIC IRQ registers (== #pins): |
1611 | */ | 1642 | */ |
1612 | for (i = 0; i < nr_ioapics; i++) { | 1643 | for (apic = 0; apic < nr_ioapics; apic++) { |
1613 | spin_lock_irqsave(&ioapic_lock, flags); | 1644 | spin_lock_irqsave(&ioapic_lock, flags); |
1614 | reg_01.raw = io_apic_read(i, 1); | 1645 | reg_01.raw = io_apic_read(apic, 1); |
1615 | spin_unlock_irqrestore(&ioapic_lock, flags); | 1646 | spin_unlock_irqrestore(&ioapic_lock, flags); |
1616 | nr_ioapic_registers[i] = reg_01.bits.entries+1; | 1647 | nr_ioapic_registers[apic] = reg_01.bits.entries+1; |
1648 | } | ||
1649 | for(apic = 0; apic < nr_ioapics; apic++) { | ||
1650 | int pin; | ||
1651 | /* See if any of the pins is in ExtINT mode */ | ||
1652 | for(pin = 0; pin < nr_ioapic_registers[i]; pin++) { | ||
1653 | struct IO_APIC_route_entry entry; | ||
1654 | spin_lock_irqsave(&ioapic_lock, flags); | ||
1655 | *(((int *)&entry) + 0) = io_apic_read(apic, 0x10 + 2 * pin); | ||
1656 | *(((int *)&entry) + 1) = io_apic_read(apic, 0x11 + 2 * pin); | ||
1657 | spin_unlock_irqrestore(&ioapic_lock, flags); | ||
1658 | |||
1659 | |||
1660 | /* If the interrupt line is enabled and in ExtInt mode | ||
1661 | * I have found the pin where the i8259 is connected. | ||
1662 | */ | ||
1663 | if ((entry.mask == 0) && (entry.delivery_mode == dest_ExtINT)) { | ||
1664 | ioapic_i8259.apic = apic; | ||
1665 | ioapic_i8259.pin = pin; | ||
1666 | goto found_i8259; | ||
1667 | } | ||
1668 | } | ||
1669 | } | ||
1670 | found_i8259: | ||
1671 | /* Look to see what if the MP table has reported the ExtINT */ | ||
1672 | /* If we could not find the appropriate pin by looking at the ioapic | ||
1673 | * the i8259 probably is not connected the ioapic but give the | ||
1674 | * mptable a chance anyway. | ||
1675 | */ | ||
1676 | i8259_pin = find_isa_irq_pin(0, mp_ExtINT); | ||
1677 | i8259_apic = find_isa_irq_apic(0, mp_ExtINT); | ||
1678 | /* Trust the MP table if nothing is setup in the hardware */ | ||
1679 | if ((ioapic_i8259.pin == -1) && (i8259_pin >= 0)) { | ||
1680 | printk(KERN_WARNING "ExtINT not setup in hardware but reported by MP table\n"); | ||
1681 | ioapic_i8259.pin = i8259_pin; | ||
1682 | ioapic_i8259.apic = i8259_apic; | ||
1683 | } | ||
1684 | /* Complain if the MP table and the hardware disagree */ | ||
1685 | if (((ioapic_i8259.apic != i8259_apic) || (ioapic_i8259.pin != i8259_pin)) && | ||
1686 | (i8259_pin >= 0) && (ioapic_i8259.pin >= 0)) | ||
1687 | { | ||
1688 | printk(KERN_WARNING "ExtINT in hardware and MP table differ\n"); | ||
1617 | } | 1689 | } |
1618 | 1690 | ||
1619 | /* | 1691 | /* |
@@ -1627,7 +1699,6 @@ static void __init enable_IO_APIC(void) | |||
1627 | */ | 1699 | */ |
1628 | void disable_IO_APIC(void) | 1700 | void disable_IO_APIC(void) |
1629 | { | 1701 | { |
1630 | int pin; | ||
1631 | /* | 1702 | /* |
1632 | * Clear the IO-APIC before rebooting: | 1703 | * Clear the IO-APIC before rebooting: |
1633 | */ | 1704 | */ |
@@ -1638,8 +1709,7 @@ void disable_IO_APIC(void) | |||
1638 | * Put that IOAPIC in virtual wire mode | 1709 | * Put that IOAPIC in virtual wire mode |
1639 | * so legacy interrupts can be delivered. | 1710 | * so legacy interrupts can be delivered. |
1640 | */ | 1711 | */ |
1641 | pin = find_isa_irq_pin(0, mp_ExtINT); | 1712 | if (ioapic_i8259.pin != -1) { |
1642 | if (pin != -1) { | ||
1643 | struct IO_APIC_route_entry entry; | 1713 | struct IO_APIC_route_entry entry; |
1644 | unsigned long flags; | 1714 | unsigned long flags; |
1645 | 1715 | ||
@@ -1650,7 +1720,7 @@ void disable_IO_APIC(void) | |||
1650 | entry.polarity = 0; /* High */ | 1720 | entry.polarity = 0; /* High */ |
1651 | entry.delivery_status = 0; | 1721 | entry.delivery_status = 0; |
1652 | entry.dest_mode = 0; /* Physical */ | 1722 | entry.dest_mode = 0; /* Physical */ |
1653 | entry.delivery_mode = 7; /* ExtInt */ | 1723 | entry.delivery_mode = dest_ExtINT; /* ExtInt */ |
1654 | entry.vector = 0; | 1724 | entry.vector = 0; |
1655 | entry.dest.physical.physical_dest = 0; | 1725 | entry.dest.physical.physical_dest = 0; |
1656 | 1726 | ||
@@ -1659,11 +1729,13 @@ void disable_IO_APIC(void) | |||
1659 | * Add it to the IO-APIC irq-routing table: | 1729 | * Add it to the IO-APIC irq-routing table: |
1660 | */ | 1730 | */ |
1661 | spin_lock_irqsave(&ioapic_lock, flags); | 1731 | spin_lock_irqsave(&ioapic_lock, flags); |
1662 | io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); | 1732 | io_apic_write(ioapic_i8259.apic, 0x11+2*ioapic_i8259.pin, |
1663 | io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); | 1733 | *(((int *)&entry)+1)); |
1734 | io_apic_write(ioapic_i8259.apic, 0x10+2*ioapic_i8259.pin, | ||
1735 | *(((int *)&entry)+0)); | ||
1664 | spin_unlock_irqrestore(&ioapic_lock, flags); | 1736 | spin_unlock_irqrestore(&ioapic_lock, flags); |
1665 | } | 1737 | } |
1666 | disconnect_bsp_APIC(pin != -1); | 1738 | disconnect_bsp_APIC(ioapic_i8259.pin != -1); |
1667 | } | 1739 | } |
1668 | 1740 | ||
1669 | /* | 1741 | /* |
@@ -2113,20 +2185,21 @@ static void setup_nmi (void) | |||
2113 | */ | 2185 | */ |
2114 | static inline void unlock_ExtINT_logic(void) | 2186 | static inline void unlock_ExtINT_logic(void) |
2115 | { | 2187 | { |
2116 | int pin, i; | 2188 | int apic, pin, i; |
2117 | struct IO_APIC_route_entry entry0, entry1; | 2189 | struct IO_APIC_route_entry entry0, entry1; |
2118 | unsigned char save_control, save_freq_select; | 2190 | unsigned char save_control, save_freq_select; |
2119 | unsigned long flags; | 2191 | unsigned long flags; |
2120 | 2192 | ||
2121 | pin = find_isa_irq_pin(8, mp_INT); | 2193 | pin = find_isa_irq_pin(8, mp_INT); |
2194 | apic = find_isa_irq_apic(8, mp_INT); | ||
2122 | if (pin == -1) | 2195 | if (pin == -1) |
2123 | return; | 2196 | return; |
2124 | 2197 | ||
2125 | spin_lock_irqsave(&ioapic_lock, flags); | 2198 | spin_lock_irqsave(&ioapic_lock, flags); |
2126 | *(((int *)&entry0) + 1) = io_apic_read(0, 0x11 + 2 * pin); | 2199 | *(((int *)&entry0) + 1) = io_apic_read(apic, 0x11 + 2 * pin); |
2127 | *(((int *)&entry0) + 0) = io_apic_read(0, 0x10 + 2 * pin); | 2200 | *(((int *)&entry0) + 0) = io_apic_read(apic, 0x10 + 2 * pin); |
2128 | spin_unlock_irqrestore(&ioapic_lock, flags); | 2201 | spin_unlock_irqrestore(&ioapic_lock, flags); |
2129 | clear_IO_APIC_pin(0, pin); | 2202 | clear_IO_APIC_pin(apic, pin); |
2130 | 2203 | ||
2131 | memset(&entry1, 0, sizeof(entry1)); | 2204 | memset(&entry1, 0, sizeof(entry1)); |
2132 | 2205 | ||
@@ -2139,8 +2212,8 @@ static inline void unlock_ExtINT_logic(void) | |||
2139 | entry1.vector = 0; | 2212 | entry1.vector = 0; |
2140 | 2213 | ||
2141 | spin_lock_irqsave(&ioapic_lock, flags); | 2214 | spin_lock_irqsave(&ioapic_lock, flags); |
2142 | io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); | 2215 | io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); |
2143 | io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); | 2216 | io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); |
2144 | spin_unlock_irqrestore(&ioapic_lock, flags); | 2217 | spin_unlock_irqrestore(&ioapic_lock, flags); |
2145 | 2218 | ||
2146 | save_control = CMOS_READ(RTC_CONTROL); | 2219 | save_control = CMOS_READ(RTC_CONTROL); |
@@ -2158,11 +2231,11 @@ static inline void unlock_ExtINT_logic(void) | |||
2158 | 2231 | ||
2159 | CMOS_WRITE(save_control, RTC_CONTROL); | 2232 | CMOS_WRITE(save_control, RTC_CONTROL); |
2160 | CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); | 2233 | CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); |
2161 | clear_IO_APIC_pin(0, pin); | 2234 | clear_IO_APIC_pin(apic, pin); |
2162 | 2235 | ||
2163 | spin_lock_irqsave(&ioapic_lock, flags); | 2236 | spin_lock_irqsave(&ioapic_lock, flags); |
2164 | io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); | 2237 | io_apic_write(apic, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); |
2165 | io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); | 2238 | io_apic_write(apic, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); |
2166 | spin_unlock_irqrestore(&ioapic_lock, flags); | 2239 | spin_unlock_irqrestore(&ioapic_lock, flags); |
2167 | } | 2240 | } |
2168 | 2241 | ||
@@ -2174,7 +2247,7 @@ static inline void unlock_ExtINT_logic(void) | |||
2174 | */ | 2247 | */ |
2175 | static inline void check_timer(void) | 2248 | static inline void check_timer(void) |
2176 | { | 2249 | { |
2177 | int pin1, pin2; | 2250 | int apic1, pin1, apic2, pin2; |
2178 | int vector; | 2251 | int vector; |
2179 | 2252 | ||
2180 | /* | 2253 | /* |
@@ -2196,10 +2269,13 @@ static inline void check_timer(void) | |||
2196 | timer_ack = 1; | 2269 | timer_ack = 1; |
2197 | enable_8259A_irq(0); | 2270 | enable_8259A_irq(0); |
2198 | 2271 | ||
2199 | pin1 = find_isa_irq_pin(0, mp_INT); | 2272 | pin1 = find_isa_irq_pin(0, mp_INT); |
2200 | pin2 = find_isa_irq_pin(0, mp_ExtINT); | 2273 | apic1 = find_isa_irq_apic(0, mp_INT); |
2274 | pin2 = ioapic_i8259.pin; | ||
2275 | apic2 = ioapic_i8259.apic; | ||
2201 | 2276 | ||
2202 | printk(KERN_INFO "..TIMER: vector=0x%02X pin1=%d pin2=%d\n", vector, pin1, pin2); | 2277 | printk(KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n", |
2278 | vector, apic1, pin1, apic2, pin2); | ||
2203 | 2279 | ||
2204 | if (pin1 != -1) { | 2280 | if (pin1 != -1) { |
2205 | /* | 2281 | /* |
@@ -2216,8 +2292,9 @@ static inline void check_timer(void) | |||
2216 | clear_IO_APIC_pin(0, pin1); | 2292 | clear_IO_APIC_pin(0, pin1); |
2217 | return; | 2293 | return; |
2218 | } | 2294 | } |
2219 | clear_IO_APIC_pin(0, pin1); | 2295 | clear_IO_APIC_pin(apic1, pin1); |
2220 | printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); | 2296 | printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to " |
2297 | "IO-APIC\n"); | ||
2221 | } | 2298 | } |
2222 | 2299 | ||
2223 | printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); | 2300 | printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); |
@@ -2226,13 +2303,13 @@ static inline void check_timer(void) | |||
2226 | /* | 2303 | /* |
2227 | * legacy devices should be connected to IO APIC #0 | 2304 | * legacy devices should be connected to IO APIC #0 |
2228 | */ | 2305 | */ |
2229 | setup_ExtINT_IRQ0_pin(pin2, vector); | 2306 | setup_ExtINT_IRQ0_pin(apic2, pin2, vector); |
2230 | if (timer_irq_works()) { | 2307 | if (timer_irq_works()) { |
2231 | printk("works.\n"); | 2308 | printk("works.\n"); |
2232 | if (pin1 != -1) | 2309 | if (pin1 != -1) |
2233 | replace_pin_at_irq(0, 0, pin1, 0, pin2); | 2310 | replace_pin_at_irq(0, apic1, pin1, apic2, pin2); |
2234 | else | 2311 | else |
2235 | add_pin_to_irq(0, 0, pin2); | 2312 | add_pin_to_irq(0, apic2, pin2); |
2236 | if (nmi_watchdog == NMI_IO_APIC) { | 2313 | if (nmi_watchdog == NMI_IO_APIC) { |
2237 | setup_nmi(); | 2314 | setup_nmi(); |
2238 | } | 2315 | } |
@@ -2241,7 +2318,7 @@ static inline void check_timer(void) | |||
2241 | /* | 2318 | /* |
2242 | * Cleanup, just in case ... | 2319 | * Cleanup, just in case ... |
2243 | */ | 2320 | */ |
2244 | clear_IO_APIC_pin(0, pin2); | 2321 | clear_IO_APIC_pin(apic2, pin2); |
2245 | } | 2322 | } |
2246 | printk(" failed.\n"); | 2323 | printk(" failed.\n"); |
2247 | 2324 | ||
@@ -2310,11 +2387,15 @@ void __init setup_IO_APIC(void) | |||
2310 | sync_Arb_IDs(); | 2387 | sync_Arb_IDs(); |
2311 | setup_IO_APIC_irqs(); | 2388 | setup_IO_APIC_irqs(); |
2312 | init_IO_APIC_traps(); | 2389 | init_IO_APIC_traps(); |
2313 | check_timer(); | ||
2314 | if (!acpi_ioapic) | 2390 | if (!acpi_ioapic) |
2315 | print_IO_APIC(); | 2391 | print_IO_APIC(); |
2316 | } | 2392 | } |
2317 | 2393 | ||
2394 | void __init IO_APIC_late_time_init(void) | ||
2395 | { | ||
2396 | check_timer(); | ||
2397 | } | ||
2398 | |||
2318 | /* | 2399 | /* |
2319 | * Called after all the initialization is done. If we didnt find any | 2400 | * Called after all the initialization is done. If we didnt find any |
2320 | * APIC bugs then we can allow the modify fast path | 2401 | * APIC bugs then we can allow the modify fast path |