1/*
2 * Programmable Interrupt Controller functions for the Freescale MPC52xx
3 * embedded CPU.
4 *
5 *
6 * Maintainer : Sylvain Munaut <tnt@246tNt.com>
7 *
8 * Based on (well, mostly copied from) the code from the 2.4 kernel by
9 * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg.
10 *
11 * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
12 * Copyright (C) 2003 Montavista Software, Inc
13 *
14 * This file is licensed under the terms of the GNU General Public License
15 * version 2. This program is licensed "as is" without any warranty of any
16 * kind, whether express or implied.
17 */
18
19#include <linux/stddef.h>
20#include <linux/init.h>
21#include <linux/sched.h>
22#include <linux/signal.h>
23#include <linux/stddef.h>
24#include <linux/delay.h>
25#include <linux/irq.h>
26
27#include <asm/io.h>
28#include <asm/processor.h>
29#include <asm/system.h>
30#include <asm/irq.h>
31#include <asm/mpc52xx.h>
32
33
34static struct mpc52xx_intr __iomem *intr;
35static struct mpc52xx_sdma __iomem *sdma;
36
37static void
38mpc52xx_ic_disable(unsigned int irq)
39{
40	u32 val;
41
42	if (irq == MPC52xx_IRQ0) {
43		val = in_be32(&intr->ctrl);
44		val &= ~(1 << 11);
45		out_be32(&intr->ctrl, val);
46	}
47	else if (irq < MPC52xx_IRQ1) {
48		BUG();
49	}
50	else if (irq <= MPC52xx_IRQ3) {
51		val = in_be32(&intr->ctrl);
52		val &= ~(1 << (10 - (irq - MPC52xx_IRQ1)));
53		out_be32(&intr->ctrl, val);
54	}
55	else if (irq < MPC52xx_SDMA_IRQ_BASE) {
56		val = in_be32(&intr->main_mask);
57		val |= 1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE));
58		out_be32(&intr->main_mask, val);
59	}
60	else if (irq < MPC52xx_PERP_IRQ_BASE) {
61		val = in_be32(&sdma->IntMask);
62		val |= 1 << (irq - MPC52xx_SDMA_IRQ_BASE);
63		out_be32(&sdma->IntMask, val);
64	}
65	else {
66		val = in_be32(&intr->per_mask);
67		val |= 1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE));
68		out_be32(&intr->per_mask, val);
69	}
70}
71
72static void
73mpc52xx_ic_enable(unsigned int irq)
74{
75	u32 val;
76
77	if (irq == MPC52xx_IRQ0) {
78		val = in_be32(&intr->ctrl);
79		val |= 1 << 11;
80		out_be32(&intr->ctrl, val);
81	}
82	else if (irq < MPC52xx_IRQ1) {
83		BUG();
84	}
85	else if (irq <= MPC52xx_IRQ3) {
86		val = in_be32(&intr->ctrl);
87		val |= 1 << (10 - (irq - MPC52xx_IRQ1));
88		out_be32(&intr->ctrl, val);
89	}
90	else if (irq < MPC52xx_SDMA_IRQ_BASE) {
91		val = in_be32(&intr->main_mask);
92		val &= ~(1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE)));
93		out_be32(&intr->main_mask, val);
94	}
95	else if (irq < MPC52xx_PERP_IRQ_BASE) {
96		val = in_be32(&sdma->IntMask);
97		val &= ~(1 << (irq - MPC52xx_SDMA_IRQ_BASE));
98		out_be32(&sdma->IntMask, val);
99	}
100	else {
101		val = in_be32(&intr->per_mask);
102		val &= ~(1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE)));
103		out_be32(&intr->per_mask, val);
104	}
105}
106
107static void
108mpc52xx_ic_ack(unsigned int irq)
109{
110	u32 val;
111
112	/*
113	 * Only some irqs are reset here, others in interrupting hardware.
114	 */
115
116	switch (irq) {
117	case MPC52xx_IRQ0:
118		val = in_be32(&intr->ctrl);
119		val |= 0x08000000;
120		out_be32(&intr->ctrl, val);
121		break;
122	case MPC52xx_CCS_IRQ:
123		val = in_be32(&intr->enc_status);
124		val |= 0x00000400;
125		out_be32(&intr->enc_status, val);
126		break;
127	case MPC52xx_IRQ1:
128		val = in_be32(&intr->ctrl);
129		val |= 0x04000000;
130		out_be32(&intr->ctrl, val);
131		break;
132	case MPC52xx_IRQ2:
133		val = in_be32(&intr->ctrl);
134		val |= 0x02000000;
135		out_be32(&intr->ctrl, val);
136		break;
137	case MPC52xx_IRQ3:
138		val = in_be32(&intr->ctrl);
139		val |= 0x01000000;
140		out_be32(&intr->ctrl, val);
141		break;
142	default:
143		if (irq >= MPC52xx_SDMA_IRQ_BASE
144		    && irq < (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)) {
145			out_be32(&sdma->IntPend,
146				 1 << (irq - MPC52xx_SDMA_IRQ_BASE));
147		}
148		break;
149	}
150}
151
152static void
153mpc52xx_ic_disable_and_ack(unsigned int irq)
154{
155	mpc52xx_ic_disable(irq);
156	mpc52xx_ic_ack(irq);
157}
158
159static void
160mpc52xx_ic_end(unsigned int irq)
161{
162	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
163		mpc52xx_ic_enable(irq);
164}
165
166static struct hw_interrupt_type mpc52xx_ic = {
167	.typename	= " MPC52xx  ",
168	.enable		= mpc52xx_ic_enable,
169	.disable	= mpc52xx_ic_disable,
170	.ack		= mpc52xx_ic_disable_and_ack,
171	.end		= mpc52xx_ic_end,
172};
173
174void __init
175mpc52xx_init_irq(void)
176{
177	int i;
178	u32 intr_ctrl;
179
180	/* Remap the necessary zones */
181	intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE);
182	sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE);
183
184	if ((intr==NULL) || (sdma==NULL))
185		panic("Can't ioremap PIC/SDMA register for init_irq !");
186
187	/* Disable all interrupt sources. */
188	out_be32(&sdma->IntPend, 0xffffffff);	/* 1 means clear pending */
189	out_be32(&sdma->IntMask, 0xffffffff);	/* 1 means disabled */
190	out_be32(&intr->per_mask, 0x7ffffc00);	/* 1 means disabled */
191	out_be32(&intr->main_mask, 0x00010fff);	/* 1 means disabled */
192	intr_ctrl = in_be32(&intr->ctrl);
193	intr_ctrl &=    0x00ff0000;	/* Keeps IRQ[0-3] config */
194	intr_ctrl |=	0x0f000000 |	/* clear IRQ 0-3 */
195			0x00001000 |	/* MEE master external enable */
196			0x00000000 |	/* 0 means disable IRQ 0-3 */
197			0x00000001;	/* CEb route critical normally */
198	out_be32(&intr->ctrl, intr_ctrl);
199
200	/* Zero a bunch of the priority settings.  */
201	out_be32(&intr->per_pri1, 0);
202	out_be32(&intr->per_pri2, 0);
203	out_be32(&intr->per_pri3, 0);
204	out_be32(&intr->main_pri1, 0);
205	out_be32(&intr->main_pri2, 0);
206
207	/* Initialize irq_desc[i].chip's with mpc52xx_ic. */
208	for (i = 0; i < NR_IRQS; i++) {
209		irq_desc[i].chip = &mpc52xx_ic;
210		irq_desc[i].status = IRQ_LEVEL;
211	}
212
213	#define IRQn_MODE(intr_ctrl,irq) (((intr_ctrl) >> (22-(i<<1))) & 0x03)
214	for (i=0 ; i<4 ; i++) {
215		int mode;
216		mode = IRQn_MODE(intr_ctrl,i);
217		if ((mode == 0x1) || (mode == 0x2))
218			irq_desc[i?MPC52xx_IRQ1+i-1:MPC52xx_IRQ0].status = 0;
219	}
220}
221
222int
223mpc52xx_get_irq(void)
224{
225	u32 status;
226	int irq = -1;
227
228	status = in_be32(&intr->enc_status);
229
230	if (status & 0x00000400) {		/* critical */
231		irq = (status >> 8) & 0x3;
232		if (irq == 2)			/* high priority peripheral */
233			goto peripheral;
234		irq += MPC52xx_CRIT_IRQ_BASE;
235	}
236	else if (status & 0x00200000) {		/* main */
237		irq = (status >> 16) & 0x1f;
238		if (irq == 4)			/* low priority peripheral */
239			goto peripheral;
240		irq += MPC52xx_MAIN_IRQ_BASE;
241	}
242	else if (status & 0x20000000) {		/* peripheral */
243peripheral:
244		irq = (status >> 24) & 0x1f;
245		if (irq == 0) {			/* bestcomm */
246			status = in_be32(&sdma->IntPend);
247			irq = ffs(status) + MPC52xx_SDMA_IRQ_BASE-1;
248		}
249		else
250			irq += MPC52xx_PERP_IRQ_BASE;
251	}
252
253	return irq;
254}
255