| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *	Intel IO-APIC support for multi-Pentium hosts. | 
 | 3 |  * | 
 | 4 |  *	Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar, Hajnalka Szabo | 
 | 5 |  * | 
 | 6 |  *	Many thanks to Stig Venaas for trying out countless experimental | 
 | 7 |  *	patches and reporting/debugging problems patiently! | 
 | 8 |  * | 
 | 9 |  *	(c) 1999, Multiple IO-APIC support, developed by | 
 | 10 |  *	Ken-ichi Yaku <yaku@css1.kbnes.nec.co.jp> and | 
 | 11 |  *      Hidemi Kishimoto <kisimoto@css1.kbnes.nec.co.jp>, | 
 | 12 |  *	further tested and cleaned up by Zach Brown <zab@redhat.com> | 
 | 13 |  *	and Ingo Molnar <mingo@redhat.com> | 
 | 14 |  * | 
 | 15 |  *	Fixes | 
 | 16 |  *	Maciej W. Rozycki	:	Bits for genuine 82489DX APICs; | 
 | 17 |  *					thanks to Eric Gilmore | 
 | 18 |  *					and Rolf G. Tews | 
 | 19 |  *					for testing these extensively | 
 | 20 |  *	Paul Diefenbaugh	:	Added full ACPI support | 
 | 21 |  */ | 
 | 22 |  | 
 | 23 | #include <linux/mm.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 24 | #include <linux/interrupt.h> | 
 | 25 | #include <linux/init.h> | 
 | 26 | #include <linux/delay.h> | 
 | 27 | #include <linux/sched.h> | 
| Yinghai Lu | d4057bd | 2008-08-19 20:50:38 -0700 | [diff] [blame] | 28 | #include <linux/pci.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 29 | #include <linux/mc146818rtc.h> | 
 | 30 | #include <linux/compiler.h> | 
 | 31 | #include <linux/acpi.h> | 
| Alexey Dobriyan | 129f694 | 2005-06-23 00:08:33 -0700 | [diff] [blame] | 32 | #include <linux/module.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 | #include <linux/sysdev.h> | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 34 | #include <linux/msi.h> | 
| Eric W. Biederman | 95d7788 | 2006-10-04 02:17:01 -0700 | [diff] [blame] | 35 | #include <linux/htirq.h> | 
| Nigel Cunningham | 7dfb710 | 2006-12-06 20:34:23 -0800 | [diff] [blame] | 36 | #include <linux/freezer.h> | 
| Eric W. Biederman | f26d6a2 | 2007-05-02 19:27:19 +0200 | [diff] [blame] | 37 | #include <linux/kthread.h> | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 38 | #include <linux/jiffies.h>	/* time_after() */ | 
| Yinghai Lu | d4057bd | 2008-08-19 20:50:38 -0700 | [diff] [blame] | 39 | #ifdef CONFIG_ACPI | 
 | 40 | #include <acpi/acpi_bus.h> | 
 | 41 | #endif | 
 | 42 | #include <linux/bootmem.h> | 
 | 43 | #include <linux/dmar.h> | 
| venkatesh.pallipadi@intel.com | 58ac1e7 | 2008-09-05 18:02:17 -0700 | [diff] [blame] | 44 | #include <linux/hpet.h> | 
| Ashok Raj | 54d5d42 | 2005-09-06 15:16:15 -0700 | [diff] [blame] | 45 |  | 
| Yinghai Lu | d4057bd | 2008-08-19 20:50:38 -0700 | [diff] [blame] | 46 | #include <asm/idle.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 47 | #include <asm/io.h> | 
 | 48 | #include <asm/smp.h> | 
 | 49 | #include <asm/desc.h> | 
| Yinghai Lu | d4057bd | 2008-08-19 20:50:38 -0700 | [diff] [blame] | 50 | #include <asm/proto.h> | 
 | 51 | #include <asm/acpi.h> | 
 | 52 | #include <asm/dma.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 | #include <asm/timer.h> | 
| Ingo Molnar | 306e440 | 2005-06-30 02:58:55 -0700 | [diff] [blame] | 54 | #include <asm/i8259.h> | 
| Don Zickus | 3e4ff11 | 2006-06-26 13:57:01 +0200 | [diff] [blame] | 55 | #include <asm/nmi.h> | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 56 | #include <asm/msidef.h> | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 57 | #include <asm/hypertransport.h> | 
| Yinghai Lu | a4dbc34 | 2008-07-25 02:14:28 -0700 | [diff] [blame] | 58 | #include <asm/setup.h> | 
| Yinghai Lu | d4057bd | 2008-08-19 20:50:38 -0700 | [diff] [blame] | 59 | #include <asm/irq_remapping.h> | 
| venkatesh.pallipadi@intel.com | 58ac1e7 | 2008-09-05 18:02:17 -0700 | [diff] [blame] | 60 | #include <asm/hpet.h> | 
| Dean Nelson | 4173a0e | 2008-10-02 12:18:21 -0500 | [diff] [blame] | 61 | #include <asm/uv/uv_hub.h> | 
 | 62 | #include <asm/uv/uv_irq.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 64 | #include <mach_ipi.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 65 | #include <mach_apic.h> | 
| Andi Kleen | 874c4fe | 2006-09-26 10:52:26 +0200 | [diff] [blame] | 66 | #include <mach_apicdef.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 |  | 
| Maciej W. Rozycki | 32f71af | 2008-07-21 00:52:49 +0100 | [diff] [blame] | 68 | #define __apicdebuginit(type) static type __init | 
 | 69 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | /* | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 71 |  *      Is the SiS APIC rmw bug present ? | 
 | 72 |  *      -1 = don't know, 0 = no, 1 = yes | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 73 |  */ | 
 | 74 | int sis_apic_bug = -1; | 
 | 75 |  | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 76 | static DEFINE_SPINLOCK(ioapic_lock); | 
 | 77 | static DEFINE_SPINLOCK(vector_lock); | 
 | 78 |  | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 79 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 80 |  * # of IRQ routing registers | 
 | 81 |  */ | 
 | 82 | int nr_ioapic_registers[MAX_IO_APICS]; | 
 | 83 |  | 
| Alexey Starikovskiy | 9f640cc | 2008-04-04 23:41:13 +0400 | [diff] [blame] | 84 | /* I/O APIC entries */ | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 85 | struct mp_config_ioapic mp_ioapics[MAX_IO_APICS]; | 
| Alexey Starikovskiy | 9f640cc | 2008-04-04 23:41:13 +0400 | [diff] [blame] | 86 | int nr_ioapics; | 
 | 87 |  | 
| Alexey Starikovskiy | 584f734 | 2008-04-04 23:41:32 +0400 | [diff] [blame] | 88 | /* MP IRQ source entries */ | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 89 | struct mp_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; | 
| Alexey Starikovskiy | 584f734 | 2008-04-04 23:41:32 +0400 | [diff] [blame] | 90 |  | 
 | 91 | /* # of MP IRQ source entries */ | 
 | 92 | int mp_irq_entries; | 
 | 93 |  | 
| Alexey Starikovskiy | 8732fc4 | 2008-05-19 19:47:16 +0400 | [diff] [blame] | 94 | #if defined (CONFIG_MCA) || defined (CONFIG_EISA) | 
 | 95 | int mp_bus_id_to_type[MAX_MP_BUSSES]; | 
 | 96 | #endif | 
 | 97 |  | 
 | 98 | DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES); | 
 | 99 |  | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 100 | int skip_ioapic_setup; | 
 | 101 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 102 | static int __init parse_noapic(char *str) | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 103 | { | 
 | 104 | 	/* disable IO-APIC */ | 
 | 105 | 	disable_ioapic_setup(); | 
 | 106 | 	return 0; | 
 | 107 | } | 
 | 108 | early_param("noapic", parse_noapic); | 
| Chuck Ebbert | 66759a0 | 2005-09-12 18:49:25 +0200 | [diff] [blame] | 109 |  | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 110 | struct irq_pin_list; | 
| Yinghai Lu | a1420f3 | 2008-08-19 20:50:24 -0700 | [diff] [blame] | 111 | struct irq_cfg { | 
| Yinghai Lu | da51a82 | 2008-08-19 20:50:25 -0700 | [diff] [blame] | 112 | 	unsigned int irq; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 113 | 	struct irq_pin_list *irq_2_pin; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 114 | 	cpumask_t domain; | 
 | 115 | 	cpumask_t old_domain; | 
 | 116 | 	unsigned move_cleanup_count; | 
| Yinghai Lu | a1420f3 | 2008-08-19 20:50:24 -0700 | [diff] [blame] | 117 | 	u8 vector; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 118 | 	u8 move_in_progress : 1; | 
| Yinghai Lu | a1420f3 | 2008-08-19 20:50:24 -0700 | [diff] [blame] | 119 | }; | 
 | 120 |  | 
| Yinghai Lu | a1420f3 | 2008-08-19 20:50:24 -0700 | [diff] [blame] | 121 | /* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */ | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 122 | static struct irq_cfg irq_cfgx[NR_IRQS] = { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 123 | 	[0]  = { .irq =  0, .domain = CPU_MASK_ALL, .vector = IRQ0_VECTOR,  }, | 
 | 124 | 	[1]  = { .irq =  1, .domain = CPU_MASK_ALL, .vector = IRQ1_VECTOR,  }, | 
 | 125 | 	[2]  = { .irq =  2, .domain = CPU_MASK_ALL, .vector = IRQ2_VECTOR,  }, | 
 | 126 | 	[3]  = { .irq =  3, .domain = CPU_MASK_ALL, .vector = IRQ3_VECTOR,  }, | 
 | 127 | 	[4]  = { .irq =  4, .domain = CPU_MASK_ALL, .vector = IRQ4_VECTOR,  }, | 
 | 128 | 	[5]  = { .irq =  5, .domain = CPU_MASK_ALL, .vector = IRQ5_VECTOR,  }, | 
 | 129 | 	[6]  = { .irq =  6, .domain = CPU_MASK_ALL, .vector = IRQ6_VECTOR,  }, | 
 | 130 | 	[7]  = { .irq =  7, .domain = CPU_MASK_ALL, .vector = IRQ7_VECTOR,  }, | 
 | 131 | 	[8]  = { .irq =  8, .domain = CPU_MASK_ALL, .vector = IRQ8_VECTOR,  }, | 
 | 132 | 	[9]  = { .irq =  9, .domain = CPU_MASK_ALL, .vector = IRQ9_VECTOR,  }, | 
 | 133 | 	[10] = { .irq = 10, .domain = CPU_MASK_ALL, .vector = IRQ10_VECTOR, }, | 
 | 134 | 	[11] = { .irq = 11, .domain = CPU_MASK_ALL, .vector = IRQ11_VECTOR, }, | 
 | 135 | 	[12] = { .irq = 12, .domain = CPU_MASK_ALL, .vector = IRQ12_VECTOR, }, | 
 | 136 | 	[13] = { .irq = 13, .domain = CPU_MASK_ALL, .vector = IRQ13_VECTOR, }, | 
 | 137 | 	[14] = { .irq = 14, .domain = CPU_MASK_ALL, .vector = IRQ14_VECTOR, }, | 
 | 138 | 	[15] = { .irq = 15, .domain = CPU_MASK_ALL, .vector = IRQ15_VECTOR, }, | 
| Yinghai Lu | a1420f3 | 2008-08-19 20:50:24 -0700 | [diff] [blame] | 139 | }; | 
 | 140 |  | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 141 | #define for_each_irq_cfg(irq, cfg)		\ | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 142 | 	for (irq = 0, cfg = irq_cfgx; irq < nr_irqs; irq++, cfg++) | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 143 |  | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 144 | static struct irq_cfg *irq_cfg(unsigned int irq) | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 145 | { | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 146 | 	return irq < nr_irqs ? irq_cfgx + irq : NULL; | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 147 | } | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 148 |  | 
 | 149 | static struct irq_cfg *irq_cfg_alloc(unsigned int irq) | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 150 | { | 
| Thomas Gleixner | 2cc21ef | 2008-10-15 14:16:55 +0200 | [diff] [blame] | 151 | 	return irq_cfg(irq); | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 152 | } | 
 | 153 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 154 | /* | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 155 |  * Rough estimation of how many shared IRQs there are, can be changed | 
 | 156 |  * anytime. | 
 | 157 |  */ | 
 | 158 | #define MAX_PLUS_SHARED_IRQS NR_IRQS | 
 | 159 | #define PIN_MAP_SIZE (MAX_PLUS_SHARED_IRQS + NR_IRQS) | 
 | 160 |  | 
 | 161 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 162 |  * This is performance-critical, we want to do it O(1) | 
 | 163 |  * | 
 | 164 |  * the indexing order of this array favors 1:1 mappings | 
 | 165 |  * between pins and IRQs. | 
 | 166 |  */ | 
 | 167 |  | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 168 | struct irq_pin_list { | 
 | 169 | 	int apic, pin; | 
 | 170 | 	struct irq_pin_list *next; | 
 | 171 | }; | 
| Yinghai Lu | 301e619 | 2008-08-19 20:50:02 -0700 | [diff] [blame] | 172 |  | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 173 | static struct irq_pin_list irq_2_pin_head[PIN_MAP_SIZE]; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 174 | static struct irq_pin_list *irq_2_pin_ptr; | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 175 |  | 
 | 176 | static void __init irq_2_pin_init(void) | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 177 | { | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 178 | 	struct irq_pin_list *pin = irq_2_pin_head; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 179 | 	int i; | 
 | 180 |  | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 181 | 	for (i = 1; i < PIN_MAP_SIZE; i++) | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 182 | 		pin[i-1].next = &pin[i]; | 
 | 183 |  | 
 | 184 | 	irq_2_pin_ptr = &pin[0]; | 
 | 185 | } | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 186 |  | 
 | 187 | static struct irq_pin_list *get_one_free_irq_2_pin(void) | 
 | 188 | { | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 189 | 	struct irq_pin_list *pin = irq_2_pin_ptr; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 190 |  | 
 | 191 | 	if (!pin) | 
 | 192 | 		panic("can not get more irq_2_pin\n"); | 
 | 193 |  | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 194 | 	irq_2_pin_ptr = pin->next; | 
 | 195 | 	pin->next = NULL; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 196 | 	return pin; | 
 | 197 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 |  | 
| Linus Torvalds | 130fe05 | 2006-11-01 09:11:00 -0800 | [diff] [blame] | 199 | struct io_apic { | 
 | 200 | 	unsigned int index; | 
 | 201 | 	unsigned int unused[3]; | 
 | 202 | 	unsigned int data; | 
 | 203 | }; | 
 | 204 |  | 
 | 205 | static __attribute_const__ struct io_apic __iomem *io_apic_base(int idx) | 
 | 206 | { | 
 | 207 | 	return (void __iomem *) __fix_to_virt(FIX_IO_APIC_BASE_0 + idx) | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 208 | 		+ (mp_ioapics[idx].mp_apicaddr & ~PAGE_MASK); | 
| Linus Torvalds | 130fe05 | 2006-11-01 09:11:00 -0800 | [diff] [blame] | 209 | } | 
 | 210 |  | 
 | 211 | static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg) | 
 | 212 | { | 
 | 213 | 	struct io_apic __iomem *io_apic = io_apic_base(apic); | 
 | 214 | 	writel(reg, &io_apic->index); | 
 | 215 | 	return readl(&io_apic->data); | 
 | 216 | } | 
 | 217 |  | 
 | 218 | static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value) | 
 | 219 | { | 
 | 220 | 	struct io_apic __iomem *io_apic = io_apic_base(apic); | 
 | 221 | 	writel(reg, &io_apic->index); | 
 | 222 | 	writel(value, &io_apic->data); | 
 | 223 | } | 
 | 224 |  | 
 | 225 | /* | 
 | 226 |  * Re-write a value: to be used for read-modify-write | 
 | 227 |  * cycles where the read already set up the index register. | 
 | 228 |  * | 
 | 229 |  * Older SiS APIC requires we rewrite the index register | 
 | 230 |  */ | 
 | 231 | static inline void io_apic_modify(unsigned int apic, unsigned int reg, unsigned int value) | 
 | 232 | { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 233 | 	struct io_apic __iomem *io_apic = io_apic_base(apic); | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 234 |  | 
 | 235 | 	if (sis_apic_bug) | 
 | 236 | 		writel(reg, &io_apic->index); | 
| Linus Torvalds | 130fe05 | 2006-11-01 09:11:00 -0800 | [diff] [blame] | 237 | 	writel(value, &io_apic->data); | 
 | 238 | } | 
 | 239 |  | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 240 | static bool io_apic_level_ack_pending(unsigned int irq) | 
 | 241 | { | 
 | 242 | 	struct irq_pin_list *entry; | 
 | 243 | 	unsigned long flags; | 
 | 244 | 	struct irq_cfg *cfg = irq_cfg(irq); | 
 | 245 |  | 
 | 246 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 247 | 	entry = cfg->irq_2_pin; | 
 | 248 | 	for (;;) { | 
 | 249 | 		unsigned int reg; | 
 | 250 | 		int pin; | 
 | 251 |  | 
 | 252 | 		if (!entry) | 
 | 253 | 			break; | 
 | 254 | 		pin = entry->pin; | 
 | 255 | 		reg = io_apic_read(entry->apic, 0x10 + pin*2); | 
 | 256 | 		/* Is the remote IRR bit set? */ | 
 | 257 | 		if (reg & IO_APIC_REDIR_REMOTE_IRR) { | 
 | 258 | 			spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 259 | 			return true; | 
 | 260 | 		} | 
 | 261 | 		if (!entry->next) | 
 | 262 | 			break; | 
 | 263 | 		entry = entry->next; | 
 | 264 | 	} | 
 | 265 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 266 |  | 
 | 267 | 	return false; | 
 | 268 | } | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 269 |  | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 270 | union entry_union { | 
 | 271 | 	struct { u32 w1, w2; }; | 
 | 272 | 	struct IO_APIC_route_entry entry; | 
 | 273 | }; | 
 | 274 |  | 
 | 275 | static struct IO_APIC_route_entry ioapic_read_entry(int apic, int pin) | 
 | 276 | { | 
 | 277 | 	union entry_union eu; | 
 | 278 | 	unsigned long flags; | 
 | 279 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 280 | 	eu.w1 = io_apic_read(apic, 0x10 + 2 * pin); | 
 | 281 | 	eu.w2 = io_apic_read(apic, 0x11 + 2 * pin); | 
 | 282 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 283 | 	return eu.entry; | 
 | 284 | } | 
 | 285 |  | 
| Linus Torvalds | f9dadfa | 2006-11-01 10:05:35 -0800 | [diff] [blame] | 286 | /* | 
 | 287 |  * When we write a new IO APIC routing entry, we need to write the high | 
 | 288 |  * word first! If the mask bit in the low word is clear, we will enable | 
 | 289 |  * the interrupt, and we need to make sure the entry is fully populated | 
 | 290 |  * before that happens. | 
 | 291 |  */ | 
| Andi Kleen | d15512f | 2006-12-07 02:14:07 +0100 | [diff] [blame] | 292 | static void | 
 | 293 | __ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) | 
 | 294 | { | 
 | 295 | 	union entry_union eu; | 
 | 296 | 	eu.entry = e; | 
 | 297 | 	io_apic_write(apic, 0x11 + 2*pin, eu.w2); | 
 | 298 | 	io_apic_write(apic, 0x10 + 2*pin, eu.w1); | 
 | 299 | } | 
 | 300 |  | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 301 | static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) | 
 | 302 | { | 
 | 303 | 	unsigned long flags; | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 304 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
| Andi Kleen | d15512f | 2006-12-07 02:14:07 +0100 | [diff] [blame] | 305 | 	__ioapic_write_entry(apic, pin, e); | 
| Linus Torvalds | f9dadfa | 2006-11-01 10:05:35 -0800 | [diff] [blame] | 306 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 307 | } | 
 | 308 |  | 
 | 309 | /* | 
 | 310 |  * When we mask an IO APIC routing entry, we need to write the low | 
 | 311 |  * word first, in order to set the mask bit before we change the | 
 | 312 |  * high bits! | 
 | 313 |  */ | 
 | 314 | static void ioapic_mask_entry(int apic, int pin) | 
 | 315 | { | 
 | 316 | 	unsigned long flags; | 
 | 317 | 	union entry_union eu = { .entry.mask = 1 }; | 
 | 318 |  | 
 | 319 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 320 | 	io_apic_write(apic, 0x10 + 2*pin, eu.w1); | 
 | 321 | 	io_apic_write(apic, 0x11 + 2*pin, eu.w2); | 
 | 322 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 323 | } | 
 | 324 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 325 | #ifdef CONFIG_SMP | 
 | 326 | static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector) | 
 | 327 | { | 
 | 328 | 	int apic, pin; | 
 | 329 | 	struct irq_cfg *cfg; | 
 | 330 | 	struct irq_pin_list *entry; | 
 | 331 |  | 
 | 332 | 	cfg = irq_cfg(irq); | 
 | 333 | 	entry = cfg->irq_2_pin; | 
 | 334 | 	for (;;) { | 
 | 335 | 		unsigned int reg; | 
 | 336 |  | 
 | 337 | 		if (!entry) | 
 | 338 | 			break; | 
 | 339 |  | 
 | 340 | 		apic = entry->apic; | 
 | 341 | 		pin = entry->pin; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 342 | #ifdef CONFIG_INTR_REMAP | 
 | 343 | 		/* | 
 | 344 | 		 * With interrupt-remapping, destination information comes | 
 | 345 | 		 * from interrupt-remapping table entry. | 
 | 346 | 		 */ | 
 | 347 | 		if (!irq_remapped(irq)) | 
 | 348 | 			io_apic_write(apic, 0x11 + pin*2, dest); | 
 | 349 | #else | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 350 | 		io_apic_write(apic, 0x11 + pin*2, dest); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 351 | #endif | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 352 | 		reg = io_apic_read(apic, 0x10 + pin*2); | 
 | 353 | 		reg &= ~IO_APIC_REDIR_VECTOR_MASK; | 
 | 354 | 		reg |= vector; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 355 | 		io_apic_modify(apic, 0x10 + pin*2, reg); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 356 | 		if (!entry->next) | 
 | 357 | 			break; | 
 | 358 | 		entry = entry->next; | 
 | 359 | 	} | 
 | 360 | } | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 361 |  | 
 | 362 | static int assign_irq_vector(int irq, cpumask_t mask); | 
 | 363 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 364 | static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) | 
 | 365 | { | 
 | 366 | 	struct irq_cfg *cfg; | 
 | 367 | 	unsigned long flags; | 
 | 368 | 	unsigned int dest; | 
 | 369 | 	cpumask_t tmp; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 370 | 	struct irq_desc *desc; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 371 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 372 | 	cpus_and(tmp, mask, cpu_online_map); | 
 | 373 | 	if (cpus_empty(tmp)) | 
 | 374 | 		return; | 
 | 375 |  | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 376 | 	cfg = irq_cfg(irq); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 377 | 	if (assign_irq_vector(irq, mask)) | 
 | 378 | 		return; | 
 | 379 |  | 
 | 380 | 	cpus_and(tmp, cfg->domain, mask); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 381 | 	dest = cpu_mask_to_apicid(tmp); | 
 | 382 | 	/* | 
 | 383 | 	 * Only the high 8 bits are valid. | 
 | 384 | 	 */ | 
 | 385 | 	dest = SET_APIC_LOGICAL_ID(dest); | 
 | 386 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 387 | 	desc = irq_to_desc(irq); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 388 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 389 | 	__target_IO_APIC_irq(irq, dest, cfg->vector); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 390 | 	desc->affinity = mask; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 391 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 392 | } | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 393 | #endif /* CONFIG_SMP */ | 
 | 394 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 395 | /* | 
 | 396 |  * The common case is 1:1 IRQ<->pin mappings. Sometimes there are | 
 | 397 |  * shared ISA-space IRQs, so we have to support them. We are super | 
 | 398 |  * fast in the common case, and fast for shared ISA-space IRQs. | 
 | 399 |  */ | 
 | 400 | static void add_pin_to_irq(unsigned int irq, int apic, int pin) | 
 | 401 | { | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 402 | 	struct irq_cfg *cfg; | 
 | 403 | 	struct irq_pin_list *entry; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 404 |  | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 405 | 	/* first time to refer irq_cfg, so with new */ | 
 | 406 | 	cfg = irq_cfg_alloc(irq); | 
 | 407 | 	entry = cfg->irq_2_pin; | 
 | 408 | 	if (!entry) { | 
 | 409 | 		entry = get_one_free_irq_2_pin(); | 
 | 410 | 		cfg->irq_2_pin = entry; | 
 | 411 | 		entry->apic = apic; | 
 | 412 | 		entry->pin = pin; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 413 | 		return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 414 | 	} | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 415 |  | 
 | 416 | 	while (entry->next) { | 
 | 417 | 		/* not again, please */ | 
 | 418 | 		if (entry->apic == apic && entry->pin == pin) | 
 | 419 | 			return; | 
 | 420 |  | 
 | 421 | 		entry = entry->next; | 
 | 422 | 	} | 
 | 423 |  | 
 | 424 | 	entry->next = get_one_free_irq_2_pin(); | 
 | 425 | 	entry = entry->next; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 426 | 	entry->apic = apic; | 
 | 427 | 	entry->pin = pin; | 
 | 428 | } | 
 | 429 |  | 
 | 430 | /* | 
 | 431 |  * Reroute an IRQ to a different pin. | 
 | 432 |  */ | 
 | 433 | static void __init replace_pin_at_irq(unsigned int irq, | 
 | 434 | 				      int oldapic, int oldpin, | 
 | 435 | 				      int newapic, int newpin) | 
 | 436 | { | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 437 | 	struct irq_cfg *cfg = irq_cfg(irq); | 
 | 438 | 	struct irq_pin_list *entry = cfg->irq_2_pin; | 
 | 439 | 	int replaced = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 440 |  | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 441 | 	while (entry) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 442 | 		if (entry->apic == oldapic && entry->pin == oldpin) { | 
 | 443 | 			entry->apic = newapic; | 
 | 444 | 			entry->pin = newpin; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 445 | 			replaced = 1; | 
 | 446 | 			/* every one is different, right? */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 447 | 			break; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 448 | 		} | 
 | 449 | 		entry = entry->next; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 450 | 	} | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 451 |  | 
 | 452 | 	/* why? call replace before add? */ | 
 | 453 | 	if (!replaced) | 
 | 454 | 		add_pin_to_irq(irq, newapic, newpin); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 455 | } | 
 | 456 |  | 
| Cyrill Gorcunov | 87783be | 2008-09-10 22:19:50 +0400 | [diff] [blame] | 457 | static inline void io_apic_modify_irq(unsigned int irq, | 
 | 458 | 				int mask_and, int mask_or, | 
 | 459 | 				void (*final)(struct irq_pin_list *entry)) | 
 | 460 | { | 
 | 461 | 	int pin; | 
 | 462 | 	struct irq_cfg *cfg; | 
 | 463 | 	struct irq_pin_list *entry; | 
 | 464 |  | 
 | 465 | 	cfg = irq_cfg(irq); | 
 | 466 | 	for (entry = cfg->irq_2_pin; entry != NULL; entry = entry->next) { | 
 | 467 | 		unsigned int reg; | 
 | 468 | 		pin = entry->pin; | 
 | 469 | 		reg = io_apic_read(entry->apic, 0x10 + pin * 2); | 
 | 470 | 		reg &= mask_and; | 
 | 471 | 		reg |= mask_or; | 
 | 472 | 		io_apic_modify(entry->apic, 0x10 + pin * 2, reg); | 
 | 473 | 		if (final) | 
 | 474 | 			final(entry); | 
 | 475 | 	} | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 476 | } | 
 | 477 |  | 
| Cyrill Gorcunov | 87783be | 2008-09-10 22:19:50 +0400 | [diff] [blame] | 478 | static void __unmask_IO_APIC_irq(unsigned int irq) | 
 | 479 | { | 
 | 480 | 	io_apic_modify_irq(irq, ~IO_APIC_REDIR_MASKED, 0, NULL); | 
 | 481 | } | 
| Yinghai Lu | 4e738e2 | 2008-08-19 20:50:47 -0700 | [diff] [blame] | 482 |  | 
 | 483 | #ifdef CONFIG_X86_64 | 
| Cyrill Gorcunov | 87783be | 2008-09-10 22:19:50 +0400 | [diff] [blame] | 484 | void io_apic_sync(struct irq_pin_list *entry) | 
| Yinghai Lu | 4e738e2 | 2008-08-19 20:50:47 -0700 | [diff] [blame] | 485 | { | 
| Cyrill Gorcunov | 87783be | 2008-09-10 22:19:50 +0400 | [diff] [blame] | 486 | 	/* | 
 | 487 | 	 * Synchronize the IO-APIC and the CPU by doing | 
 | 488 | 	 * a dummy read from the IO-APIC | 
 | 489 | 	 */ | 
 | 490 | 	struct io_apic __iomem *io_apic; | 
 | 491 | 	io_apic = io_apic_base(entry->apic); | 
| Yinghai Lu | 4e738e2 | 2008-08-19 20:50:47 -0700 | [diff] [blame] | 492 | 	readl(&io_apic->data); | 
 | 493 | } | 
 | 494 |  | 
| Cyrill Gorcunov | 87783be | 2008-09-10 22:19:50 +0400 | [diff] [blame] | 495 | static void __mask_IO_APIC_irq(unsigned int irq) | 
 | 496 | { | 
 | 497 | 	io_apic_modify_irq(irq, ~0, IO_APIC_REDIR_MASKED, &io_apic_sync); | 
 | 498 | } | 
 | 499 | #else /* CONFIG_X86_32 */ | 
 | 500 | static void __mask_IO_APIC_irq(unsigned int irq) | 
 | 501 | { | 
 | 502 | 	io_apic_modify_irq(irq, ~0, IO_APIC_REDIR_MASKED, NULL); | 
 | 503 | } | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 504 |  | 
| Cyrill Gorcunov | 87783be | 2008-09-10 22:19:50 +0400 | [diff] [blame] | 505 | static void __mask_and_edge_IO_APIC_irq(unsigned int irq) | 
 | 506 | { | 
 | 507 | 	io_apic_modify_irq(irq, ~IO_APIC_REDIR_LEVEL_TRIGGER, | 
 | 508 | 			IO_APIC_REDIR_MASKED, NULL); | 
 | 509 | } | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 510 |  | 
| Cyrill Gorcunov | 87783be | 2008-09-10 22:19:50 +0400 | [diff] [blame] | 511 | static void __unmask_and_level_IO_APIC_irq(unsigned int irq) | 
 | 512 | { | 
 | 513 | 	io_apic_modify_irq(irq, ~IO_APIC_REDIR_MASKED, | 
 | 514 | 			IO_APIC_REDIR_LEVEL_TRIGGER, NULL); | 
 | 515 | } | 
 | 516 | #endif /* CONFIG_X86_32 */ | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 517 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 518 | static void mask_IO_APIC_irq (unsigned int irq) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 519 | { | 
 | 520 | 	unsigned long flags; | 
 | 521 |  | 
 | 522 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 523 | 	__mask_IO_APIC_irq(irq); | 
 | 524 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 525 | } | 
 | 526 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 527 | static void unmask_IO_APIC_irq (unsigned int irq) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 528 | { | 
 | 529 | 	unsigned long flags; | 
 | 530 |  | 
 | 531 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 532 | 	__unmask_IO_APIC_irq(irq); | 
 | 533 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 534 | } | 
 | 535 |  | 
 | 536 | static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) | 
 | 537 | { | 
 | 538 | 	struct IO_APIC_route_entry entry; | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 539 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 540 | 	/* Check delivery_mode to be sure we're not clearing an SMI pin */ | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 541 | 	entry = ioapic_read_entry(apic, pin); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 542 | 	if (entry.delivery_mode == dest_SMI) | 
 | 543 | 		return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 544 | 	/* | 
 | 545 | 	 * Disable it in the IO-APIC irq-routing table: | 
 | 546 | 	 */ | 
| Linus Torvalds | f9dadfa | 2006-11-01 10:05:35 -0800 | [diff] [blame] | 547 | 	ioapic_mask_entry(apic, pin); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 548 | } | 
 | 549 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 550 | static void clear_IO_APIC (void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 551 | { | 
 | 552 | 	int apic, pin; | 
 | 553 |  | 
 | 554 | 	for (apic = 0; apic < nr_ioapics; apic++) | 
 | 555 | 		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) | 
 | 556 | 			clear_IO_APIC_pin(apic, pin); | 
 | 557 | } | 
 | 558 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 559 | #if !defined(CONFIG_SMP) && defined(CONFIG_X86_32) | 
| Harvey Harrison | 75604d7 | 2008-01-30 13:31:17 +0100 | [diff] [blame] | 560 | void send_IPI_self(int vector) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 561 | { | 
 | 562 | 	unsigned int cfg; | 
 | 563 |  | 
 | 564 | 	/* | 
 | 565 | 	 * Wait for idle. | 
 | 566 | 	 */ | 
 | 567 | 	apic_wait_icr_idle(); | 
 | 568 | 	cfg = APIC_DM_FIXED | APIC_DEST_SELF | vector | APIC_DEST_LOGICAL; | 
 | 569 | 	/* | 
 | 570 | 	 * Send the IPI. The write to APIC_ICR fires this off. | 
 | 571 | 	 */ | 
| Maciej W. Rozycki | 593f4a7 | 2008-07-16 19:15:30 +0100 | [diff] [blame] | 572 | 	apic_write(APIC_ICR, cfg); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 573 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 574 | #endif /* !CONFIG_SMP && CONFIG_X86_32*/ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 575 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 576 | #ifdef CONFIG_X86_32 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 577 | /* | 
 | 578 |  * support for broken MP BIOSs, enables hand-redirection of PIRQ0-7 to | 
 | 579 |  * specific CPU-side IRQs. | 
 | 580 |  */ | 
 | 581 |  | 
 | 582 | #define MAX_PIRQS 8 | 
 | 583 | static int pirq_entries [MAX_PIRQS]; | 
 | 584 | static int pirqs_enabled; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 585 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 586 | static int __init ioapic_pirq_setup(char *str) | 
 | 587 | { | 
 | 588 | 	int i, max; | 
 | 589 | 	int ints[MAX_PIRQS+1]; | 
 | 590 |  | 
 | 591 | 	get_options(str, ARRAY_SIZE(ints), ints); | 
 | 592 |  | 
 | 593 | 	for (i = 0; i < MAX_PIRQS; i++) | 
 | 594 | 		pirq_entries[i] = -1; | 
 | 595 |  | 
 | 596 | 	pirqs_enabled = 1; | 
 | 597 | 	apic_printk(APIC_VERBOSE, KERN_INFO | 
 | 598 | 			"PIRQ redirection, working around broken MP-BIOS.\n"); | 
 | 599 | 	max = MAX_PIRQS; | 
 | 600 | 	if (ints[0] < MAX_PIRQS) | 
 | 601 | 		max = ints[0]; | 
 | 602 |  | 
 | 603 | 	for (i = 0; i < max; i++) { | 
 | 604 | 		apic_printk(APIC_VERBOSE, KERN_DEBUG | 
 | 605 | 				"... PIRQ%d -> IRQ %d\n", i, ints[i+1]); | 
 | 606 | 		/* | 
 | 607 | 		 * PIRQs are mapped upside down, usually. | 
 | 608 | 		 */ | 
 | 609 | 		pirq_entries[MAX_PIRQS-i-1] = ints[i+1]; | 
 | 610 | 	} | 
 | 611 | 	return 1; | 
 | 612 | } | 
 | 613 |  | 
 | 614 | __setup("pirq=", ioapic_pirq_setup); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 615 | #endif /* CONFIG_X86_32 */ | 
 | 616 |  | 
 | 617 | #ifdef CONFIG_INTR_REMAP | 
 | 618 | /* I/O APIC RTE contents at the OS boot up */ | 
 | 619 | static struct IO_APIC_route_entry *early_ioapic_entries[MAX_IO_APICS]; | 
 | 620 |  | 
 | 621 | /* | 
 | 622 |  * Saves and masks all the unmasked IO-APIC RTE's | 
 | 623 |  */ | 
 | 624 | int save_mask_IO_APIC_setup(void) | 
 | 625 | { | 
 | 626 | 	union IO_APIC_reg_01 reg_01; | 
 | 627 | 	unsigned long flags; | 
 | 628 | 	int apic, pin; | 
 | 629 |  | 
 | 630 | 	/* | 
 | 631 | 	 * The number of IO-APIC IRQ registers (== #pins): | 
 | 632 | 	 */ | 
 | 633 | 	for (apic = 0; apic < nr_ioapics; apic++) { | 
 | 634 | 		spin_lock_irqsave(&ioapic_lock, flags); | 
 | 635 | 		reg_01.raw = io_apic_read(apic, 1); | 
 | 636 | 		spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 637 | 		nr_ioapic_registers[apic] = reg_01.bits.entries+1; | 
 | 638 | 	} | 
 | 639 |  | 
 | 640 | 	for (apic = 0; apic < nr_ioapics; apic++) { | 
 | 641 | 		early_ioapic_entries[apic] = | 
 | 642 | 			kzalloc(sizeof(struct IO_APIC_route_entry) * | 
 | 643 | 				nr_ioapic_registers[apic], GFP_KERNEL); | 
 | 644 | 		if (!early_ioapic_entries[apic]) | 
| Cyrill Gorcunov | 5ffa4eb | 2008-09-18 23:37:57 +0400 | [diff] [blame] | 645 | 			goto nomem; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 646 | 	} | 
 | 647 |  | 
 | 648 | 	for (apic = 0; apic < nr_ioapics; apic++) | 
 | 649 | 		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { | 
 | 650 | 			struct IO_APIC_route_entry entry; | 
 | 651 |  | 
 | 652 | 			entry = early_ioapic_entries[apic][pin] = | 
 | 653 | 				ioapic_read_entry(apic, pin); | 
 | 654 | 			if (!entry.mask) { | 
 | 655 | 				entry.mask = 1; | 
 | 656 | 				ioapic_write_entry(apic, pin, entry); | 
 | 657 | 			} | 
 | 658 | 		} | 
| Cyrill Gorcunov | 5ffa4eb | 2008-09-18 23:37:57 +0400 | [diff] [blame] | 659 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 660 | 	return 0; | 
| Cyrill Gorcunov | 5ffa4eb | 2008-09-18 23:37:57 +0400 | [diff] [blame] | 661 |  | 
 | 662 | nomem: | 
| Cyrill Gorcunov | c1370b4 | 2008-09-23 23:00:02 +0400 | [diff] [blame] | 663 | 	while (apic >= 0) | 
 | 664 | 		kfree(early_ioapic_entries[apic--]); | 
| Cyrill Gorcunov | 5ffa4eb | 2008-09-18 23:37:57 +0400 | [diff] [blame] | 665 | 	memset(early_ioapic_entries, 0, | 
 | 666 | 		ARRAY_SIZE(early_ioapic_entries)); | 
 | 667 |  | 
 | 668 | 	return -ENOMEM; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 669 | } | 
 | 670 |  | 
 | 671 | void restore_IO_APIC_setup(void) | 
 | 672 | { | 
 | 673 | 	int apic, pin; | 
 | 674 |  | 
| Cyrill Gorcunov | 5ffa4eb | 2008-09-18 23:37:57 +0400 | [diff] [blame] | 675 | 	for (apic = 0; apic < nr_ioapics; apic++) { | 
 | 676 | 		if (!early_ioapic_entries[apic]) | 
 | 677 | 			break; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 678 | 		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) | 
 | 679 | 			ioapic_write_entry(apic, pin, | 
 | 680 | 					   early_ioapic_entries[apic][pin]); | 
| Cyrill Gorcunov | 5ffa4eb | 2008-09-18 23:37:57 +0400 | [diff] [blame] | 681 | 		kfree(early_ioapic_entries[apic]); | 
 | 682 | 		early_ioapic_entries[apic] = NULL; | 
 | 683 | 	} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 684 | } | 
 | 685 |  | 
 | 686 | void reinit_intr_remapped_IO_APIC(int intr_remapping) | 
 | 687 | { | 
 | 688 | 	/* | 
 | 689 | 	 * for now plain restore of previous settings. | 
 | 690 | 	 * TBD: In the case of OS enabling interrupt-remapping, | 
 | 691 | 	 * IO-APIC RTE's need to be setup to point to interrupt-remapping | 
 | 692 | 	 * table entries. for now, do a plain restore, and wait for | 
 | 693 | 	 * the setup_IO_APIC_irqs() to do proper initialization. | 
 | 694 | 	 */ | 
 | 695 | 	restore_IO_APIC_setup(); | 
 | 696 | } | 
 | 697 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 698 |  | 
 | 699 | /* | 
 | 700 |  * Find the IRQ entry number of a certain pin. | 
 | 701 |  */ | 
 | 702 | static int find_irq_entry(int apic, int pin, int type) | 
 | 703 | { | 
 | 704 | 	int i; | 
 | 705 |  | 
 | 706 | 	for (i = 0; i < mp_irq_entries; i++) | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 707 | 		if (mp_irqs[i].mp_irqtype == type && | 
 | 708 | 		    (mp_irqs[i].mp_dstapic == mp_ioapics[apic].mp_apicid || | 
 | 709 | 		     mp_irqs[i].mp_dstapic == MP_APIC_ALL) && | 
 | 710 | 		    mp_irqs[i].mp_dstirq == pin) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 711 | 			return i; | 
 | 712 |  | 
 | 713 | 	return -1; | 
 | 714 | } | 
 | 715 |  | 
 | 716 | /* | 
 | 717 |  * Find the pin to which IRQ[irq] (ISA) is connected | 
 | 718 |  */ | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 719 | static int __init find_isa_irq_pin(int irq, int type) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 720 | { | 
 | 721 | 	int i; | 
 | 722 |  | 
 | 723 | 	for (i = 0; i < mp_irq_entries; i++) { | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 724 | 		int lbus = mp_irqs[i].mp_srcbus; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 725 |  | 
| Alexey Starikovskiy | d27e2b8 | 2008-03-20 14:54:18 +0300 | [diff] [blame] | 726 | 		if (test_bit(lbus, mp_bus_not_pci) && | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 727 | 		    (mp_irqs[i].mp_irqtype == type) && | 
 | 728 | 		    (mp_irqs[i].mp_srcbusirq == irq)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 729 |  | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 730 | 			return mp_irqs[i].mp_dstirq; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 731 | 	} | 
 | 732 | 	return -1; | 
 | 733 | } | 
 | 734 |  | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 735 | static int __init find_isa_irq_apic(int irq, int type) | 
 | 736 | { | 
 | 737 | 	int i; | 
 | 738 |  | 
 | 739 | 	for (i = 0; i < mp_irq_entries; i++) { | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 740 | 		int lbus = mp_irqs[i].mp_srcbus; | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 741 |  | 
| Alexey Starikovskiy | 73b2961 | 2008-03-20 14:54:24 +0300 | [diff] [blame] | 742 | 		if (test_bit(lbus, mp_bus_not_pci) && | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 743 | 		    (mp_irqs[i].mp_irqtype == type) && | 
 | 744 | 		    (mp_irqs[i].mp_srcbusirq == irq)) | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 745 | 			break; | 
 | 746 | 	} | 
 | 747 | 	if (i < mp_irq_entries) { | 
 | 748 | 		int apic; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 749 | 		for(apic = 0; apic < nr_ioapics; apic++) { | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 750 | 			if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic) | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 751 | 				return apic; | 
 | 752 | 		} | 
 | 753 | 	} | 
 | 754 |  | 
 | 755 | 	return -1; | 
 | 756 | } | 
 | 757 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 758 | /* | 
 | 759 |  * Find a specific PCI IRQ entry. | 
 | 760 |  * Not an __init, possibly needed by modules | 
 | 761 |  */ | 
 | 762 | static int pin_2_irq(int idx, int apic, int pin); | 
 | 763 |  | 
 | 764 | int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin) | 
 | 765 | { | 
 | 766 | 	int apic, i, best_guess = -1; | 
 | 767 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 768 | 	apic_printk(APIC_DEBUG, "querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n", | 
 | 769 | 		bus, slot, pin); | 
| Alexey Starikovskiy | ce6444d | 2008-05-19 19:47:09 +0400 | [diff] [blame] | 770 | 	if (test_bit(bus, mp_bus_not_pci)) { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 771 | 		apic_printk(APIC_VERBOSE, "PCI BIOS passed nonexistent PCI bus %d!\n", bus); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 772 | 		return -1; | 
 | 773 | 	} | 
 | 774 | 	for (i = 0; i < mp_irq_entries; i++) { | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 775 | 		int lbus = mp_irqs[i].mp_srcbus; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 776 |  | 
 | 777 | 		for (apic = 0; apic < nr_ioapics; apic++) | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 778 | 			if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic || | 
 | 779 | 			    mp_irqs[i].mp_dstapic == MP_APIC_ALL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 780 | 				break; | 
 | 781 |  | 
| Alexey Starikovskiy | 47cab82 | 2008-03-20 14:54:30 +0300 | [diff] [blame] | 782 | 		if (!test_bit(lbus, mp_bus_not_pci) && | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 783 | 		    !mp_irqs[i].mp_irqtype && | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 784 | 		    (bus == lbus) && | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 785 | 		    (slot == ((mp_irqs[i].mp_srcbusirq >> 2) & 0x1f))) { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 786 | 			int irq = pin_2_irq(i,apic,mp_irqs[i].mp_dstirq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 787 |  | 
 | 788 | 			if (!(apic || IO_APIC_IRQ(irq))) | 
 | 789 | 				continue; | 
 | 790 |  | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 791 | 			if (pin == (mp_irqs[i].mp_srcbusirq & 3)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 792 | 				return irq; | 
 | 793 | 			/* | 
 | 794 | 			 * Use the first all-but-pin matching entry as a | 
 | 795 | 			 * best-guess fuzzy result for broken mptables. | 
 | 796 | 			 */ | 
 | 797 | 			if (best_guess < 0) | 
 | 798 | 				best_guess = irq; | 
 | 799 | 		} | 
 | 800 | 	} | 
 | 801 | 	return best_guess; | 
 | 802 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 803 |  | 
| Alexey Dobriyan | 129f694 | 2005-06-23 00:08:33 -0700 | [diff] [blame] | 804 | EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 805 |  | 
| Alexey Starikovskiy | c0a282c | 2008-03-20 14:55:02 +0300 | [diff] [blame] | 806 | #if defined(CONFIG_EISA) || defined(CONFIG_MCA) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 807 | /* | 
 | 808 |  * EISA Edge/Level control register, ELCR | 
 | 809 |  */ | 
 | 810 | static int EISA_ELCR(unsigned int irq) | 
 | 811 | { | 
 | 812 | 	if (irq < 16) { | 
 | 813 | 		unsigned int port = 0x4d0 + (irq >> 3); | 
 | 814 | 		return (inb(port) >> (irq & 7)) & 1; | 
 | 815 | 	} | 
 | 816 | 	apic_printk(APIC_VERBOSE, KERN_INFO | 
 | 817 | 			"Broken MPtable reports ISA irq %d\n", irq); | 
 | 818 | 	return 0; | 
 | 819 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 820 |  | 
| Alexey Starikovskiy | c0a282c | 2008-03-20 14:55:02 +0300 | [diff] [blame] | 821 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 822 |  | 
| Alexey Starikovskiy | 6728801 | 2008-03-20 14:54:36 +0300 | [diff] [blame] | 823 | /* ISA interrupts are always polarity zero edge triggered, | 
 | 824 |  * when listed as conforming in the MP table. */ | 
 | 825 |  | 
 | 826 | #define default_ISA_trigger(idx)	(0) | 
 | 827 | #define default_ISA_polarity(idx)	(0) | 
 | 828 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 829 | /* EISA interrupts are always polarity zero and can be edge or level | 
 | 830 |  * trigger depending on the ELCR value.  If an interrupt is listed as | 
 | 831 |  * EISA conforming in the MP table, that means its trigger type must | 
 | 832 |  * be read in from the ELCR */ | 
 | 833 |  | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 834 | #define default_EISA_trigger(idx)	(EISA_ELCR(mp_irqs[idx].mp_srcbusirq)) | 
| Alexey Starikovskiy | 6728801 | 2008-03-20 14:54:36 +0300 | [diff] [blame] | 835 | #define default_EISA_polarity(idx)	default_ISA_polarity(idx) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 836 |  | 
 | 837 | /* PCI interrupts are always polarity one level triggered, | 
 | 838 |  * when listed as conforming in the MP table. */ | 
 | 839 |  | 
 | 840 | #define default_PCI_trigger(idx)	(1) | 
 | 841 | #define default_PCI_polarity(idx)	(1) | 
 | 842 |  | 
 | 843 | /* MCA interrupts are always polarity zero level triggered, | 
 | 844 |  * when listed as conforming in the MP table. */ | 
 | 845 |  | 
 | 846 | #define default_MCA_trigger(idx)	(1) | 
| Alexey Starikovskiy | 6728801 | 2008-03-20 14:54:36 +0300 | [diff] [blame] | 847 | #define default_MCA_polarity(idx)	default_ISA_polarity(idx) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 848 |  | 
| Shaohua Li | 61fd47e | 2007-11-17 01:05:28 -0500 | [diff] [blame] | 849 | static int MPBIOS_polarity(int idx) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 850 | { | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 851 | 	int bus = mp_irqs[idx].mp_srcbus; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 852 | 	int polarity; | 
 | 853 |  | 
 | 854 | 	/* | 
 | 855 | 	 * Determine IRQ line polarity (high active or low active): | 
 | 856 | 	 */ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 857 | 	switch (mp_irqs[idx].mp_irqflag & 3) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 858 | 	{ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 859 | 		case 0: /* conforms, ie. bus-type dependent polarity */ | 
 | 860 | 			if (test_bit(bus, mp_bus_not_pci)) | 
 | 861 | 				polarity = default_ISA_polarity(idx); | 
 | 862 | 			else | 
 | 863 | 				polarity = default_PCI_polarity(idx); | 
 | 864 | 			break; | 
 | 865 | 		case 1: /* high active */ | 
 | 866 | 		{ | 
 | 867 | 			polarity = 0; | 
 | 868 | 			break; | 
 | 869 | 		} | 
 | 870 | 		case 2: /* reserved */ | 
 | 871 | 		{ | 
 | 872 | 			printk(KERN_WARNING "broken BIOS!!\n"); | 
 | 873 | 			polarity = 1; | 
 | 874 | 			break; | 
 | 875 | 		} | 
 | 876 | 		case 3: /* low active */ | 
 | 877 | 		{ | 
 | 878 | 			polarity = 1; | 
 | 879 | 			break; | 
 | 880 | 		} | 
 | 881 | 		default: /* invalid */ | 
 | 882 | 		{ | 
 | 883 | 			printk(KERN_WARNING "broken BIOS!!\n"); | 
 | 884 | 			polarity = 1; | 
 | 885 | 			break; | 
 | 886 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 887 | 	} | 
 | 888 | 	return polarity; | 
 | 889 | } | 
 | 890 |  | 
 | 891 | static int MPBIOS_trigger(int idx) | 
 | 892 | { | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 893 | 	int bus = mp_irqs[idx].mp_srcbus; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 894 | 	int trigger; | 
 | 895 |  | 
 | 896 | 	/* | 
 | 897 | 	 * Determine IRQ trigger mode (edge or level sensitive): | 
 | 898 | 	 */ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 899 | 	switch ((mp_irqs[idx].mp_irqflag>>2) & 3) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 900 | 	{ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 901 | 		case 0: /* conforms, ie. bus-type dependent */ | 
 | 902 | 			if (test_bit(bus, mp_bus_not_pci)) | 
 | 903 | 				trigger = default_ISA_trigger(idx); | 
 | 904 | 			else | 
 | 905 | 				trigger = default_PCI_trigger(idx); | 
| Alexey Starikovskiy | c0a282c | 2008-03-20 14:55:02 +0300 | [diff] [blame] | 906 | #if defined(CONFIG_EISA) || defined(CONFIG_MCA) | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 907 | 			switch (mp_bus_id_to_type[bus]) { | 
 | 908 | 				case MP_BUS_ISA: /* ISA pin */ | 
 | 909 | 				{ | 
 | 910 | 					/* set before the switch */ | 
 | 911 | 					break; | 
 | 912 | 				} | 
 | 913 | 				case MP_BUS_EISA: /* EISA pin */ | 
 | 914 | 				{ | 
 | 915 | 					trigger = default_EISA_trigger(idx); | 
 | 916 | 					break; | 
 | 917 | 				} | 
 | 918 | 				case MP_BUS_PCI: /* PCI pin */ | 
 | 919 | 				{ | 
 | 920 | 					/* set before the switch */ | 
 | 921 | 					break; | 
 | 922 | 				} | 
 | 923 | 				case MP_BUS_MCA: /* MCA pin */ | 
 | 924 | 				{ | 
 | 925 | 					trigger = default_MCA_trigger(idx); | 
 | 926 | 					break; | 
 | 927 | 				} | 
 | 928 | 				default: | 
 | 929 | 				{ | 
 | 930 | 					printk(KERN_WARNING "broken BIOS!!\n"); | 
 | 931 | 					trigger = 1; | 
 | 932 | 					break; | 
 | 933 | 				} | 
 | 934 | 			} | 
 | 935 | #endif | 
 | 936 | 			break; | 
 | 937 | 		case 1: /* edge */ | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 938 | 		{ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 939 | 			trigger = 0; | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 940 | 			break; | 
 | 941 | 		} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 942 | 		case 2: /* reserved */ | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 943 | 		{ | 
 | 944 | 			printk(KERN_WARNING "broken BIOS!!\n"); | 
 | 945 | 			trigger = 1; | 
 | 946 | 			break; | 
 | 947 | 		} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 948 | 		case 3: /* level */ | 
 | 949 | 		{ | 
 | 950 | 			trigger = 1; | 
 | 951 | 			break; | 
 | 952 | 		} | 
 | 953 | 		default: /* invalid */ | 
 | 954 | 		{ | 
 | 955 | 			printk(KERN_WARNING "broken BIOS!!\n"); | 
 | 956 | 			trigger = 0; | 
 | 957 | 			break; | 
 | 958 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 959 | 	} | 
 | 960 | 	return trigger; | 
 | 961 | } | 
 | 962 |  | 
 | 963 | static inline int irq_polarity(int idx) | 
 | 964 | { | 
 | 965 | 	return MPBIOS_polarity(idx); | 
 | 966 | } | 
 | 967 |  | 
 | 968 | static inline int irq_trigger(int idx) | 
 | 969 | { | 
 | 970 | 	return MPBIOS_trigger(idx); | 
 | 971 | } | 
 | 972 |  | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 973 | int (*ioapic_renumber_irq)(int ioapic, int irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 974 | static int pin_2_irq(int idx, int apic, int pin) | 
 | 975 | { | 
 | 976 | 	int irq, i; | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 977 | 	int bus = mp_irqs[idx].mp_srcbus; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 978 |  | 
 | 979 | 	/* | 
 | 980 | 	 * Debugging check, we are in big trouble if this message pops up! | 
 | 981 | 	 */ | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 982 | 	if (mp_irqs[idx].mp_dstirq != pin) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 983 | 		printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n"); | 
 | 984 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 985 | 	if (test_bit(bus, mp_bus_not_pci)) { | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 986 | 		irq = mp_irqs[idx].mp_srcbusirq; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 987 | 	} else { | 
| Alexey Starikovskiy | 643befe | 2008-03-20 14:54:49 +0300 | [diff] [blame] | 988 | 		/* | 
 | 989 | 		 * PCI IRQs are mapped in order | 
 | 990 | 		 */ | 
 | 991 | 		i = irq = 0; | 
 | 992 | 		while (i < apic) | 
 | 993 | 			irq += nr_ioapic_registers[i++]; | 
 | 994 | 		irq += pin; | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 995 | 		/* | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 996 |                  * For MPS mode, so far only needed by ES7000 platform | 
 | 997 |                  */ | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 998 | 		if (ioapic_renumber_irq) | 
 | 999 | 			irq = ioapic_renumber_irq(apic, irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1000 | 	} | 
 | 1001 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1002 | #ifdef CONFIG_X86_32 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1003 | 	/* | 
 | 1004 | 	 * PCI IRQ command line redirection. Yes, limits are hardcoded. | 
 | 1005 | 	 */ | 
 | 1006 | 	if ((pin >= 16) && (pin <= 23)) { | 
 | 1007 | 		if (pirq_entries[pin-16] != -1) { | 
 | 1008 | 			if (!pirq_entries[pin-16]) { | 
 | 1009 | 				apic_printk(APIC_VERBOSE, KERN_DEBUG | 
 | 1010 | 						"disabling PIRQ%d\n", pin-16); | 
 | 1011 | 			} else { | 
 | 1012 | 				irq = pirq_entries[pin-16]; | 
 | 1013 | 				apic_printk(APIC_VERBOSE, KERN_DEBUG | 
 | 1014 | 						"using PIRQ%d -> IRQ %d\n", | 
 | 1015 | 						pin-16, irq); | 
 | 1016 | 			} | 
 | 1017 | 		} | 
 | 1018 | 	} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1019 | #endif | 
 | 1020 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1021 | 	return irq; | 
 | 1022 | } | 
 | 1023 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1024 | void lock_vector_lock(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1025 | { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1026 | 	/* Used to the online set of cpus does not change | 
 | 1027 | 	 * during assign_irq_vector. | 
 | 1028 | 	 */ | 
 | 1029 | 	spin_lock(&vector_lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1030 | } | 
 | 1031 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1032 | void unlock_vector_lock(void) | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 1033 | { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1034 | 	spin_unlock(&vector_lock); | 
 | 1035 | } | 
 | 1036 |  | 
 | 1037 | static int __assign_irq_vector(int irq, cpumask_t mask) | 
 | 1038 | { | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 1039 | 	/* | 
 | 1040 | 	 * NOTE! The local APIC isn't very good at handling | 
 | 1041 | 	 * multiple interrupts at the same interrupt level. | 
 | 1042 | 	 * As the interrupt level is determined by taking the | 
 | 1043 | 	 * vector number and shifting that right by 4, we | 
 | 1044 | 	 * want to spread these out a bit so that they don't | 
 | 1045 | 	 * all fall in the same interrupt level. | 
 | 1046 | 	 * | 
 | 1047 | 	 * Also, we've got to be careful not to trash gate | 
 | 1048 | 	 * 0x80, because int 0x80 is hm, kind of importantish. ;) | 
 | 1049 | 	 */ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1050 | 	static int current_vector = FIRST_DEVICE_VECTOR, current_offset = 0; | 
 | 1051 | 	unsigned int old_vector; | 
 | 1052 | 	int cpu; | 
 | 1053 | 	struct irq_cfg *cfg; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1054 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1055 | 	cfg = irq_cfg(irq); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1056 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1057 | 	/* Only try and allocate irqs on cpus that are present */ | 
 | 1058 | 	cpus_and(mask, mask, cpu_online_map); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1059 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1060 | 	if ((cfg->move_in_progress) || cfg->move_cleanup_count) | 
 | 1061 | 		return -EBUSY; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1062 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1063 | 	old_vector = cfg->vector; | 
 | 1064 | 	if (old_vector) { | 
 | 1065 | 		cpumask_t tmp; | 
 | 1066 | 		cpus_and(tmp, cfg->domain, mask); | 
 | 1067 | 		if (!cpus_empty(tmp)) | 
 | 1068 | 			return 0; | 
 | 1069 | 	} | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1070 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1071 | 	for_each_cpu_mask_nr(cpu, mask) { | 
 | 1072 | 		cpumask_t domain, new_mask; | 
 | 1073 | 		int new_cpu; | 
 | 1074 | 		int vector, offset; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1075 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1076 | 		domain = vector_allocation_domain(cpu); | 
 | 1077 | 		cpus_and(new_mask, domain, cpu_online_map); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1078 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1079 | 		vector = current_vector; | 
 | 1080 | 		offset = current_offset; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1081 | next: | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1082 | 		vector += 8; | 
 | 1083 | 		if (vector >= first_system_vector) { | 
 | 1084 | 			/* If we run out of vectors on large boxen, must share them. */ | 
 | 1085 | 			offset = (offset + 1) % 8; | 
 | 1086 | 			vector = FIRST_DEVICE_VECTOR + offset; | 
| Yinghai Lu | 7a959cf | 2008-08-19 20:50:32 -0700 | [diff] [blame] | 1087 | 		} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1088 | 		if (unlikely(current_vector == vector)) | 
 | 1089 | 			continue; | 
 | 1090 | #ifdef CONFIG_X86_64 | 
 | 1091 | 		if (vector == IA32_SYSCALL_VECTOR) | 
 | 1092 | 			goto next; | 
 | 1093 | #else | 
 | 1094 | 		if (vector == SYSCALL_VECTOR) | 
 | 1095 | 			goto next; | 
 | 1096 | #endif | 
 | 1097 | 		for_each_cpu_mask_nr(new_cpu, new_mask) | 
 | 1098 | 			if (per_cpu(vector_irq, new_cpu)[vector] != -1) | 
 | 1099 | 				goto next; | 
 | 1100 | 		/* Found one! */ | 
 | 1101 | 		current_vector = vector; | 
 | 1102 | 		current_offset = offset; | 
 | 1103 | 		if (old_vector) { | 
 | 1104 | 			cfg->move_in_progress = 1; | 
 | 1105 | 			cfg->old_domain = cfg->domain; | 
 | 1106 | 		} | 
 | 1107 | 		for_each_cpu_mask_nr(new_cpu, new_mask) | 
 | 1108 | 			per_cpu(vector_irq, new_cpu)[vector] = irq; | 
 | 1109 | 		cfg->vector = vector; | 
 | 1110 | 		cfg->domain = domain; | 
 | 1111 | 		return 0; | 
 | 1112 | 	} | 
 | 1113 | 	return -ENOSPC; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1114 | } | 
 | 1115 |  | 
 | 1116 | static int assign_irq_vector(int irq, cpumask_t mask) | 
 | 1117 | { | 
 | 1118 | 	int err; | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 1119 | 	unsigned long flags; | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 1120 |  | 
 | 1121 | 	spin_lock_irqsave(&vector_lock, flags); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1122 | 	err = __assign_irq_vector(irq, mask); | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 1123 | 	spin_unlock_irqrestore(&vector_lock, flags); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1124 | 	return err; | 
 | 1125 | } | 
 | 1126 |  | 
 | 1127 | static void __clear_irq_vector(int irq) | 
 | 1128 | { | 
 | 1129 | 	struct irq_cfg *cfg; | 
 | 1130 | 	cpumask_t mask; | 
 | 1131 | 	int cpu, vector; | 
 | 1132 |  | 
 | 1133 | 	cfg = irq_cfg(irq); | 
 | 1134 | 	BUG_ON(!cfg->vector); | 
 | 1135 |  | 
 | 1136 | 	vector = cfg->vector; | 
 | 1137 | 	cpus_and(mask, cfg->domain, cpu_online_map); | 
 | 1138 | 	for_each_cpu_mask_nr(cpu, mask) | 
 | 1139 | 		per_cpu(vector_irq, cpu)[vector] = -1; | 
 | 1140 |  | 
 | 1141 | 	cfg->vector = 0; | 
 | 1142 | 	cpus_clear(cfg->domain); | 
 | 1143 | } | 
 | 1144 |  | 
 | 1145 | void __setup_vector_irq(int cpu) | 
 | 1146 | { | 
 | 1147 | 	/* Initialize vector_irq on a new cpu */ | 
 | 1148 | 	/* This function must be called with vector_lock held */ | 
 | 1149 | 	int irq, vector; | 
 | 1150 | 	struct irq_cfg *cfg; | 
 | 1151 |  | 
 | 1152 | 	/* Mark the inuse vectors */ | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 1153 | 	for_each_irq_cfg(irq, cfg) { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1154 | 		if (!cpu_isset(cpu, cfg->domain)) | 
 | 1155 | 			continue; | 
 | 1156 | 		vector = cfg->vector; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1157 | 		per_cpu(vector_irq, cpu)[vector] = irq; | 
 | 1158 | 	} | 
 | 1159 | 	/* Mark the free vectors */ | 
 | 1160 | 	for (vector = 0; vector < NR_VECTORS; ++vector) { | 
 | 1161 | 		irq = per_cpu(vector_irq, cpu)[vector]; | 
 | 1162 | 		if (irq < 0) | 
 | 1163 | 			continue; | 
 | 1164 |  | 
 | 1165 | 		cfg = irq_cfg(irq); | 
 | 1166 | 		if (!cpu_isset(cpu, cfg->domain)) | 
 | 1167 | 			per_cpu(vector_irq, cpu)[vector] = -1; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1168 | 	} | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 1169 | } | 
| Glauber Costa | 3fde690 | 2008-05-28 20:34:19 -0700 | [diff] [blame] | 1170 |  | 
| Ingo Molnar | f5b9ed7 | 2006-10-04 02:16:26 -0700 | [diff] [blame] | 1171 | static struct irq_chip ioapic_chip; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1172 | #ifdef CONFIG_INTR_REMAP | 
 | 1173 | static struct irq_chip ir_ioapic_chip; | 
 | 1174 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1175 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1176 | #define IOAPIC_AUTO     -1 | 
 | 1177 | #define IOAPIC_EDGE     0 | 
 | 1178 | #define IOAPIC_LEVEL    1 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1179 |  | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 1180 | #ifdef CONFIG_X86_32 | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 1181 | static inline int IO_APIC_irq_trigger(int irq) | 
 | 1182 | { | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 1183 | 	int apic, idx, pin; | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 1184 |  | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 1185 | 	for (apic = 0; apic < nr_ioapics; apic++) { | 
 | 1186 | 		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { | 
 | 1187 | 			idx = find_irq_entry(apic, pin, mp_INT); | 
 | 1188 | 			if ((idx != -1) && (irq == pin_2_irq(idx, apic, pin))) | 
 | 1189 | 				return irq_trigger(idx); | 
 | 1190 | 		} | 
 | 1191 | 	} | 
 | 1192 | 	/* | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1193 |          * nonexistent IRQs are edge default | 
 | 1194 |          */ | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 1195 | 	return 0; | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 1196 | } | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 1197 | #else | 
 | 1198 | static inline int IO_APIC_irq_trigger(int irq) | 
 | 1199 | { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1200 | 	return 1; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 1201 | } | 
 | 1202 | #endif | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 1203 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1204 | static void ioapic_register_intr(int irq, unsigned long trigger) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1205 | { | 
| Yinghai Lu | 08678b0 | 2008-08-19 20:50:05 -0700 | [diff] [blame] | 1206 | 	struct irq_desc *desc; | 
 | 1207 |  | 
| Thomas Gleixner | ee32c97 | 2008-10-15 14:34:09 +0200 | [diff] [blame] | 1208 | 	desc = irq_to_desc(irq); | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 1209 |  | 
| Jan Beulich | 6ebcc00 | 2006-06-26 13:56:46 +0200 | [diff] [blame] | 1210 | 	if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) || | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 1211 | 	    trigger == IOAPIC_LEVEL) | 
| Yinghai Lu | 08678b0 | 2008-08-19 20:50:05 -0700 | [diff] [blame] | 1212 | 		desc->status |= IRQ_LEVEL; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 1213 | 	else | 
 | 1214 | 		desc->status &= ~IRQ_LEVEL; | 
 | 1215 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1216 | #ifdef CONFIG_INTR_REMAP | 
 | 1217 | 	if (irq_remapped(irq)) { | 
 | 1218 | 		desc->status |= IRQ_MOVE_PCNTXT; | 
 | 1219 | 		if (trigger) | 
 | 1220 | 			set_irq_chip_and_handler_name(irq, &ir_ioapic_chip, | 
 | 1221 | 						      handle_fasteoi_irq, | 
 | 1222 | 						     "fasteoi"); | 
 | 1223 | 		else | 
 | 1224 | 			set_irq_chip_and_handler_name(irq, &ir_ioapic_chip, | 
 | 1225 | 						      handle_edge_irq, "edge"); | 
 | 1226 | 		return; | 
 | 1227 | 	} | 
 | 1228 | #endif | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 1229 | 	if ((trigger == IOAPIC_AUTO && IO_APIC_irq_trigger(irq)) || | 
 | 1230 | 	    trigger == IOAPIC_LEVEL) | 
| Ingo Molnar | a460e74 | 2006-10-17 00:10:03 -0700 | [diff] [blame] | 1231 | 		set_irq_chip_and_handler_name(irq, &ioapic_chip, | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1232 | 					      handle_fasteoi_irq, | 
 | 1233 | 					      "fasteoi"); | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 1234 | 	else | 
| Ingo Molnar | a460e74 | 2006-10-17 00:10:03 -0700 | [diff] [blame] | 1235 | 		set_irq_chip_and_handler_name(irq, &ioapic_chip, | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1236 | 					      handle_edge_irq, "edge"); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1237 | } | 
 | 1238 |  | 
 | 1239 | static int setup_ioapic_entry(int apic, int irq, | 
 | 1240 | 			      struct IO_APIC_route_entry *entry, | 
 | 1241 | 			      unsigned int destination, int trigger, | 
 | 1242 | 			      int polarity, int vector) | 
 | 1243 | { | 
 | 1244 | 	/* | 
 | 1245 | 	 * add it to the IO-APIC irq-routing table: | 
 | 1246 | 	 */ | 
 | 1247 | 	memset(entry,0,sizeof(*entry)); | 
 | 1248 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1249 | #ifdef CONFIG_INTR_REMAP | 
 | 1250 | 	if (intr_remapping_enabled) { | 
 | 1251 | 		struct intel_iommu *iommu = map_ioapic_to_ir(apic); | 
 | 1252 | 		struct irte irte; | 
 | 1253 | 		struct IR_IO_APIC_route_entry *ir_entry = | 
 | 1254 | 			(struct IR_IO_APIC_route_entry *) entry; | 
 | 1255 | 		int index; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1256 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1257 | 		if (!iommu) | 
 | 1258 | 			panic("No mapping iommu for ioapic %d\n", apic); | 
 | 1259 |  | 
 | 1260 | 		index = alloc_irte(iommu, irq, 1); | 
 | 1261 | 		if (index < 0) | 
 | 1262 | 			panic("Failed to allocate IRTE for ioapic %d\n", apic); | 
 | 1263 |  | 
 | 1264 | 		memset(&irte, 0, sizeof(irte)); | 
 | 1265 |  | 
 | 1266 | 		irte.present = 1; | 
 | 1267 | 		irte.dst_mode = INT_DEST_MODE; | 
 | 1268 | 		irte.trigger_mode = trigger; | 
 | 1269 | 		irte.dlvry_mode = INT_DELIVERY_MODE; | 
 | 1270 | 		irte.vector = vector; | 
 | 1271 | 		irte.dest_id = IRTE_DEST(destination); | 
 | 1272 |  | 
 | 1273 | 		modify_irte(irq, &irte); | 
 | 1274 |  | 
 | 1275 | 		ir_entry->index2 = (index >> 15) & 0x1; | 
 | 1276 | 		ir_entry->zero = 0; | 
 | 1277 | 		ir_entry->format = 1; | 
 | 1278 | 		ir_entry->index = (index & 0x7fff); | 
 | 1279 | 	} else | 
 | 1280 | #endif | 
 | 1281 | 	{ | 
 | 1282 | 		entry->delivery_mode = INT_DELIVERY_MODE; | 
 | 1283 | 		entry->dest_mode = INT_DEST_MODE; | 
 | 1284 | 		entry->dest = destination; | 
 | 1285 | 	} | 
 | 1286 |  | 
 | 1287 | 	entry->mask = 0;				/* enable IRQ */ | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1288 | 	entry->trigger = trigger; | 
 | 1289 | 	entry->polarity = polarity; | 
 | 1290 | 	entry->vector = vector; | 
 | 1291 |  | 
 | 1292 | 	/* Mask level triggered irqs. | 
 | 1293 | 	 * Use IRQ_DELAYED_DISABLE for edge triggered irqs. | 
 | 1294 | 	 */ | 
 | 1295 | 	if (trigger) | 
 | 1296 | 		entry->mask = 1; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1297 | 	return 0; | 
 | 1298 | } | 
 | 1299 |  | 
 | 1300 | static void setup_IO_APIC_irq(int apic, int pin, unsigned int irq, | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1301 | 			      int trigger, int polarity) | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 1302 | { | 
 | 1303 | 	struct irq_cfg *cfg; | 
 | 1304 | 	struct IO_APIC_route_entry entry; | 
 | 1305 | 	cpumask_t mask; | 
 | 1306 |  | 
 | 1307 | 	if (!IO_APIC_IRQ(irq)) | 
 | 1308 | 		return; | 
 | 1309 |  | 
 | 1310 | 	cfg = irq_cfg(irq); | 
 | 1311 |  | 
 | 1312 | 	mask = TARGET_CPUS; | 
 | 1313 | 	if (assign_irq_vector(irq, mask)) | 
 | 1314 | 		return; | 
 | 1315 |  | 
 | 1316 | 	cpus_and(mask, cfg->domain, mask); | 
 | 1317 |  | 
 | 1318 | 	apic_printk(APIC_VERBOSE,KERN_DEBUG | 
 | 1319 | 		    "IOAPIC[%d]: Set routing entry (%d-%d -> 0x%x -> " | 
 | 1320 | 		    "IRQ %d Mode:%i Active:%i)\n", | 
 | 1321 | 		    apic, mp_ioapics[apic].mp_apicid, pin, cfg->vector, | 
 | 1322 | 		    irq, trigger, polarity); | 
 | 1323 |  | 
 | 1324 |  | 
 | 1325 | 	if (setup_ioapic_entry(mp_ioapics[apic].mp_apicid, irq, &entry, | 
 | 1326 | 			       cpu_mask_to_apicid(mask), trigger, polarity, | 
 | 1327 | 			       cfg->vector)) { | 
 | 1328 | 		printk("Failed to setup ioapic entry for ioapic  %d, pin %d\n", | 
 | 1329 | 		       mp_ioapics[apic].mp_apicid, pin); | 
 | 1330 | 		__clear_irq_vector(irq); | 
 | 1331 | 		return; | 
 | 1332 | 	} | 
 | 1333 |  | 
 | 1334 | 	ioapic_register_intr(irq, trigger); | 
 | 1335 | 	if (irq < 16) | 
 | 1336 | 		disable_8259A_irq(irq); | 
 | 1337 |  | 
 | 1338 | 	ioapic_write_entry(apic, pin, entry); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1339 | } | 
 | 1340 |  | 
 | 1341 | static void __init setup_IO_APIC_irqs(void) | 
 | 1342 | { | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1343 | 	int apic, pin, idx, irq; | 
 | 1344 | 	int notcon = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1345 |  | 
 | 1346 | 	apic_printk(APIC_VERBOSE, KERN_DEBUG "init IO_APIC IRQs\n"); | 
 | 1347 |  | 
 | 1348 | 	for (apic = 0; apic < nr_ioapics; apic++) { | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1349 | 		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1350 |  | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1351 | 			idx = find_irq_entry(apic, pin, mp_INT); | 
 | 1352 | 			if (idx == -1) { | 
| Cyrill Gorcunov | 2a554fb | 2008-09-08 19:38:06 +0400 | [diff] [blame] | 1353 | 				if (!notcon) { | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1354 | 					notcon = 1; | 
| Cyrill Gorcunov | 2a554fb | 2008-09-08 19:38:06 +0400 | [diff] [blame] | 1355 | 					apic_printk(APIC_VERBOSE, | 
 | 1356 | 						KERN_DEBUG " %d-%d", | 
 | 1357 | 						mp_ioapics[apic].mp_apicid, | 
 | 1358 | 						pin); | 
 | 1359 | 				} else | 
 | 1360 | 					apic_printk(APIC_VERBOSE, " %d-%d", | 
 | 1361 | 						mp_ioapics[apic].mp_apicid, | 
 | 1362 | 						pin); | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1363 | 				continue; | 
 | 1364 | 			} | 
| Cyrill Gorcunov | 56ffa1a | 2008-09-13 13:11:16 +0400 | [diff] [blame] | 1365 | 			if (notcon) { | 
 | 1366 | 				apic_printk(APIC_VERBOSE, | 
 | 1367 | 					" (apicid-pin) not connected\n"); | 
 | 1368 | 				notcon = 0; | 
 | 1369 | 			} | 
| Yinghai Lu | 20d225b | 2007-10-17 18:04:41 +0200 | [diff] [blame] | 1370 |  | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1371 | 			irq = pin_2_irq(idx, apic, pin); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1372 | #ifdef CONFIG_X86_32 | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1373 | 			if (multi_timer_check(apic, irq)) | 
 | 1374 | 				continue; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1375 | #endif | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1376 | 			add_pin_to_irq(irq, apic, pin); | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 1377 |  | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1378 | 			setup_IO_APIC_irq(apic, pin, irq, | 
 | 1379 | 					irq_trigger(idx), irq_polarity(idx)); | 
 | 1380 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1381 | 	} | 
 | 1382 |  | 
| Cyrill Gorcunov | 3c2cbd2 | 2008-09-06 14:15:33 +0400 | [diff] [blame] | 1383 | 	if (notcon) | 
 | 1384 | 		apic_printk(APIC_VERBOSE, | 
| Cyrill Gorcunov | 2a554fb | 2008-09-08 19:38:06 +0400 | [diff] [blame] | 1385 | 			" (apicid-pin) not connected\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1386 | } | 
 | 1387 |  | 
 | 1388 | /* | 
| Maciej W. Rozycki | f7633ce | 2008-05-27 21:19:34 +0100 | [diff] [blame] | 1389 |  * Set up the timer pin, possibly with the 8259A-master behind. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1390 |  */ | 
| Maciej W. Rozycki | f7633ce | 2008-05-27 21:19:34 +0100 | [diff] [blame] | 1391 | static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin, | 
 | 1392 | 					int vector) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1393 | { | 
 | 1394 | 	struct IO_APIC_route_entry entry; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1395 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1396 | #ifdef CONFIG_INTR_REMAP | 
 | 1397 | 	if (intr_remapping_enabled) | 
 | 1398 | 		return; | 
 | 1399 | #endif | 
 | 1400 |  | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 1401 | 	memset(&entry, 0, sizeof(entry)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1402 |  | 
 | 1403 | 	/* | 
 | 1404 | 	 * We use logical delivery to get the timer IRQ | 
 | 1405 | 	 * to the first CPU. | 
 | 1406 | 	 */ | 
 | 1407 | 	entry.dest_mode = INT_DEST_MODE; | 
| Maciej W. Rozycki | 03be750 | 2008-05-27 21:19:45 +0100 | [diff] [blame] | 1408 | 	entry.mask = 1;					/* mask IRQ now */ | 
| Yinghai Lu | d83e94a | 2008-08-19 20:50:33 -0700 | [diff] [blame] | 1409 | 	entry.dest = cpu_mask_to_apicid(TARGET_CPUS); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1410 | 	entry.delivery_mode = INT_DELIVERY_MODE; | 
 | 1411 | 	entry.polarity = 0; | 
 | 1412 | 	entry.trigger = 0; | 
 | 1413 | 	entry.vector = vector; | 
 | 1414 |  | 
 | 1415 | 	/* | 
 | 1416 | 	 * The timer IRQ doesn't have to know that behind the | 
| Maciej W. Rozycki | f7633ce | 2008-05-27 21:19:34 +0100 | [diff] [blame] | 1417 | 	 * scene we may have a 8259A-master in AEOI mode ... | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1418 | 	 */ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1419 | 	set_irq_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq, "edge"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1420 |  | 
 | 1421 | 	/* | 
 | 1422 | 	 * Add it to the IO-APIC irq-routing table: | 
 | 1423 | 	 */ | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 1424 | 	ioapic_write_entry(apic, pin, entry); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1425 | } | 
 | 1426 |  | 
| Maciej W. Rozycki | 32f71af | 2008-07-21 00:52:49 +0100 | [diff] [blame] | 1427 |  | 
 | 1428 | __apicdebuginit(void) print_IO_APIC(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1429 | { | 
 | 1430 | 	int apic, i; | 
 | 1431 | 	union IO_APIC_reg_00 reg_00; | 
 | 1432 | 	union IO_APIC_reg_01 reg_01; | 
 | 1433 | 	union IO_APIC_reg_02 reg_02; | 
 | 1434 | 	union IO_APIC_reg_03 reg_03; | 
 | 1435 | 	unsigned long flags; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 1436 | 	struct irq_cfg *cfg; | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 1437 | 	unsigned int irq; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1438 |  | 
 | 1439 | 	if (apic_verbosity == APIC_QUIET) | 
 | 1440 | 		return; | 
 | 1441 |  | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 1442 | 	printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1443 | 	for (i = 0; i < nr_ioapics; i++) | 
 | 1444 | 		printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n", | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1445 | 		       mp_ioapics[i].mp_apicid, nr_ioapic_registers[i]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1446 |  | 
 | 1447 | 	/* | 
 | 1448 | 	 * We are a bit conservative about what we expect.  We have to | 
 | 1449 | 	 * know about every hardware change ASAP. | 
 | 1450 | 	 */ | 
 | 1451 | 	printk(KERN_INFO "testing the IO APIC.......................\n"); | 
 | 1452 |  | 
 | 1453 | 	for (apic = 0; apic < nr_ioapics; apic++) { | 
 | 1454 |  | 
 | 1455 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 1456 | 	reg_00.raw = io_apic_read(apic, 0); | 
 | 1457 | 	reg_01.raw = io_apic_read(apic, 1); | 
 | 1458 | 	if (reg_01.bits.version >= 0x10) | 
 | 1459 | 		reg_02.raw = io_apic_read(apic, 2); | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 1460 | 	if (reg_01.bits.version >= 0x20) | 
 | 1461 | 		reg_03.raw = io_apic_read(apic, 3); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1462 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 1463 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1464 | 	printk("\n"); | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1465 | 	printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mp_apicid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1466 | 	printk(KERN_DEBUG ".... register #00: %08X\n", reg_00.raw); | 
 | 1467 | 	printk(KERN_DEBUG ".......    : physical APIC id: %02X\n", reg_00.bits.ID); | 
 | 1468 | 	printk(KERN_DEBUG ".......    : Delivery Type: %X\n", reg_00.bits.delivery_type); | 
 | 1469 | 	printk(KERN_DEBUG ".......    : LTS          : %X\n", reg_00.bits.LTS); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1470 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1471 | 	printk(KERN_DEBUG ".... register #01: %08X\n", *(int *)®_01); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1472 | 	printk(KERN_DEBUG ".......     : max redirection entries: %04X\n", reg_01.bits.entries); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1473 |  | 
 | 1474 | 	printk(KERN_DEBUG ".......     : PRQ implemented: %X\n", reg_01.bits.PRQ); | 
 | 1475 | 	printk(KERN_DEBUG ".......     : IO APIC version: %04X\n", reg_01.bits.version); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1476 |  | 
 | 1477 | 	/* | 
 | 1478 | 	 * Some Intel chipsets with IO APIC VERSION of 0x1? don't have reg_02, | 
 | 1479 | 	 * but the value of reg_02 is read as the previous read register | 
 | 1480 | 	 * value, so ignore it if reg_02 == reg_01. | 
 | 1481 | 	 */ | 
 | 1482 | 	if (reg_01.bits.version >= 0x10 && reg_02.raw != reg_01.raw) { | 
 | 1483 | 		printk(KERN_DEBUG ".... register #02: %08X\n", reg_02.raw); | 
 | 1484 | 		printk(KERN_DEBUG ".......     : arbitration: %02X\n", reg_02.bits.arbitration); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1485 | 	} | 
 | 1486 |  | 
 | 1487 | 	/* | 
 | 1488 | 	 * Some Intel chipsets with IO APIC VERSION of 0x2? don't have reg_02 | 
 | 1489 | 	 * or reg_03, but the value of reg_0[23] is read as the previous read | 
 | 1490 | 	 * register value, so ignore it if reg_03 == reg_0[12]. | 
 | 1491 | 	 */ | 
 | 1492 | 	if (reg_01.bits.version >= 0x20 && reg_03.raw != reg_02.raw && | 
 | 1493 | 	    reg_03.raw != reg_01.raw) { | 
 | 1494 | 		printk(KERN_DEBUG ".... register #03: %08X\n", reg_03.raw); | 
 | 1495 | 		printk(KERN_DEBUG ".......     : Boot DT    : %X\n", reg_03.bits.boot_DT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1496 | 	} | 
 | 1497 |  | 
 | 1498 | 	printk(KERN_DEBUG ".... IRQ redirection table:\n"); | 
 | 1499 |  | 
| Yinghai Lu | d83e94a | 2008-08-19 20:50:33 -0700 | [diff] [blame] | 1500 | 	printk(KERN_DEBUG " NR Dst Mask Trig IRR Pol" | 
 | 1501 | 			  " Stat Dmod Deli Vect:   \n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1502 |  | 
 | 1503 | 	for (i = 0; i <= reg_01.bits.entries; i++) { | 
 | 1504 | 		struct IO_APIC_route_entry entry; | 
 | 1505 |  | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 1506 | 		entry = ioapic_read_entry(apic, i); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1507 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1508 | 		printk(KERN_DEBUG " %02x %03X ", | 
 | 1509 | 			i, | 
 | 1510 | 			entry.dest | 
 | 1511 | 		); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1512 |  | 
 | 1513 | 		printk("%1d    %1d    %1d   %1d   %1d    %1d    %1d    %02X\n", | 
 | 1514 | 			entry.mask, | 
 | 1515 | 			entry.trigger, | 
 | 1516 | 			entry.irr, | 
 | 1517 | 			entry.polarity, | 
 | 1518 | 			entry.delivery_status, | 
 | 1519 | 			entry.dest_mode, | 
 | 1520 | 			entry.delivery_mode, | 
 | 1521 | 			entry.vector | 
 | 1522 | 		); | 
 | 1523 | 	} | 
 | 1524 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1525 | 	printk(KERN_DEBUG "IRQ to pin mappings:\n"); | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 1526 | 	for_each_irq_cfg(irq, cfg) { | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 1527 | 		struct irq_pin_list *entry = cfg->irq_2_pin; | 
 | 1528 | 		if (!entry) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1529 | 			continue; | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 1530 | 		printk(KERN_DEBUG "IRQ%d ", irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1531 | 		for (;;) { | 
 | 1532 | 			printk("-> %d:%d", entry->apic, entry->pin); | 
 | 1533 | 			if (!entry->next) | 
 | 1534 | 				break; | 
| Yinghai Lu | 0f978f4 | 2008-08-19 20:50:26 -0700 | [diff] [blame] | 1535 | 			entry = entry->next; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1536 | 		} | 
 | 1537 | 		printk("\n"); | 
 | 1538 | 	} | 
 | 1539 |  | 
 | 1540 | 	printk(KERN_INFO ".................................... done.\n"); | 
 | 1541 |  | 
 | 1542 | 	return; | 
 | 1543 | } | 
 | 1544 |  | 
| Maciej W. Rozycki | 32f71af | 2008-07-21 00:52:49 +0100 | [diff] [blame] | 1545 | __apicdebuginit(void) print_APIC_bitfield(int base) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1546 | { | 
 | 1547 | 	unsigned int v; | 
 | 1548 | 	int i, j; | 
 | 1549 |  | 
 | 1550 | 	if (apic_verbosity == APIC_QUIET) | 
 | 1551 | 		return; | 
 | 1552 |  | 
 | 1553 | 	printk(KERN_DEBUG "0123456789abcdef0123456789abcdef\n" KERN_DEBUG); | 
 | 1554 | 	for (i = 0; i < 8; i++) { | 
 | 1555 | 		v = apic_read(base + i*0x10); | 
 | 1556 | 		for (j = 0; j < 32; j++) { | 
 | 1557 | 			if (v & (1<<j)) | 
 | 1558 | 				printk("1"); | 
 | 1559 | 			else | 
 | 1560 | 				printk("0"); | 
 | 1561 | 		} | 
 | 1562 | 		printk("\n"); | 
 | 1563 | 	} | 
 | 1564 | } | 
 | 1565 |  | 
| Maciej W. Rozycki | 32f71af | 2008-07-21 00:52:49 +0100 | [diff] [blame] | 1566 | __apicdebuginit(void) print_local_APIC(void *dummy) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1567 | { | 
 | 1568 | 	unsigned int v, ver, maxlvt; | 
| Hiroshi Shimamoto | 7ab6af7 | 2008-07-30 17:36:48 -0700 | [diff] [blame] | 1569 | 	u64 icr; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1570 |  | 
 | 1571 | 	if (apic_verbosity == APIC_QUIET) | 
 | 1572 | 		return; | 
 | 1573 |  | 
 | 1574 | 	printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n", | 
 | 1575 | 		smp_processor_id(), hard_smp_processor_id()); | 
| Andreas Herrmann | 6682311 | 2008-06-05 16:35:10 +0200 | [diff] [blame] | 1576 | 	v = apic_read(APIC_ID); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1577 | 	printk(KERN_INFO "... APIC ID:      %08x (%01x)\n", v, read_apic_id()); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1578 | 	v = apic_read(APIC_LVR); | 
 | 1579 | 	printk(KERN_INFO "... APIC VERSION: %08x\n", v); | 
 | 1580 | 	ver = GET_APIC_VERSION(v); | 
| Thomas Gleixner | e05d723 | 2007-02-16 01:27:58 -0800 | [diff] [blame] | 1581 | 	maxlvt = lapic_get_maxlvt(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1582 |  | 
 | 1583 | 	v = apic_read(APIC_TASKPRI); | 
 | 1584 | 	printk(KERN_DEBUG "... APIC TASKPRI: %08x (%02x)\n", v, v & APIC_TPRI_MASK); | 
 | 1585 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1586 | 	if (APIC_INTEGRATED(ver)) {                     /* !82489DX */ | 
| Yinghai Lu | a11b5ab | 2008-09-03 16:58:31 -0700 | [diff] [blame] | 1587 | 		if (!APIC_XAPIC(ver)) { | 
 | 1588 | 			v = apic_read(APIC_ARBPRI); | 
 | 1589 | 			printk(KERN_DEBUG "... APIC ARBPRI: %08x (%02x)\n", v, | 
 | 1590 | 			       v & APIC_ARBPRI_MASK); | 
 | 1591 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1592 | 		v = apic_read(APIC_PROCPRI); | 
 | 1593 | 		printk(KERN_DEBUG "... APIC PROCPRI: %08x\n", v); | 
 | 1594 | 	} | 
 | 1595 |  | 
| Yinghai Lu | a11b5ab | 2008-09-03 16:58:31 -0700 | [diff] [blame] | 1596 | 	/* | 
 | 1597 | 	 * Remote read supported only in the 82489DX and local APIC for | 
 | 1598 | 	 * Pentium processors. | 
 | 1599 | 	 */ | 
 | 1600 | 	if (!APIC_INTEGRATED(ver) || maxlvt == 3) { | 
 | 1601 | 		v = apic_read(APIC_RRR); | 
 | 1602 | 		printk(KERN_DEBUG "... APIC RRR: %08x\n", v); | 
 | 1603 | 	} | 
 | 1604 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1605 | 	v = apic_read(APIC_LDR); | 
 | 1606 | 	printk(KERN_DEBUG "... APIC LDR: %08x\n", v); | 
| Yinghai Lu | a11b5ab | 2008-09-03 16:58:31 -0700 | [diff] [blame] | 1607 | 	if (!x2apic_enabled()) { | 
 | 1608 | 		v = apic_read(APIC_DFR); | 
 | 1609 | 		printk(KERN_DEBUG "... APIC DFR: %08x\n", v); | 
 | 1610 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1611 | 	v = apic_read(APIC_SPIV); | 
 | 1612 | 	printk(KERN_DEBUG "... APIC SPIV: %08x\n", v); | 
 | 1613 |  | 
 | 1614 | 	printk(KERN_DEBUG "... APIC ISR field:\n"); | 
 | 1615 | 	print_APIC_bitfield(APIC_ISR); | 
 | 1616 | 	printk(KERN_DEBUG "... APIC TMR field:\n"); | 
 | 1617 | 	print_APIC_bitfield(APIC_TMR); | 
 | 1618 | 	printk(KERN_DEBUG "... APIC IRR field:\n"); | 
 | 1619 | 	print_APIC_bitfield(APIC_IRR); | 
 | 1620 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1621 | 	if (APIC_INTEGRATED(ver)) {             /* !82489DX */ | 
 | 1622 | 		if (maxlvt > 3)         /* Due to the Pentium erratum 3AP. */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1623 | 			apic_write(APIC_ESR, 0); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1624 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1625 | 		v = apic_read(APIC_ESR); | 
 | 1626 | 		printk(KERN_DEBUG "... APIC ESR: %08x\n", v); | 
 | 1627 | 	} | 
 | 1628 |  | 
| Hiroshi Shimamoto | 7ab6af7 | 2008-07-30 17:36:48 -0700 | [diff] [blame] | 1629 | 	icr = apic_icr_read(); | 
| Ingo Molnar | 0c425ce | 2008-08-18 13:04:26 +0200 | [diff] [blame] | 1630 | 	printk(KERN_DEBUG "... APIC ICR: %08x\n", (u32)icr); | 
 | 1631 | 	printk(KERN_DEBUG "... APIC ICR2: %08x\n", (u32)(icr >> 32)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1632 |  | 
 | 1633 | 	v = apic_read(APIC_LVTT); | 
 | 1634 | 	printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); | 
 | 1635 |  | 
 | 1636 | 	if (maxlvt > 3) {                       /* PC is LVT#4. */ | 
 | 1637 | 		v = apic_read(APIC_LVTPC); | 
 | 1638 | 		printk(KERN_DEBUG "... APIC LVTPC: %08x\n", v); | 
 | 1639 | 	} | 
 | 1640 | 	v = apic_read(APIC_LVT0); | 
 | 1641 | 	printk(KERN_DEBUG "... APIC LVT0: %08x\n", v); | 
 | 1642 | 	v = apic_read(APIC_LVT1); | 
 | 1643 | 	printk(KERN_DEBUG "... APIC LVT1: %08x\n", v); | 
 | 1644 |  | 
 | 1645 | 	if (maxlvt > 2) {			/* ERR is LVT#3. */ | 
 | 1646 | 		v = apic_read(APIC_LVTERR); | 
 | 1647 | 		printk(KERN_DEBUG "... APIC LVTERR: %08x\n", v); | 
 | 1648 | 	} | 
 | 1649 |  | 
 | 1650 | 	v = apic_read(APIC_TMICT); | 
 | 1651 | 	printk(KERN_DEBUG "... APIC TMICT: %08x\n", v); | 
 | 1652 | 	v = apic_read(APIC_TMCCT); | 
 | 1653 | 	printk(KERN_DEBUG "... APIC TMCCT: %08x\n", v); | 
 | 1654 | 	v = apic_read(APIC_TDCR); | 
 | 1655 | 	printk(KERN_DEBUG "... APIC TDCR: %08x\n", v); | 
 | 1656 | 	printk("\n"); | 
 | 1657 | } | 
 | 1658 |  | 
| Maciej W. Rozycki | 32f71af | 2008-07-21 00:52:49 +0100 | [diff] [blame] | 1659 | __apicdebuginit(void) print_all_local_APICs(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1660 | { | 
| Yinghai Lu | ffd5aae | 2008-08-19 20:50:50 -0700 | [diff] [blame] | 1661 | 	int cpu; | 
 | 1662 |  | 
 | 1663 | 	preempt_disable(); | 
 | 1664 | 	for_each_online_cpu(cpu) | 
 | 1665 | 		smp_call_function_single(cpu, print_local_APIC, NULL, 1); | 
 | 1666 | 	preempt_enable(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1667 | } | 
 | 1668 |  | 
| Maciej W. Rozycki | 32f71af | 2008-07-21 00:52:49 +0100 | [diff] [blame] | 1669 | __apicdebuginit(void) print_PIC(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1670 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1671 | 	unsigned int v; | 
 | 1672 | 	unsigned long flags; | 
 | 1673 |  | 
 | 1674 | 	if (apic_verbosity == APIC_QUIET) | 
 | 1675 | 		return; | 
 | 1676 |  | 
 | 1677 | 	printk(KERN_DEBUG "\nprinting PIC contents\n"); | 
 | 1678 |  | 
 | 1679 | 	spin_lock_irqsave(&i8259A_lock, flags); | 
 | 1680 |  | 
 | 1681 | 	v = inb(0xa1) << 8 | inb(0x21); | 
 | 1682 | 	printk(KERN_DEBUG "... PIC  IMR: %04x\n", v); | 
 | 1683 |  | 
 | 1684 | 	v = inb(0xa0) << 8 | inb(0x20); | 
 | 1685 | 	printk(KERN_DEBUG "... PIC  IRR: %04x\n", v); | 
 | 1686 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1687 | 	outb(0x0b,0xa0); | 
 | 1688 | 	outb(0x0b,0x20); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1689 | 	v = inb(0xa0) << 8 | inb(0x20); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1690 | 	outb(0x0a,0xa0); | 
 | 1691 | 	outb(0x0a,0x20); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1692 |  | 
 | 1693 | 	spin_unlock_irqrestore(&i8259A_lock, flags); | 
 | 1694 |  | 
 | 1695 | 	printk(KERN_DEBUG "... PIC  ISR: %04x\n", v); | 
 | 1696 |  | 
 | 1697 | 	v = inb(0x4d1) << 8 | inb(0x4d0); | 
 | 1698 | 	printk(KERN_DEBUG "... PIC ELCR: %04x\n", v); | 
 | 1699 | } | 
 | 1700 |  | 
| Maciej W. Rozycki | 32f71af | 2008-07-21 00:52:49 +0100 | [diff] [blame] | 1701 | __apicdebuginit(int) print_all_ICs(void) | 
 | 1702 | { | 
 | 1703 | 	print_PIC(); | 
 | 1704 | 	print_all_local_APICs(); | 
 | 1705 | 	print_IO_APIC(); | 
 | 1706 |  | 
 | 1707 | 	return 0; | 
 | 1708 | } | 
 | 1709 |  | 
 | 1710 | fs_initcall(print_all_ICs); | 
 | 1711 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1712 |  | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 1713 | /* Where if anywhere is the i8259 connect in external int mode */ | 
 | 1714 | static struct { int pin, apic; } ioapic_i8259 = { -1, -1 }; | 
 | 1715 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1716 | void __init enable_IO_APIC(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1717 | { | 
 | 1718 | 	union IO_APIC_reg_01 reg_01; | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1719 | 	int i8259_apic, i8259_pin; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1720 | 	int apic; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1721 | 	unsigned long flags; | 
 | 1722 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1723 | #ifdef CONFIG_X86_32 | 
 | 1724 | 	int i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1725 | 	if (!pirqs_enabled) | 
 | 1726 | 		for (i = 0; i < MAX_PIRQS; i++) | 
 | 1727 | 			pirq_entries[i] = -1; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1728 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1729 |  | 
 | 1730 | 	/* | 
 | 1731 | 	 * The number of IO-APIC IRQ registers (== #pins): | 
 | 1732 | 	 */ | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1733 | 	for (apic = 0; apic < nr_ioapics; apic++) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1734 | 		spin_lock_irqsave(&ioapic_lock, flags); | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1735 | 		reg_01.raw = io_apic_read(apic, 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1736 | 		spin_unlock_irqrestore(&ioapic_lock, flags); | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1737 | 		nr_ioapic_registers[apic] = reg_01.bits.entries+1; | 
 | 1738 | 	} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1739 | 	for(apic = 0; apic < nr_ioapics; apic++) { | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1740 | 		int pin; | 
 | 1741 | 		/* See if any of the pins is in ExtINT mode */ | 
| Eric W. Biederman | 1008fdd | 2006-01-11 22:46:06 +0100 | [diff] [blame] | 1742 | 		for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) { | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1743 | 			struct IO_APIC_route_entry entry; | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 1744 | 			entry = ioapic_read_entry(apic, pin); | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1745 |  | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1746 | 			/* If the interrupt line is enabled and in ExtInt mode | 
 | 1747 | 			 * I have found the pin where the i8259 is connected. | 
 | 1748 | 			 */ | 
 | 1749 | 			if ((entry.mask == 0) && (entry.delivery_mode == dest_ExtINT)) { | 
 | 1750 | 				ioapic_i8259.apic = apic; | 
 | 1751 | 				ioapic_i8259.pin  = pin; | 
 | 1752 | 				goto found_i8259; | 
 | 1753 | 			} | 
 | 1754 | 		} | 
 | 1755 | 	} | 
 | 1756 |  found_i8259: | 
 | 1757 | 	/* Look to see what if the MP table has reported the ExtINT */ | 
 | 1758 | 	/* If we could not find the appropriate pin by looking at the ioapic | 
 | 1759 | 	 * the i8259 probably is not connected the ioapic but give the | 
 | 1760 | 	 * mptable a chance anyway. | 
 | 1761 | 	 */ | 
 | 1762 | 	i8259_pin  = find_isa_irq_pin(0, mp_ExtINT); | 
 | 1763 | 	i8259_apic = find_isa_irq_apic(0, mp_ExtINT); | 
 | 1764 | 	/* Trust the MP table if nothing is setup in the hardware */ | 
 | 1765 | 	if ((ioapic_i8259.pin == -1) && (i8259_pin >= 0)) { | 
 | 1766 | 		printk(KERN_WARNING "ExtINT not setup in hardware but reported by MP table\n"); | 
 | 1767 | 		ioapic_i8259.pin  = i8259_pin; | 
 | 1768 | 		ioapic_i8259.apic = i8259_apic; | 
 | 1769 | 	} | 
 | 1770 | 	/* Complain if the MP table and the hardware disagree */ | 
 | 1771 | 	if (((ioapic_i8259.apic != i8259_apic) || (ioapic_i8259.pin != i8259_pin)) && | 
 | 1772 | 		(i8259_pin >= 0) && (ioapic_i8259.pin >= 0)) | 
 | 1773 | 	{ | 
 | 1774 | 		printk(KERN_WARNING "ExtINT in hardware and MP table differ\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1775 | 	} | 
 | 1776 |  | 
 | 1777 | 	/* | 
 | 1778 | 	 * Do not trust the IO-APIC being empty at bootup | 
 | 1779 | 	 */ | 
 | 1780 | 	clear_IO_APIC(); | 
 | 1781 | } | 
 | 1782 |  | 
 | 1783 | /* | 
 | 1784 |  * Not an __init, needed by the reboot code | 
 | 1785 |  */ | 
 | 1786 | void disable_IO_APIC(void) | 
 | 1787 | { | 
 | 1788 | 	/* | 
 | 1789 | 	 * Clear the IO-APIC before rebooting: | 
 | 1790 | 	 */ | 
 | 1791 | 	clear_IO_APIC(); | 
 | 1792 |  | 
| Eric W. Biederman | 650927e | 2005-06-25 14:57:44 -0700 | [diff] [blame] | 1793 | 	/* | 
| Karsten Wiese | 0b968d2 | 2005-09-09 12:59:04 +0200 | [diff] [blame] | 1794 | 	 * If the i8259 is routed through an IOAPIC | 
| Eric W. Biederman | 650927e | 2005-06-25 14:57:44 -0700 | [diff] [blame] | 1795 | 	 * Put that IOAPIC in virtual wire mode | 
| Karsten Wiese | 0b968d2 | 2005-09-09 12:59:04 +0200 | [diff] [blame] | 1796 | 	 * so legacy interrupts can be delivered. | 
| Eric W. Biederman | 650927e | 2005-06-25 14:57:44 -0700 | [diff] [blame] | 1797 | 	 */ | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1798 | 	if (ioapic_i8259.pin != -1) { | 
| Eric W. Biederman | 650927e | 2005-06-25 14:57:44 -0700 | [diff] [blame] | 1799 | 		struct IO_APIC_route_entry entry; | 
| Eric W. Biederman | 650927e | 2005-06-25 14:57:44 -0700 | [diff] [blame] | 1800 |  | 
 | 1801 | 		memset(&entry, 0, sizeof(entry)); | 
 | 1802 | 		entry.mask            = 0; /* Enabled */ | 
 | 1803 | 		entry.trigger         = 0; /* Edge */ | 
 | 1804 | 		entry.irr             = 0; | 
 | 1805 | 		entry.polarity        = 0; /* High */ | 
 | 1806 | 		entry.delivery_status = 0; | 
 | 1807 | 		entry.dest_mode       = 0; /* Physical */ | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1808 | 		entry.delivery_mode   = dest_ExtINT; /* ExtInt */ | 
| Eric W. Biederman | 650927e | 2005-06-25 14:57:44 -0700 | [diff] [blame] | 1809 | 		entry.vector          = 0; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1810 | 		entry.dest            = read_apic_id(); | 
| Eric W. Biederman | 650927e | 2005-06-25 14:57:44 -0700 | [diff] [blame] | 1811 |  | 
 | 1812 | 		/* | 
 | 1813 | 		 * Add it to the IO-APIC irq-routing table: | 
 | 1814 | 		 */ | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 1815 | 		ioapic_write_entry(ioapic_i8259.apic, ioapic_i8259.pin, entry); | 
| Eric W. Biederman | 650927e | 2005-06-25 14:57:44 -0700 | [diff] [blame] | 1816 | 	} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1817 |  | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 1818 | 	disconnect_bsp_APIC(ioapic_i8259.pin != -1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1819 | } | 
 | 1820 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1821 | #ifdef CONFIG_X86_32 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1822 | /* | 
 | 1823 |  * function to set the IO-APIC physical IDs based on the | 
 | 1824 |  * values stored in the MPC table. | 
 | 1825 |  * | 
 | 1826 |  * by Matt Domsch <Matt_Domsch@dell.com>  Tue Dec 21 12:25:05 CST 1999 | 
 | 1827 |  */ | 
 | 1828 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1829 | static void __init setup_ioapic_ids_from_mpc(void) | 
 | 1830 | { | 
 | 1831 | 	union IO_APIC_reg_00 reg_00; | 
 | 1832 | 	physid_mask_t phys_id_present_map; | 
 | 1833 | 	int apic; | 
 | 1834 | 	int i; | 
 | 1835 | 	unsigned char old_id; | 
 | 1836 | 	unsigned long flags; | 
 | 1837 |  | 
| Yinghai Lu | a4dbc34 | 2008-07-25 02:14:28 -0700 | [diff] [blame] | 1838 | 	if (x86_quirks->setup_ioapic_ids && x86_quirks->setup_ioapic_ids()) | 
| Yinghai Lu | d49c428 | 2008-06-08 18:31:54 -0700 | [diff] [blame] | 1839 | 		return; | 
| Yinghai Lu | d49c428 | 2008-06-08 18:31:54 -0700 | [diff] [blame] | 1840 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1841 | 	/* | 
| Natalie Protasevich | ca05fea | 2005-06-23 00:08:22 -0700 | [diff] [blame] | 1842 | 	 * Don't check I/O APIC IDs for xAPIC systems.  They have | 
 | 1843 | 	 * no meaning without the serial APIC bus. | 
 | 1844 | 	 */ | 
| Shaohua Li | 7c5c1e4 | 2006-03-23 02:59:53 -0800 | [diff] [blame] | 1845 | 	if (!(boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) | 
 | 1846 | 		|| APIC_XAPIC(apic_version[boot_cpu_physical_apicid])) | 
| Natalie Protasevich | ca05fea | 2005-06-23 00:08:22 -0700 | [diff] [blame] | 1847 | 		return; | 
 | 1848 | 	/* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1849 | 	 * This is broken; anything with a real cpu count has to | 
 | 1850 | 	 * circumvent this idiocy regardless. | 
 | 1851 | 	 */ | 
 | 1852 | 	phys_id_present_map = ioapic_phys_id_map(phys_cpu_present_map); | 
 | 1853 |  | 
 | 1854 | 	/* | 
 | 1855 | 	 * Set the IOAPIC ID to the value stored in the MPC table. | 
 | 1856 | 	 */ | 
 | 1857 | 	for (apic = 0; apic < nr_ioapics; apic++) { | 
 | 1858 |  | 
 | 1859 | 		/* Read the register 0 value */ | 
 | 1860 | 		spin_lock_irqsave(&ioapic_lock, flags); | 
 | 1861 | 		reg_00.raw = io_apic_read(apic, 0); | 
 | 1862 | 		spin_unlock_irqrestore(&ioapic_lock, flags); | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 1863 |  | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1864 | 		old_id = mp_ioapics[apic].mp_apicid; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1865 |  | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1866 | 		if (mp_ioapics[apic].mp_apicid >= get_physical_broadcast()) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1867 | 			printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n", | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1868 | 				apic, mp_ioapics[apic].mp_apicid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1869 | 			printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", | 
 | 1870 | 				reg_00.bits.ID); | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1871 | 			mp_ioapics[apic].mp_apicid = reg_00.bits.ID; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1872 | 		} | 
 | 1873 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1874 | 		/* | 
 | 1875 | 		 * Sanity check, is the ID really free? Every APIC in a | 
 | 1876 | 		 * system must have a unique ID or we get lots of nice | 
 | 1877 | 		 * 'stuck on smp_invalidate_needed IPI wait' messages. | 
 | 1878 | 		 */ | 
 | 1879 | 		if (check_apicid_used(phys_id_present_map, | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1880 | 					mp_ioapics[apic].mp_apicid)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1881 | 			printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n", | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1882 | 				apic, mp_ioapics[apic].mp_apicid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1883 | 			for (i = 0; i < get_physical_broadcast(); i++) | 
 | 1884 | 				if (!physid_isset(i, phys_id_present_map)) | 
 | 1885 | 					break; | 
 | 1886 | 			if (i >= get_physical_broadcast()) | 
 | 1887 | 				panic("Max APIC ID exceeded!\n"); | 
 | 1888 | 			printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n", | 
 | 1889 | 				i); | 
 | 1890 | 			physid_set(i, phys_id_present_map); | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1891 | 			mp_ioapics[apic].mp_apicid = i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1892 | 		} else { | 
 | 1893 | 			physid_mask_t tmp; | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1894 | 			tmp = apicid_to_cpu_present(mp_ioapics[apic].mp_apicid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1895 | 			apic_printk(APIC_VERBOSE, "Setting %d in the " | 
 | 1896 | 					"phys_id_present_map\n", | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1897 | 					mp_ioapics[apic].mp_apicid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1898 | 			physids_or(phys_id_present_map, phys_id_present_map, tmp); | 
 | 1899 | 		} | 
 | 1900 |  | 
 | 1901 |  | 
 | 1902 | 		/* | 
 | 1903 | 		 * We need to adjust the IRQ routing table | 
 | 1904 | 		 * if the ID changed. | 
 | 1905 | 		 */ | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1906 | 		if (old_id != mp_ioapics[apic].mp_apicid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1907 | 			for (i = 0; i < mp_irq_entries; i++) | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 1908 | 				if (mp_irqs[i].mp_dstapic == old_id) | 
 | 1909 | 					mp_irqs[i].mp_dstapic | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1910 | 						= mp_ioapics[apic].mp_apicid; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1911 |  | 
 | 1912 | 		/* | 
 | 1913 | 		 * Read the right value from the MPC table and | 
 | 1914 | 		 * write it into the ID register. | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 1915 | 		 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1916 | 		apic_printk(APIC_VERBOSE, KERN_INFO | 
 | 1917 | 			"...changing IO-APIC physical APIC ID to %d ...", | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1918 | 			mp_ioapics[apic].mp_apicid); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1919 |  | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1920 | 		reg_00.bits.ID = mp_ioapics[apic].mp_apicid; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1921 | 		spin_lock_irqsave(&ioapic_lock, flags); | 
| Yinghai Lu | a2d332f | 2008-08-21 12:56:32 -0700 | [diff] [blame] | 1922 | 		io_apic_write(apic, 0, reg_00.raw); | 
 | 1923 | 		spin_unlock_irqrestore(&ioapic_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1924 |  | 
 | 1925 | 		/* | 
 | 1926 | 		 * Sanity check | 
 | 1927 | 		 */ | 
 | 1928 | 		spin_lock_irqsave(&ioapic_lock, flags); | 
 | 1929 | 		reg_00.raw = io_apic_read(apic, 0); | 
 | 1930 | 		spin_unlock_irqrestore(&ioapic_lock, flags); | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 1931 | 		if (reg_00.bits.ID != mp_ioapics[apic].mp_apicid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1932 | 			printk("could not set ID!\n"); | 
 | 1933 | 		else | 
 | 1934 | 			apic_printk(APIC_VERBOSE, " ok.\n"); | 
 | 1935 | 	} | 
 | 1936 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1937 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1938 |  | 
| Zachary Amsden | 7ce0bcf | 2007-02-13 13:26:21 +0100 | [diff] [blame] | 1939 | int no_timer_check __initdata; | 
| Zachary Amsden | 8542b20 | 2006-12-07 02:14:09 +0100 | [diff] [blame] | 1940 |  | 
 | 1941 | static int __init notimercheck(char *s) | 
 | 1942 | { | 
 | 1943 | 	no_timer_check = 1; | 
 | 1944 | 	return 1; | 
 | 1945 | } | 
 | 1946 | __setup("no_timer_check", notimercheck); | 
 | 1947 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1948 | /* | 
 | 1949 |  * There is a nasty bug in some older SMP boards, their mptable lies | 
 | 1950 |  * about the timer IRQ. We do the following to work around the situation: | 
 | 1951 |  * | 
 | 1952 |  *	- timer IRQ defaults to IO-APIC IRQ | 
 | 1953 |  *	- if this function detects that timer IRQs are defunct, then we fall | 
 | 1954 |  *	  back to ISA timer IRQs | 
 | 1955 |  */ | 
| Adrian Bunk | f0a7a5c | 2007-07-21 17:10:29 +0200 | [diff] [blame] | 1956 | static int __init timer_irq_works(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1957 | { | 
 | 1958 | 	unsigned long t1 = jiffies; | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 1959 | 	unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1960 |  | 
| Zachary Amsden | 8542b20 | 2006-12-07 02:14:09 +0100 | [diff] [blame] | 1961 | 	if (no_timer_check) | 
 | 1962 | 		return 1; | 
 | 1963 |  | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 1964 | 	local_save_flags(flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1965 | 	local_irq_enable(); | 
 | 1966 | 	/* Let ten ticks pass... */ | 
 | 1967 | 	mdelay((10 * 1000) / HZ); | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 1968 | 	local_irq_restore(flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1969 |  | 
 | 1970 | 	/* | 
 | 1971 | 	 * Expect a few ticks at least, to be sure some possible | 
 | 1972 | 	 * glue logic does not lock up after one or two first | 
 | 1973 | 	 * ticks in a non-ExtINT mode.  Also the local APIC | 
 | 1974 | 	 * might have cached one ExtINT interrupt.  Finally, at | 
 | 1975 | 	 * least one tick may be lost due to delays. | 
 | 1976 | 	 */ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 1977 |  | 
 | 1978 | 	/* jiffies wrap? */ | 
| Julia Lawall | 1d16b53 | 2008-01-30 13:32:19 +0100 | [diff] [blame] | 1979 | 	if (time_after(jiffies, t1 + 4)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1980 | 		return 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1981 | 	return 0; | 
 | 1982 | } | 
 | 1983 |  | 
 | 1984 | /* | 
 | 1985 |  * In the SMP+IOAPIC case it might happen that there are an unspecified | 
 | 1986 |  * number of pending IRQ events unhandled. These cases are very rare, | 
 | 1987 |  * so we 'resend' these IRQs via IPIs, to the same CPU. It's much | 
 | 1988 |  * better to do it this way as thus we do not have to be aware of | 
 | 1989 |  * 'pending' interrupts in the IRQ path, except at this point. | 
 | 1990 |  */ | 
 | 1991 | /* | 
 | 1992 |  * Edge triggered needs to resend any interrupt | 
 | 1993 |  * that was delayed but this is now handled in the device | 
 | 1994 |  * independent code. | 
 | 1995 |  */ | 
 | 1996 |  | 
 | 1997 | /* | 
 | 1998 |  * Starting up a edge-triggered IO-APIC interrupt is | 
 | 1999 |  * nasty - we need to make sure that we get the edge. | 
 | 2000 |  * If it is already asserted for some reason, we need | 
 | 2001 |  * return 1 to indicate that is was pending. | 
 | 2002 |  * | 
 | 2003 |  * This is not complete - we should be able to fake | 
 | 2004 |  * an edge even if it isn't on the 8259A... | 
 | 2005 |  */ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2006 |  | 
| Ingo Molnar | f5b9ed7 | 2006-10-04 02:16:26 -0700 | [diff] [blame] | 2007 | static unsigned int startup_ioapic_irq(unsigned int irq) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2008 | { | 
 | 2009 | 	int was_pending = 0; | 
 | 2010 | 	unsigned long flags; | 
 | 2011 |  | 
 | 2012 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 2013 | 	if (irq < 16) { | 
 | 2014 | 		disable_8259A_irq(irq); | 
 | 2015 | 		if (i8259A_irq_pending(irq)) | 
 | 2016 | 			was_pending = 1; | 
 | 2017 | 	} | 
 | 2018 | 	__unmask_IO_APIC_irq(irq); | 
 | 2019 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 2020 |  | 
 | 2021 | 	return was_pending; | 
 | 2022 | } | 
 | 2023 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2024 | #ifdef CONFIG_X86_64 | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 2025 | static int ioapic_retrigger_irq(unsigned int irq) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2026 | { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2027 |  | 
 | 2028 | 	struct irq_cfg *cfg = irq_cfg(irq); | 
 | 2029 | 	unsigned long flags; | 
 | 2030 |  | 
 | 2031 | 	spin_lock_irqsave(&vector_lock, flags); | 
 | 2032 | 	send_IPI_mask(cpumask_of_cpu(first_cpu(cfg->domain)), cfg->vector); | 
 | 2033 | 	spin_unlock_irqrestore(&vector_lock, flags); | 
| Ingo Molnar | c0ad90a | 2006-06-29 02:24:44 -0700 | [diff] [blame] | 2034 |  | 
 | 2035 | 	return 1; | 
 | 2036 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2037 | #else | 
 | 2038 | static int ioapic_retrigger_irq(unsigned int irq) | 
 | 2039 | { | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2040 | 	send_IPI_self(irq_cfg(irq)->vector); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2041 |  | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2042 | 	return 1; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2043 | } | 
 | 2044 | #endif | 
 | 2045 |  | 
 | 2046 | /* | 
 | 2047 |  * Level and edge triggered IO-APIC interrupts need different handling, | 
 | 2048 |  * so we use two separate IRQ descriptors. Edge triggered IRQs can be | 
 | 2049 |  * handled with the level-triggered descriptor, but that one has slightly | 
 | 2050 |  * more overhead. Level-triggered interrupts cannot be handled with the | 
 | 2051 |  * edge-triggered handler, without risking IRQ storms and other ugly | 
 | 2052 |  * races. | 
 | 2053 |  */ | 
| Ingo Molnar | c0ad90a | 2006-06-29 02:24:44 -0700 | [diff] [blame] | 2054 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2055 | #ifdef CONFIG_SMP | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2056 |  | 
 | 2057 | #ifdef CONFIG_INTR_REMAP | 
 | 2058 | static void ir_irq_migration(struct work_struct *work); | 
 | 2059 |  | 
 | 2060 | static DECLARE_DELAYED_WORK(ir_migration_work, ir_irq_migration); | 
 | 2061 |  | 
 | 2062 | /* | 
 | 2063 |  * Migrate the IO-APIC irq in the presence of intr-remapping. | 
 | 2064 |  * | 
 | 2065 |  * For edge triggered, irq migration is a simple atomic update(of vector | 
 | 2066 |  * and cpu destination) of IRTE and flush the hardware cache. | 
 | 2067 |  * | 
 | 2068 |  * For level triggered, we need to modify the io-apic RTE aswell with the update | 
 | 2069 |  * vector information, along with modifying IRTE with vector and destination. | 
 | 2070 |  * So irq migration for level triggered is little  bit more complex compared to | 
 | 2071 |  * edge triggered migration. But the good news is, we use the same algorithm | 
 | 2072 |  * for level triggered migration as we have today, only difference being, | 
 | 2073 |  * we now initiate the irq migration from process context instead of the | 
 | 2074 |  * interrupt context. | 
 | 2075 |  * | 
 | 2076 |  * In future, when we do a directed EOI (combined with cpu EOI broadcast | 
 | 2077 |  * suppression) to the IO-APIC, level triggered irq migration will also be | 
 | 2078 |  * as simple as edge triggered migration and we can do the irq migration | 
 | 2079 |  * with a simple atomic update to IO-APIC RTE. | 
 | 2080 |  */ | 
 | 2081 | static void migrate_ioapic_irq(int irq, cpumask_t mask) | 
 | 2082 | { | 
 | 2083 | 	struct irq_cfg *cfg; | 
 | 2084 | 	struct irq_desc *desc; | 
 | 2085 | 	cpumask_t tmp, cleanup_mask; | 
 | 2086 | 	struct irte irte; | 
 | 2087 | 	int modify_ioapic_rte; | 
 | 2088 | 	unsigned int dest; | 
 | 2089 | 	unsigned long flags; | 
 | 2090 |  | 
 | 2091 | 	cpus_and(tmp, mask, cpu_online_map); | 
 | 2092 | 	if (cpus_empty(tmp)) | 
 | 2093 | 		return; | 
 | 2094 |  | 
 | 2095 | 	if (get_irte(irq, &irte)) | 
 | 2096 | 		return; | 
 | 2097 |  | 
 | 2098 | 	if (assign_irq_vector(irq, mask)) | 
 | 2099 | 		return; | 
 | 2100 |  | 
 | 2101 | 	cfg = irq_cfg(irq); | 
 | 2102 | 	cpus_and(tmp, cfg->domain, mask); | 
 | 2103 | 	dest = cpu_mask_to_apicid(tmp); | 
 | 2104 |  | 
 | 2105 | 	desc = irq_to_desc(irq); | 
 | 2106 | 	modify_ioapic_rte = desc->status & IRQ_LEVEL; | 
 | 2107 | 	if (modify_ioapic_rte) { | 
 | 2108 | 		spin_lock_irqsave(&ioapic_lock, flags); | 
 | 2109 | 		__target_IO_APIC_irq(irq, dest, cfg->vector); | 
 | 2110 | 		spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 2111 | 	} | 
 | 2112 |  | 
 | 2113 | 	irte.vector = cfg->vector; | 
 | 2114 | 	irte.dest_id = IRTE_DEST(dest); | 
 | 2115 |  | 
 | 2116 | 	/* | 
 | 2117 | 	 * Modified the IRTE and flushes the Interrupt entry cache. | 
 | 2118 | 	 */ | 
 | 2119 | 	modify_irte(irq, &irte); | 
 | 2120 |  | 
 | 2121 | 	if (cfg->move_in_progress) { | 
 | 2122 | 		cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map); | 
 | 2123 | 		cfg->move_cleanup_count = cpus_weight(cleanup_mask); | 
 | 2124 | 		send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR); | 
 | 2125 | 		cfg->move_in_progress = 0; | 
 | 2126 | 	} | 
 | 2127 |  | 
 | 2128 | 	desc->affinity = mask; | 
 | 2129 | } | 
 | 2130 |  | 
 | 2131 | static int migrate_irq_remapped_level(int irq) | 
 | 2132 | { | 
 | 2133 | 	int ret = -1; | 
 | 2134 | 	struct irq_desc *desc = irq_to_desc(irq); | 
 | 2135 |  | 
 | 2136 | 	mask_IO_APIC_irq(irq); | 
 | 2137 |  | 
 | 2138 | 	if (io_apic_level_ack_pending(irq)) { | 
 | 2139 | 		/* | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2140 | 		 * Interrupt in progress. Migrating irq now will change the | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2141 | 		 * vector information in the IO-APIC RTE and that will confuse | 
 | 2142 | 		 * the EOI broadcast performed by cpu. | 
 | 2143 | 		 * So, delay the irq migration to the next instance. | 
 | 2144 | 		 */ | 
 | 2145 | 		schedule_delayed_work(&ir_migration_work, 1); | 
 | 2146 | 		goto unmask; | 
 | 2147 | 	} | 
 | 2148 |  | 
 | 2149 | 	/* everthing is clear. we have right of way */ | 
 | 2150 | 	migrate_ioapic_irq(irq, desc->pending_mask); | 
 | 2151 |  | 
 | 2152 | 	ret = 0; | 
 | 2153 | 	desc->status &= ~IRQ_MOVE_PENDING; | 
 | 2154 | 	cpus_clear(desc->pending_mask); | 
 | 2155 |  | 
 | 2156 | unmask: | 
 | 2157 | 	unmask_IO_APIC_irq(irq); | 
 | 2158 | 	return ret; | 
 | 2159 | } | 
 | 2160 |  | 
 | 2161 | static void ir_irq_migration(struct work_struct *work) | 
 | 2162 | { | 
 | 2163 | 	unsigned int irq; | 
 | 2164 | 	struct irq_desc *desc; | 
 | 2165 |  | 
 | 2166 | 	for_each_irq_desc(irq, desc) { | 
 | 2167 | 		if (desc->status & IRQ_MOVE_PENDING) { | 
 | 2168 | 			unsigned long flags; | 
 | 2169 |  | 
 | 2170 | 			spin_lock_irqsave(&desc->lock, flags); | 
 | 2171 | 			if (!desc->chip->set_affinity || | 
 | 2172 | 			    !(desc->status & IRQ_MOVE_PENDING)) { | 
 | 2173 | 				desc->status &= ~IRQ_MOVE_PENDING; | 
 | 2174 | 				spin_unlock_irqrestore(&desc->lock, flags); | 
 | 2175 | 				continue; | 
 | 2176 | 			} | 
 | 2177 |  | 
 | 2178 | 			desc->chip->set_affinity(irq, desc->pending_mask); | 
 | 2179 | 			spin_unlock_irqrestore(&desc->lock, flags); | 
 | 2180 | 		} | 
 | 2181 | 	} | 
 | 2182 | } | 
 | 2183 |  | 
 | 2184 | /* | 
 | 2185 |  * Migrates the IRQ destination in the process context. | 
 | 2186 |  */ | 
 | 2187 | static void set_ir_ioapic_affinity_irq(unsigned int irq, cpumask_t mask) | 
 | 2188 | { | 
 | 2189 | 	struct irq_desc *desc = irq_to_desc(irq); | 
 | 2190 |  | 
 | 2191 | 	if (desc->status & IRQ_LEVEL) { | 
 | 2192 | 		desc->status |= IRQ_MOVE_PENDING; | 
 | 2193 | 		desc->pending_mask = mask; | 
 | 2194 | 		migrate_irq_remapped_level(irq); | 
 | 2195 | 		return; | 
 | 2196 | 	} | 
 | 2197 |  | 
 | 2198 | 	migrate_ioapic_irq(irq, mask); | 
 | 2199 | } | 
 | 2200 | #endif | 
 | 2201 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2202 | asmlinkage void smp_irq_move_cleanup_interrupt(void) | 
 | 2203 | { | 
 | 2204 | 	unsigned vector, me; | 
 | 2205 | 	ack_APIC_irq(); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2206 | #ifdef CONFIG_X86_64 | 
 | 2207 | 	exit_idle(); | 
 | 2208 | #endif | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2209 | 	irq_enter(); | 
 | 2210 |  | 
 | 2211 | 	me = smp_processor_id(); | 
 | 2212 | 	for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS; vector++) { | 
 | 2213 | 		unsigned int irq; | 
 | 2214 | 		struct irq_desc *desc; | 
 | 2215 | 		struct irq_cfg *cfg; | 
 | 2216 | 		irq = __get_cpu_var(vector_irq)[vector]; | 
 | 2217 |  | 
 | 2218 | 		desc = irq_to_desc(irq); | 
 | 2219 | 		if (!desc) | 
 | 2220 | 			continue; | 
 | 2221 |  | 
 | 2222 | 		cfg = irq_cfg(irq); | 
 | 2223 | 		spin_lock(&desc->lock); | 
 | 2224 | 		if (!cfg->move_cleanup_count) | 
 | 2225 | 			goto unlock; | 
 | 2226 |  | 
 | 2227 | 		if ((vector == cfg->vector) && cpu_isset(me, cfg->domain)) | 
 | 2228 | 			goto unlock; | 
 | 2229 |  | 
 | 2230 | 		__get_cpu_var(vector_irq)[vector] = -1; | 
 | 2231 | 		cfg->move_cleanup_count--; | 
 | 2232 | unlock: | 
 | 2233 | 		spin_unlock(&desc->lock); | 
 | 2234 | 	} | 
 | 2235 |  | 
 | 2236 | 	irq_exit(); | 
 | 2237 | } | 
 | 2238 |  | 
 | 2239 | static void irq_complete_move(unsigned int irq) | 
 | 2240 | { | 
 | 2241 | 	struct irq_cfg *cfg = irq_cfg(irq); | 
 | 2242 | 	unsigned vector, me; | 
 | 2243 |  | 
 | 2244 | 	if (likely(!cfg->move_in_progress)) | 
 | 2245 | 		return; | 
 | 2246 |  | 
 | 2247 | 	vector = ~get_irq_regs()->orig_ax; | 
 | 2248 | 	me = smp_processor_id(); | 
 | 2249 | 	if ((vector == cfg->vector) && cpu_isset(me, cfg->domain)) { | 
 | 2250 | 		cpumask_t cleanup_mask; | 
 | 2251 |  | 
 | 2252 | 		cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map); | 
 | 2253 | 		cfg->move_cleanup_count = cpus_weight(cleanup_mask); | 
 | 2254 | 		send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR); | 
 | 2255 | 		cfg->move_in_progress = 0; | 
 | 2256 | 	} | 
 | 2257 | } | 
 | 2258 | #else | 
 | 2259 | static inline void irq_complete_move(unsigned int irq) {} | 
 | 2260 | #endif | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2261 | #ifdef CONFIG_INTR_REMAP | 
 | 2262 | static void ack_x2apic_level(unsigned int irq) | 
 | 2263 | { | 
 | 2264 | 	ack_x2APIC_irq(); | 
 | 2265 | } | 
 | 2266 |  | 
 | 2267 | static void ack_x2apic_edge(unsigned int irq) | 
 | 2268 | { | 
 | 2269 | 	ack_x2APIC_irq(); | 
 | 2270 | } | 
 | 2271 | #endif | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2272 |  | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 2273 | static void ack_apic_edge(unsigned int irq) | 
 | 2274 | { | 
 | 2275 | 	irq_complete_move(irq); | 
 | 2276 | 	move_native_irq(irq); | 
 | 2277 | 	ack_APIC_irq(); | 
 | 2278 | } | 
 | 2279 |  | 
| Yinghai Lu | 3eb2cce | 2008-08-19 20:50:48 -0700 | [diff] [blame] | 2280 | atomic_t irq_mis_count; | 
| Yinghai Lu | 3eb2cce | 2008-08-19 20:50:48 -0700 | [diff] [blame] | 2281 |  | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2282 | static void ack_apic_level(unsigned int irq) | 
 | 2283 | { | 
| Yinghai Lu | 3eb2cce | 2008-08-19 20:50:48 -0700 | [diff] [blame] | 2284 | #ifdef CONFIG_X86_32 | 
 | 2285 | 	unsigned long v; | 
 | 2286 | 	int i; | 
 | 2287 | #endif | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2288 | 	int do_unmask_irq = 0; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2289 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2290 | 	irq_complete_move(irq); | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2291 | #ifdef CONFIG_GENERIC_PENDING_IRQ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2292 | 	/* If we are moving the irq we need to mask it */ | 
 | 2293 | 	if (unlikely(irq_to_desc(irq)->status & IRQ_MOVE_PENDING)) { | 
 | 2294 | 		do_unmask_irq = 1; | 
 | 2295 | 		mask_IO_APIC_irq(irq); | 
 | 2296 | 	} | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2297 | #endif | 
 | 2298 |  | 
| Yinghai Lu | 3eb2cce | 2008-08-19 20:50:48 -0700 | [diff] [blame] | 2299 | #ifdef CONFIG_X86_32 | 
 | 2300 | 	/* | 
 | 2301 | 	* It appears there is an erratum which affects at least version 0x11 | 
 | 2302 | 	* of I/O APIC (that's the 82093AA and cores integrated into various | 
 | 2303 | 	* chipsets).  Under certain conditions a level-triggered interrupt is | 
 | 2304 | 	* erroneously delivered as edge-triggered one but the respective IRR | 
 | 2305 | 	* bit gets set nevertheless.  As a result the I/O unit expects an EOI | 
 | 2306 | 	* message but it will never arrive and further interrupts are blocked | 
 | 2307 | 	* from the source.  The exact reason is so far unknown, but the | 
 | 2308 | 	* phenomenon was observed when two consecutive interrupt requests | 
 | 2309 | 	* from a given source get delivered to the same CPU and the source is | 
 | 2310 | 	* temporarily disabled in between. | 
 | 2311 | 	* | 
 | 2312 | 	* A workaround is to simulate an EOI message manually.  We achieve it | 
 | 2313 | 	* by setting the trigger mode to edge and then to level when the edge | 
 | 2314 | 	* trigger mode gets detected in the TMR of a local APIC for a | 
 | 2315 | 	* level-triggered interrupt.  We mask the source for the time of the | 
 | 2316 | 	* operation to prevent an edge-triggered interrupt escaping meanwhile. | 
 | 2317 | 	* The idea is from Manfred Spraul.  --macro | 
 | 2318 | 	*/ | 
 | 2319 | 	i = irq_cfg(irq)->vector; | 
 | 2320 |  | 
 | 2321 | 	v = apic_read(APIC_TMR + ((i & ~0x1f) >> 1)); | 
 | 2322 | #endif | 
 | 2323 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2324 | 	/* | 
 | 2325 | 	 * We must acknowledge the irq before we move it or the acknowledge will | 
 | 2326 | 	 * not propagate properly. | 
 | 2327 | 	 */ | 
 | 2328 | 	ack_APIC_irq(); | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2329 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2330 | 	/* Now we can move and renable the irq */ | 
 | 2331 | 	if (unlikely(do_unmask_irq)) { | 
 | 2332 | 		/* Only migrate the irq if the ack has been received. | 
 | 2333 | 		 * | 
 | 2334 | 		 * On rare occasions the broadcast level triggered ack gets | 
 | 2335 | 		 * delayed going to ioapics, and if we reprogram the | 
 | 2336 | 		 * vector while Remote IRR is still set the irq will never | 
 | 2337 | 		 * fire again. | 
 | 2338 | 		 * | 
 | 2339 | 		 * To prevent this scenario we read the Remote IRR bit | 
 | 2340 | 		 * of the ioapic.  This has two effects. | 
 | 2341 | 		 * - On any sane system the read of the ioapic will | 
 | 2342 | 		 *   flush writes (and acks) going to the ioapic from | 
 | 2343 | 		 *   this cpu. | 
 | 2344 | 		 * - We get to see if the ACK has actually been delivered. | 
 | 2345 | 		 * | 
 | 2346 | 		 * Based on failed experiments of reprogramming the | 
 | 2347 | 		 * ioapic entry from outside of irq context starting | 
 | 2348 | 		 * with masking the ioapic entry and then polling until | 
 | 2349 | 		 * Remote IRR was clear before reprogramming the | 
 | 2350 | 		 * ioapic I don't trust the Remote IRR bit to be | 
 | 2351 | 		 * completey accurate. | 
 | 2352 | 		 * | 
 | 2353 | 		 * However there appears to be no other way to plug | 
 | 2354 | 		 * this race, so if the Remote IRR bit is not | 
 | 2355 | 		 * accurate and is causing problems then it is a hardware bug | 
 | 2356 | 		 * and you can go talk to the chipset vendor about it. | 
 | 2357 | 		 */ | 
 | 2358 | 		if (!io_apic_level_ack_pending(irq)) | 
 | 2359 | 			move_masked_irq(irq); | 
 | 2360 | 		unmask_IO_APIC_irq(irq); | 
 | 2361 | 	} | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 2362 |  | 
| Yinghai Lu | 3eb2cce | 2008-08-19 20:50:48 -0700 | [diff] [blame] | 2363 | #ifdef CONFIG_X86_32 | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 2364 | 	if (!(v & (1 << (i & 0x1f)))) { | 
 | 2365 | 		atomic_inc(&irq_mis_count); | 
 | 2366 | 		spin_lock(&ioapic_lock); | 
 | 2367 | 		__mask_and_edge_IO_APIC_irq(irq); | 
 | 2368 | 		__unmask_and_level_IO_APIC_irq(irq); | 
 | 2369 | 		spin_unlock(&ioapic_lock); | 
 | 2370 | 	} | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2371 | #endif | 
| Yinghai Lu | 3eb2cce | 2008-08-19 20:50:48 -0700 | [diff] [blame] | 2372 | } | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 2373 |  | 
| Ingo Molnar | f5b9ed7 | 2006-10-04 02:16:26 -0700 | [diff] [blame] | 2374 | static struct irq_chip ioapic_chip __read_mostly = { | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2375 | 	.name		= "IO-APIC", | 
 | 2376 | 	.startup	= startup_ioapic_irq, | 
 | 2377 | 	.mask		= mask_IO_APIC_irq, | 
 | 2378 | 	.unmask		= unmask_IO_APIC_irq, | 
 | 2379 | 	.ack		= ack_apic_edge, | 
 | 2380 | 	.eoi		= ack_apic_level, | 
| Ashok Raj | 54d5d42 | 2005-09-06 15:16:15 -0700 | [diff] [blame] | 2381 | #ifdef CONFIG_SMP | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2382 | 	.set_affinity	= set_ioapic_affinity_irq, | 
| Ashok Raj | 54d5d42 | 2005-09-06 15:16:15 -0700 | [diff] [blame] | 2383 | #endif | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 2384 | 	.retrigger	= ioapic_retrigger_irq, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2385 | }; | 
 | 2386 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2387 | #ifdef CONFIG_INTR_REMAP | 
 | 2388 | static struct irq_chip ir_ioapic_chip __read_mostly = { | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2389 | 	.name		= "IR-IO-APIC", | 
 | 2390 | 	.startup	= startup_ioapic_irq, | 
 | 2391 | 	.mask		= mask_IO_APIC_irq, | 
 | 2392 | 	.unmask		= unmask_IO_APIC_irq, | 
 | 2393 | 	.ack		= ack_x2apic_edge, | 
 | 2394 | 	.eoi		= ack_x2apic_level, | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2395 | #ifdef CONFIG_SMP | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2396 | 	.set_affinity	= set_ir_ioapic_affinity_irq, | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2397 | #endif | 
 | 2398 | 	.retrigger	= ioapic_retrigger_irq, | 
 | 2399 | }; | 
 | 2400 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2401 |  | 
 | 2402 | static inline void init_IO_APIC_traps(void) | 
 | 2403 | { | 
 | 2404 | 	int irq; | 
| Yinghai Lu | 08678b0 | 2008-08-19 20:50:05 -0700 | [diff] [blame] | 2405 | 	struct irq_desc *desc; | 
| Yinghai Lu | da51a82 | 2008-08-19 20:50:25 -0700 | [diff] [blame] | 2406 | 	struct irq_cfg *cfg; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2407 |  | 
 | 2408 | 	/* | 
 | 2409 | 	 * NOTE! The local APIC isn't very good at handling | 
 | 2410 | 	 * multiple interrupts at the same interrupt level. | 
 | 2411 | 	 * As the interrupt level is determined by taking the | 
 | 2412 | 	 * vector number and shifting that right by 4, we | 
 | 2413 | 	 * want to spread these out a bit so that they don't | 
 | 2414 | 	 * all fall in the same interrupt level. | 
 | 2415 | 	 * | 
 | 2416 | 	 * Also, we've got to be careful not to trash gate | 
 | 2417 | 	 * 0x80, because int 0x80 is hm, kind of importantish. ;) | 
 | 2418 | 	 */ | 
| Yinghai Lu | 8f09cd2 | 2008-08-19 20:50:51 -0700 | [diff] [blame] | 2419 | 	for_each_irq_cfg(irq, cfg) { | 
| Yinghai Lu | da51a82 | 2008-08-19 20:50:25 -0700 | [diff] [blame] | 2420 | 		if (IO_APIC_IRQ(irq) && !cfg->vector) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2421 | 			/* | 
 | 2422 | 			 * Hmm.. We don't have an entry for this, | 
 | 2423 | 			 * so default to an old-fashioned 8259 | 
 | 2424 | 			 * interrupt if we can.. | 
 | 2425 | 			 */ | 
 | 2426 | 			if (irq < 16) | 
 | 2427 | 				make_8259A_irq(irq); | 
| Yinghai Lu | 08678b0 | 2008-08-19 20:50:05 -0700 | [diff] [blame] | 2428 | 			else { | 
 | 2429 | 				desc = irq_to_desc(irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2430 | 				/* Strange. Oh, well.. */ | 
| Yinghai Lu | 08678b0 | 2008-08-19 20:50:05 -0700 | [diff] [blame] | 2431 | 				desc->chip = &no_irq_chip; | 
 | 2432 | 			} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2433 | 		} | 
 | 2434 | 	} | 
 | 2435 | } | 
 | 2436 |  | 
| Ingo Molnar | f5b9ed7 | 2006-10-04 02:16:26 -0700 | [diff] [blame] | 2437 | /* | 
 | 2438 |  * The local APIC irq-chip implementation: | 
 | 2439 |  */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2440 |  | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2441 | static void mask_lapic_irq(unsigned int irq) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2442 | { | 
 | 2443 | 	unsigned long v; | 
 | 2444 |  | 
 | 2445 | 	v = apic_read(APIC_LVT0); | 
| Maciej W. Rozycki | 593f4a7 | 2008-07-16 19:15:30 +0100 | [diff] [blame] | 2446 | 	apic_write(APIC_LVT0, v | APIC_LVT_MASKED); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2447 | } | 
 | 2448 |  | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2449 | static void unmask_lapic_irq(unsigned int irq) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2450 | { | 
| Ingo Molnar | f5b9ed7 | 2006-10-04 02:16:26 -0700 | [diff] [blame] | 2451 | 	unsigned long v; | 
 | 2452 |  | 
 | 2453 | 	v = apic_read(APIC_LVT0); | 
| Maciej W. Rozycki | 593f4a7 | 2008-07-16 19:15:30 +0100 | [diff] [blame] | 2454 | 	apic_write(APIC_LVT0, v & ~APIC_LVT_MASKED); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2455 | } | 
 | 2456 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2457 | static void ack_lapic_irq (unsigned int irq) | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 2458 | { | 
 | 2459 | 	ack_APIC_irq(); | 
 | 2460 | } | 
 | 2461 |  | 
| Ingo Molnar | f5b9ed7 | 2006-10-04 02:16:26 -0700 | [diff] [blame] | 2462 | static struct irq_chip lapic_chip __read_mostly = { | 
| Maciej W. Rozycki | 9a1c619 | 2008-05-27 21:19:09 +0100 | [diff] [blame] | 2463 | 	.name		= "local-APIC", | 
| Ingo Molnar | f5b9ed7 | 2006-10-04 02:16:26 -0700 | [diff] [blame] | 2464 | 	.mask		= mask_lapic_irq, | 
 | 2465 | 	.unmask		= unmask_lapic_irq, | 
| Maciej W. Rozycki | c88ac1d | 2008-07-11 19:35:17 +0100 | [diff] [blame] | 2466 | 	.ack		= ack_lapic_irq, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2467 | }; | 
 | 2468 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2469 | static void lapic_register_intr(int irq) | 
| Maciej W. Rozycki | c88ac1d | 2008-07-11 19:35:17 +0100 | [diff] [blame] | 2470 | { | 
| Yinghai Lu | 08678b0 | 2008-08-19 20:50:05 -0700 | [diff] [blame] | 2471 | 	struct irq_desc *desc; | 
 | 2472 |  | 
 | 2473 | 	desc = irq_to_desc(irq); | 
 | 2474 | 	desc->status &= ~IRQ_LEVEL; | 
| Maciej W. Rozycki | c88ac1d | 2008-07-11 19:35:17 +0100 | [diff] [blame] | 2475 | 	set_irq_chip_and_handler_name(irq, &lapic_chip, handle_edge_irq, | 
 | 2476 | 				      "edge"); | 
| Maciej W. Rozycki | c88ac1d | 2008-07-11 19:35:17 +0100 | [diff] [blame] | 2477 | } | 
 | 2478 |  | 
| Jan Beulich | e942710 | 2008-01-30 13:31:24 +0100 | [diff] [blame] | 2479 | static void __init setup_nmi(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2480 | { | 
 | 2481 | 	/* | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2482 | 	 * Dirty trick to enable the NMI watchdog ... | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2483 | 	 * We put the 8259A master into AEOI mode and | 
 | 2484 | 	 * unmask on all local APICs LVT0 as NMI. | 
 | 2485 | 	 * | 
 | 2486 | 	 * The idea to use the 8259A in AEOI mode ('8259A Virtual Wire') | 
 | 2487 | 	 * is from Maciej W. Rozycki - so we do not have to EOI from | 
 | 2488 | 	 * the NMI handler or the timer interrupt. | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2489 | 	 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2490 | 	apic_printk(APIC_VERBOSE, KERN_INFO "activating NMI Watchdog ..."); | 
 | 2491 |  | 
| Jan Beulich | e942710 | 2008-01-30 13:31:24 +0100 | [diff] [blame] | 2492 | 	enable_NMI_through_LVT0(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2493 |  | 
 | 2494 | 	apic_printk(APIC_VERBOSE, " done.\n"); | 
 | 2495 | } | 
 | 2496 |  | 
 | 2497 | /* | 
 | 2498 |  * This looks a bit hackish but it's about the only one way of sending | 
 | 2499 |  * a few INTA cycles to 8259As and any associated glue logic.  ICR does | 
 | 2500 |  * not support the ExtINT mode, unfortunately.  We need to send these | 
 | 2501 |  * cycles as some i82489DX-based boards have glue logic that keeps the | 
 | 2502 |  * 8259A interrupt line asserted until INTA.  --macro | 
 | 2503 |  */ | 
| Jacek Luczak | 28acf28 | 2008-04-12 17:41:12 +0200 | [diff] [blame] | 2504 | static inline void __init unlock_ExtINT_logic(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2505 | { | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 2506 | 	int apic, pin, i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2507 | 	struct IO_APIC_route_entry entry0, entry1; | 
 | 2508 | 	unsigned char save_control, save_freq_select; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2509 |  | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 2510 | 	pin  = find_isa_irq_pin(8, mp_INT); | 
| Adrian Bunk | 956fb53 | 2006-12-07 02:14:11 +0100 | [diff] [blame] | 2511 | 	if (pin == -1) { | 
 | 2512 | 		WARN_ON_ONCE(1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2513 | 		return; | 
| Adrian Bunk | 956fb53 | 2006-12-07 02:14:11 +0100 | [diff] [blame] | 2514 | 	} | 
 | 2515 | 	apic = find_isa_irq_apic(8, mp_INT); | 
 | 2516 | 	if (apic == -1) { | 
 | 2517 | 		WARN_ON_ONCE(1); | 
 | 2518 | 		return; | 
 | 2519 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2520 |  | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 2521 | 	entry0 = ioapic_read_entry(apic, pin); | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 2522 | 	clear_IO_APIC_pin(apic, pin); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2523 |  | 
 | 2524 | 	memset(&entry1, 0, sizeof(entry1)); | 
 | 2525 |  | 
 | 2526 | 	entry1.dest_mode = 0;			/* physical delivery */ | 
 | 2527 | 	entry1.mask = 0;			/* unmask IRQ now */ | 
| Yinghai Lu | d83e94a | 2008-08-19 20:50:33 -0700 | [diff] [blame] | 2528 | 	entry1.dest = hard_smp_processor_id(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2529 | 	entry1.delivery_mode = dest_ExtINT; | 
 | 2530 | 	entry1.polarity = entry0.polarity; | 
 | 2531 | 	entry1.trigger = 0; | 
 | 2532 | 	entry1.vector = 0; | 
 | 2533 |  | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 2534 | 	ioapic_write_entry(apic, pin, entry1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2535 |  | 
 | 2536 | 	save_control = CMOS_READ(RTC_CONTROL); | 
 | 2537 | 	save_freq_select = CMOS_READ(RTC_FREQ_SELECT); | 
 | 2538 | 	CMOS_WRITE((save_freq_select & ~RTC_RATE_SELECT) | 0x6, | 
 | 2539 | 		   RTC_FREQ_SELECT); | 
 | 2540 | 	CMOS_WRITE(save_control | RTC_PIE, RTC_CONTROL); | 
 | 2541 |  | 
 | 2542 | 	i = 100; | 
 | 2543 | 	while (i-- > 0) { | 
 | 2544 | 		mdelay(10); | 
 | 2545 | 		if ((CMOS_READ(RTC_INTR_FLAGS) & RTC_PF) == RTC_PF) | 
 | 2546 | 			i -= 10; | 
 | 2547 | 	} | 
 | 2548 |  | 
 | 2549 | 	CMOS_WRITE(save_control, RTC_CONTROL); | 
 | 2550 | 	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 2551 | 	clear_IO_APIC_pin(apic, pin); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2552 |  | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 2553 | 	ioapic_write_entry(apic, pin, entry0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2554 | } | 
 | 2555 |  | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 2556 | static int disable_timer_pin_1 __initdata; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2557 | /* Actually the next is obsolete, but keep it for paranoid reasons -AK */ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2558 | static int __init disable_timer_pin_setup(char *arg) | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 2559 | { | 
 | 2560 | 	disable_timer_pin_1 = 1; | 
 | 2561 | 	return 0; | 
 | 2562 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2563 | early_param("disable_timer_pin_1", disable_timer_pin_setup); | 
| Yinghai Lu | efa2559 | 2008-08-19 20:50:36 -0700 | [diff] [blame] | 2564 |  | 
 | 2565 | int timer_through_8259 __initdata; | 
 | 2566 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2567 | /* | 
 | 2568 |  * This code may look a bit paranoid, but it's supposed to cooperate with | 
 | 2569 |  * a wide range of boards and BIOS bugs.  Fortunately only the timer IRQ | 
 | 2570 |  * is so screwy.  Thanks to Brian Perkins for testing/hacking this beast | 
 | 2571 |  * fanatically on his truly buggy board. | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2572 |  * | 
 | 2573 |  * FIXME: really need to revamp this for all platforms. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2574 |  */ | 
| Zachary Amsden | 8542b20 | 2006-12-07 02:14:09 +0100 | [diff] [blame] | 2575 | static inline void __init check_timer(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2576 | { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2577 | 	struct irq_cfg *cfg = irq_cfg(0); | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 2578 | 	int apic1, pin1, apic2, pin2; | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 2579 | 	unsigned long flags; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2580 | 	unsigned int ver; | 
 | 2581 | 	int no_pin1 = 0; | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 2582 |  | 
 | 2583 | 	local_irq_save(flags); | 
| Maciej W. Rozycki | d4d25de | 2007-11-26 20:42:19 +0100 | [diff] [blame] | 2584 |  | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2585 | 	ver = apic_read(APIC_LVR); | 
 | 2586 | 	ver = GET_APIC_VERSION(ver); | 
| Ingo Molnar | 6e90894 | 2008-03-21 14:32:36 +0100 | [diff] [blame] | 2587 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2588 | 	/* | 
 | 2589 | 	 * get/set the timer IRQ vector: | 
 | 2590 | 	 */ | 
 | 2591 | 	disable_8259A_irq(0); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2592 | 	assign_irq_vector(0, TARGET_CPUS); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2593 |  | 
 | 2594 | 	/* | 
| Maciej W. Rozycki | d11d579 | 2008-05-21 22:09:11 +0100 | [diff] [blame] | 2595 | 	 * As IRQ0 is to be enabled in the 8259A, the virtual | 
 | 2596 | 	 * wire has to be disabled in the local APIC.  Also | 
 | 2597 | 	 * timer interrupts need to be acknowledged manually in | 
 | 2598 | 	 * the 8259A for the i82489DX when using the NMI | 
 | 2599 | 	 * watchdog as that APIC treats NMIs as level-triggered. | 
 | 2600 | 	 * The AEOI mode will finish them in the 8259A | 
 | 2601 | 	 * automatically. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2602 | 	 */ | 
| Maciej W. Rozycki | 593f4a7 | 2008-07-16 19:15:30 +0100 | [diff] [blame] | 2603 | 	apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2604 | 	init_8259A(1); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2605 | #ifdef CONFIG_X86_32 | 
| Maciej W. Rozycki | d11d579 | 2008-05-21 22:09:11 +0100 | [diff] [blame] | 2606 | 	timer_ack = (nmi_watchdog == NMI_IO_APIC && !APIC_INTEGRATED(ver)); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2607 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2608 |  | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 2609 | 	pin1  = find_isa_irq_pin(0, mp_INT); | 
 | 2610 | 	apic1 = find_isa_irq_apic(0, mp_INT); | 
 | 2611 | 	pin2  = ioapic_i8259.pin; | 
 | 2612 | 	apic2 = ioapic_i8259.apic; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2613 |  | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2614 | 	apic_printk(APIC_QUIET, KERN_INFO "..TIMER: vector=0x%02X " | 
 | 2615 | 		    "apic1=%d pin1=%d apic2=%d pin2=%d\n", | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2616 | 		    cfg->vector, apic1, pin1, apic2, pin2); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2617 |  | 
| Maciej W. Rozycki | 691874f | 2008-05-27 21:19:51 +0100 | [diff] [blame] | 2618 | 	/* | 
 | 2619 | 	 * Some BIOS writers are clueless and report the ExtINTA | 
 | 2620 | 	 * I/O APIC input from the cascaded 8259A as the timer | 
 | 2621 | 	 * interrupt input.  So just in case, if only one pin | 
 | 2622 | 	 * was found above, try it both directly and through the | 
 | 2623 | 	 * 8259A. | 
 | 2624 | 	 */ | 
 | 2625 | 	if (pin1 == -1) { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2626 | #ifdef CONFIG_INTR_REMAP | 
 | 2627 | 		if (intr_remapping_enabled) | 
 | 2628 | 			panic("BIOS bug: timer not connected to IO-APIC"); | 
 | 2629 | #endif | 
| Maciej W. Rozycki | 691874f | 2008-05-27 21:19:51 +0100 | [diff] [blame] | 2630 | 		pin1 = pin2; | 
 | 2631 | 		apic1 = apic2; | 
 | 2632 | 		no_pin1 = 1; | 
 | 2633 | 	} else if (pin2 == -1) { | 
 | 2634 | 		pin2 = pin1; | 
 | 2635 | 		apic2 = apic1; | 
 | 2636 | 	} | 
 | 2637 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2638 | 	if (pin1 != -1) { | 
 | 2639 | 		/* | 
 | 2640 | 		 * Ok, does IRQ0 through the IOAPIC work? | 
 | 2641 | 		 */ | 
| Maciej W. Rozycki | 691874f | 2008-05-27 21:19:51 +0100 | [diff] [blame] | 2642 | 		if (no_pin1) { | 
 | 2643 | 			add_pin_to_irq(0, apic1, pin1); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2644 | 			setup_timer_IRQ0_pin(apic1, pin1, cfg->vector); | 
| Maciej W. Rozycki | 691874f | 2008-05-27 21:19:51 +0100 | [diff] [blame] | 2645 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2646 | 		unmask_IO_APIC_irq(0); | 
 | 2647 | 		if (timer_irq_works()) { | 
 | 2648 | 			if (nmi_watchdog == NMI_IO_APIC) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2649 | 				setup_nmi(); | 
 | 2650 | 				enable_8259A_irq(0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2651 | 			} | 
| Chuck Ebbert | 66759a0 | 2005-09-12 18:49:25 +0200 | [diff] [blame] | 2652 | 			if (disable_timer_pin_1 > 0) | 
 | 2653 | 				clear_IO_APIC_pin(0, pin1); | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 2654 | 			goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2655 | 		} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2656 | #ifdef CONFIG_INTR_REMAP | 
 | 2657 | 		if (intr_remapping_enabled) | 
 | 2658 | 			panic("timer doesn't work through Interrupt-remapped IO-APIC"); | 
 | 2659 | #endif | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 2660 | 		clear_IO_APIC_pin(apic1, pin1); | 
| Maciej W. Rozycki | 691874f | 2008-05-27 21:19:51 +0100 | [diff] [blame] | 2661 | 		if (!no_pin1) | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2662 | 			apic_printk(APIC_QUIET, KERN_ERR "..MP-BIOS bug: " | 
 | 2663 | 				    "8254 timer not connected to IO-APIC\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2664 |  | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2665 | 		apic_printk(APIC_QUIET, KERN_INFO "...trying to set up timer " | 
 | 2666 | 			    "(IRQ0) through the 8259A ...\n"); | 
 | 2667 | 		apic_printk(APIC_QUIET, KERN_INFO | 
 | 2668 | 			    "..... (found apic %d pin %d) ...\n", apic2, pin2); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2669 | 		/* | 
 | 2670 | 		 * legacy devices should be connected to IO APIC #0 | 
 | 2671 | 		 */ | 
| Maciej W. Rozycki | 691874f | 2008-05-27 21:19:51 +0100 | [diff] [blame] | 2672 | 		replace_pin_at_irq(0, apic1, pin1, apic2, pin2); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2673 | 		setup_timer_IRQ0_pin(apic2, pin2, cfg->vector); | 
| Maciej W. Rozycki | 24742ec | 2008-05-27 21:19:40 +0100 | [diff] [blame] | 2674 | 		unmask_IO_APIC_irq(0); | 
| Maciej W. Rozycki | ecd2947 | 2008-05-21 22:09:19 +0100 | [diff] [blame] | 2675 | 		enable_8259A_irq(0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2676 | 		if (timer_irq_works()) { | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2677 | 			apic_printk(APIC_QUIET, KERN_INFO "....... works.\n"); | 
| Maciej W. Rozycki | 35542c5 | 2008-05-21 22:10:22 +0100 | [diff] [blame] | 2678 | 			timer_through_8259 = 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2679 | 			if (nmi_watchdog == NMI_IO_APIC) { | 
| Maciej W. Rozycki | 60134eb | 2008-05-21 22:09:34 +0100 | [diff] [blame] | 2680 | 				disable_8259A_irq(0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2681 | 				setup_nmi(); | 
| Maciej W. Rozycki | 60134eb | 2008-05-21 22:09:34 +0100 | [diff] [blame] | 2682 | 				enable_8259A_irq(0); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2683 | 			} | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 2684 | 			goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2685 | 		} | 
 | 2686 | 		/* | 
 | 2687 | 		 * Cleanup, just in case ... | 
 | 2688 | 		 */ | 
| Maciej W. Rozycki | ecd2947 | 2008-05-21 22:09:19 +0100 | [diff] [blame] | 2689 | 		disable_8259A_irq(0); | 
| Eric W. Biederman | fcfd636 | 2005-10-30 14:59:39 -0800 | [diff] [blame] | 2690 | 		clear_IO_APIC_pin(apic2, pin2); | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2691 | 		apic_printk(APIC_QUIET, KERN_INFO "....... failed.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2692 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2693 |  | 
 | 2694 | 	if (nmi_watchdog == NMI_IO_APIC) { | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2695 | 		apic_printk(APIC_QUIET, KERN_WARNING "timer doesn't work " | 
 | 2696 | 			    "through the IO-APIC - disabling NMI Watchdog!\n"); | 
| Cyrill Gorcunov | 067fa0f | 2008-05-29 22:32:30 +0400 | [diff] [blame] | 2697 | 		nmi_watchdog = NMI_NONE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2698 | 	} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2699 | #ifdef CONFIG_X86_32 | 
| Maciej W. Rozycki | d11d579 | 2008-05-21 22:09:11 +0100 | [diff] [blame] | 2700 | 	timer_ack = 0; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2701 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2702 |  | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2703 | 	apic_printk(APIC_QUIET, KERN_INFO | 
 | 2704 | 		    "...trying to set up timer as Virtual Wire IRQ...\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2705 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2706 | 	lapic_register_intr(0); | 
 | 2707 | 	apic_write(APIC_LVT0, APIC_DM_FIXED | cfg->vector);	/* Fixed mode */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2708 | 	enable_8259A_irq(0); | 
 | 2709 |  | 
 | 2710 | 	if (timer_irq_works()) { | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2711 | 		apic_printk(APIC_QUIET, KERN_INFO "..... works.\n"); | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 2712 | 		goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2713 | 	} | 
| Maciej W. Rozycki | e67465f | 2008-05-21 22:09:26 +0100 | [diff] [blame] | 2714 | 	disable_8259A_irq(0); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2715 | 	apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector); | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2716 | 	apic_printk(APIC_QUIET, KERN_INFO "..... failed.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2717 |  | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2718 | 	apic_printk(APIC_QUIET, KERN_INFO | 
 | 2719 | 		    "...trying to set up timer as ExtINT IRQ...\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2720 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2721 | 	init_8259A(0); | 
 | 2722 | 	make_8259A_irq(0); | 
| Maciej W. Rozycki | 593f4a7 | 2008-07-16 19:15:30 +0100 | [diff] [blame] | 2723 | 	apic_write(APIC_LVT0, APIC_DM_EXTINT); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2724 |  | 
 | 2725 | 	unlock_ExtINT_logic(); | 
 | 2726 |  | 
 | 2727 | 	if (timer_irq_works()) { | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2728 | 		apic_printk(APIC_QUIET, KERN_INFO "..... works.\n"); | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 2729 | 		goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2730 | 	} | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2731 | 	apic_printk(APIC_QUIET, KERN_INFO "..... failed :(.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2732 | 	panic("IO-APIC + timer doesn't work!  Boot with apic=debug and send a " | 
| Maciej W. Rozycki | 49a66a0 | 2008-07-14 19:08:13 +0100 | [diff] [blame] | 2733 | 		"report.  Then try booting with the 'noapic' option.\n"); | 
| Ingo Molnar | 4aae070 | 2007-12-18 18:05:58 +0100 | [diff] [blame] | 2734 | out: | 
 | 2735 | 	local_irq_restore(flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2736 | } | 
 | 2737 |  | 
 | 2738 | /* | 
| Maciej W. Rozycki | af17478 | 2008-07-11 19:35:23 +0100 | [diff] [blame] | 2739 |  * Traditionally ISA IRQ2 is the cascade IRQ, and is not available | 
 | 2740 |  * to devices.  However there may be an I/O APIC pin available for | 
 | 2741 |  * this interrupt regardless.  The pin may be left unconnected, but | 
 | 2742 |  * typically it will be reused as an ExtINT cascade interrupt for | 
 | 2743 |  * the master 8259A.  In the MPS case such a pin will normally be | 
 | 2744 |  * reported as an ExtINT interrupt in the MP table.  With ACPI | 
 | 2745 |  * there is no provision for ExtINT interrupts, and in the absence | 
 | 2746 |  * of an override it would be treated as an ordinary ISA I/O APIC | 
 | 2747 |  * interrupt, that is edge-triggered and unmasked by default.  We | 
 | 2748 |  * used to do this, but it caused problems on some systems because | 
 | 2749 |  * of the NMI watchdog and sometimes IRQ0 of the 8254 timer using | 
 | 2750 |  * the same ExtINT cascade interrupt to drive the local APIC of the | 
 | 2751 |  * bootstrap processor.  Therefore we refrain from routing IRQ2 to | 
 | 2752 |  * the I/O APIC in all cases now.  No actual device should request | 
 | 2753 |  * it anyway.  --macro | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2754 |  */ | 
 | 2755 | #define PIC_IRQS	(1 << PIC_CASCADE_IR) | 
 | 2756 |  | 
 | 2757 | void __init setup_IO_APIC(void) | 
 | 2758 | { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2759 |  | 
 | 2760 | #ifdef CONFIG_X86_32 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2761 | 	enable_IO_APIC(); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2762 | #else | 
 | 2763 | 	/* | 
 | 2764 | 	 * calling enable_IO_APIC() is moved to setup_local_APIC for BP | 
 | 2765 | 	 */ | 
 | 2766 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2767 |  | 
| Maciej W. Rozycki | af17478 | 2008-07-11 19:35:23 +0100 | [diff] [blame] | 2768 | 	io_apic_irqs = ~PIC_IRQS; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2769 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2770 | 	apic_printk(APIC_VERBOSE, "ENABLING IO-APIC IRQs\n"); | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2771 | 	/* | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2772 |          * Set up IO-APIC IRQ routing. | 
 | 2773 |          */ | 
 | 2774 | #ifdef CONFIG_X86_32 | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2775 | 	if (!acpi_ioapic) | 
 | 2776 | 		setup_ioapic_ids_from_mpc(); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2777 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2778 | 	sync_Arb_IDs(); | 
 | 2779 | 	setup_IO_APIC_irqs(); | 
 | 2780 | 	init_IO_APIC_traps(); | 
| Linus Torvalds | 1e4c85f | 2005-10-31 19:16:17 -0800 | [diff] [blame] | 2781 | 	check_timer(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2782 | } | 
 | 2783 |  | 
 | 2784 | /* | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2785 |  *      Called after all the initialization is done. If we didnt find any | 
 | 2786 |  *      APIC bugs then we can allow the modify fast path | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2787 |  */ | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2788 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2789 | static int __init io_apic_bug_finalize(void) | 
 | 2790 | { | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 2791 | 	if (sis_apic_bug == -1) | 
 | 2792 | 		sis_apic_bug = 0; | 
 | 2793 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2794 | } | 
 | 2795 |  | 
 | 2796 | late_initcall(io_apic_bug_finalize); | 
 | 2797 |  | 
 | 2798 | struct sysfs_ioapic_data { | 
 | 2799 | 	struct sys_device dev; | 
 | 2800 | 	struct IO_APIC_route_entry entry[0]; | 
 | 2801 | }; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2802 | static struct sysfs_ioapic_data * mp_ioapic_data[MAX_IO_APICS]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2803 |  | 
| Pavel Machek | 438510f | 2005-04-16 15:25:24 -0700 | [diff] [blame] | 2804 | static int ioapic_suspend(struct sys_device *dev, pm_message_t state) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2805 | { | 
 | 2806 | 	struct IO_APIC_route_entry *entry; | 
 | 2807 | 	struct sysfs_ioapic_data *data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2808 | 	int i; | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2809 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2810 | 	data = container_of(dev, struct sysfs_ioapic_data, dev); | 
 | 2811 | 	entry = data->entry; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2812 | 	for (i = 0; i < nr_ioapic_registers[dev->id]; i ++, entry ++ ) | 
 | 2813 | 		*entry = ioapic_read_entry(dev->id, i); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2814 |  | 
 | 2815 | 	return 0; | 
 | 2816 | } | 
 | 2817 |  | 
 | 2818 | static int ioapic_resume(struct sys_device *dev) | 
 | 2819 | { | 
 | 2820 | 	struct IO_APIC_route_entry *entry; | 
 | 2821 | 	struct sysfs_ioapic_data *data; | 
 | 2822 | 	unsigned long flags; | 
 | 2823 | 	union IO_APIC_reg_00 reg_00; | 
 | 2824 | 	int i; | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2825 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2826 | 	data = container_of(dev, struct sysfs_ioapic_data, dev); | 
 | 2827 | 	entry = data->entry; | 
 | 2828 |  | 
 | 2829 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 2830 | 	reg_00.raw = io_apic_read(dev->id, 0); | 
| Alexey Starikovskiy | ec2cd0a | 2008-05-14 19:03:10 +0400 | [diff] [blame] | 2831 | 	if (reg_00.bits.ID != mp_ioapics[dev->id].mp_apicid) { | 
 | 2832 | 		reg_00.bits.ID = mp_ioapics[dev->id].mp_apicid; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2833 | 		io_apic_write(dev->id, 0, reg_00.raw); | 
 | 2834 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2835 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2836 | 	for (i = 0; i < nr_ioapic_registers[dev->id]; i++) | 
| Andi Kleen | cf4c6a2 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 2837 | 		ioapic_write_entry(dev->id, i, entry[i]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2838 |  | 
 | 2839 | 	return 0; | 
 | 2840 | } | 
 | 2841 |  | 
 | 2842 | static struct sysdev_class ioapic_sysdev_class = { | 
| Kay Sievers | af5ca3f | 2007-12-20 02:09:39 +0100 | [diff] [blame] | 2843 | 	.name = "ioapic", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2844 | 	.suspend = ioapic_suspend, | 
 | 2845 | 	.resume = ioapic_resume, | 
 | 2846 | }; | 
 | 2847 |  | 
 | 2848 | static int __init ioapic_init_sysfs(void) | 
 | 2849 | { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2850 | 	struct sys_device * dev; | 
 | 2851 | 	int i, size, error; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2852 |  | 
 | 2853 | 	error = sysdev_class_register(&ioapic_sysdev_class); | 
 | 2854 | 	if (error) | 
 | 2855 | 		return error; | 
 | 2856 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2857 | 	for (i = 0; i < nr_ioapics; i++ ) { | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2858 | 		size = sizeof(struct sys_device) + nr_ioapic_registers[i] | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2859 | 			* sizeof(struct IO_APIC_route_entry); | 
| Christophe Jaillet | 25556c1 | 2008-06-22 22:13:48 +0200 | [diff] [blame] | 2860 | 		mp_ioapic_data[i] = kzalloc(size, GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2861 | 		if (!mp_ioapic_data[i]) { | 
 | 2862 | 			printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i); | 
 | 2863 | 			continue; | 
 | 2864 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2865 | 		dev = &mp_ioapic_data[i]->dev; | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 2866 | 		dev->id = i; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2867 | 		dev->cls = &ioapic_sysdev_class; | 
 | 2868 | 		error = sysdev_register(dev); | 
 | 2869 | 		if (error) { | 
 | 2870 | 			kfree(mp_ioapic_data[i]); | 
 | 2871 | 			mp_ioapic_data[i] = NULL; | 
 | 2872 | 			printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i); | 
 | 2873 | 			continue; | 
 | 2874 | 		} | 
 | 2875 | 	} | 
 | 2876 |  | 
 | 2877 | 	return 0; | 
 | 2878 | } | 
 | 2879 |  | 
 | 2880 | device_initcall(ioapic_init_sysfs); | 
 | 2881 |  | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2882 | /* | 
| Eric W. Biederman | 95d7788 | 2006-10-04 02:17:01 -0700 | [diff] [blame] | 2883 |  * Dynamic irq allocate and deallocation | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2884 |  */ | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 2885 | unsigned int create_irq_nr(unsigned int irq_want) | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2886 | { | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 2887 | 	/* Allocate an unused irq */ | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2888 | 	unsigned int irq; | 
 | 2889 | 	unsigned int new; | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2890 | 	unsigned long flags; | 
| Yinghai Lu | da51a82 | 2008-08-19 20:50:25 -0700 | [diff] [blame] | 2891 | 	struct irq_cfg *cfg_new; | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2892 |  | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 2893 | 	irq_want = nr_irqs - 1; | 
 | 2894 |  | 
 | 2895 | 	irq = 0; | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 2896 | 	spin_lock_irqsave(&vector_lock, flags); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2897 | 	for (new = irq_want; new > 0; new--) { | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 2898 | 		if (platform_legacy_irq(new)) | 
 | 2899 | 			continue; | 
| Yinghai Lu | da51a82 | 2008-08-19 20:50:25 -0700 | [diff] [blame] | 2900 | 		cfg_new = irq_cfg(new); | 
 | 2901 | 		if (cfg_new && cfg_new->vector != 0) | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 2902 | 			continue; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 2903 | 		/* check if need to create one */ | 
| Yinghai Lu | da51a82 | 2008-08-19 20:50:25 -0700 | [diff] [blame] | 2904 | 		if (!cfg_new) | 
 | 2905 | 			cfg_new = irq_cfg_alloc(new); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2906 | 		if (__assign_irq_vector(new, TARGET_CPUS) == 0) | 
| Eric W. Biederman | ace80ab | 2006-10-04 02:16:47 -0700 | [diff] [blame] | 2907 | 			irq = new; | 
 | 2908 | 		break; | 
 | 2909 | 	} | 
 | 2910 | 	spin_unlock_irqrestore(&vector_lock, flags); | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2911 |  | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 2912 | 	if (irq > 0) { | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2913 | 		dynamic_irq_init(irq); | 
 | 2914 | 	} | 
 | 2915 | 	return irq; | 
 | 2916 | } | 
 | 2917 |  | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 2918 | int create_irq(void) | 
 | 2919 | { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2920 | 	int irq; | 
 | 2921 |  | 
 | 2922 | 	irq = create_irq_nr(nr_irqs - 1); | 
 | 2923 |  | 
 | 2924 | 	if (irq == 0) | 
 | 2925 | 		irq = -1; | 
 | 2926 |  | 
 | 2927 | 	return irq; | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 2928 | } | 
 | 2929 |  | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2930 | void destroy_irq(unsigned int irq) | 
 | 2931 | { | 
 | 2932 | 	unsigned long flags; | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2933 |  | 
 | 2934 | 	dynamic_irq_cleanup(irq); | 
 | 2935 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2936 | #ifdef CONFIG_INTR_REMAP | 
 | 2937 | 	free_irte(irq); | 
 | 2938 | #endif | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2939 | 	spin_lock_irqsave(&vector_lock, flags); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2940 | 	__clear_irq_vector(irq); | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2941 | 	spin_unlock_irqrestore(&vector_lock, flags); | 
 | 2942 | } | 
| Eric W. Biederman | 3fc471e | 2006-10-04 02:16:39 -0700 | [diff] [blame] | 2943 |  | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 2944 | /* | 
| Simon Arlott | 27b46d7 | 2007-10-20 01:13:56 +0200 | [diff] [blame] | 2945 |  * MSI message composition | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 2946 |  */ | 
 | 2947 | #ifdef CONFIG_PCI_MSI | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 2948 | static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg) | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 2949 | { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2950 | 	struct irq_cfg *cfg; | 
 | 2951 | 	int err; | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 2952 | 	unsigned dest; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2953 | 	cpumask_t tmp; | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 2954 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2955 | 	tmp = TARGET_CPUS; | 
 | 2956 | 	err = assign_irq_vector(irq, tmp); | 
 | 2957 | 	if (err) | 
 | 2958 | 		return err; | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 2959 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2960 | 	cfg = irq_cfg(irq); | 
 | 2961 | 	cpus_and(tmp, cfg->domain, tmp); | 
 | 2962 | 	dest = cpu_mask_to_apicid(tmp); | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 2963 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2964 | #ifdef CONFIG_INTR_REMAP | 
 | 2965 | 	if (irq_remapped(irq)) { | 
 | 2966 | 		struct irte irte; | 
 | 2967 | 		int ir_index; | 
 | 2968 | 		u16 sub_handle; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2969 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2970 | 		ir_index = map_irq_to_irte_handle(irq, &sub_handle); | 
 | 2971 | 		BUG_ON(ir_index == -1); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 2972 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 2973 | 		memset (&irte, 0, sizeof(irte)); | 
 | 2974 |  | 
 | 2975 | 		irte.present = 1; | 
 | 2976 | 		irte.dst_mode = INT_DEST_MODE; | 
 | 2977 | 		irte.trigger_mode = 0; /* edge */ | 
 | 2978 | 		irte.dlvry_mode = INT_DELIVERY_MODE; | 
 | 2979 | 		irte.vector = cfg->vector; | 
 | 2980 | 		irte.dest_id = IRTE_DEST(dest); | 
 | 2981 |  | 
 | 2982 | 		modify_irte(irq, &irte); | 
 | 2983 |  | 
 | 2984 | 		msg->address_hi = MSI_ADDR_BASE_HI; | 
 | 2985 | 		msg->data = sub_handle; | 
 | 2986 | 		msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT | | 
 | 2987 | 				  MSI_ADDR_IR_SHV | | 
 | 2988 | 				  MSI_ADDR_IR_INDEX1(ir_index) | | 
 | 2989 | 				  MSI_ADDR_IR_INDEX2(ir_index); | 
 | 2990 | 	} else | 
 | 2991 | #endif | 
 | 2992 | 	{ | 
 | 2993 | 		msg->address_hi = MSI_ADDR_BASE_HI; | 
 | 2994 | 		msg->address_lo = | 
 | 2995 | 			MSI_ADDR_BASE_LO | | 
 | 2996 | 			((INT_DEST_MODE == 0) ? | 
 | 2997 | 				MSI_ADDR_DEST_MODE_PHYSICAL: | 
 | 2998 | 				MSI_ADDR_DEST_MODE_LOGICAL) | | 
 | 2999 | 			((INT_DELIVERY_MODE != dest_LowestPrio) ? | 
 | 3000 | 				MSI_ADDR_REDIRECTION_CPU: | 
 | 3001 | 				MSI_ADDR_REDIRECTION_LOWPRI) | | 
 | 3002 | 			MSI_ADDR_DEST_ID(dest); | 
 | 3003 |  | 
 | 3004 | 		msg->data = | 
 | 3005 | 			MSI_DATA_TRIGGER_EDGE | | 
 | 3006 | 			MSI_DATA_LEVEL_ASSERT | | 
 | 3007 | 			((INT_DELIVERY_MODE != dest_LowestPrio) ? | 
 | 3008 | 				MSI_DATA_DELIVERY_FIXED: | 
 | 3009 | 				MSI_DATA_DELIVERY_LOWPRI) | | 
 | 3010 | 			MSI_DATA_VECTOR(cfg->vector); | 
 | 3011 | 	} | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3012 | 	return err; | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 3013 | } | 
 | 3014 |  | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3015 | #ifdef CONFIG_SMP | 
 | 3016 | static void set_msi_irq_affinity(unsigned int irq, cpumask_t mask) | 
 | 3017 | { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3018 | 	struct irq_cfg *cfg; | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3019 | 	struct msi_msg msg; | 
 | 3020 | 	unsigned int dest; | 
 | 3021 | 	cpumask_t tmp; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3022 | 	struct irq_desc *desc; | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3023 |  | 
 | 3024 | 	cpus_and(tmp, mask, cpu_online_map); | 
 | 3025 | 	if (cpus_empty(tmp)) | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3026 | 		return; | 
 | 3027 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3028 | 	if (assign_irq_vector(irq, mask)) | 
 | 3029 | 		return; | 
 | 3030 |  | 
 | 3031 | 	cfg = irq_cfg(irq); | 
 | 3032 | 	cpus_and(tmp, cfg->domain, mask); | 
 | 3033 | 	dest = cpu_mask_to_apicid(tmp); | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3034 |  | 
 | 3035 | 	read_msi_msg(irq, &msg); | 
 | 3036 |  | 
 | 3037 | 	msg.data &= ~MSI_DATA_VECTOR_MASK; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3038 | 	msg.data |= MSI_DATA_VECTOR(cfg->vector); | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3039 | 	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; | 
 | 3040 | 	msg.address_lo |= MSI_ADDR_DEST_ID(dest); | 
 | 3041 |  | 
 | 3042 | 	write_msi_msg(irq, &msg); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3043 | 	desc = irq_to_desc(irq); | 
 | 3044 | 	desc->affinity = mask; | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3045 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3046 |  | 
 | 3047 | #ifdef CONFIG_INTR_REMAP | 
 | 3048 | /* | 
 | 3049 |  * Migrate the MSI irq to another cpumask. This migration is | 
 | 3050 |  * done in the process context using interrupt-remapping hardware. | 
 | 3051 |  */ | 
 | 3052 | static void ir_set_msi_irq_affinity(unsigned int irq, cpumask_t mask) | 
 | 3053 | { | 
 | 3054 | 	struct irq_cfg *cfg; | 
 | 3055 | 	unsigned int dest; | 
 | 3056 | 	cpumask_t tmp, cleanup_mask; | 
 | 3057 | 	struct irte irte; | 
 | 3058 | 	struct irq_desc *desc; | 
 | 3059 |  | 
 | 3060 | 	cpus_and(tmp, mask, cpu_online_map); | 
 | 3061 | 	if (cpus_empty(tmp)) | 
 | 3062 | 		return; | 
 | 3063 |  | 
 | 3064 | 	if (get_irte(irq, &irte)) | 
 | 3065 | 		return; | 
 | 3066 |  | 
 | 3067 | 	if (assign_irq_vector(irq, mask)) | 
 | 3068 | 		return; | 
 | 3069 |  | 
 | 3070 | 	cfg = irq_cfg(irq); | 
 | 3071 | 	cpus_and(tmp, cfg->domain, mask); | 
 | 3072 | 	dest = cpu_mask_to_apicid(tmp); | 
 | 3073 |  | 
 | 3074 | 	irte.vector = cfg->vector; | 
 | 3075 | 	irte.dest_id = IRTE_DEST(dest); | 
 | 3076 |  | 
 | 3077 | 	/* | 
 | 3078 | 	 * atomically update the IRTE with the new destination and vector. | 
 | 3079 | 	 */ | 
 | 3080 | 	modify_irte(irq, &irte); | 
 | 3081 |  | 
 | 3082 | 	/* | 
 | 3083 | 	 * After this point, all the interrupts will start arriving | 
 | 3084 | 	 * at the new destination. So, time to cleanup the previous | 
 | 3085 | 	 * vector allocation. | 
 | 3086 | 	 */ | 
 | 3087 | 	if (cfg->move_in_progress) { | 
 | 3088 | 		cpus_and(cleanup_mask, cfg->old_domain, cpu_online_map); | 
 | 3089 | 		cfg->move_cleanup_count = cpus_weight(cleanup_mask); | 
 | 3090 | 		send_IPI_mask(cleanup_mask, IRQ_MOVE_CLEANUP_VECTOR); | 
 | 3091 | 		cfg->move_in_progress = 0; | 
 | 3092 | 	} | 
 | 3093 |  | 
 | 3094 | 	desc = irq_to_desc(irq); | 
 | 3095 | 	desc->affinity = mask; | 
 | 3096 | } | 
 | 3097 | #endif | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3098 | #endif /* CONFIG_SMP */ | 
 | 3099 |  | 
 | 3100 | /* | 
 | 3101 |  * IRQ Chip for MSI PCI/PCI-X/PCI-Express Devices, | 
 | 3102 |  * which implement the MSI or MSI-X Capability Structure. | 
 | 3103 |  */ | 
 | 3104 | static struct irq_chip msi_chip = { | 
 | 3105 | 	.name		= "PCI-MSI", | 
 | 3106 | 	.unmask		= unmask_msi_irq, | 
 | 3107 | 	.mask		= mask_msi_irq, | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 3108 | 	.ack		= ack_apic_edge, | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3109 | #ifdef CONFIG_SMP | 
 | 3110 | 	.set_affinity	= set_msi_irq_affinity, | 
 | 3111 | #endif | 
 | 3112 | 	.retrigger	= ioapic_retrigger_irq, | 
 | 3113 | }; | 
 | 3114 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3115 | #ifdef CONFIG_INTR_REMAP | 
 | 3116 | static struct irq_chip msi_ir_chip = { | 
 | 3117 | 	.name		= "IR-PCI-MSI", | 
 | 3118 | 	.unmask		= unmask_msi_irq, | 
 | 3119 | 	.mask		= mask_msi_irq, | 
 | 3120 | 	.ack		= ack_x2apic_edge, | 
 | 3121 | #ifdef CONFIG_SMP | 
 | 3122 | 	.set_affinity	= ir_set_msi_irq_affinity, | 
 | 3123 | #endif | 
 | 3124 | 	.retrigger	= ioapic_retrigger_irq, | 
 | 3125 | }; | 
 | 3126 |  | 
 | 3127 | /* | 
 | 3128 |  * Map the PCI dev to the corresponding remapping hardware unit | 
 | 3129 |  * and allocate 'nvec' consecutive interrupt-remapping table entries | 
 | 3130 |  * in it. | 
 | 3131 |  */ | 
 | 3132 | static int msi_alloc_irte(struct pci_dev *dev, int irq, int nvec) | 
 | 3133 | { | 
 | 3134 | 	struct intel_iommu *iommu; | 
 | 3135 | 	int index; | 
 | 3136 |  | 
 | 3137 | 	iommu = map_dev_to_ir(dev); | 
 | 3138 | 	if (!iommu) { | 
 | 3139 | 		printk(KERN_ERR | 
 | 3140 | 		       "Unable to map PCI %s to iommu\n", pci_name(dev)); | 
 | 3141 | 		return -ENOENT; | 
 | 3142 | 	} | 
 | 3143 |  | 
 | 3144 | 	index = alloc_irte(iommu, irq, nvec); | 
 | 3145 | 	if (index < 0) { | 
 | 3146 | 		printk(KERN_ERR | 
 | 3147 | 		       "Unable to allocate %d IRTE for PCI %s\n", nvec, | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 3148 | 		       pci_name(dev)); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3149 | 		return -ENOSPC; | 
 | 3150 | 	} | 
 | 3151 | 	return index; | 
 | 3152 | } | 
 | 3153 | #endif | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 3154 |  | 
 | 3155 | static int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc, int irq) | 
 | 3156 | { | 
 | 3157 | 	int ret; | 
 | 3158 | 	struct msi_msg msg; | 
 | 3159 |  | 
 | 3160 | 	ret = msi_compose_msg(dev, irq, &msg); | 
 | 3161 | 	if (ret < 0) | 
 | 3162 | 		return ret; | 
 | 3163 |  | 
 | 3164 | 	set_irq_msi(irq, desc); | 
 | 3165 | 	write_msi_msg(irq, &msg); | 
 | 3166 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3167 | #ifdef CONFIG_INTR_REMAP | 
 | 3168 | 	if (irq_remapped(irq)) { | 
 | 3169 | 		struct irq_desc *desc = irq_to_desc(irq); | 
 | 3170 | 		/* | 
 | 3171 | 		 * irq migration in process context | 
 | 3172 | 		 */ | 
 | 3173 | 		desc->status |= IRQ_MOVE_PCNTXT; | 
 | 3174 | 		set_irq_chip_and_handler_name(irq, &msi_ir_chip, handle_edge_irq, "edge"); | 
 | 3175 | 	} else | 
 | 3176 | #endif | 
 | 3177 | 		set_irq_chip_and_handler_name(irq, &msi_chip, handle_edge_irq, "edge"); | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 3178 |  | 
| Yinghai Lu | c81bba4 | 2008-09-25 11:53:11 -0700 | [diff] [blame] | 3179 | 	dev_printk(KERN_DEBUG, &dev->dev, "irq %d for MSI/MSI-X\n", irq); | 
 | 3180 |  | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 3181 | 	return 0; | 
 | 3182 | } | 
 | 3183 |  | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 3184 | static unsigned int build_irq_for_pci_dev(struct pci_dev *dev) | 
 | 3185 | { | 
 | 3186 | 	unsigned int irq; | 
 | 3187 |  | 
 | 3188 | 	irq = dev->bus->number; | 
 | 3189 | 	irq <<= 8; | 
 | 3190 | 	irq |= dev->devfn; | 
 | 3191 | 	irq <<= 12; | 
 | 3192 |  | 
 | 3193 | 	return irq; | 
 | 3194 | } | 
 | 3195 |  | 
| Eric W. Biederman | f7feaca | 2007-01-28 12:56:37 -0700 | [diff] [blame] | 3196 | int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3197 | { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3198 | 	unsigned int irq; | 
 | 3199 | 	int ret; | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 3200 | 	unsigned int irq_want; | 
 | 3201 |  | 
 | 3202 | 	irq_want = build_irq_for_pci_dev(dev) + 0x100; | 
 | 3203 |  | 
 | 3204 | 	irq = create_irq_nr(irq_want); | 
| Yinghai Lu | 199751d | 2008-08-19 20:50:27 -0700 | [diff] [blame] | 3205 | 	if (irq == 0) | 
 | 3206 | 		return -1; | 
| Eric W. Biederman | f7feaca | 2007-01-28 12:56:37 -0700 | [diff] [blame] | 3207 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3208 | #ifdef CONFIG_INTR_REMAP | 
 | 3209 | 	if (!intr_remapping_enabled) | 
 | 3210 | 		goto no_ir; | 
 | 3211 |  | 
 | 3212 | 	ret = msi_alloc_irte(dev, irq, 1); | 
 | 3213 | 	if (ret < 0) | 
 | 3214 | 		goto error; | 
 | 3215 | no_ir: | 
 | 3216 | #endif | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 3217 | 	ret = setup_msi_irq(dev, desc, irq); | 
| Eric W. Biederman | f7feaca | 2007-01-28 12:56:37 -0700 | [diff] [blame] | 3218 | 	if (ret < 0) { | 
 | 3219 | 		destroy_irq(irq); | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3220 | 		return ret; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3221 | 	} | 
| Michael Ellerman | 7fe3730 | 2007-04-18 19:39:21 +1000 | [diff] [blame] | 3222 | 	return 0; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3223 |  | 
 | 3224 | #ifdef CONFIG_INTR_REMAP | 
 | 3225 | error: | 
 | 3226 | 	destroy_irq(irq); | 
 | 3227 | 	return ret; | 
 | 3228 | #endif | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3229 | } | 
 | 3230 |  | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 3231 | int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | 
 | 3232 | { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3233 | 	unsigned int irq; | 
 | 3234 | 	int ret, sub_handle; | 
 | 3235 | 	struct msi_desc *desc; | 
 | 3236 | 	unsigned int irq_want; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 3237 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3238 | #ifdef CONFIG_INTR_REMAP | 
 | 3239 | 	struct intel_iommu *iommu = 0; | 
 | 3240 | 	int index = 0; | 
 | 3241 | #endif | 
 | 3242 |  | 
 | 3243 | 	irq_want = build_irq_for_pci_dev(dev) + 0x100; | 
 | 3244 | 	sub_handle = 0; | 
 | 3245 | 	list_for_each_entry(desc, &dev->msi_list, list) { | 
 | 3246 | 		irq = create_irq_nr(irq_want--); | 
 | 3247 | 		if (irq == 0) | 
 | 3248 | 			return -1; | 
 | 3249 | #ifdef CONFIG_INTR_REMAP | 
 | 3250 | 		if (!intr_remapping_enabled) | 
 | 3251 | 			goto no_ir; | 
 | 3252 |  | 
 | 3253 | 		if (!sub_handle) { | 
 | 3254 | 			/* | 
 | 3255 | 			 * allocate the consecutive block of IRTE's | 
 | 3256 | 			 * for 'nvec' | 
 | 3257 | 			 */ | 
 | 3258 | 			index = msi_alloc_irte(dev, irq, nvec); | 
 | 3259 | 			if (index < 0) { | 
 | 3260 | 				ret = index; | 
 | 3261 | 				goto error; | 
 | 3262 | 			} | 
 | 3263 | 		} else { | 
 | 3264 | 			iommu = map_dev_to_ir(dev); | 
 | 3265 | 			if (!iommu) { | 
 | 3266 | 				ret = -ENOENT; | 
 | 3267 | 				goto error; | 
 | 3268 | 			} | 
 | 3269 | 			/* | 
 | 3270 | 			 * setup the mapping between the irq and the IRTE | 
 | 3271 | 			 * base index, the sub_handle pointing to the | 
 | 3272 | 			 * appropriate interrupt remap table entry. | 
 | 3273 | 			 */ | 
 | 3274 | 			set_irte_irq(irq, iommu, index, sub_handle); | 
 | 3275 | 		} | 
 | 3276 | no_ir: | 
 | 3277 | #endif | 
 | 3278 | 		ret = setup_msi_irq(dev, desc, irq); | 
 | 3279 | 		if (ret < 0) | 
 | 3280 | 			goto error; | 
 | 3281 | 		sub_handle++; | 
 | 3282 | 	} | 
 | 3283 | 	return 0; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 3284 |  | 
 | 3285 | error: | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3286 | 	destroy_irq(irq); | 
 | 3287 | 	return ret; | 
| Yinghai Lu | 047c8fd | 2008-08-19 20:50:41 -0700 | [diff] [blame] | 3288 | } | 
 | 3289 |  | 
| Eric W. Biederman | 3b7d192 | 2006-10-04 02:16:59 -0700 | [diff] [blame] | 3290 | void arch_teardown_msi_irq(unsigned int irq) | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 3291 | { | 
| Eric W. Biederman | f7feaca | 2007-01-28 12:56:37 -0700 | [diff] [blame] | 3292 | 	destroy_irq(irq); | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 3293 | } | 
 | 3294 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3295 | #ifdef CONFIG_DMAR | 
 | 3296 | #ifdef CONFIG_SMP | 
 | 3297 | static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask) | 
 | 3298 | { | 
 | 3299 | 	struct irq_cfg *cfg; | 
 | 3300 | 	struct msi_msg msg; | 
 | 3301 | 	unsigned int dest; | 
 | 3302 | 	cpumask_t tmp; | 
 | 3303 | 	struct irq_desc *desc; | 
| Eric W. Biederman | 2d3fcc1 | 2006-10-04 02:16:43 -0700 | [diff] [blame] | 3304 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3305 | 	cpus_and(tmp, mask, cpu_online_map); | 
 | 3306 | 	if (cpus_empty(tmp)) | 
 | 3307 | 		return; | 
 | 3308 |  | 
 | 3309 | 	if (assign_irq_vector(irq, mask)) | 
 | 3310 | 		return; | 
 | 3311 |  | 
 | 3312 | 	cfg = irq_cfg(irq); | 
 | 3313 | 	cpus_and(tmp, cfg->domain, mask); | 
 | 3314 | 	dest = cpu_mask_to_apicid(tmp); | 
 | 3315 |  | 
 | 3316 | 	dmar_msi_read(irq, &msg); | 
 | 3317 |  | 
 | 3318 | 	msg.data &= ~MSI_DATA_VECTOR_MASK; | 
 | 3319 | 	msg.data |= MSI_DATA_VECTOR(cfg->vector); | 
 | 3320 | 	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; | 
 | 3321 | 	msg.address_lo |= MSI_ADDR_DEST_ID(dest); | 
 | 3322 |  | 
 | 3323 | 	dmar_msi_write(irq, &msg); | 
 | 3324 | 	desc = irq_to_desc(irq); | 
 | 3325 | 	desc->affinity = mask; | 
 | 3326 | } | 
 | 3327 | #endif /* CONFIG_SMP */ | 
 | 3328 |  | 
 | 3329 | struct irq_chip dmar_msi_type = { | 
 | 3330 | 	.name = "DMAR_MSI", | 
 | 3331 | 	.unmask = dmar_msi_unmask, | 
 | 3332 | 	.mask = dmar_msi_mask, | 
 | 3333 | 	.ack = ack_apic_edge, | 
 | 3334 | #ifdef CONFIG_SMP | 
 | 3335 | 	.set_affinity = dmar_msi_set_affinity, | 
 | 3336 | #endif | 
 | 3337 | 	.retrigger = ioapic_retrigger_irq, | 
 | 3338 | }; | 
 | 3339 |  | 
 | 3340 | int arch_setup_dmar_msi(unsigned int irq) | 
 | 3341 | { | 
 | 3342 | 	int ret; | 
 | 3343 | 	struct msi_msg msg; | 
 | 3344 |  | 
 | 3345 | 	ret = msi_compose_msg(NULL, irq, &msg); | 
 | 3346 | 	if (ret < 0) | 
 | 3347 | 		return ret; | 
 | 3348 | 	dmar_msi_write(irq, &msg); | 
 | 3349 | 	set_irq_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq, | 
 | 3350 | 		"edge"); | 
 | 3351 | 	return 0; | 
 | 3352 | } | 
 | 3353 | #endif | 
 | 3354 |  | 
| venkatesh.pallipadi@intel.com | 58ac1e7 | 2008-09-05 18:02:17 -0700 | [diff] [blame] | 3355 | #ifdef CONFIG_HPET_TIMER | 
 | 3356 |  | 
 | 3357 | #ifdef CONFIG_SMP | 
 | 3358 | static void hpet_msi_set_affinity(unsigned int irq, cpumask_t mask) | 
 | 3359 | { | 
 | 3360 | 	struct irq_cfg *cfg; | 
 | 3361 | 	struct irq_desc *desc; | 
 | 3362 | 	struct msi_msg msg; | 
 | 3363 | 	unsigned int dest; | 
 | 3364 | 	cpumask_t tmp; | 
 | 3365 |  | 
 | 3366 | 	cpus_and(tmp, mask, cpu_online_map); | 
 | 3367 | 	if (cpus_empty(tmp)) | 
 | 3368 | 		return; | 
 | 3369 |  | 
 | 3370 | 	if (assign_irq_vector(irq, mask)) | 
 | 3371 | 		return; | 
 | 3372 |  | 
 | 3373 | 	cfg = irq_cfg(irq); | 
 | 3374 | 	cpus_and(tmp, cfg->domain, mask); | 
 | 3375 | 	dest = cpu_mask_to_apicid(tmp); | 
 | 3376 |  | 
 | 3377 | 	hpet_msi_read(irq, &msg); | 
 | 3378 |  | 
 | 3379 | 	msg.data &= ~MSI_DATA_VECTOR_MASK; | 
 | 3380 | 	msg.data |= MSI_DATA_VECTOR(cfg->vector); | 
 | 3381 | 	msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; | 
 | 3382 | 	msg.address_lo |= MSI_ADDR_DEST_ID(dest); | 
 | 3383 |  | 
 | 3384 | 	hpet_msi_write(irq, &msg); | 
 | 3385 | 	desc = irq_to_desc(irq); | 
 | 3386 | 	desc->affinity = mask; | 
 | 3387 | } | 
 | 3388 | #endif /* CONFIG_SMP */ | 
 | 3389 |  | 
 | 3390 | struct irq_chip hpet_msi_type = { | 
 | 3391 | 	.name = "HPET_MSI", | 
 | 3392 | 	.unmask = hpet_msi_unmask, | 
 | 3393 | 	.mask = hpet_msi_mask, | 
 | 3394 | 	.ack = ack_apic_edge, | 
 | 3395 | #ifdef CONFIG_SMP | 
 | 3396 | 	.set_affinity = hpet_msi_set_affinity, | 
 | 3397 | #endif | 
 | 3398 | 	.retrigger = ioapic_retrigger_irq, | 
 | 3399 | }; | 
 | 3400 |  | 
 | 3401 | int arch_setup_hpet_msi(unsigned int irq) | 
 | 3402 | { | 
 | 3403 | 	int ret; | 
 | 3404 | 	struct msi_msg msg; | 
 | 3405 |  | 
 | 3406 | 	ret = msi_compose_msg(NULL, irq, &msg); | 
 | 3407 | 	if (ret < 0) | 
 | 3408 | 		return ret; | 
 | 3409 |  | 
 | 3410 | 	hpet_msi_write(irq, &msg); | 
 | 3411 | 	set_irq_chip_and_handler_name(irq, &hpet_msi_type, handle_edge_irq, | 
 | 3412 | 		"edge"); | 
| Yinghai Lu | c81bba4 | 2008-09-25 11:53:11 -0700 | [diff] [blame] | 3413 |  | 
| venkatesh.pallipadi@intel.com | 58ac1e7 | 2008-09-05 18:02:17 -0700 | [diff] [blame] | 3414 | 	return 0; | 
 | 3415 | } | 
 | 3416 | #endif | 
 | 3417 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3418 | #endif /* CONFIG_PCI_MSI */ | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3419 | /* | 
 | 3420 |  * Hypertransport interrupt support | 
 | 3421 |  */ | 
 | 3422 | #ifdef CONFIG_HT_IRQ | 
 | 3423 |  | 
 | 3424 | #ifdef CONFIG_SMP | 
 | 3425 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3426 | static void target_ht_irq(unsigned int irq, unsigned int dest, u8 vector) | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3427 | { | 
| Eric W. Biederman | ec68307 | 2006-11-08 17:44:57 -0800 | [diff] [blame] | 3428 | 	struct ht_irq_msg msg; | 
 | 3429 | 	fetch_ht_irq_msg(irq, &msg); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3430 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3431 | 	msg.address_lo &= ~(HT_IRQ_LOW_VECTOR_MASK | HT_IRQ_LOW_DEST_ID_MASK); | 
| Eric W. Biederman | ec68307 | 2006-11-08 17:44:57 -0800 | [diff] [blame] | 3432 | 	msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3433 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3434 | 	msg.address_lo |= HT_IRQ_LOW_VECTOR(vector) | HT_IRQ_LOW_DEST_ID(dest); | 
| Eric W. Biederman | ec68307 | 2006-11-08 17:44:57 -0800 | [diff] [blame] | 3435 | 	msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3436 |  | 
| Eric W. Biederman | ec68307 | 2006-11-08 17:44:57 -0800 | [diff] [blame] | 3437 | 	write_ht_irq_msg(irq, &msg); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3438 | } | 
 | 3439 |  | 
 | 3440 | static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) | 
 | 3441 | { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3442 | 	struct irq_cfg *cfg; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3443 | 	unsigned int dest; | 
 | 3444 | 	cpumask_t tmp; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3445 | 	struct irq_desc *desc; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3446 |  | 
 | 3447 | 	cpus_and(tmp, mask, cpu_online_map); | 
 | 3448 | 	if (cpus_empty(tmp)) | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3449 | 		return; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3450 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3451 | 	if (assign_irq_vector(irq, mask)) | 
 | 3452 | 		return; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3453 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3454 | 	cfg = irq_cfg(irq); | 
 | 3455 | 	cpus_and(tmp, cfg->domain, mask); | 
 | 3456 | 	dest = cpu_mask_to_apicid(tmp); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3457 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3458 | 	target_ht_irq(irq, dest, cfg->vector); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3459 | 	desc = irq_to_desc(irq); | 
 | 3460 | 	desc->affinity = mask; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3461 | } | 
 | 3462 | #endif | 
 | 3463 |  | 
| Aneesh Kumar K.V | c37e108 | 2006-10-11 01:20:43 -0700 | [diff] [blame] | 3464 | static struct irq_chip ht_irq_chip = { | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3465 | 	.name		= "PCI-HT", | 
 | 3466 | 	.mask		= mask_ht_irq, | 
 | 3467 | 	.unmask		= unmask_ht_irq, | 
| Yinghai Lu | 1d02519 | 2008-08-19 20:50:34 -0700 | [diff] [blame] | 3468 | 	.ack		= ack_apic_edge, | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3469 | #ifdef CONFIG_SMP | 
 | 3470 | 	.set_affinity	= set_ht_irq_affinity, | 
 | 3471 | #endif | 
 | 3472 | 	.retrigger	= ioapic_retrigger_irq, | 
 | 3473 | }; | 
 | 3474 |  | 
 | 3475 | int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) | 
 | 3476 | { | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3477 | 	struct irq_cfg *cfg; | 
 | 3478 | 	int err; | 
 | 3479 | 	cpumask_t tmp; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3480 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3481 | 	tmp = TARGET_CPUS; | 
 | 3482 | 	err = assign_irq_vector(irq, tmp); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3483 | 	if (!err) { | 
| Eric W. Biederman | ec68307 | 2006-11-08 17:44:57 -0800 | [diff] [blame] | 3484 | 		struct ht_irq_msg msg; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3485 | 		unsigned dest; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3486 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3487 | 		cfg = irq_cfg(irq); | 
 | 3488 | 		cpus_and(tmp, cfg->domain, tmp); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3489 | 		dest = cpu_mask_to_apicid(tmp); | 
 | 3490 |  | 
| Eric W. Biederman | ec68307 | 2006-11-08 17:44:57 -0800 | [diff] [blame] | 3491 | 		msg.address_hi = HT_IRQ_HIGH_DEST_ID(dest); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3492 |  | 
| Eric W. Biederman | ec68307 | 2006-11-08 17:44:57 -0800 | [diff] [blame] | 3493 | 		msg.address_lo = | 
 | 3494 | 			HT_IRQ_LOW_BASE | | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3495 | 			HT_IRQ_LOW_DEST_ID(dest) | | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3496 | 			HT_IRQ_LOW_VECTOR(cfg->vector) | | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3497 | 			((INT_DEST_MODE == 0) ? | 
 | 3498 | 				HT_IRQ_LOW_DM_PHYSICAL : | 
 | 3499 | 				HT_IRQ_LOW_DM_LOGICAL) | | 
 | 3500 | 			HT_IRQ_LOW_RQEOI_EDGE | | 
 | 3501 | 			((INT_DELIVERY_MODE != dest_LowestPrio) ? | 
 | 3502 | 				HT_IRQ_LOW_MT_FIXED : | 
 | 3503 | 				HT_IRQ_LOW_MT_ARBITRATED) | | 
 | 3504 | 			HT_IRQ_LOW_IRQ_MASKED; | 
 | 3505 |  | 
| Eric W. Biederman | ec68307 | 2006-11-08 17:44:57 -0800 | [diff] [blame] | 3506 | 		write_ht_irq_msg(irq, &msg); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3507 |  | 
| Ingo Molnar | a460e74 | 2006-10-17 00:10:03 -0700 | [diff] [blame] | 3508 | 		set_irq_chip_and_handler_name(irq, &ht_irq_chip, | 
 | 3509 | 					      handle_edge_irq, "edge"); | 
| Yinghai Lu | c81bba4 | 2008-09-25 11:53:11 -0700 | [diff] [blame] | 3510 |  | 
 | 3511 | 		dev_printk(KERN_DEBUG, &dev->dev, "irq %d for HT\n", irq); | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3512 | 	} | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3513 | 	return err; | 
| Eric W. Biederman | 8b955b0 | 2006-10-04 02:16:55 -0700 | [diff] [blame] | 3514 | } | 
 | 3515 | #endif /* CONFIG_HT_IRQ */ | 
 | 3516 |  | 
| Dean Nelson | 4173a0e | 2008-10-02 12:18:21 -0500 | [diff] [blame] | 3517 | #ifdef CONFIG_X86_64 | 
 | 3518 | /* | 
 | 3519 |  * Re-target the irq to the specified CPU and enable the specified MMR located | 
 | 3520 |  * on the specified blade to allow the sending of MSIs to the specified CPU. | 
 | 3521 |  */ | 
 | 3522 | int arch_enable_uv_irq(char *irq_name, unsigned int irq, int cpu, int mmr_blade, | 
 | 3523 | 		       unsigned long mmr_offset) | 
 | 3524 | { | 
 | 3525 | 	const cpumask_t *eligible_cpu = get_cpu_mask(cpu); | 
 | 3526 | 	struct irq_cfg *cfg; | 
 | 3527 | 	int mmr_pnode; | 
 | 3528 | 	unsigned long mmr_value; | 
 | 3529 | 	struct uv_IO_APIC_route_entry *entry; | 
 | 3530 | 	unsigned long flags; | 
 | 3531 | 	int err; | 
 | 3532 |  | 
 | 3533 | 	err = assign_irq_vector(irq, *eligible_cpu); | 
 | 3534 | 	if (err != 0) | 
 | 3535 | 		return err; | 
 | 3536 |  | 
 | 3537 | 	spin_lock_irqsave(&vector_lock, flags); | 
 | 3538 | 	set_irq_chip_and_handler_name(irq, &uv_irq_chip, handle_percpu_irq, | 
 | 3539 | 				      irq_name); | 
 | 3540 | 	spin_unlock_irqrestore(&vector_lock, flags); | 
 | 3541 |  | 
 | 3542 | 	cfg = irq_cfg(irq); | 
 | 3543 |  | 
 | 3544 | 	mmr_value = 0; | 
 | 3545 | 	entry = (struct uv_IO_APIC_route_entry *)&mmr_value; | 
 | 3546 | 	BUG_ON(sizeof(struct uv_IO_APIC_route_entry) != sizeof(unsigned long)); | 
 | 3547 |  | 
 | 3548 | 	entry->vector = cfg->vector; | 
 | 3549 | 	entry->delivery_mode = INT_DELIVERY_MODE; | 
 | 3550 | 	entry->dest_mode = INT_DEST_MODE; | 
 | 3551 | 	entry->polarity = 0; | 
 | 3552 | 	entry->trigger = 0; | 
 | 3553 | 	entry->mask = 0; | 
 | 3554 | 	entry->dest = cpu_mask_to_apicid(*eligible_cpu); | 
 | 3555 |  | 
 | 3556 | 	mmr_pnode = uv_blade_to_pnode(mmr_blade); | 
 | 3557 | 	uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value); | 
 | 3558 |  | 
 | 3559 | 	return irq; | 
 | 3560 | } | 
 | 3561 |  | 
 | 3562 | /* | 
 | 3563 |  * Disable the specified MMR located on the specified blade so that MSIs are | 
 | 3564 |  * longer allowed to be sent. | 
 | 3565 |  */ | 
 | 3566 | void arch_disable_uv_irq(int mmr_blade, unsigned long mmr_offset) | 
 | 3567 | { | 
 | 3568 | 	unsigned long mmr_value; | 
 | 3569 | 	struct uv_IO_APIC_route_entry *entry; | 
 | 3570 | 	int mmr_pnode; | 
 | 3571 |  | 
 | 3572 | 	mmr_value = 0; | 
 | 3573 | 	entry = (struct uv_IO_APIC_route_entry *)&mmr_value; | 
 | 3574 | 	BUG_ON(sizeof(struct uv_IO_APIC_route_entry) != sizeof(unsigned long)); | 
 | 3575 |  | 
 | 3576 | 	entry->mask = 1; | 
 | 3577 |  | 
 | 3578 | 	mmr_pnode = uv_blade_to_pnode(mmr_blade); | 
 | 3579 | 	uv_write_global_mmr64(mmr_pnode, mmr_offset, mmr_value); | 
 | 3580 | } | 
 | 3581 | #endif /* CONFIG_X86_64 */ | 
 | 3582 |  | 
| Yinghai Lu | 9d6a4d0 | 2008-08-19 20:50:52 -0700 | [diff] [blame] | 3583 | int __init io_apic_get_redir_entries (int ioapic) | 
 | 3584 | { | 
 | 3585 | 	union IO_APIC_reg_01	reg_01; | 
 | 3586 | 	unsigned long flags; | 
 | 3587 |  | 
 | 3588 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 3589 | 	reg_01.raw = io_apic_read(ioapic, 1); | 
 | 3590 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 3591 |  | 
 | 3592 | 	return reg_01.bits.entries; | 
 | 3593 | } | 
 | 3594 |  | 
 | 3595 | int __init probe_nr_irqs(void) | 
 | 3596 | { | 
 | 3597 | 	int idx; | 
 | 3598 | 	int nr = 0; | 
| Yinghai Lu | 052c0bf | 2008-08-21 13:10:09 -0700 | [diff] [blame] | 3599 | #ifndef CONFIG_XEN | 
 | 3600 | 	int nr_min = 32; | 
 | 3601 | #else | 
 | 3602 | 	int nr_min = NR_IRQS; | 
 | 3603 | #endif | 
| Yinghai Lu | 9d6a4d0 | 2008-08-19 20:50:52 -0700 | [diff] [blame] | 3604 |  | 
 | 3605 | 	for (idx = 0; idx < nr_ioapics; idx++) | 
| Yinghai Lu | 052c0bf | 2008-08-21 13:10:09 -0700 | [diff] [blame] | 3606 | 		nr += io_apic_get_redir_entries(idx) + 1; | 
| Yinghai Lu | 9d6a4d0 | 2008-08-19 20:50:52 -0700 | [diff] [blame] | 3607 |  | 
 | 3608 | 	/* double it for hotplug and msi and nmi */ | 
 | 3609 | 	nr <<= 1; | 
 | 3610 |  | 
 | 3611 | 	/* something wrong ? */ | 
| Yinghai Lu | 052c0bf | 2008-08-21 13:10:09 -0700 | [diff] [blame] | 3612 | 	if (nr < nr_min) | 
 | 3613 | 		nr = nr_min; | 
| Yinghai Lu | 9d6a4d0 | 2008-08-19 20:50:52 -0700 | [diff] [blame] | 3614 |  | 
 | 3615 | 	return nr; | 
 | 3616 | } | 
 | 3617 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3618 | /* -------------------------------------------------------------------------- | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3619 |                           ACPI-based IOAPIC Configuration | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3620 |    -------------------------------------------------------------------------- */ | 
 | 3621 |  | 
| Len Brown | 888ba6c | 2005-08-24 12:07:20 -0400 | [diff] [blame] | 3622 | #ifdef CONFIG_ACPI | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3623 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3624 | #ifdef CONFIG_X86_32 | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 3625 | int __init io_apic_get_unique_id(int ioapic, int apic_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3626 | { | 
 | 3627 | 	union IO_APIC_reg_00 reg_00; | 
 | 3628 | 	static physid_mask_t apic_id_map = PHYSID_MASK_NONE; | 
 | 3629 | 	physid_mask_t tmp; | 
 | 3630 | 	unsigned long flags; | 
 | 3631 | 	int i = 0; | 
 | 3632 |  | 
 | 3633 | 	/* | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 3634 | 	 * The P4 platform supports up to 256 APIC IDs on two separate APIC | 
 | 3635 | 	 * buses (one for LAPICs, one for IOAPICs), where predecessors only | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3636 | 	 * supports up to 16 on one shared APIC bus. | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 3637 | 	 * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3638 | 	 * TBD: Expand LAPIC/IOAPIC support on P4-class systems to take full | 
 | 3639 | 	 *      advantage of new APIC bus architecture. | 
 | 3640 | 	 */ | 
 | 3641 |  | 
 | 3642 | 	if (physids_empty(apic_id_map)) | 
 | 3643 | 		apic_id_map = ioapic_phys_id_map(phys_cpu_present_map); | 
 | 3644 |  | 
 | 3645 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 3646 | 	reg_00.raw = io_apic_read(ioapic, 0); | 
 | 3647 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 3648 |  | 
 | 3649 | 	if (apic_id >= get_physical_broadcast()) { | 
 | 3650 | 		printk(KERN_WARNING "IOAPIC[%d]: Invalid apic_id %d, trying " | 
 | 3651 | 			"%d\n", ioapic, apic_id, reg_00.bits.ID); | 
 | 3652 | 		apic_id = reg_00.bits.ID; | 
 | 3653 | 	} | 
 | 3654 |  | 
 | 3655 | 	/* | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 3656 | 	 * Every APIC in a system must have a unique ID or we get lots of nice | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3657 | 	 * 'stuck on smp_invalidate_needed IPI wait' messages. | 
 | 3658 | 	 */ | 
 | 3659 | 	if (check_apicid_used(apic_id_map, apic_id)) { | 
 | 3660 |  | 
 | 3661 | 		for (i = 0; i < get_physical_broadcast(); i++) { | 
 | 3662 | 			if (!check_apicid_used(apic_id_map, i)) | 
 | 3663 | 				break; | 
 | 3664 | 		} | 
 | 3665 |  | 
 | 3666 | 		if (i == get_physical_broadcast()) | 
 | 3667 | 			panic("Max apic_id exceeded!\n"); | 
 | 3668 |  | 
 | 3669 | 		printk(KERN_WARNING "IOAPIC[%d]: apic_id %d already used, " | 
 | 3670 | 			"trying %d\n", ioapic, apic_id, i); | 
 | 3671 |  | 
 | 3672 | 		apic_id = i; | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 3673 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3674 |  | 
 | 3675 | 	tmp = apicid_to_cpu_present(apic_id); | 
 | 3676 | 	physids_or(apic_id_map, apic_id_map, tmp); | 
 | 3677 |  | 
 | 3678 | 	if (reg_00.bits.ID != apic_id) { | 
 | 3679 | 		reg_00.bits.ID = apic_id; | 
 | 3680 |  | 
 | 3681 | 		spin_lock_irqsave(&ioapic_lock, flags); | 
 | 3682 | 		io_apic_write(ioapic, 0, reg_00.raw); | 
 | 3683 | 		reg_00.raw = io_apic_read(ioapic, 0); | 
 | 3684 | 		spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 3685 |  | 
 | 3686 | 		/* Sanity check */ | 
| Andreas Deresch | 6070f9e | 2006-02-26 04:18:34 +0100 | [diff] [blame] | 3687 | 		if (reg_00.bits.ID != apic_id) { | 
 | 3688 | 			printk("IOAPIC[%d]: Unable to change apic_id!\n", ioapic); | 
 | 3689 | 			return -1; | 
 | 3690 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3691 | 	} | 
 | 3692 |  | 
 | 3693 | 	apic_printk(APIC_VERBOSE, KERN_INFO | 
 | 3694 | 			"IOAPIC[%d]: Assigned apic_id %d\n", ioapic, apic_id); | 
 | 3695 |  | 
 | 3696 | 	return apic_id; | 
 | 3697 | } | 
 | 3698 |  | 
| Paolo Ciarrocchi | 3606244 | 2008-06-08 13:07:18 +0200 | [diff] [blame] | 3699 | int __init io_apic_get_version(int ioapic) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3700 | { | 
 | 3701 | 	union IO_APIC_reg_01	reg_01; | 
 | 3702 | 	unsigned long flags; | 
 | 3703 |  | 
 | 3704 | 	spin_lock_irqsave(&ioapic_lock, flags); | 
 | 3705 | 	reg_01.raw = io_apic_read(ioapic, 1); | 
 | 3706 | 	spin_unlock_irqrestore(&ioapic_lock, flags); | 
 | 3707 |  | 
 | 3708 | 	return reg_01.bits.version; | 
 | 3709 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3710 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3711 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3712 | int io_apic_set_pci_routing (int ioapic, int pin, int irq, int triggering, int polarity) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3713 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3714 | 	if (!IO_APIC_IRQ(irq)) { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3715 | 		apic_printk(APIC_QUIET,KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3716 | 			ioapic); | 
 | 3717 | 		return -EINVAL; | 
 | 3718 | 	} | 
 | 3719 |  | 
 | 3720 | 	/* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3721 | 	 * IRQs < 16 are already in the irq_2_pin[] map | 
 | 3722 | 	 */ | 
 | 3723 | 	if (irq >= 16) | 
 | 3724 | 		add_pin_to_irq(irq, ioapic, pin); | 
 | 3725 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3726 | 	setup_IO_APIC_irq(ioapic, pin, irq, triggering, polarity); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 3727 |  | 
 | 3728 | 	return 0; | 
 | 3729 | } | 
 | 3730 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3731 |  | 
| Shaohua Li | 61fd47e | 2007-11-17 01:05:28 -0500 | [diff] [blame] | 3732 | int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity) | 
 | 3733 | { | 
 | 3734 | 	int i; | 
 | 3735 |  | 
 | 3736 | 	if (skip_ioapic_setup) | 
 | 3737 | 		return -1; | 
 | 3738 |  | 
 | 3739 | 	for (i = 0; i < mp_irq_entries; i++) | 
| Alexey Starikovskiy | 2fddb6e28 | 2008-05-14 19:03:17 +0400 | [diff] [blame] | 3740 | 		if (mp_irqs[i].mp_irqtype == mp_INT && | 
 | 3741 | 		    mp_irqs[i].mp_srcbusirq == bus_irq) | 
| Shaohua Li | 61fd47e | 2007-11-17 01:05:28 -0500 | [diff] [blame] | 3742 | 			break; | 
 | 3743 | 	if (i >= mp_irq_entries) | 
 | 3744 | 		return -1; | 
 | 3745 |  | 
 | 3746 | 	*trigger = irq_trigger(i); | 
 | 3747 | 	*polarity = irq_polarity(i); | 
 | 3748 | 	return 0; | 
 | 3749 | } | 
 | 3750 |  | 
| Len Brown | 888ba6c | 2005-08-24 12:07:20 -0400 | [diff] [blame] | 3751 | #endif /* CONFIG_ACPI */ | 
| Rusty Russell | 1a3f239 | 2006-09-26 10:52:32 +0200 | [diff] [blame] | 3752 |  | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3753 | /* | 
 | 3754 |  * This function currently is only a helper for the i386 smp boot process where | 
 | 3755 |  * we need to reprogram the ioredtbls to cater for the cpus which have come online | 
 | 3756 |  * so mask in all cases should simply be TARGET_CPUS | 
 | 3757 |  */ | 
 | 3758 | #ifdef CONFIG_SMP | 
 | 3759 | void __init setup_ioapic_dest(void) | 
 | 3760 | { | 
 | 3761 | 	int pin, ioapic, irq, irq_entry; | 
 | 3762 | 	struct irq_cfg *cfg; | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3763 |  | 
 | 3764 | 	if (skip_ioapic_setup == 1) | 
 | 3765 | 		return; | 
 | 3766 |  | 
 | 3767 | 	for (ioapic = 0; ioapic < nr_ioapics; ioapic++) { | 
 | 3768 | 		for (pin = 0; pin < nr_ioapic_registers[ioapic]; pin++) { | 
 | 3769 | 			irq_entry = find_irq_entry(ioapic, pin, mp_INT); | 
 | 3770 | 			if (irq_entry == -1) | 
 | 3771 | 				continue; | 
 | 3772 | 			irq = pin_2_irq(irq_entry, ioapic, pin); | 
 | 3773 |  | 
 | 3774 | 			/* setup_IO_APIC_irqs could fail to get vector for some device | 
 | 3775 | 			 * when you have too many devices, because at that time only boot | 
 | 3776 | 			 * cpu is online. | 
 | 3777 | 			 */ | 
 | 3778 | 			cfg = irq_cfg(irq); | 
 | 3779 | 			if (!cfg->vector) | 
 | 3780 | 				setup_IO_APIC_irq(ioapic, pin, irq, | 
 | 3781 | 						  irq_trigger(irq_entry), | 
 | 3782 | 						  irq_polarity(irq_entry)); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3783 | #ifdef CONFIG_INTR_REMAP | 
 | 3784 | 			else if (intr_remapping_enabled) | 
 | 3785 | 				set_ir_ioapic_affinity_irq(irq, TARGET_CPUS); | 
 | 3786 | #endif | 
 | 3787 | 			else | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3788 | 				set_ioapic_affinity_irq(irq, TARGET_CPUS); | 
| Yinghai Lu | 497c9a1 | 2008-08-19 20:50:28 -0700 | [diff] [blame] | 3789 | 		} | 
 | 3790 |  | 
 | 3791 | 	} | 
 | 3792 | } | 
 | 3793 | #endif | 
 | 3794 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3795 | #define IOAPIC_RESOURCE_NAME_SIZE 11 | 
 | 3796 |  | 
 | 3797 | static struct resource *ioapic_resources; | 
 | 3798 |  | 
 | 3799 | static struct resource * __init ioapic_setup_resources(void) | 
 | 3800 | { | 
 | 3801 | 	unsigned long n; | 
 | 3802 | 	struct resource *res; | 
 | 3803 | 	char *mem; | 
 | 3804 | 	int i; | 
 | 3805 |  | 
 | 3806 | 	if (nr_ioapics <= 0) | 
 | 3807 | 		return NULL; | 
 | 3808 |  | 
 | 3809 | 	n = IOAPIC_RESOURCE_NAME_SIZE + sizeof(struct resource); | 
 | 3810 | 	n *= nr_ioapics; | 
 | 3811 |  | 
 | 3812 | 	mem = alloc_bootmem(n); | 
 | 3813 | 	res = (void *)mem; | 
 | 3814 |  | 
 | 3815 | 	if (mem != NULL) { | 
 | 3816 | 		mem += sizeof(struct resource) * nr_ioapics; | 
 | 3817 |  | 
 | 3818 | 		for (i = 0; i < nr_ioapics; i++) { | 
 | 3819 | 			res[i].name = mem; | 
 | 3820 | 			res[i].flags = IORESOURCE_MEM | IORESOURCE_BUSY; | 
 | 3821 | 			sprintf(mem,  "IOAPIC %u", i); | 
 | 3822 | 			mem += IOAPIC_RESOURCE_NAME_SIZE; | 
 | 3823 | 		} | 
 | 3824 | 	} | 
 | 3825 |  | 
 | 3826 | 	ioapic_resources = res; | 
 | 3827 |  | 
 | 3828 | 	return res; | 
 | 3829 | } | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3830 |  | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3831 | void __init ioapic_init_mappings(void) | 
 | 3832 | { | 
 | 3833 | 	unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3834 | 	struct resource *ioapic_res; | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 3835 | 	int i; | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3836 |  | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 3837 | 	irq_2_pin_init(); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3838 | 	ioapic_res = ioapic_setup_resources(); | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3839 | 	for (i = 0; i < nr_ioapics; i++) { | 
 | 3840 | 		if (smp_found_config) { | 
 | 3841 | 			ioapic_phys = mp_ioapics[i].mp_apicaddr; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3842 | #ifdef CONFIG_X86_32 | 
| Thomas Gleixner | d6c88a5 | 2008-10-15 15:27:23 +0200 | [diff] [blame] | 3843 | 			if (!ioapic_phys) { | 
 | 3844 | 				printk(KERN_ERR | 
 | 3845 | 				       "WARNING: bogus zero IO-APIC " | 
 | 3846 | 				       "address found in MPTABLE, " | 
 | 3847 | 				       "disabling IO/APIC support!\n"); | 
 | 3848 | 				smp_found_config = 0; | 
 | 3849 | 				skip_ioapic_setup = 1; | 
 | 3850 | 				goto fake_ioapic_page; | 
 | 3851 | 			} | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3852 | #endif | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3853 | 		} else { | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3854 | #ifdef CONFIG_X86_32 | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3855 | fake_ioapic_page: | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3856 | #endif | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3857 | 			ioapic_phys = (unsigned long) | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3858 | 				alloc_bootmem_pages(PAGE_SIZE); | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3859 | 			ioapic_phys = __pa(ioapic_phys); | 
 | 3860 | 		} | 
 | 3861 | 		set_fixmap_nocache(idx, ioapic_phys); | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3862 | 		apic_printk(APIC_VERBOSE, | 
 | 3863 | 			    "mapped IOAPIC to %08lx (%08lx)\n", | 
 | 3864 | 			    __fix_to_virt(idx), ioapic_phys); | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3865 | 		idx++; | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3866 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3867 | 		if (ioapic_res != NULL) { | 
 | 3868 | 			ioapic_res->start = ioapic_phys; | 
 | 3869 | 			ioapic_res->end = ioapic_phys + (4 * 1024) - 1; | 
 | 3870 | 			ioapic_res++; | 
 | 3871 | 		} | 
| Yinghai Lu | f3294a3 | 2008-06-27 01:41:56 -0700 | [diff] [blame] | 3872 | 	} | 
 | 3873 | } | 
 | 3874 |  | 
| Ingo Molnar | 54168ed | 2008-08-20 09:07:45 +0200 | [diff] [blame] | 3875 | static int __init ioapic_insert_resources(void) | 
 | 3876 | { | 
 | 3877 | 	int i; | 
 | 3878 | 	struct resource *r = ioapic_resources; | 
 | 3879 |  | 
 | 3880 | 	if (!r) { | 
 | 3881 | 		printk(KERN_ERR | 
 | 3882 | 		       "IO APIC resources could be not be allocated.\n"); | 
 | 3883 | 		return -1; | 
 | 3884 | 	} | 
 | 3885 |  | 
 | 3886 | 	for (i = 0; i < nr_ioapics; i++) { | 
 | 3887 | 		insert_resource(&iomem_resource, r); | 
 | 3888 | 		r++; | 
 | 3889 | 	} | 
 | 3890 |  | 
 | 3891 | 	return 0; | 
 | 3892 | } | 
 | 3893 |  | 
 | 3894 | /* Insert the IO APIC resources after PCI initialization has occured to handle | 
 | 3895 |  * IO APICS that are mapped in on a BAR in PCI space. */ | 
 | 3896 | late_initcall(ioapic_insert_resources); |