Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/alpha/kernel/irq_i8259.c | |
3 | * | |
4 | * This is the 'legacy' 8259A Programmable Interrupt Controller, | |
5 | * present in the majority of PC/AT boxes. | |
6 | * | |
7 | * Started hacking from linux-2.3.30pre6/arch/i386/kernel/i8259.c. | |
8 | */ | |
9 | ||
1da177e4 LT |
10 | #include <linux/init.h> |
11 | #include <linux/cache.h> | |
12 | #include <linux/sched.h> | |
13 | #include <linux/irq.h> | |
14 | #include <linux/interrupt.h> | |
15 | ||
16 | #include <asm/io.h> | |
17 | ||
18 | #include "proto.h" | |
19 | #include "irq_impl.h" | |
20 | ||
21 | ||
22 | /* Note mask bit is true for DISABLED irqs. */ | |
23 | static unsigned int cached_irq_mask = 0xffff; | |
24 | static DEFINE_SPINLOCK(i8259_irq_lock); | |
25 | ||
26 | static inline void | |
27 | i8259_update_irq_hw(unsigned int irq, unsigned long mask) | |
28 | { | |
29 | int port = 0x21; | |
30 | if (irq & 8) mask >>= 8; | |
31 | if (irq & 8) port = 0xA1; | |
32 | outb(mask, port); | |
33 | } | |
34 | ||
35 | inline void | |
ff53afe6 | 36 | i8259a_enable_irq(struct irq_data *d) |
1da177e4 LT |
37 | { |
38 | spin_lock(&i8259_irq_lock); | |
ff53afe6 | 39 | i8259_update_irq_hw(d->irq, cached_irq_mask &= ~(1 << d->irq)); |
1da177e4 LT |
40 | spin_unlock(&i8259_irq_lock); |
41 | } | |
42 | ||
43 | static inline void | |
44 | __i8259a_disable_irq(unsigned int irq) | |
45 | { | |
46 | i8259_update_irq_hw(irq, cached_irq_mask |= 1 << irq); | |
47 | } | |
48 | ||
49 | void | |
ff53afe6 | 50 | i8259a_disable_irq(struct irq_data *d) |
1da177e4 LT |
51 | { |
52 | spin_lock(&i8259_irq_lock); | |
ff53afe6 | 53 | __i8259a_disable_irq(d->irq); |
1da177e4 LT |
54 | spin_unlock(&i8259_irq_lock); |
55 | } | |
56 | ||
57 | void | |
ff53afe6 | 58 | i8259a_mask_and_ack_irq(struct irq_data *d) |
1da177e4 | 59 | { |
ff53afe6 TG |
60 | unsigned int irq = d->irq; |
61 | ||
1da177e4 LT |
62 | spin_lock(&i8259_irq_lock); |
63 | __i8259a_disable_irq(irq); | |
64 | ||
65 | /* Ack the interrupt making it the lowest priority. */ | |
66 | if (irq >= 8) { | |
67 | outb(0xE0 | (irq - 8), 0xa0); /* ack the slave */ | |
68 | irq = 2; | |
69 | } | |
70 | outb(0xE0 | irq, 0x20); /* ack the master */ | |
71 | spin_unlock(&i8259_irq_lock); | |
72 | } | |
73 | ||
44377f62 | 74 | struct irq_chip i8259a_irq_type = { |
8ab1221c | 75 | .name = "XT-PIC", |
ff53afe6 TG |
76 | .irq_unmask = i8259a_enable_irq, |
77 | .irq_mask = i8259a_disable_irq, | |
78 | .irq_mask_ack = i8259a_mask_and_ack_irq, | |
1da177e4 LT |
79 | }; |
80 | ||
81 | void __init | |
82 | init_i8259a_irqs(void) | |
83 | { | |
84 | static struct irqaction cascade = { | |
85 | .handler = no_action, | |
86 | .name = "cascade", | |
87 | }; | |
88 | ||
89 | long i; | |
90 | ||
91 | outb(0xff, 0x21); /* mask all of 8259A-1 */ | |
92 | outb(0xff, 0xA1); /* mask all of 8259A-2 */ | |
93 | ||
94 | for (i = 0; i < 16; i++) { | |
a9eb076b | 95 | irq_set_chip_and_handler(i, &i8259a_irq_type, handle_level_irq); |
1da177e4 LT |
96 | } |
97 | ||
98 | setup_irq(2, &cascade); | |
99 | } | |
100 | ||
101 | ||
102 | #if defined(CONFIG_ALPHA_GENERIC) | |
103 | # define IACK_SC alpha_mv.iack_sc | |
104 | #elif defined(CONFIG_ALPHA_APECS) | |
105 | # define IACK_SC APECS_IACK_SC | |
106 | #elif defined(CONFIG_ALPHA_LCA) | |
107 | # define IACK_SC LCA_IACK_SC | |
108 | #elif defined(CONFIG_ALPHA_CIA) | |
109 | # define IACK_SC CIA_IACK_SC | |
110 | #elif defined(CONFIG_ALPHA_PYXIS) | |
111 | # define IACK_SC PYXIS_IACK_SC | |
112 | #elif defined(CONFIG_ALPHA_TITAN) | |
113 | # define IACK_SC TITAN_IACK_SC | |
114 | #elif defined(CONFIG_ALPHA_TSUNAMI) | |
115 | # define IACK_SC TSUNAMI_IACK_SC | |
116 | #elif defined(CONFIG_ALPHA_IRONGATE) | |
117 | # define IACK_SC IRONGATE_IACK_SC | |
118 | #endif | |
119 | /* Note that CONFIG_ALPHA_POLARIS is intentionally left out here, since | |
120 | sys_rx164 wants to use isa_no_iack_sc_device_interrupt for some reason. */ | |
121 | ||
122 | #if defined(IACK_SC) | |
123 | void | |
7ca56053 | 124 | isa_device_interrupt(unsigned long vector) |
1da177e4 LT |
125 | { |
126 | /* | |
127 | * Generate a PCI interrupt acknowledge cycle. The PIC will | |
128 | * respond with the interrupt vector of the highest priority | |
129 | * interrupt that is pending. The PALcode sets up the | |
130 | * interrupts vectors such that irq level L generates vector L. | |
131 | */ | |
132 | int j = *(vuip) IACK_SC; | |
133 | j &= 0xff; | |
3dbb8c62 | 134 | handle_irq(j); |
1da177e4 LT |
135 | } |
136 | #endif | |
137 | ||
138 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) | |
139 | void | |
3dbb8c62 | 140 | isa_no_iack_sc_device_interrupt(unsigned long vector) |
1da177e4 LT |
141 | { |
142 | unsigned long pic; | |
143 | ||
144 | /* | |
145 | * It seems to me that the probability of two or more *device* | |
146 | * interrupts occurring at almost exactly the same time is | |
147 | * pretty low. So why pay the price of checking for | |
148 | * additional interrupts here if the common case can be | |
149 | * handled so much easier? | |
150 | */ | |
151 | /* | |
152 | * The first read of gives you *all* interrupting lines. | |
153 | * Therefore, read the mask register and and out those lines | |
154 | * not enabled. Note that some documentation has 21 and a1 | |
155 | * write only. This is not true. | |
156 | */ | |
157 | pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ | |
158 | pic &= 0xFFFB; /* mask out cascade & hibits */ | |
159 | ||
160 | while (pic) { | |
161 | int j = ffz(~pic); | |
162 | pic &= pic - 1; | |
3dbb8c62 | 163 | handle_irq(j); |
1da177e4 LT |
164 | } |
165 | } | |
166 | #endif |