]>
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 | ||
44377f62 | 72 | struct irq_chip i8259a_irq_type = { |
8ab1221c | 73 | .name = "XT-PIC", |
7d209c81 KM |
74 | .unmask = i8259a_enable_irq, |
75 | .mask = i8259a_disable_irq, | |
76 | .mask_ack = i8259a_mask_and_ack_irq, | |
1da177e4 LT |
77 | }; |
78 | ||
79 | void __init | |
80 | init_i8259a_irqs(void) | |
81 | { | |
82 | static struct irqaction cascade = { | |
83 | .handler = no_action, | |
84 | .name = "cascade", | |
85 | }; | |
86 | ||
87 | long i; | |
88 | ||
89 | outb(0xff, 0x21); /* mask all of 8259A-1 */ | |
90 | outb(0xff, 0xA1); /* mask all of 8259A-2 */ | |
91 | ||
92 | for (i = 0; i < 16; i++) { | |
7d209c81 | 93 | set_irq_chip_and_handler(i, &i8259a_irq_type, handle_level_irq); |
1da177e4 LT |
94 | } |
95 | ||
96 | setup_irq(2, &cascade); | |
97 | } | |
98 | ||
99 | ||
100 | #if defined(CONFIG_ALPHA_GENERIC) | |
101 | # define IACK_SC alpha_mv.iack_sc | |
102 | #elif defined(CONFIG_ALPHA_APECS) | |
103 | # define IACK_SC APECS_IACK_SC | |
104 | #elif defined(CONFIG_ALPHA_LCA) | |
105 | # define IACK_SC LCA_IACK_SC | |
106 | #elif defined(CONFIG_ALPHA_CIA) | |
107 | # define IACK_SC CIA_IACK_SC | |
108 | #elif defined(CONFIG_ALPHA_PYXIS) | |
109 | # define IACK_SC PYXIS_IACK_SC | |
110 | #elif defined(CONFIG_ALPHA_TITAN) | |
111 | # define IACK_SC TITAN_IACK_SC | |
112 | #elif defined(CONFIG_ALPHA_TSUNAMI) | |
113 | # define IACK_SC TSUNAMI_IACK_SC | |
114 | #elif defined(CONFIG_ALPHA_IRONGATE) | |
115 | # define IACK_SC IRONGATE_IACK_SC | |
116 | #endif | |
117 | /* Note that CONFIG_ALPHA_POLARIS is intentionally left out here, since | |
118 | sys_rx164 wants to use isa_no_iack_sc_device_interrupt for some reason. */ | |
119 | ||
120 | #if defined(IACK_SC) | |
121 | void | |
7ca56053 | 122 | isa_device_interrupt(unsigned long vector) |
1da177e4 LT |
123 | { |
124 | /* | |
125 | * Generate a PCI interrupt acknowledge cycle. The PIC will | |
126 | * respond with the interrupt vector of the highest priority | |
127 | * interrupt that is pending. The PALcode sets up the | |
128 | * interrupts vectors such that irq level L generates vector L. | |
129 | */ | |
130 | int j = *(vuip) IACK_SC; | |
131 | j &= 0xff; | |
3dbb8c62 | 132 | handle_irq(j); |
1da177e4 LT |
133 | } |
134 | #endif | |
135 | ||
136 | #if defined(CONFIG_ALPHA_GENERIC) || !defined(IACK_SC) | |
137 | void | |
3dbb8c62 | 138 | isa_no_iack_sc_device_interrupt(unsigned long vector) |
1da177e4 LT |
139 | { |
140 | unsigned long pic; | |
141 | ||
142 | /* | |
143 | * It seems to me that the probability of two or more *device* | |
144 | * interrupts occurring at almost exactly the same time is | |
145 | * pretty low. So why pay the price of checking for | |
146 | * additional interrupts here if the common case can be | |
147 | * handled so much easier? | |
148 | */ | |
149 | /* | |
150 | * The first read of gives you *all* interrupting lines. | |
151 | * Therefore, read the mask register and and out those lines | |
152 | * not enabled. Note that some documentation has 21 and a1 | |
153 | * write only. This is not true. | |
154 | */ | |
155 | pic = inb(0x20) | (inb(0xA0) << 8); /* read isr */ | |
156 | pic &= 0xFFFB; /* mask out cascade & hibits */ | |
157 | ||
158 | while (pic) { | |
159 | int j = ffz(~pic); | |
160 | pic &= pic - 1; | |
3dbb8c62 | 161 | handle_irq(j); |
1da177e4 LT |
162 | } |
163 | } | |
164 | #endif |