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 | |
36 | i8259a_enable_irq(unsigned int irq) | |
37 | { | |
38 | spin_lock(&i8259_irq_lock); | |
39 | i8259_update_irq_hw(irq, cached_irq_mask &= ~(1 << irq)); | |
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 | |
50 | i8259a_disable_irq(unsigned int irq) | |
51 | { | |
52 | spin_lock(&i8259_irq_lock); | |
53 | __i8259a_disable_irq(irq); | |
54 | spin_unlock(&i8259_irq_lock); | |
55 | } | |
56 | ||
57 | void | |
58 | i8259a_mask_and_ack_irq(unsigned int irq) | |
59 | { | |
60 | spin_lock(&i8259_irq_lock); | |
61 | __i8259a_disable_irq(irq); | |
62 | ||
63 | /* Ack the interrupt making it the lowest priority. */ | |
64 | if (irq >= 8) { | |
65 | outb(0xE0 | (irq - 8), 0xa0); /* ack the slave */ | |
66 | irq = 2; | |
67 | } | |
68 | outb(0xE0 | irq, 0x20); /* ack the master */ | |
69 | spin_unlock(&i8259_irq_lock); | |
70 | } | |
71 | ||
72 | unsigned int | |
73 | i8259a_startup_irq(unsigned int irq) | |
74 | { | |
75 | i8259a_enable_irq(irq); | |
76 | return 0; /* never anything pending */ | |
77 | } | |
78 | ||
79 | void | |
80 | i8259a_end_irq(unsigned int irq) | |
81 | { | |
82 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | |
83 | i8259a_enable_irq(irq); | |
84 | } | |
85 | ||
44377f62 | 86 | struct irq_chip i8259a_irq_type = { |
1da177e4 LT |
87 | .typename = "XT-PIC", |
88 | .startup = i8259a_startup_irq, | |
89 | .shutdown = i8259a_disable_irq, | |
90 | .enable = i8259a_enable_irq, | |
91 | .disable = i8259a_disable_irq, | |
92 | .ack = i8259a_mask_and_ack_irq, | |
93 | .end = i8259a_end_irq, | |
94 | }; | |
95 | ||
96 | void __init | |
97 | init_i8259a_irqs(void) | |
98 | { | |
99 | static struct irqaction cascade = { | |
100 | .handler = no_action, | |
101 | .name = "cascade", | |
102 | }; | |
103 | ||
104 | long i; | |
105 | ||
106 | outb(0xff, 0x21); /* mask all of 8259A-1 */ | |
107 | outb(0xff, 0xA1); /* mask all of 8259A-2 */ | |
108 | ||
109 | for (i = 0; i < 16; i++) { | |
110 | irq_desc[i].status = IRQ_DISABLED; | |
d1bef4ed | 111 | irq_desc[i].chip = &i8259a_irq_type; |
1da177e4 LT |
112 | } |
113 | ||
114 | setup_irq(2, &cascade); | |
115 | } | |
116 | ||
117 | ||
118 | #if defined(CONFIG_ALPHA_GENERIC) | |
119 | # define IACK_SC alpha_mv.iack_sc | |
120 | #elif defined(CONFIG_ALPHA_APECS) | |
121 | # define IACK_SC APECS_IACK_SC | |
122 | #elif defined(CONFIG_ALPHA_LCA) | |
123 | # define IACK_SC LCA_IACK_SC | |
124 | #elif defined(CONFIG_ALPHA_CIA) | |
125 | # define IACK_SC CIA_IACK_SC | |
126 | #elif defined(CONFIG_ALPHA_PYXIS) | |
127 | # define IACK_SC PYXIS_IACK_SC | |
128 | #elif defined(CONFIG_ALPHA_TITAN) | |
129 | # define IACK_SC TITAN_IACK_SC | |
130 | #elif defined(CONFIG_ALPHA_TSUNAMI) | |
131 | # define IACK_SC TSUNAMI_IACK_SC | |
132 | #elif defined(CONFIG_ALPHA_IRONGATE) | |
133 | # define IACK_SC IRONGATE_IACK_SC | |
134 | #endif | |
135 | /* Note that CONFIG_ALPHA_POLARIS is intentionally left out here, since | |
136 | sys_rx164 wants to use isa_no_iack_sc_device_interrupt for some reason. */ | |
137 | ||
138 | #if defined(IACK_SC) | |
139 | void | |
7ca56053 | 140 | isa_device_interrupt(unsigned long vector) |
1da177e4 LT |
141 | { |
142 | /* | |
143 | * Generate a PCI interrupt acknowledge cycle. The PIC will | |
144 | * respond with the interrupt vector of the highest priority | |
145 | * interrupt that is pending. The PALcode sets up the | |
146 | * interrupts vectors such that irq level L generates vector L. | |
147 | */ | |
148 | int j = *(vuip) IACK_SC; | |
149 | j &= 0xff; | |
3dbb8c62 | 150 | handle_irq(j); |
1da177e4 LT |
151 | } |
152 | #endif | |
153 | ||
154 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) | |
155 | void | |
3dbb8c62 | 156 | isa_no_iack_sc_device_interrupt(unsigned long vector) |
1da177e4 LT |
157 | { |
158 | unsigned long pic; | |
159 | ||
160 | /* | |
161 | * It seems to me that the probability of two or more *device* | |
162 | * interrupts occurring at almost exactly the same time is | |
163 | * pretty low. So why pay the price of checking for | |
164 | * additional interrupts here if the common case can be | |
165 | * handled so much easier? | |
166 | */ | |
167 | /* | |
168 | * The first read of gives you *all* interrupting lines. | |
169 | * Therefore, read the mask register and and out those lines | |
170 | * not enabled. Note that some documentation has 21 and a1 | |
171 | * write only. This is not true. | |
172 | */ | |
173 | pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ | |
174 | pic &= 0xFFFB; /* mask out cascade & hibits */ | |
175 | ||
176 | while (pic) { | |
177 | int j = ffz(~pic); | |
178 | pic &= pic - 1; | |
3dbb8c62 | 179 | handle_irq(j); |
1da177e4 LT |
180 | } |
181 | } | |
182 | #endif |