aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/io_apic.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2005-10-30 17:59:39 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2005-10-30 20:37:12 -0500
commitfcfd636a728fe2b8fb8c8fd8c557302059580577 (patch)
treef210e59393a9439875f5e65ff4b92c5a7fe7b9bb /arch/i386/kernel/io_apic.c
parent9338316c9323682d32270d83b106472a50ab6da4 (diff)
[PATCH] i386 io_apic.c: Memorize at bootup where the i8259 is connected
Currently we attempt to restore virtual wire mode on reboot, which only works if we can figure out where the i8259 is connected. This is very useful when we kexec another kernel and likely helpful when dealing with a BIOS that make assumptions about how the system is setup. Since the acpi MADT table does not provide the location where the i8259 is connected we have to look at the hardware to figure it out. Most systems have the i8259 connected the local apic of the cpu so won't be affected but people running Opteron and some serverworks chipsets should be able to use kexec now. In addition this patch removes the hard coded assumption that the io_apic that delivers isa interrups is always known to the kernel as io_apic 0. As there does not appear to be anything to guarantee that assumption is true. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/io_apic.c')
-rw-r--r--arch/i386/kernel/io_apic.c147
1 files changed, 112 insertions, 35 deletions
diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c
index fb3991e8229e..cc5d7ac5b2e7 100644
--- a/arch/i386/kernel/io_apic.c
+++ b/arch/i386/kernel/io_apic.c
@@ -46,6 +46,9 @@
46int (*ioapic_renumber_irq)(int ioapic, int irq); 46int (*ioapic_renumber_irq)(int ioapic, int irq);
47atomic_t irq_mis_count; 47atomic_t irq_mis_count;
48 48
49/* Where if anywhere is the i8259 connect in external int mode */
50static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
51
49static DEFINE_SPINLOCK(ioapic_lock); 52static 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 */
741static int find_isa_irq_pin(int irq, int type) 744static 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
764static 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 */
1256static void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) 1286static 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)
1595static void __init enable_IO_APIC(void) 1625static 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 */
1628void disable_IO_APIC(void) 1700void 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 */
2114static inline void unlock_ExtINT_logic(void) 2186static 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 */
2175static inline void check_timer(void) 2248static 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