1 /*
2  * arch/ppc/platforms/cpc700_pic.c
3  *
4  * Interrupt controller support for IBM Spruce
5  *
6  * Authors: Mark Greer, Matt Porter, and Johnnie Peters
7  *	    mgreer@mvista.com
8  *          mporter@mvista.com
9  *          jpeters@mvista.com
10  *
11  * 2001-2002 (c) MontaVista, Software, Inc.  This file is licensed under
12  * the terms of the GNU General Public License version 2.  This program
13  * is licensed "as is" without any warranty of any kind, whether express
14  * or implied.
15  */
16 
17 #include <linux/stddef.h>
18 #include <linux/init.h>
19 #include <linux/sched.h>
20 #include <linux/signal.h>
21 #include <linux/irq.h>
22 
23 #include <asm/io.h>
24 #include <asm/processor.h>
25 #include <asm/system.h>
26 #include <asm/irq.h>
27 
28 #include "cpc700.h"
29 
30 static void
cpc700_unmask_irq(unsigned int irq)31 cpc700_unmask_irq(unsigned int irq)
32 {
33 	unsigned int tr_bits;
34 
35 	/*
36 	 * IRQ 31 is largest IRQ supported.
37 	 * IRQs 17-19 are reserved.
38 	 */
39 	if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
40 		tr_bits = CPC700_IN_32(CPC700_UIC_UICTR);
41 
42 		if ((tr_bits & (1 << (31 - irq))) == 0) {
43 			/* level trigger interrupt, clear bit in status
44 			 * register */
45 			CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq));
46 		}
47 
48 		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
49 		ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq);
50 
51 		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
52 	}
53 	return;
54 }
55 
56 static void
cpc700_mask_irq(unsigned int irq)57 cpc700_mask_irq(unsigned int irq)
58 {
59 	/*
60 	 * IRQ 31 is largest IRQ supported.
61 	 * IRQs 17-19 are reserved.
62 	 */
63 	if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
64 		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
65 		ppc_cached_irq_mask[0] &=
66 			~CPC700_UIC_IRQ_BIT(irq);
67 
68 		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
69 	}
70 	return;
71 }
72 
73 static void
cpc700_mask_and_ack_irq(unsigned int irq)74 cpc700_mask_and_ack_irq(unsigned int irq)
75 {
76 	u_int	bit;
77 
78 	/*
79 	 * IRQ 31 is largest IRQ supported.
80 	 * IRQs 17-19 are reserved.
81 	 */
82 	if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
83 		/* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
84 		bit = CPC700_UIC_IRQ_BIT(irq);
85 
86 		ppc_cached_irq_mask[0] &= ~bit;
87 		CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
88 		CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */
89 	}
90 	return;
91 }
92 
93 static struct hw_interrupt_type cpc700_pic = {
94 	"CPC700 PIC",
95 	NULL,
96 	NULL,
97 	cpc700_unmask_irq,
98 	cpc700_mask_irq,
99 	cpc700_mask_and_ack_irq,
100 	NULL,
101 	NULL
102 };
103 
104 __init static void
cpc700_pic_init_irq(unsigned int irq)105 cpc700_pic_init_irq(unsigned int irq)
106 {
107 	unsigned int tmp;
108 
109 	/* Set interrupt sense */
110 	tmp = CPC700_IN_32(CPC700_UIC_UICTR);
111 	if (cpc700_irq_assigns[irq][0] == 0) {
112 		tmp &= ~CPC700_UIC_IRQ_BIT(irq);
113 	} else {
114 		tmp |= CPC700_UIC_IRQ_BIT(irq);
115 	}
116 	CPC700_OUT_32(CPC700_UIC_UICTR, tmp);
117 
118 	/* Set interrupt polarity */
119 	tmp = CPC700_IN_32(CPC700_UIC_UICPR);
120 	if (cpc700_irq_assigns[irq][1]) {
121 		tmp |= CPC700_UIC_IRQ_BIT(irq);
122 	} else {
123 		tmp &= ~CPC700_UIC_IRQ_BIT(irq);
124 	}
125 	CPC700_OUT_32(CPC700_UIC_UICPR, tmp);
126 
127 	/* Set interrupt critical */
128 	tmp = CPC700_IN_32(CPC700_UIC_UICCR);
129 	tmp |= CPC700_UIC_IRQ_BIT(irq);
130 	CPC700_OUT_32(CPC700_UIC_UICCR, tmp);
131 
132 	return;
133 }
134 
135 __init void
cpc700_init_IRQ(void)136 cpc700_init_IRQ(void)
137 {
138 	int i;
139 
140 	ppc_cached_irq_mask[0] = 0;
141 	CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000);    /* Disable all irq's */
142 	CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff);    /* Clear cur intrs */
143 	CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff);    /* Gen INT not MCP */
144 	CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000);    /* Active low */
145 	CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000);    /* Level Sensitive */
146 	CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI);
147 						        /* IRQ 0 is highest */
148 
149 	for (i = 0; i < 17; i++) {
150 		irq_desc[i].handler = &cpc700_pic;
151 		cpc700_pic_init_irq(i);
152 	}
153 
154 	for (i = 20; i < 32; i++) {
155 		irq_desc[i].handler = &cpc700_pic;
156 		cpc700_pic_init_irq(i);
157 	}
158 
159 	return;
160 }
161 
162 
163 
164 /*
165  * Find the highest IRQ that generating an interrupt, if any.
166  */
167 int
cpc700_get_irq(struct pt_regs * regs)168 cpc700_get_irq(struct pt_regs *regs)
169 {
170 	int irq = 0;
171 	u_int irq_status, irq_test = 1;
172 
173 	irq_status = CPC700_IN_32(CPC700_UIC_UICMSR);
174 
175 	do
176 	{
177 		if (irq_status & irq_test)
178 			break;
179 		irq++;
180 		irq_test <<= 1;
181 	} while (irq < NR_IRQS);
182 
183 
184 	if (irq == NR_IRQS)
185 	    irq = 33;
186 
187 	return (31 - irq);
188 }
189