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