diff options
Diffstat (limited to 'arch/mips/sgi-ip22/ip22-eisa.c')
-rw-r--r-- | arch/mips/sgi-ip22/ip22-eisa.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip22/ip22-eisa.c b/arch/mips/sgi-ip22/ip22-eisa.c new file mode 100644 index 000000000000..0ab4abf65d58 --- /dev/null +++ b/arch/mips/sgi-ip22/ip22-eisa.c | |||
@@ -0,0 +1,308 @@ | |||
1 | /* | ||
2 | * Basic EISA bus support for the SGI Indigo-2. | ||
3 | * | ||
4 | * (C) 2002 Pascal Dameme <netinet@freesurf.fr> | ||
5 | * and Marc Zyngier <mzyngier@freesurf.fr> | ||
6 | * | ||
7 | * This code is released under both the GPL version 2 and BSD | ||
8 | * licenses. Either license may be used. | ||
9 | * | ||
10 | * This code offers a very basic support for this EISA bus present in | ||
11 | * the SGI Indigo-2. It currently only supports PIO (forget about DMA | ||
12 | * for the time being). This is enough for a low-end ethernet card, | ||
13 | * but forget about your favorite SCSI card... | ||
14 | * | ||
15 | * TODO : | ||
16 | * - Fix bugs... | ||
17 | * - Add ISA support | ||
18 | * - Add DMA (yeah, right...). | ||
19 | * - Fix more bugs. | ||
20 | */ | ||
21 | |||
22 | #include <linux/config.h> | ||
23 | #include <linux/eisa.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/kernel_stat.h> | ||
28 | #include <linux/signal.h> | ||
29 | #include <linux/sched.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <asm/irq.h> | ||
33 | #include <asm/mipsregs.h> | ||
34 | #include <asm/addrspace.h> | ||
35 | #include <asm/processor.h> | ||
36 | #include <asm/sgi/ioc.h> | ||
37 | #include <asm/sgi/mc.h> | ||
38 | #include <asm/sgi/ip22.h> | ||
39 | |||
40 | #define EISA_MAX_SLOTS 4 | ||
41 | #define EISA_MAX_IRQ 16 | ||
42 | |||
43 | #define EISA_TO_PHYS(x) (0x00080000 | (x)) | ||
44 | #define EISA_TO_KSEG1(x) ((void *) KSEG1ADDR(EISA_TO_PHYS((x)))) | ||
45 | |||
46 | #define EIU_MODE_REG 0x0009ffc0 | ||
47 | #define EIU_STAT_REG 0x0009ffc4 | ||
48 | #define EIU_PREMPT_REG 0x0009ffc8 | ||
49 | #define EIU_QUIET_REG 0x0009ffcc | ||
50 | #define EIU_INTRPT_ACK 0x00090004 | ||
51 | |||
52 | #define EISA_DMA1_STATUS 8 | ||
53 | #define EISA_INT1_CTRL 0x20 | ||
54 | #define EISA_INT1_MASK 0x21 | ||
55 | #define EISA_INT2_CTRL 0xA0 | ||
56 | #define EISA_INT2_MASK 0xA1 | ||
57 | #define EISA_DMA2_STATUS 0xD0 | ||
58 | #define EISA_DMA2_WRITE_SINGLE 0xD4 | ||
59 | #define EISA_EXT_NMI_RESET_CTRL 0x461 | ||
60 | #define EISA_INT1_EDGE_LEVEL 0x4D0 | ||
61 | #define EISA_INT2_EDGE_LEVEL 0x4D1 | ||
62 | #define EISA_VENDOR_ID_OFFSET 0xC80 | ||
63 | |||
64 | #define EIU_WRITE_32(x,y) { *((u32 *) KSEG1ADDR(x)) = (u32) (y); mb(); } | ||
65 | #define EIU_READ_8(x) *((u8 *) KSEG1ADDR(x)) | ||
66 | #define EISA_WRITE_8(x,y) { *((u8 *) EISA_TO_KSEG1(x)) = (u8) (y); mb(); } | ||
67 | #define EISA_READ_8(x) *((u8 *) EISA_TO_KSEG1(x)) | ||
68 | |||
69 | static char *decode_eisa_sig(u8 * sig) | ||
70 | { | ||
71 | static char sig_str[8]; | ||
72 | u16 rev; | ||
73 | |||
74 | if (sig[0] & 0x80) | ||
75 | return NULL; | ||
76 | |||
77 | sig_str[0] = ((sig[0] >> 2) & 0x1f) + ('A' - 1); | ||
78 | sig_str[1] = (((sig[0] & 3) << 3) | (sig[1] >> 5)) + ('A' - 1); | ||
79 | sig_str[2] = (sig[1] & 0x1f) + ('A' - 1); | ||
80 | rev = (sig[2] << 8) | sig[3]; | ||
81 | sprintf(sig_str + 3, "%04X", rev); | ||
82 | |||
83 | return sig_str; | ||
84 | } | ||
85 | |||
86 | static void ip22_eisa_intr(int irq, void *dev_id, struct pt_regs *regs) | ||
87 | { | ||
88 | u8 eisa_irq; | ||
89 | u8 dma1, dma2; | ||
90 | |||
91 | eisa_irq = EIU_READ_8(EIU_INTRPT_ACK); | ||
92 | dma1 = EISA_READ_8(EISA_DMA1_STATUS); | ||
93 | dma2 = EISA_READ_8(EISA_DMA2_STATUS); | ||
94 | |||
95 | if (eisa_irq >= EISA_MAX_IRQ) { | ||
96 | /* Oops, Bad Stuff Happened... */ | ||
97 | printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq); | ||
98 | |||
99 | EISA_WRITE_8(EISA_INT2_CTRL, 0x20); | ||
100 | EISA_WRITE_8(EISA_INT1_CTRL, 0x20); | ||
101 | } else | ||
102 | do_IRQ(eisa_irq, regs); | ||
103 | } | ||
104 | |||
105 | static void enable_eisa1_irq(unsigned int irq) | ||
106 | { | ||
107 | unsigned long flags; | ||
108 | u8 mask; | ||
109 | |||
110 | local_irq_save(flags); | ||
111 | |||
112 | mask = EISA_READ_8(EISA_INT1_MASK); | ||
113 | mask &= ~((u8) (1 << irq)); | ||
114 | EISA_WRITE_8(EISA_INT1_MASK, mask); | ||
115 | |||
116 | local_irq_restore(flags); | ||
117 | } | ||
118 | |||
119 | static unsigned int startup_eisa1_irq(unsigned int irq) | ||
120 | { | ||
121 | u8 edge; | ||
122 | |||
123 | /* Only use edge interrupts for EISA */ | ||
124 | |||
125 | edge = EISA_READ_8(EISA_INT1_EDGE_LEVEL); | ||
126 | edge &= ~((u8) (1 << irq)); | ||
127 | EISA_WRITE_8(EISA_INT1_EDGE_LEVEL, edge); | ||
128 | |||
129 | enable_eisa1_irq(irq); | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static void disable_eisa1_irq(unsigned int irq) | ||
134 | { | ||
135 | u8 mask; | ||
136 | |||
137 | mask = EISA_READ_8(EISA_INT1_MASK); | ||
138 | mask |= ((u8) (1 << irq)); | ||
139 | EISA_WRITE_8(EISA_INT1_MASK, mask); | ||
140 | } | ||
141 | |||
142 | #define shutdown_eisa1_irq disable_eisa1_irq | ||
143 | |||
144 | static void mask_and_ack_eisa1_irq(unsigned int irq) | ||
145 | { | ||
146 | disable_eisa1_irq(irq); | ||
147 | |||
148 | EISA_WRITE_8(EISA_INT1_CTRL, 0x20); | ||
149 | } | ||
150 | |||
151 | static void end_eisa1_irq(unsigned int irq) | ||
152 | { | ||
153 | if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) | ||
154 | enable_eisa1_irq(irq); | ||
155 | } | ||
156 | |||
157 | static struct hw_interrupt_type ip22_eisa1_irq_type = { | ||
158 | .typename = "IP22 EISA", | ||
159 | .startup = startup_eisa1_irq, | ||
160 | .shutdown = shutdown_eisa1_irq, | ||
161 | .enable = enable_eisa1_irq, | ||
162 | .disable = disable_eisa1_irq, | ||
163 | .ack = mask_and_ack_eisa1_irq, | ||
164 | .end = end_eisa1_irq, | ||
165 | }; | ||
166 | |||
167 | static void enable_eisa2_irq(unsigned int irq) | ||
168 | { | ||
169 | unsigned long flags; | ||
170 | u8 mask; | ||
171 | |||
172 | local_irq_save(flags); | ||
173 | |||
174 | mask = EISA_READ_8(EISA_INT2_MASK); | ||
175 | mask &= ~((u8) (1 << (irq - 8))); | ||
176 | EISA_WRITE_8(EISA_INT2_MASK, mask); | ||
177 | |||
178 | local_irq_restore(flags); | ||
179 | } | ||
180 | |||
181 | static unsigned int startup_eisa2_irq(unsigned int irq) | ||
182 | { | ||
183 | u8 edge; | ||
184 | |||
185 | /* Only use edge interrupts for EISA */ | ||
186 | |||
187 | edge = EISA_READ_8(EISA_INT2_EDGE_LEVEL); | ||
188 | edge &= ~((u8) (1 << (irq - 8))); | ||
189 | EISA_WRITE_8(EISA_INT2_EDGE_LEVEL, edge); | ||
190 | |||
191 | enable_eisa2_irq(irq); | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static void disable_eisa2_irq(unsigned int irq) | ||
196 | { | ||
197 | u8 mask; | ||
198 | |||
199 | mask = EISA_READ_8(EISA_INT2_MASK); | ||
200 | mask |= ((u8) (1 << (irq - 8))); | ||
201 | EISA_WRITE_8(EISA_INT2_MASK, mask); | ||
202 | } | ||
203 | |||
204 | #define shutdown_eisa2_irq disable_eisa2_irq | ||
205 | |||
206 | static void mask_and_ack_eisa2_irq(unsigned int irq) | ||
207 | { | ||
208 | disable_eisa2_irq(irq); | ||
209 | |||
210 | EISA_WRITE_8(EISA_INT2_CTRL, 0x20); | ||
211 | EISA_WRITE_8(EISA_INT1_CTRL, 0x20); | ||
212 | } | ||
213 | |||
214 | static void end_eisa2_irq(unsigned int irq) | ||
215 | { | ||
216 | if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) | ||
217 | enable_eisa2_irq(irq); | ||
218 | } | ||
219 | |||
220 | static struct hw_interrupt_type ip22_eisa2_irq_type = { | ||
221 | .typename = "IP22 EISA", | ||
222 | .startup = startup_eisa2_irq, | ||
223 | .shutdown = shutdown_eisa2_irq, | ||
224 | .enable = enable_eisa2_irq, | ||
225 | .disable = disable_eisa2_irq, | ||
226 | .ack = mask_and_ack_eisa2_irq, | ||
227 | .end = end_eisa2_irq, | ||
228 | }; | ||
229 | |||
230 | static struct irqaction eisa_action = { | ||
231 | .handler = ip22_eisa_intr, | ||
232 | .name = "EISA", | ||
233 | }; | ||
234 | |||
235 | static struct irqaction cascade_action = { | ||
236 | .handler = no_action, | ||
237 | .name = "EISA cascade", | ||
238 | }; | ||
239 | |||
240 | int __init ip22_eisa_init(void) | ||
241 | { | ||
242 | int i, c; | ||
243 | char *str; | ||
244 | u8 *slot_addr; | ||
245 | |||
246 | if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) { | ||
247 | printk(KERN_INFO "EISA: bus not present.\n"); | ||
248 | return 1; | ||
249 | } | ||
250 | |||
251 | printk(KERN_INFO "EISA: Probing bus...\n"); | ||
252 | for (c = 0, i = 1; i <= EISA_MAX_SLOTS; i++) { | ||
253 | slot_addr = | ||
254 | (u8 *) EISA_TO_KSEG1((0x1000 * i) + | ||
255 | EISA_VENDOR_ID_OFFSET); | ||
256 | if ((str = decode_eisa_sig(slot_addr))) { | ||
257 | printk(KERN_INFO "EISA: slot %d : %s detected.\n", | ||
258 | i, str); | ||
259 | c++; | ||
260 | } | ||
261 | } | ||
262 | printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c < 2 ? "" : "s"); | ||
263 | #ifdef CONFIG_ISA | ||
264 | printk(KERN_INFO "ISA support compiled in.\n"); | ||
265 | #endif | ||
266 | |||
267 | /* Warning : BlackMagicAhead(tm). | ||
268 | Please wave your favorite dead chicken over the busses */ | ||
269 | |||
270 | /* First say hello to the EIU */ | ||
271 | EIU_WRITE_32(EIU_PREMPT_REG, 0x0000FFFF); | ||
272 | EIU_WRITE_32(EIU_QUIET_REG, 1); | ||
273 | EIU_WRITE_32(EIU_MODE_REG, 0x40f3c07F); | ||
274 | |||
275 | /* Now be nice to the EISA chipset */ | ||
276 | EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 1); | ||
277 | for (i = 0; i < 10000; i++); /* Wait long enough for the dust to settle */ | ||
278 | EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 0); | ||
279 | EISA_WRITE_8(EISA_INT1_CTRL, 0x11); | ||
280 | EISA_WRITE_8(EISA_INT2_CTRL, 0x11); | ||
281 | EISA_WRITE_8(EISA_INT1_MASK, 0); | ||
282 | EISA_WRITE_8(EISA_INT2_MASK, 8); | ||
283 | EISA_WRITE_8(EISA_INT1_MASK, 4); | ||
284 | EISA_WRITE_8(EISA_INT2_MASK, 2); | ||
285 | EISA_WRITE_8(EISA_INT1_MASK, 1); | ||
286 | EISA_WRITE_8(EISA_INT2_MASK, 1); | ||
287 | EISA_WRITE_8(EISA_INT1_MASK, 0xfb); | ||
288 | EISA_WRITE_8(EISA_INT2_MASK, 0xff); | ||
289 | EISA_WRITE_8(EISA_DMA2_WRITE_SINGLE, 0); | ||
290 | |||
291 | for (i = SGINT_EISA; i < (SGINT_EISA + EISA_MAX_IRQ); i++) { | ||
292 | irq_desc[i].status = IRQ_DISABLED; | ||
293 | irq_desc[i].action = 0; | ||
294 | irq_desc[i].depth = 1; | ||
295 | if (i < (SGINT_EISA + 8)) | ||
296 | irq_desc[i].handler = &ip22_eisa1_irq_type; | ||
297 | else | ||
298 | irq_desc[i].handler = &ip22_eisa2_irq_type; | ||
299 | } | ||
300 | |||
301 | /* Cannot use request_irq because of kmalloc not being ready at such | ||
302 | * an early stage. Yes, I've been bitten... */ | ||
303 | setup_irq(SGI_EISA_IRQ, &eisa_action); | ||
304 | setup_irq(SGINT_EISA + 2, &cascade_action); | ||
305 | |||
306 | EISA_bus = 1; | ||
307 | return 0; | ||
308 | } | ||