diff options
Diffstat (limited to 'arch/i386/kernel/io_apic.c')
-rw-r--r-- | arch/i386/kernel/io_apic.c | 96 |
1 files changed, 78 insertions, 18 deletions
diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 350192d6ab98..3b7a63e0ed1a 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c | |||
@@ -91,6 +91,46 @@ static struct irq_pin_list { | |||
91 | int apic, pin, next; | 91 | int apic, pin, next; |
92 | } irq_2_pin[PIN_MAP_SIZE]; | 92 | } irq_2_pin[PIN_MAP_SIZE]; |
93 | 93 | ||
94 | struct io_apic { | ||
95 | unsigned int index; | ||
96 | unsigned int unused[3]; | ||
97 | unsigned int data; | ||
98 | }; | ||
99 | |||
100 | static __attribute_const__ struct io_apic __iomem *io_apic_base(int idx) | ||
101 | { | ||
102 | return (void __iomem *) __fix_to_virt(FIX_IO_APIC_BASE_0 + idx) | ||
103 | + (mp_ioapics[idx].mpc_apicaddr & ~PAGE_MASK); | ||
104 | } | ||
105 | |||
106 | static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg) | ||
107 | { | ||
108 | struct io_apic __iomem *io_apic = io_apic_base(apic); | ||
109 | writel(reg, &io_apic->index); | ||
110 | return readl(&io_apic->data); | ||
111 | } | ||
112 | |||
113 | static inline void io_apic_write(unsigned int apic, unsigned int reg, unsigned int value) | ||
114 | { | ||
115 | struct io_apic __iomem *io_apic = io_apic_base(apic); | ||
116 | writel(reg, &io_apic->index); | ||
117 | writel(value, &io_apic->data); | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * Re-write a value: to be used for read-modify-write | ||
122 | * cycles where the read already set up the index register. | ||
123 | * | ||
124 | * Older SiS APIC requires we rewrite the index register | ||
125 | */ | ||
126 | static inline void io_apic_modify(unsigned int apic, unsigned int reg, unsigned int value) | ||
127 | { | ||
128 | volatile struct io_apic *io_apic = io_apic_base(apic); | ||
129 | if (sis_apic_bug) | ||
130 | writel(reg, &io_apic->index); | ||
131 | writel(value, &io_apic->data); | ||
132 | } | ||
133 | |||
94 | union entry_union { | 134 | union entry_union { |
95 | struct { u32 w1, w2; }; | 135 | struct { u32 w1, w2; }; |
96 | struct IO_APIC_route_entry entry; | 136 | struct IO_APIC_route_entry entry; |
@@ -107,12 +147,34 @@ static struct IO_APIC_route_entry ioapic_read_entry(int apic, int pin) | |||
107 | return eu.entry; | 147 | return eu.entry; |
108 | } | 148 | } |
109 | 149 | ||
150 | /* | ||
151 | * When we write a new IO APIC routing entry, we need to write the high | ||
152 | * word first! If the mask bit in the low word is clear, we will enable | ||
153 | * the interrupt, and we need to make sure the entry is fully populated | ||
154 | * before that happens. | ||
155 | */ | ||
110 | static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) | 156 | static void ioapic_write_entry(int apic, int pin, struct IO_APIC_route_entry e) |
111 | { | 157 | { |
112 | unsigned long flags; | 158 | unsigned long flags; |
113 | union entry_union eu; | 159 | union entry_union eu; |
114 | eu.entry = e; | 160 | eu.entry = e; |
115 | spin_lock_irqsave(&ioapic_lock, flags); | 161 | spin_lock_irqsave(&ioapic_lock, flags); |
162 | io_apic_write(apic, 0x11 + 2*pin, eu.w2); | ||
163 | io_apic_write(apic, 0x10 + 2*pin, eu.w1); | ||
164 | spin_unlock_irqrestore(&ioapic_lock, flags); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * When we mask an IO APIC routing entry, we need to write the low | ||
169 | * word first, in order to set the mask bit before we change the | ||
170 | * high bits! | ||
171 | */ | ||
172 | static void ioapic_mask_entry(int apic, int pin) | ||
173 | { | ||
174 | unsigned long flags; | ||
175 | union entry_union eu = { .entry.mask = 1 }; | ||
176 | |||
177 | spin_lock_irqsave(&ioapic_lock, flags); | ||
116 | io_apic_write(apic, 0x10 + 2*pin, eu.w1); | 178 | io_apic_write(apic, 0x10 + 2*pin, eu.w1); |
117 | io_apic_write(apic, 0x11 + 2*pin, eu.w2); | 179 | io_apic_write(apic, 0x11 + 2*pin, eu.w2); |
118 | spin_unlock_irqrestore(&ioapic_lock, flags); | 180 | spin_unlock_irqrestore(&ioapic_lock, flags); |
@@ -234,9 +296,7 @@ static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin) | |||
234 | /* | 296 | /* |
235 | * Disable it in the IO-APIC irq-routing table: | 297 | * Disable it in the IO-APIC irq-routing table: |
236 | */ | 298 | */ |
237 | memset(&entry, 0, sizeof(entry)); | 299 | ioapic_mask_entry(apic, pin); |
238 | entry.mask = 1; | ||
239 | ioapic_write_entry(apic, pin, entry); | ||
240 | } | 300 | } |
241 | 301 | ||
242 | static void clear_IO_APIC (void) | 302 | static void clear_IO_APIC (void) |
@@ -1227,9 +1287,11 @@ static void ioapic_register_intr(int irq, int vector, unsigned long trigger) | |||
1227 | trigger == IOAPIC_LEVEL) | 1287 | trigger == IOAPIC_LEVEL) |
1228 | set_irq_chip_and_handler_name(irq, &ioapic_chip, | 1288 | set_irq_chip_and_handler_name(irq, &ioapic_chip, |
1229 | handle_fasteoi_irq, "fasteoi"); | 1289 | handle_fasteoi_irq, "fasteoi"); |
1230 | else | 1290 | else { |
1291 | irq_desc[irq].status |= IRQ_DELAYED_DISABLE; | ||
1231 | set_irq_chip_and_handler_name(irq, &ioapic_chip, | 1292 | set_irq_chip_and_handler_name(irq, &ioapic_chip, |
1232 | handle_edge_irq, "edge"); | 1293 | handle_edge_irq, "edge"); |
1294 | } | ||
1233 | set_intr_gate(vector, interrupt[irq]); | 1295 | set_intr_gate(vector, interrupt[irq]); |
1234 | } | 1296 | } |
1235 | 1297 | ||
@@ -2564,18 +2626,16 @@ void arch_teardown_msi_irq(unsigned int irq) | |||
2564 | 2626 | ||
2565 | static void target_ht_irq(unsigned int irq, unsigned int dest) | 2627 | static void target_ht_irq(unsigned int irq, unsigned int dest) |
2566 | { | 2628 | { |
2567 | u32 low, high; | 2629 | struct ht_irq_msg msg; |
2568 | low = read_ht_irq_low(irq); | 2630 | fetch_ht_irq_msg(irq, &msg); |
2569 | high = read_ht_irq_high(irq); | ||
2570 | 2631 | ||
2571 | low &= ~(HT_IRQ_LOW_DEST_ID_MASK); | 2632 | msg.address_lo &= ~(HT_IRQ_LOW_DEST_ID_MASK); |
2572 | high &= ~(HT_IRQ_HIGH_DEST_ID_MASK); | 2633 | msg.address_hi &= ~(HT_IRQ_HIGH_DEST_ID_MASK); |
2573 | 2634 | ||
2574 | low |= HT_IRQ_LOW_DEST_ID(dest); | 2635 | msg.address_lo |= HT_IRQ_LOW_DEST_ID(dest); |
2575 | high |= HT_IRQ_HIGH_DEST_ID(dest); | 2636 | msg.address_hi |= HT_IRQ_HIGH_DEST_ID(dest); |
2576 | 2637 | ||
2577 | write_ht_irq_low(irq, low); | 2638 | write_ht_irq_msg(irq, &msg); |
2578 | write_ht_irq_high(irq, high); | ||
2579 | } | 2639 | } |
2580 | 2640 | ||
2581 | static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) | 2641 | static void set_ht_irq_affinity(unsigned int irq, cpumask_t mask) |
@@ -2613,7 +2673,7 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) | |||
2613 | 2673 | ||
2614 | vector = assign_irq_vector(irq); | 2674 | vector = assign_irq_vector(irq); |
2615 | if (vector >= 0) { | 2675 | if (vector >= 0) { |
2616 | u32 low, high; | 2676 | struct ht_irq_msg msg; |
2617 | unsigned dest; | 2677 | unsigned dest; |
2618 | cpumask_t tmp; | 2678 | cpumask_t tmp; |
2619 | 2679 | ||
@@ -2621,9 +2681,10 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) | |||
2621 | cpu_set(vector >> 8, tmp); | 2681 | cpu_set(vector >> 8, tmp); |
2622 | dest = cpu_mask_to_apicid(tmp); | 2682 | dest = cpu_mask_to_apicid(tmp); |
2623 | 2683 | ||
2624 | high = HT_IRQ_HIGH_DEST_ID(dest); | 2684 | msg.address_hi = HT_IRQ_HIGH_DEST_ID(dest); |
2625 | 2685 | ||
2626 | low = HT_IRQ_LOW_BASE | | 2686 | msg.address_lo = |
2687 | HT_IRQ_LOW_BASE | | ||
2627 | HT_IRQ_LOW_DEST_ID(dest) | | 2688 | HT_IRQ_LOW_DEST_ID(dest) | |
2628 | HT_IRQ_LOW_VECTOR(vector) | | 2689 | HT_IRQ_LOW_VECTOR(vector) | |
2629 | ((INT_DEST_MODE == 0) ? | 2690 | ((INT_DEST_MODE == 0) ? |
@@ -2635,8 +2696,7 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev) | |||
2635 | HT_IRQ_LOW_MT_ARBITRATED) | | 2696 | HT_IRQ_LOW_MT_ARBITRATED) | |
2636 | HT_IRQ_LOW_IRQ_MASKED; | 2697 | HT_IRQ_LOW_IRQ_MASKED; |
2637 | 2698 | ||
2638 | write_ht_irq_low(irq, low); | 2699 | write_ht_irq_msg(irq, &msg); |
2639 | write_ht_irq_high(irq, high); | ||
2640 | 2700 | ||
2641 | set_irq_chip_and_handler_name(irq, &ht_irq_chip, | 2701 | set_irq_chip_and_handler_name(irq, &ht_irq_chip, |
2642 | handle_edge_irq, "edge"); | 2702 | handle_edge_irq, "edge"); |