xref: /OK3568_Linux_fs/kernel/arch/mips/sgi-ip22/ip22-int.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC
4*4882a593Smuzhiyun  *	       found on INDY and Indigo2 workstations.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
7*4882a593Smuzhiyun  * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
8*4882a593Smuzhiyun  * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu)
9*4882a593Smuzhiyun  *		      - Indigo2 changes
10*4882a593Smuzhiyun  *		      - Interrupt handling fixes
11*4882a593Smuzhiyun  * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org)
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun #include <linux/types.h>
14*4882a593Smuzhiyun #include <linux/init.h>
15*4882a593Smuzhiyun #include <linux/kernel_stat.h>
16*4882a593Smuzhiyun #include <linux/interrupt.h>
17*4882a593Smuzhiyun #include <linux/ftrace.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include <asm/irq_cpu.h>
20*4882a593Smuzhiyun #include <asm/sgi/hpc3.h>
21*4882a593Smuzhiyun #include <asm/sgi/ip22.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /* So far nothing hangs here */
24*4882a593Smuzhiyun #undef USE_LIO3_IRQ
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun struct sgint_regs *sgint;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun static char lc0msk_to_irqnr[256];
29*4882a593Smuzhiyun static char lc1msk_to_irqnr[256];
30*4882a593Smuzhiyun static char lc2msk_to_irqnr[256];
31*4882a593Smuzhiyun static char lc3msk_to_irqnr[256];
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun extern int ip22_eisa_init(void);
34*4882a593Smuzhiyun 
enable_local0_irq(struct irq_data * d)35*4882a593Smuzhiyun static void enable_local0_irq(struct irq_data *d)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun 	/* don't allow mappable interrupt to be enabled from setup_irq,
38*4882a593Smuzhiyun 	 * we have our own way to do so */
39*4882a593Smuzhiyun 	if (d->irq != SGI_MAP_0_IRQ)
40*4882a593Smuzhiyun 		sgint->imask0 |= (1 << (d->irq - SGINT_LOCAL0));
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun 
disable_local0_irq(struct irq_data * d)43*4882a593Smuzhiyun static void disable_local0_irq(struct irq_data *d)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun 	sgint->imask0 &= ~(1 << (d->irq - SGINT_LOCAL0));
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun static struct irq_chip ip22_local0_irq_type = {
49*4882a593Smuzhiyun 	.name		= "IP22 local 0",
50*4882a593Smuzhiyun 	.irq_mask	= disable_local0_irq,
51*4882a593Smuzhiyun 	.irq_unmask	= enable_local0_irq,
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun 
enable_local1_irq(struct irq_data * d)54*4882a593Smuzhiyun static void enable_local1_irq(struct irq_data *d)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun 	/* don't allow mappable interrupt to be enabled from setup_irq,
57*4882a593Smuzhiyun 	 * we have our own way to do so */
58*4882a593Smuzhiyun 	if (d->irq != SGI_MAP_1_IRQ)
59*4882a593Smuzhiyun 		sgint->imask1 |= (1 << (d->irq - SGINT_LOCAL1));
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
disable_local1_irq(struct irq_data * d)62*4882a593Smuzhiyun static void disable_local1_irq(struct irq_data *d)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun 	sgint->imask1 &= ~(1 << (d->irq - SGINT_LOCAL1));
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun static struct irq_chip ip22_local1_irq_type = {
68*4882a593Smuzhiyun 	.name		= "IP22 local 1",
69*4882a593Smuzhiyun 	.irq_mask	= disable_local1_irq,
70*4882a593Smuzhiyun 	.irq_unmask	= enable_local1_irq,
71*4882a593Smuzhiyun };
72*4882a593Smuzhiyun 
enable_local2_irq(struct irq_data * d)73*4882a593Smuzhiyun static void enable_local2_irq(struct irq_data *d)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun 	sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
76*4882a593Smuzhiyun 	sgint->cmeimask0 |= (1 << (d->irq - SGINT_LOCAL2));
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
disable_local2_irq(struct irq_data * d)79*4882a593Smuzhiyun static void disable_local2_irq(struct irq_data *d)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun 	sgint->cmeimask0 &= ~(1 << (d->irq - SGINT_LOCAL2));
82*4882a593Smuzhiyun 	if (!sgint->cmeimask0)
83*4882a593Smuzhiyun 		sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun static struct irq_chip ip22_local2_irq_type = {
87*4882a593Smuzhiyun 	.name		= "IP22 local 2",
88*4882a593Smuzhiyun 	.irq_mask	= disable_local2_irq,
89*4882a593Smuzhiyun 	.irq_unmask	= enable_local2_irq,
90*4882a593Smuzhiyun };
91*4882a593Smuzhiyun 
enable_local3_irq(struct irq_data * d)92*4882a593Smuzhiyun static void enable_local3_irq(struct irq_data *d)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
95*4882a593Smuzhiyun 	sgint->cmeimask1 |= (1 << (d->irq - SGINT_LOCAL3));
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
disable_local3_irq(struct irq_data * d)98*4882a593Smuzhiyun static void disable_local3_irq(struct irq_data *d)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun 	sgint->cmeimask1 &= ~(1 << (d->irq - SGINT_LOCAL3));
101*4882a593Smuzhiyun 	if (!sgint->cmeimask1)
102*4882a593Smuzhiyun 		sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun static struct irq_chip ip22_local3_irq_type = {
106*4882a593Smuzhiyun 	.name		= "IP22 local 3",
107*4882a593Smuzhiyun 	.irq_mask	= disable_local3_irq,
108*4882a593Smuzhiyun 	.irq_unmask	= enable_local3_irq,
109*4882a593Smuzhiyun };
110*4882a593Smuzhiyun 
indy_local0_irqdispatch(void)111*4882a593Smuzhiyun static void indy_local0_irqdispatch(void)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	u8 mask = sgint->istat0 & sgint->imask0;
114*4882a593Smuzhiyun 	u8 mask2;
115*4882a593Smuzhiyun 	int irq;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	if (mask & SGINT_ISTAT0_LIO2) {
118*4882a593Smuzhiyun 		mask2 = sgint->vmeistat & sgint->cmeimask0;
119*4882a593Smuzhiyun 		irq = lc2msk_to_irqnr[mask2];
120*4882a593Smuzhiyun 	} else
121*4882a593Smuzhiyun 		irq = lc0msk_to_irqnr[mask];
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	/*
124*4882a593Smuzhiyun 	 * workaround for INT2 bug; if irq == 0, INT2 has seen a fifo full
125*4882a593Smuzhiyun 	 * irq, but failed to latch it into status register
126*4882a593Smuzhiyun 	 */
127*4882a593Smuzhiyun 	if (irq)
128*4882a593Smuzhiyun 		do_IRQ(irq);
129*4882a593Smuzhiyun 	else
130*4882a593Smuzhiyun 		do_IRQ(SGINT_LOCAL0 + 0);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
indy_local1_irqdispatch(void)133*4882a593Smuzhiyun static void indy_local1_irqdispatch(void)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	u8 mask = sgint->istat1 & sgint->imask1;
136*4882a593Smuzhiyun 	u8 mask2;
137*4882a593Smuzhiyun 	int irq;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	if (mask & SGINT_ISTAT1_LIO3) {
140*4882a593Smuzhiyun 		mask2 = sgint->vmeistat & sgint->cmeimask1;
141*4882a593Smuzhiyun 		irq = lc3msk_to_irqnr[mask2];
142*4882a593Smuzhiyun 	} else
143*4882a593Smuzhiyun 		irq = lc1msk_to_irqnr[mask];
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	/* if irq == 0, then the interrupt has already been cleared */
146*4882a593Smuzhiyun 	if (irq)
147*4882a593Smuzhiyun 		do_IRQ(irq);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun extern void ip22_be_interrupt(int irq);
151*4882a593Smuzhiyun 
indy_buserror_irq(void)152*4882a593Smuzhiyun static void __irq_entry indy_buserror_irq(void)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	int irq = SGI_BUSERR_IRQ;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	irq_enter();
157*4882a593Smuzhiyun 	kstat_incr_irq_this_cpu(irq);
158*4882a593Smuzhiyun 	ip22_be_interrupt(irq);
159*4882a593Smuzhiyun 	irq_exit();
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun #ifdef USE_LIO3_IRQ
163*4882a593Smuzhiyun #define SGI_INTERRUPTS	SGINT_END
164*4882a593Smuzhiyun #else
165*4882a593Smuzhiyun #define SGI_INTERRUPTS	SGINT_LOCAL3
166*4882a593Smuzhiyun #endif
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun extern void indy_8254timer_irq(void);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun /*
171*4882a593Smuzhiyun  * IRQs on the INDY look basically (barring software IRQs which we don't use
172*4882a593Smuzhiyun  * at all) like:
173*4882a593Smuzhiyun  *
174*4882a593Smuzhiyun  *	MIPS IRQ	Source
175*4882a593Smuzhiyun  *	--------	------
176*4882a593Smuzhiyun  *	       0	Software (ignored)
177*4882a593Smuzhiyun  *	       1	Software (ignored)
178*4882a593Smuzhiyun  *	       2	Local IRQ level zero
179*4882a593Smuzhiyun  *	       3	Local IRQ level one
180*4882a593Smuzhiyun  *	       4	8254 Timer zero
181*4882a593Smuzhiyun  *	       5	8254 Timer one
182*4882a593Smuzhiyun  *	       6	Bus Error
183*4882a593Smuzhiyun  *	       7	R4k timer (what we use)
184*4882a593Smuzhiyun  *
185*4882a593Smuzhiyun  * We handle the IRQ according to _our_ priority which is:
186*4882a593Smuzhiyun  *
187*4882a593Smuzhiyun  * Highest ----	    R4k Timer
188*4882a593Smuzhiyun  *		    Local IRQ zero
189*4882a593Smuzhiyun  *		    Local IRQ one
190*4882a593Smuzhiyun  *		    Bus Error
191*4882a593Smuzhiyun  *		    8254 Timer zero
192*4882a593Smuzhiyun  * Lowest  ----	    8254 Timer one
193*4882a593Smuzhiyun  *
194*4882a593Smuzhiyun  * then we just return, if multiple IRQs are pending then we will just take
195*4882a593Smuzhiyun  * another exception, big deal.
196*4882a593Smuzhiyun  */
197*4882a593Smuzhiyun 
plat_irq_dispatch(void)198*4882a593Smuzhiyun asmlinkage void plat_irq_dispatch(void)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	unsigned int pending = read_c0_status() & read_c0_cause();
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	/*
203*4882a593Smuzhiyun 	 * First we check for r4k counter/timer IRQ.
204*4882a593Smuzhiyun 	 */
205*4882a593Smuzhiyun 	if (pending & CAUSEF_IP7)
206*4882a593Smuzhiyun 		do_IRQ(SGI_TIMER_IRQ);
207*4882a593Smuzhiyun 	else if (pending & CAUSEF_IP2)
208*4882a593Smuzhiyun 		indy_local0_irqdispatch();
209*4882a593Smuzhiyun 	else if (pending & CAUSEF_IP3)
210*4882a593Smuzhiyun 		indy_local1_irqdispatch();
211*4882a593Smuzhiyun 	else if (pending & CAUSEF_IP6)
212*4882a593Smuzhiyun 		indy_buserror_irq();
213*4882a593Smuzhiyun 	else if (pending & (CAUSEF_IP4 | CAUSEF_IP5))
214*4882a593Smuzhiyun 		indy_8254timer_irq();
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun 
arch_init_irq(void)217*4882a593Smuzhiyun void __init arch_init_irq(void)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun 	int i;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	/* Init local mask --> irq tables. */
222*4882a593Smuzhiyun 	for (i = 0; i < 256; i++) {
223*4882a593Smuzhiyun 		if (i & 0x80) {
224*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7;
225*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7;
226*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7;
227*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7;
228*4882a593Smuzhiyun 		} else if (i & 0x40) {
229*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6;
230*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6;
231*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6;
232*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6;
233*4882a593Smuzhiyun 		} else if (i & 0x20) {
234*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5;
235*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5;
236*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5;
237*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5;
238*4882a593Smuzhiyun 		} else if (i & 0x10) {
239*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4;
240*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4;
241*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4;
242*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4;
243*4882a593Smuzhiyun 		} else if (i & 0x08) {
244*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3;
245*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3;
246*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3;
247*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3;
248*4882a593Smuzhiyun 		} else if (i & 0x04) {
249*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2;
250*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2;
251*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2;
252*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2;
253*4882a593Smuzhiyun 		} else if (i & 0x02) {
254*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1;
255*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1;
256*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1;
257*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1;
258*4882a593Smuzhiyun 		} else if (i & 0x01) {
259*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0;
260*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0;
261*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0;
262*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0;
263*4882a593Smuzhiyun 		} else {
264*4882a593Smuzhiyun 			lc0msk_to_irqnr[i] = 0;
265*4882a593Smuzhiyun 			lc1msk_to_irqnr[i] = 0;
266*4882a593Smuzhiyun 			lc2msk_to_irqnr[i] = 0;
267*4882a593Smuzhiyun 			lc3msk_to_irqnr[i] = 0;
268*4882a593Smuzhiyun 		}
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	/* Mask out all interrupts. */
272*4882a593Smuzhiyun 	sgint->imask0 = 0;
273*4882a593Smuzhiyun 	sgint->imask1 = 0;
274*4882a593Smuzhiyun 	sgint->cmeimask0 = 0;
275*4882a593Smuzhiyun 	sgint->cmeimask1 = 0;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	/* init CPU irqs */
278*4882a593Smuzhiyun 	mips_cpu_irq_init();
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) {
281*4882a593Smuzhiyun 		struct irq_chip *handler;
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 		if (i < SGINT_LOCAL1)
284*4882a593Smuzhiyun 			handler		= &ip22_local0_irq_type;
285*4882a593Smuzhiyun 		else if (i < SGINT_LOCAL2)
286*4882a593Smuzhiyun 			handler		= &ip22_local1_irq_type;
287*4882a593Smuzhiyun 		else if (i < SGINT_LOCAL3)
288*4882a593Smuzhiyun 			handler		= &ip22_local2_irq_type;
289*4882a593Smuzhiyun 		else
290*4882a593Smuzhiyun 			handler		= &ip22_local3_irq_type;
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 		irq_set_chip_and_handler(i, handler, handle_level_irq);
293*4882a593Smuzhiyun 	}
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	/* vector handler. this register the IRQ as non-sharable */
296*4882a593Smuzhiyun 	if (request_irq(SGI_LOCAL_0_IRQ, no_action, IRQF_NO_THREAD,
297*4882a593Smuzhiyun 			"local0 cascade", NULL))
298*4882a593Smuzhiyun 		pr_err("Failed to register local0 cascade interrupt\n");
299*4882a593Smuzhiyun 	if (request_irq(SGI_LOCAL_1_IRQ, no_action, IRQF_NO_THREAD,
300*4882a593Smuzhiyun 			"local1 cascade", NULL))
301*4882a593Smuzhiyun 		pr_err("Failed to register local1 cascade interrupt\n");
302*4882a593Smuzhiyun 	if (request_irq(SGI_BUSERR_IRQ, no_action, IRQF_NO_THREAD,
303*4882a593Smuzhiyun 			"Bus Error", NULL))
304*4882a593Smuzhiyun 		pr_err("Failed to register Bus Error interrupt\n");
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	/* cascade in cascade. i love Indy ;-) */
307*4882a593Smuzhiyun 	if (request_irq(SGI_MAP_0_IRQ, no_action, IRQF_NO_THREAD,
308*4882a593Smuzhiyun 			"mapable0 cascade", NULL))
309*4882a593Smuzhiyun 		pr_err("Failed to register mapable0 cascade interrupt\n");
310*4882a593Smuzhiyun #ifdef USE_LIO3_IRQ
311*4882a593Smuzhiyun 	if (request_irq(SGI_MAP_1_IRQ, no_action, IRQF_NO_THREAD,
312*4882a593Smuzhiyun 			"mapable1 cascade", NULL))
313*4882a593Smuzhiyun 		pr_err("Failed to register mapable1 cascade interrupt\n");
314*4882a593Smuzhiyun #endif
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun #ifdef CONFIG_EISA
317*4882a593Smuzhiyun 	if (ip22_is_fullhouse())	/* Only Indigo-2 has EISA stuff */
318*4882a593Smuzhiyun 		ip22_eisa_init();
319*4882a593Smuzhiyun #endif
320*4882a593Smuzhiyun }
321