diff options
Diffstat (limited to 'arch/ppc64/kernel/i8259.c')
-rw-r--r-- | arch/ppc64/kernel/i8259.c | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/i8259.c b/arch/ppc64/kernel/i8259.c new file mode 100644 index 000000000000..74dcfd68fc75 --- /dev/null +++ b/arch/ppc64/kernel/i8259.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * c 2001 PPC64 Team, IBM Corp | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | #include <linux/stddef.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/signal.h> | ||
13 | #include <linux/cache.h> | ||
14 | #include <linux/irq.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <asm/io.h> | ||
17 | #include <asm/ppcdebug.h> | ||
18 | #include "i8259.h" | ||
19 | |||
20 | unsigned char cached_8259[2] = { 0xff, 0xff }; | ||
21 | #define cached_A1 (cached_8259[0]) | ||
22 | #define cached_21 (cached_8259[1]) | ||
23 | |||
24 | static __cacheline_aligned_in_smp DEFINE_SPINLOCK(i8259_lock); | ||
25 | |||
26 | static int i8259_pic_irq_offset; | ||
27 | static int i8259_present; | ||
28 | |||
29 | int i8259_irq(int cpu) | ||
30 | { | ||
31 | int irq; | ||
32 | |||
33 | spin_lock/*_irqsave*/(&i8259_lock/*, flags*/); | ||
34 | /* | ||
35 | * Perform an interrupt acknowledge cycle on controller 1 | ||
36 | */ | ||
37 | outb(0x0C, 0x20); | ||
38 | irq = inb(0x20) & 7; | ||
39 | if (irq == 2) | ||
40 | { | ||
41 | /* | ||
42 | * Interrupt is cascaded so perform interrupt | ||
43 | * acknowledge on controller 2 | ||
44 | */ | ||
45 | outb(0x0C, 0xA0); | ||
46 | irq = (inb(0xA0) & 7) + 8; | ||
47 | } | ||
48 | else if (irq==7) | ||
49 | { | ||
50 | /* | ||
51 | * This may be a spurious interrupt | ||
52 | * | ||
53 | * Read the interrupt status register. If the most | ||
54 | * significant bit is not set then there is no valid | ||
55 | * interrupt | ||
56 | */ | ||
57 | outb(0x0b, 0x20); | ||
58 | if(~inb(0x20)&0x80) { | ||
59 | spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); | ||
60 | return -1; | ||
61 | } | ||
62 | } | ||
63 | spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); | ||
64 | return irq; | ||
65 | } | ||
66 | |||
67 | static void i8259_mask_and_ack_irq(unsigned int irq_nr) | ||
68 | { | ||
69 | unsigned long flags; | ||
70 | |||
71 | spin_lock_irqsave(&i8259_lock, flags); | ||
72 | if ( irq_nr >= i8259_pic_irq_offset ) | ||
73 | irq_nr -= i8259_pic_irq_offset; | ||
74 | |||
75 | if (irq_nr > 7) { | ||
76 | cached_A1 |= 1 << (irq_nr-8); | ||
77 | inb(0xA1); /* DUMMY */ | ||
78 | outb(cached_A1,0xA1); | ||
79 | outb(0x20,0xA0); /* Non-specific EOI */ | ||
80 | outb(0x20,0x20); /* Non-specific EOI to cascade */ | ||
81 | } else { | ||
82 | cached_21 |= 1 << irq_nr; | ||
83 | inb(0x21); /* DUMMY */ | ||
84 | outb(cached_21,0x21); | ||
85 | outb(0x20,0x20); /* Non-specific EOI */ | ||
86 | } | ||
87 | spin_unlock_irqrestore(&i8259_lock, flags); | ||
88 | } | ||
89 | |||
90 | static void i8259_set_irq_mask(int irq_nr) | ||
91 | { | ||
92 | outb(cached_A1,0xA1); | ||
93 | outb(cached_21,0x21); | ||
94 | } | ||
95 | |||
96 | static void i8259_mask_irq(unsigned int irq_nr) | ||
97 | { | ||
98 | unsigned long flags; | ||
99 | |||
100 | spin_lock_irqsave(&i8259_lock, flags); | ||
101 | if ( irq_nr >= i8259_pic_irq_offset ) | ||
102 | irq_nr -= i8259_pic_irq_offset; | ||
103 | if ( irq_nr < 8 ) | ||
104 | cached_21 |= 1 << irq_nr; | ||
105 | else | ||
106 | cached_A1 |= 1 << (irq_nr-8); | ||
107 | i8259_set_irq_mask(irq_nr); | ||
108 | spin_unlock_irqrestore(&i8259_lock, flags); | ||
109 | } | ||
110 | |||
111 | static void i8259_unmask_irq(unsigned int irq_nr) | ||
112 | { | ||
113 | unsigned long flags; | ||
114 | |||
115 | spin_lock_irqsave(&i8259_lock, flags); | ||
116 | if ( irq_nr >= i8259_pic_irq_offset ) | ||
117 | irq_nr -= i8259_pic_irq_offset; | ||
118 | if ( irq_nr < 8 ) | ||
119 | cached_21 &= ~(1 << irq_nr); | ||
120 | else | ||
121 | cached_A1 &= ~(1 << (irq_nr-8)); | ||
122 | i8259_set_irq_mask(irq_nr); | ||
123 | spin_unlock_irqrestore(&i8259_lock, flags); | ||
124 | } | ||
125 | |||
126 | static void i8259_end_irq(unsigned int irq) | ||
127 | { | ||
128 | if (!(get_irq_desc(irq)->status & (IRQ_DISABLED|IRQ_INPROGRESS)) && | ||
129 | get_irq_desc(irq)->action) | ||
130 | i8259_unmask_irq(irq); | ||
131 | } | ||
132 | |||
133 | struct hw_interrupt_type i8259_pic = { | ||
134 | .typename = " i8259 ", | ||
135 | .enable = i8259_unmask_irq, | ||
136 | .disable = i8259_mask_irq, | ||
137 | .ack = i8259_mask_and_ack_irq, | ||
138 | .end = i8259_end_irq, | ||
139 | }; | ||
140 | |||
141 | void __init i8259_init(int offset) | ||
142 | { | ||
143 | unsigned long flags; | ||
144 | |||
145 | spin_lock_irqsave(&i8259_lock, flags); | ||
146 | i8259_pic_irq_offset = offset; | ||
147 | i8259_present = 1; | ||
148 | /* init master interrupt controller */ | ||
149 | outb(0x11, 0x20); /* Start init sequence */ | ||
150 | outb(0x00, 0x21); /* Vector base */ | ||
151 | outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ | ||
152 | outb(0x01, 0x21); /* Select 8086 mode */ | ||
153 | outb(0xFF, 0x21); /* Mask all */ | ||
154 | /* init slave interrupt controller */ | ||
155 | outb(0x11, 0xA0); /* Start init sequence */ | ||
156 | outb(0x08, 0xA1); /* Vector base */ | ||
157 | outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ | ||
158 | outb(0x01, 0xA1); /* Select 8086 mode */ | ||
159 | outb(0xFF, 0xA1); /* Mask all */ | ||
160 | outb(cached_A1, 0xA1); | ||
161 | outb(cached_21, 0x21); | ||
162 | spin_unlock_irqrestore(&i8259_lock, flags); | ||
163 | |||
164 | } | ||
165 | |||
166 | static int i8259_request_cascade(void) | ||
167 | { | ||
168 | if (!i8259_present) | ||
169 | return -ENODEV; | ||
170 | |||
171 | request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT, | ||
172 | "82c59 secondary cascade", NULL ); | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | arch_initcall(i8259_request_cascade); | ||