1 /*
2  *  arch/mips/philips/nino/irq.c
3  *
4  *  Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com)
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  *  Interrupt service routines for Philips Nino
11  */
12 #include <linux/config.h>
13 #include <linux/init.h>
14 #include <linux/irq.h>
15 #include <linux/sched.h>
16 #include <linux/interrupt.h>
17 #include <asm/io.h>
18 #include <asm/mipsregs.h>
19 #include <asm/tx3912.h>
20 
21 #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
22 
enable_irq6(unsigned int irq)23 static void enable_irq6(unsigned int irq)
24 {
25 	if(irq == 0) {
26 		outl(inl(TX3912_INT6_ENABLE) |
27 			TX3912_INT6_ENABLE_PRIORITYMASK_PERINT,
28 			TX3912_INT6_ENABLE);
29 		outl(inl(TX3912_INT5_ENABLE) | TX3912_INT5_PERINT,
30 			TX3912_INT5_ENABLE);
31 	}
32 	if(irq == 3) {
33 		outl(inl(TX3912_INT6_ENABLE) |
34 			TX3912_INT6_ENABLE_PRIORITYMASK_UARTARXINT,
35 			TX3912_INT6_ENABLE);
36 		outl(inl(TX3912_INT2_ENABLE) | TX3912_INT2_UARTA_RX_BITS,
37 			TX3912_INT2_ENABLE);
38 	}
39 }
40 
startup_irq6(unsigned int irq)41 static unsigned int startup_irq6(unsigned int irq)
42 {
43 	enable_irq6(irq);
44 
45 	return 0;		/* Never anything pending  */
46 }
47 
disable_irq6(unsigned int irq)48 static void disable_irq6(unsigned int irq)
49 {
50 	if(irq == 0) {
51 		outl(inl(TX3912_INT6_ENABLE) &
52 			~TX3912_INT6_ENABLE_PRIORITYMASK_PERINT,
53 			TX3912_INT6_ENABLE);
54 		outl(inl(TX3912_INT5_ENABLE) & ~TX3912_INT5_PERINT,
55 			TX3912_INT5_ENABLE);
56 		outl(inl(TX3912_INT5_CLEAR) | TX3912_INT5_PERINT,
57 			TX3912_INT5_CLEAR);
58 	}
59 	if(irq == 3) {
60 		outl(inl(TX3912_INT6_ENABLE) &
61 			~TX3912_INT6_ENABLE_PRIORITYMASK_UARTARXINT,
62 			TX3912_INT6_ENABLE);
63 		outl(inl(TX3912_INT2_ENABLE) & ~TX3912_INT2_UARTA_RX_BITS,
64 			TX3912_INT2_ENABLE);
65 	}
66 }
67 
68 #define shutdown_irq6		disable_irq6
69 #define mask_and_ack_irq6	disable_irq6
70 
end_irq6(unsigned int irq)71 static void end_irq6(unsigned int irq)
72 {
73 	if(!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
74 		enable_irq6(irq);
75 }
76 
77 static struct hw_interrupt_type irq6_type = {
78 	"MIPS",
79 	startup_irq6,
80 	shutdown_irq6,
81 	enable_irq6,
82 	disable_irq6,
83 	mask_and_ack_irq6,
84 	end_irq6,
85 	NULL
86 };
87 
irq6_dispatch(struct pt_regs * regs)88 void irq6_dispatch(struct pt_regs *regs)
89 {
90 	int irq = -1;
91 
92 	if((inl(TX3912_INT6_STATUS) & TX3912_INT6_STATUS_INTVEC_UARTARXINT) ==
93 		TX3912_INT6_STATUS_INTVEC_UARTARXINT) {
94 		irq = 3;
95 		goto done;
96 	}
97 	if ((inl(TX3912_INT6_STATUS) & TX3912_INT6_STATUS_INTVEC_PERINT) ==
98 		TX3912_INT6_STATUS_INTVEC_PERINT) {
99 		irq = 0;
100 		goto done;
101 	}
102 
103 	/* if irq == -1, then interrupt was cleared or is invalid */
104 	if (irq == -1) {
105 		panic("Unhandled High Priority PR31700 Interrupt = 0x%08x",
106 			inl(TX3912_INT6_STATUS));
107 	}
108 
109 done:
110 	do_IRQ(irq, regs);
111 }
112 
enable_irq4(unsigned int irq)113 static void enable_irq4(unsigned int irq)
114 {
115 	set_c0_status(STATUSF_IP4);
116 	if (irq == 2) {
117 		outl(inl(TX3912_INT2_CLEAR) | TX3912_INT2_UARTA_TX_BITS,
118 			TX3912_INT2_CLEAR);
119 		outl(inl(TX3912_INT2_ENABLE) | TX3912_INT2_UARTA_TX_BITS,
120 			TX3912_INT2_ENABLE);
121 	}
122 }
123 
startup_irq4(unsigned int irq)124 static unsigned int startup_irq4(unsigned int irq)
125 {
126 	enable_irq4(irq);
127 
128 	return 0;		/* Never anything pending  */
129 }
130 
disable_irq4(unsigned int irq)131 static void disable_irq4(unsigned int irq)
132 {
133 	clear_c0_status(STATUSF_IP4);
134 }
135 
136 #define shutdown_irq4		disable_irq4
137 #define mask_and_ack_irq4	disable_irq4
138 
end_irq4(unsigned int irq)139 static void end_irq4(unsigned int irq)
140 {
141 	if(!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
142 		enable_irq4(irq);
143 }
144 
145 static struct hw_interrupt_type irq4_type = {
146 	"MIPS",
147 	startup_irq4,
148 	shutdown_irq4,
149 	enable_irq4,
150 	disable_irq4,
151 	mask_and_ack_irq4,
152 	end_irq4,
153 	NULL
154 };
155 
irq4_dispatch(struct pt_regs * regs)156 void irq4_dispatch(struct pt_regs *regs)
157 {
158 	int irq = -1;
159 
160 	if(inl(TX3912_INT2_STATUS) & TX3912_INT2_UARTA_TX_BITS) {
161 		irq = 2;
162 		goto done;
163 	}
164 
165 	/* if irq == -1, then interrupt was cleared or is invalid */
166 	if (irq == -1) {
167 		printk("PR31700 Interrupt Status Register 1 = 0x%08x\n",
168 			inl(TX3912_INT1_STATUS));
169 		printk("PR31700 Interrupt Status Register 2 = 0x%08x\n",
170 			inl(TX3912_INT2_STATUS));
171 		printk("PR31700 Interrupt Status Register 3 = 0x%08x\n",
172 			inl(TX3912_INT3_STATUS));
173 		printk("PR31700 Interrupt Status Register 4 = 0x%08x\n",
174 			inl(TX3912_INT4_STATUS));
175 		printk("PR31700 Interrupt Status Register 5 = 0x%08x\n",
176 			inl(TX3912_INT5_STATUS));
177 		panic("Unhandled Low Priority PR31700 Interrupt");
178 	}
179 
180 done:
181 	do_IRQ(irq, regs);
182 	return;
183 }
184 
irq_bad(struct pt_regs * regs)185 void irq_bad(struct pt_regs *regs)
186 {
187 	/* This should never happen */
188 	printk(" CAUSE register = 0x%08lx\n", regs->cp0_cause);
189 	printk("STATUS register = 0x%08lx\n", regs->cp0_status);
190 	printk("   EPC register = 0x%08lx\n", regs->cp0_epc);
191 	panic("Stray interrupt, spinning...");
192 }
193 
nino_irq_setup(void)194 void __init nino_irq_setup(void)
195 {
196 	extern asmlinkage void ninoIRQ(void);
197 
198 	unsigned int i;
199 
200 	/* Disable all hardware interrupts */
201 	change_c0_status(ST0_IM, 0x00);
202 
203 	/* Clear interrupts */
204 	outl(0xffffffff, TX3912_INT1_CLEAR);
205 	outl(0xffffffff, TX3912_INT2_CLEAR);
206 	outl(0xffffffff, TX3912_INT3_CLEAR);
207 	outl(0xffffffff, TX3912_INT4_CLEAR);
208 	outl(0xffffffff, TX3912_INT5_CLEAR);
209 
210 	/*
211 	 * Disable all PR31700 interrupts. We let the various
212 	 * device drivers in the system register themselves
213 	 * and set the proper hardware bits.
214 	 */
215 	outl(0x00000000, TX3912_INT1_ENABLE);
216 	outl(0x00000000, TX3912_INT2_ENABLE);
217 	outl(0x00000000, TX3912_INT3_ENABLE);
218 	outl(0x00000000, TX3912_INT4_ENABLE);
219 	outl(0x00000000, TX3912_INT5_ENABLE);
220 
221 	/* Initialize IRQ vector table */
222 	init_generic_irq();
223 
224 	/* Initialize IRQ action handlers */
225 	for (i = 0; i < 16; i++) {
226 		hw_irq_controller *handler = NULL;
227 		if (i == 0 || i == 3)
228 			handler		= &irq6_type;
229 		else
230 			handler		= &irq4_type;
231 
232 		irq_desc[i].status	= IRQ_DISABLED;
233 		irq_desc[i].action	= 0;
234 		irq_desc[i].depth	= 1;
235 		irq_desc[i].handler	= handler;
236 	}
237 
238 	/* Set up the external interrupt exception vector */
239 	set_except_vector(0, ninoIRQ);
240 
241 	/* Enable high priority interrupts */
242 	outl(TX3912_INT6_ENABLE_GLOBALEN | TX3912_INT6_ENABLE_HIGH_PRIORITY,
243 		TX3912_INT6_ENABLE);
244 
245 	/* Enable all interrupts */
246 	change_c0_status(ST0_IM, ALLINTS);
247 }
248 
249 void (*irq_setup)(void);
250 
init_IRQ(void)251 void __init init_IRQ(void)
252 {
253 #ifdef CONFIG_KGDB
254 	extern void breakpoint(void);
255 	extern void set_debug_traps(void);
256 
257 	printk("Wait for gdb client connection ...\n");
258 	set_debug_traps();
259 	breakpoint();
260 #endif
261 
262 	/* Invoke board-specific irq setup */
263 	irq_setup();
264 }
265