Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/mips/kernel/i8259.c
10818 views
1
/*
2
* This file is subject to the terms and conditions of the GNU General Public
3
* License. See the file "COPYING" in the main directory of this archive
4
* for more details.
5
*
6
* Code to handle x86 style IRQs plus some generic interrupt stuff.
7
*
8
* Copyright (C) 1992 Linus Torvalds
9
* Copyright (C) 1994 - 2000 Ralf Baechle
10
*/
11
#include <linux/delay.h>
12
#include <linux/init.h>
13
#include <linux/ioport.h>
14
#include <linux/interrupt.h>
15
#include <linux/kernel.h>
16
#include <linux/spinlock.h>
17
#include <linux/syscore_ops.h>
18
#include <linux/irq.h>
19
20
#include <asm/i8259.h>
21
#include <asm/io.h>
22
23
/*
24
* This is the 'legacy' 8259A Programmable Interrupt Controller,
25
* present in the majority of PC/AT boxes.
26
* plus some generic x86 specific things if generic specifics makes
27
* any sense at all.
28
* this file should become arch/i386/kernel/irq.c when the old irq.c
29
* moves to arch independent land
30
*/
31
32
static int i8259A_auto_eoi = -1;
33
DEFINE_RAW_SPINLOCK(i8259A_lock);
34
static void disable_8259A_irq(struct irq_data *d);
35
static void enable_8259A_irq(struct irq_data *d);
36
static void mask_and_ack_8259A(struct irq_data *d);
37
static void init_8259A(int auto_eoi);
38
39
static struct irq_chip i8259A_chip = {
40
.name = "XT-PIC",
41
.irq_mask = disable_8259A_irq,
42
.irq_disable = disable_8259A_irq,
43
.irq_unmask = enable_8259A_irq,
44
.irq_mask_ack = mask_and_ack_8259A,
45
#ifdef CONFIG_MIPS_MT_SMTC_IRQAFF
46
.irq_set_affinity = plat_set_irq_affinity,
47
#endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */
48
};
49
50
/*
51
* 8259A PIC functions to handle ISA devices:
52
*/
53
54
/*
55
* This contains the irq mask for both 8259A irq controllers,
56
*/
57
static unsigned int cached_irq_mask = 0xffff;
58
59
#define cached_master_mask (cached_irq_mask)
60
#define cached_slave_mask (cached_irq_mask >> 8)
61
62
static void disable_8259A_irq(struct irq_data *d)
63
{
64
unsigned int mask, irq = d->irq - I8259A_IRQ_BASE;
65
unsigned long flags;
66
67
mask = 1 << irq;
68
raw_spin_lock_irqsave(&i8259A_lock, flags);
69
cached_irq_mask |= mask;
70
if (irq & 8)
71
outb(cached_slave_mask, PIC_SLAVE_IMR);
72
else
73
outb(cached_master_mask, PIC_MASTER_IMR);
74
raw_spin_unlock_irqrestore(&i8259A_lock, flags);
75
}
76
77
static void enable_8259A_irq(struct irq_data *d)
78
{
79
unsigned int mask, irq = d->irq - I8259A_IRQ_BASE;
80
unsigned long flags;
81
82
mask = ~(1 << irq);
83
raw_spin_lock_irqsave(&i8259A_lock, flags);
84
cached_irq_mask &= mask;
85
if (irq & 8)
86
outb(cached_slave_mask, PIC_SLAVE_IMR);
87
else
88
outb(cached_master_mask, PIC_MASTER_IMR);
89
raw_spin_unlock_irqrestore(&i8259A_lock, flags);
90
}
91
92
int i8259A_irq_pending(unsigned int irq)
93
{
94
unsigned int mask;
95
unsigned long flags;
96
int ret;
97
98
irq -= I8259A_IRQ_BASE;
99
mask = 1 << irq;
100
raw_spin_lock_irqsave(&i8259A_lock, flags);
101
if (irq < 8)
102
ret = inb(PIC_MASTER_CMD) & mask;
103
else
104
ret = inb(PIC_SLAVE_CMD) & (mask >> 8);
105
raw_spin_unlock_irqrestore(&i8259A_lock, flags);
106
107
return ret;
108
}
109
110
void make_8259A_irq(unsigned int irq)
111
{
112
disable_irq_nosync(irq);
113
irq_set_chip_and_handler(irq, &i8259A_chip, handle_level_irq);
114
enable_irq(irq);
115
}
116
117
/*
118
* This function assumes to be called rarely. Switching between
119
* 8259A registers is slow.
120
* This has to be protected by the irq controller spinlock
121
* before being called.
122
*/
123
static inline int i8259A_irq_real(unsigned int irq)
124
{
125
int value;
126
int irqmask = 1 << irq;
127
128
if (irq < 8) {
129
outb(0x0B, PIC_MASTER_CMD); /* ISR register */
130
value = inb(PIC_MASTER_CMD) & irqmask;
131
outb(0x0A, PIC_MASTER_CMD); /* back to the IRR register */
132
return value;
133
}
134
outb(0x0B, PIC_SLAVE_CMD); /* ISR register */
135
value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
136
outb(0x0A, PIC_SLAVE_CMD); /* back to the IRR register */
137
return value;
138
}
139
140
/*
141
* Careful! The 8259A is a fragile beast, it pretty
142
* much _has_ to be done exactly like this (mask it
143
* first, _then_ send the EOI, and the order of EOI
144
* to the two 8259s is important!
145
*/
146
static void mask_and_ack_8259A(struct irq_data *d)
147
{
148
unsigned int irqmask, irq = d->irq - I8259A_IRQ_BASE;
149
unsigned long flags;
150
151
irqmask = 1 << irq;
152
raw_spin_lock_irqsave(&i8259A_lock, flags);
153
/*
154
* Lightweight spurious IRQ detection. We do not want
155
* to overdo spurious IRQ handling - it's usually a sign
156
* of hardware problems, so we only do the checks we can
157
* do without slowing down good hardware unnecessarily.
158
*
159
* Note that IRQ7 and IRQ15 (the two spurious IRQs
160
* usually resulting from the 8259A-1|2 PICs) occur
161
* even if the IRQ is masked in the 8259A. Thus we
162
* can check spurious 8259A IRQs without doing the
163
* quite slow i8259A_irq_real() call for every IRQ.
164
* This does not cover 100% of spurious interrupts,
165
* but should be enough to warn the user that there
166
* is something bad going on ...
167
*/
168
if (cached_irq_mask & irqmask)
169
goto spurious_8259A_irq;
170
cached_irq_mask |= irqmask;
171
172
handle_real_irq:
173
if (irq & 8) {
174
inb(PIC_SLAVE_IMR); /* DUMMY - (do we need this?) */
175
outb(cached_slave_mask, PIC_SLAVE_IMR);
176
outb(0x60+(irq&7), PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
177
outb(0x60+PIC_CASCADE_IR, PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
178
} else {
179
inb(PIC_MASTER_IMR); /* DUMMY - (do we need this?) */
180
outb(cached_master_mask, PIC_MASTER_IMR);
181
outb(0x60+irq, PIC_MASTER_CMD); /* 'Specific EOI to master */
182
}
183
smtc_im_ack_irq(irq);
184
raw_spin_unlock_irqrestore(&i8259A_lock, flags);
185
return;
186
187
spurious_8259A_irq:
188
/*
189
* this is the slow path - should happen rarely.
190
*/
191
if (i8259A_irq_real(irq))
192
/*
193
* oops, the IRQ _is_ in service according to the
194
* 8259A - not spurious, go handle it.
195
*/
196
goto handle_real_irq;
197
198
{
199
static int spurious_irq_mask;
200
/*
201
* At this point we can be sure the IRQ is spurious,
202
* lets ACK and report it. [once per IRQ]
203
*/
204
if (!(spurious_irq_mask & irqmask)) {
205
printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq);
206
spurious_irq_mask |= irqmask;
207
}
208
atomic_inc(&irq_err_count);
209
/*
210
* Theoretically we do not have to handle this IRQ,
211
* but in Linux this does not cause problems and is
212
* simpler for us.
213
*/
214
goto handle_real_irq;
215
}
216
}
217
218
static void i8259A_resume(void)
219
{
220
if (i8259A_auto_eoi >= 0)
221
init_8259A(i8259A_auto_eoi);
222
}
223
224
static void i8259A_shutdown(void)
225
{
226
/* Put the i8259A into a quiescent state that
227
* the kernel initialization code can get it
228
* out of.
229
*/
230
if (i8259A_auto_eoi >= 0) {
231
outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
232
outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-1 */
233
}
234
}
235
236
static struct syscore_ops i8259_syscore_ops = {
237
.resume = i8259A_resume,
238
.shutdown = i8259A_shutdown,
239
};
240
241
static int __init i8259A_init_sysfs(void)
242
{
243
register_syscore_ops(&i8259_syscore_ops);
244
return 0;
245
}
246
247
device_initcall(i8259A_init_sysfs);
248
249
static void init_8259A(int auto_eoi)
250
{
251
unsigned long flags;
252
253
i8259A_auto_eoi = auto_eoi;
254
255
raw_spin_lock_irqsave(&i8259A_lock, flags);
256
257
outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
258
outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
259
260
/*
261
* outb_p - this has to work on a wide range of PC hardware.
262
*/
263
outb_p(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
264
outb_p(I8259A_IRQ_BASE + 0, PIC_MASTER_IMR); /* ICW2: 8259A-1 IR0 mapped to I8259A_IRQ_BASE + 0x00 */
265
outb_p(1U << PIC_CASCADE_IR, PIC_MASTER_IMR); /* 8259A-1 (the master) has a slave on IR2 */
266
if (auto_eoi) /* master does Auto EOI */
267
outb_p(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
268
else /* master expects normal EOI */
269
outb_p(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
270
271
outb_p(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */
272
outb_p(I8259A_IRQ_BASE + 8, PIC_SLAVE_IMR); /* ICW2: 8259A-2 IR0 mapped to I8259A_IRQ_BASE + 0x08 */
273
outb_p(PIC_CASCADE_IR, PIC_SLAVE_IMR); /* 8259A-2 is a slave on master's IR2 */
274
outb_p(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */
275
if (auto_eoi)
276
/*
277
* In AEOI mode we just have to mask the interrupt
278
* when acking.
279
*/
280
i8259A_chip.irq_mask_ack = disable_8259A_irq;
281
else
282
i8259A_chip.irq_mask_ack = mask_and_ack_8259A;
283
284
udelay(100); /* wait for 8259A to initialize */
285
286
outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
287
outb(cached_slave_mask, PIC_SLAVE_IMR); /* restore slave IRQ mask */
288
289
raw_spin_unlock_irqrestore(&i8259A_lock, flags);
290
}
291
292
/*
293
* IRQ2 is cascade interrupt to second interrupt controller
294
*/
295
static struct irqaction irq2 = {
296
.handler = no_action,
297
.name = "cascade",
298
};
299
300
static struct resource pic1_io_resource = {
301
.name = "pic1",
302
.start = PIC_MASTER_CMD,
303
.end = PIC_MASTER_IMR,
304
.flags = IORESOURCE_BUSY
305
};
306
307
static struct resource pic2_io_resource = {
308
.name = "pic2",
309
.start = PIC_SLAVE_CMD,
310
.end = PIC_SLAVE_IMR,
311
.flags = IORESOURCE_BUSY
312
};
313
314
/*
315
* On systems with i8259-style interrupt controllers we assume for
316
* driver compatibility reasons interrupts 0 - 15 to be the i8259
317
* interrupts even if the hardware uses a different interrupt numbering.
318
*/
319
void __init init_i8259_irqs(void)
320
{
321
int i;
322
323
insert_resource(&ioport_resource, &pic1_io_resource);
324
insert_resource(&ioport_resource, &pic2_io_resource);
325
326
init_8259A(0);
327
328
for (i = I8259A_IRQ_BASE; i < I8259A_IRQ_BASE + 16; i++) {
329
irq_set_chip_and_handler(i, &i8259A_chip, handle_level_irq);
330
irq_set_probe(i);
331
}
332
333
setup_irq(I8259A_IRQ_BASE + PIC_CASCADE_IR, &irq2);
334
}
335
336