Commit | Line | Data |
---|---|---|
1da177e4 LT |
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; | |
42a3b4f2 | 245 | |
1da177e4 LT |
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 | } |