1 /*
2  * (C) Copyright 2006
3  * Detlev Zundel, DENX Software Engineering, dzu@denx.de
4  *
5  * (C) Copyright -2003
6  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
7  *
8  * (C) Copyright 2001
9  * Josh Huber <huber@mclx.com>, Mission Critical Linux, Inc.
10  *
11  * See file CREDITS for list of people who contributed to this
12  * project.
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License as
16  * published by the Free Software Foundation; either version 2 of
17  * the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
27  * MA 02111-1307 USA
28  */
29 
30 /* this section was ripped out of arch/powerpc/syslib/mpc52xx_pic.c in the
31  * Linux 2.6 source with the following copyright.
32  *
33  * Based on (well, mostly copied from) the code from the 2.4 kernel by
34  * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg.
35  *
36  * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
37  * Copyright (C) 2003 Montavista Software, Inc
38  */
39 
40 #include <common.h>
41 #include <asm/processor.h>
42 #include <asm/io.h>
43 #include <command.h>
44 
45 struct irq_action {
46 	interrupt_handler_t *handler;
47 	void *arg;
48 	ulong count;
49 };
50 
51 static struct irq_action irq_handlers[NR_IRQS];
52 
53 static struct mpc5xxx_intr *intr;
54 static struct mpc5xxx_sdma *sdma;
55 
mpc5xxx_ic_disable(unsigned int irq)56 static void mpc5xxx_ic_disable(unsigned int irq)
57 {
58 	u32 val;
59 
60 	if (irq == MPC5XXX_IRQ0) {
61 		val = in_be32(&intr->ctrl);
62 		val &= ~(1 << 11);
63 		out_be32(&intr->ctrl, val);
64 	} else if (irq < MPC5XXX_IRQ1) {
65 		BUG();
66 	} else if (irq <= MPC5XXX_IRQ3) {
67 		val = in_be32(&intr->ctrl);
68 		val &= ~(1 << (10 - (irq - MPC5XXX_IRQ1)));
69 		out_be32(&intr->ctrl, val);
70 	} else if (irq < MPC5XXX_SDMA_IRQ_BASE) {
71 		val = in_be32(&intr->main_mask);
72 		val |= 1 << (16 - (irq - MPC5XXX_MAIN_IRQ_BASE));
73 		out_be32(&intr->main_mask, val);
74 	} else if (irq < MPC5XXX_PERP_IRQ_BASE) {
75 		val = in_be32(&sdma->IntMask);
76 		val |= 1 << (irq - MPC5XXX_SDMA_IRQ_BASE);
77 		out_be32(&sdma->IntMask, val);
78 	} else {
79 		val = in_be32(&intr->per_mask);
80 		val |= 1 << (31 - (irq - MPC5XXX_PERP_IRQ_BASE));
81 		out_be32(&intr->per_mask, val);
82 	}
83 }
84 
mpc5xxx_ic_enable(unsigned int irq)85 static void mpc5xxx_ic_enable(unsigned int irq)
86 {
87 	u32 val;
88 
89 	if (irq == MPC5XXX_IRQ0) {
90 		val = in_be32(&intr->ctrl);
91 		val |= 1 << 11;
92 		out_be32(&intr->ctrl, val);
93 	} else if (irq < MPC5XXX_IRQ1) {
94 		BUG();
95 	} else if (irq <= MPC5XXX_IRQ3) {
96 		val = in_be32(&intr->ctrl);
97 		val |= 1 << (10 - (irq - MPC5XXX_IRQ1));
98 		out_be32(&intr->ctrl, val);
99 	} else if (irq < MPC5XXX_SDMA_IRQ_BASE) {
100 		val = in_be32(&intr->main_mask);
101 		val &= ~(1 << (16 - (irq - MPC5XXX_MAIN_IRQ_BASE)));
102 		out_be32(&intr->main_mask, val);
103 	} else if (irq < MPC5XXX_PERP_IRQ_BASE) {
104 		val = in_be32(&sdma->IntMask);
105 		val &= ~(1 << (irq - MPC5XXX_SDMA_IRQ_BASE));
106 		out_be32(&sdma->IntMask, val);
107 	} else {
108 		val = in_be32(&intr->per_mask);
109 		val &= ~(1 << (31 - (irq - MPC5XXX_PERP_IRQ_BASE)));
110 		out_be32(&intr->per_mask, val);
111 	}
112 }
113 
mpc5xxx_ic_ack(unsigned int irq)114 static void mpc5xxx_ic_ack(unsigned int irq)
115 {
116 	u32 val;
117 
118 	/*
119 	 * Only some irqs are reset here, others in interrupting hardware.
120 	 */
121 
122 	switch (irq) {
123 	case MPC5XXX_IRQ0:
124 		val = in_be32(&intr->ctrl);
125 		val |= 0x08000000;
126 		out_be32(&intr->ctrl, val);
127 		break;
128 	case MPC5XXX_CCS_IRQ:
129 		val = in_be32(&intr->enc_status);
130 		val |= 0x00000400;
131 		out_be32(&intr->enc_status, val);
132 		break;
133 	case MPC5XXX_IRQ1:
134 		val = in_be32(&intr->ctrl);
135 		val |= 0x04000000;
136 		out_be32(&intr->ctrl, val);
137 		break;
138 	case MPC5XXX_IRQ2:
139 		val = in_be32(&intr->ctrl);
140 		val |= 0x02000000;
141 		out_be32(&intr->ctrl, val);
142 		break;
143 	case MPC5XXX_IRQ3:
144 		val = in_be32(&intr->ctrl);
145 		val |= 0x01000000;
146 		out_be32(&intr->ctrl, val);
147 		break;
148 	default:
149 		if (irq >= MPC5XXX_SDMA_IRQ_BASE
150 		    && irq < (MPC5XXX_SDMA_IRQ_BASE + MPC5XXX_SDMA_IRQ_NUM)) {
151 			out_be32(&sdma->IntPend,
152 				 1 << (irq - MPC5XXX_SDMA_IRQ_BASE));
153 		}
154 		break;
155 	}
156 }
157 
mpc5xxx_ic_disable_and_ack(unsigned int irq)158 static void mpc5xxx_ic_disable_and_ack(unsigned int irq)
159 {
160 	mpc5xxx_ic_disable(irq);
161 	mpc5xxx_ic_ack(irq);
162 }
163 
mpc5xxx_ic_end(unsigned int irq)164 static void mpc5xxx_ic_end(unsigned int irq)
165 {
166 	mpc5xxx_ic_enable(irq);
167 }
168 
mpc5xxx_init_irq(void)169 void mpc5xxx_init_irq(void)
170 {
171 	u32 intr_ctrl;
172 
173 	/* Remap the necessary zones */
174 	intr = (struct mpc5xxx_intr *)(MPC5XXX_ICTL);
175 	sdma = (struct mpc5xxx_sdma *)(MPC5XXX_SDMA);
176 
177 	/* Disable all interrupt sources. */
178 	out_be32(&sdma->IntPend, 0xffffffff);	/* 1 means clear pending */
179 	out_be32(&sdma->IntMask, 0xffffffff);	/* 1 means disabled */
180 	out_be32(&intr->per_mask, 0x7ffffc00);	/* 1 means disabled */
181 	out_be32(&intr->main_mask, 0x00010fff);	/* 1 means disabled */
182 	intr_ctrl = in_be32(&intr->ctrl);
183 	intr_ctrl |= 0x0f000000 |	/* clear IRQ 0-3 */
184 	    0x00ff0000 |	/* IRQ 0-3 level sensitive low active */
185 	    0x00001000 |	/* MEE master external enable */
186 	    0x00000000 |	/* 0 means disable IRQ 0-3 */
187 	    0x00000001;		/* CEb route critical normally */
188 	out_be32(&intr->ctrl, intr_ctrl);
189 
190 	/* Zero a bunch of the priority settings.  */
191 	out_be32(&intr->per_pri1, 0);
192 	out_be32(&intr->per_pri2, 0);
193 	out_be32(&intr->per_pri3, 0);
194 	out_be32(&intr->main_pri1, 0);
195 	out_be32(&intr->main_pri2, 0);
196 }
197 
mpc5xxx_get_irq(struct pt_regs * regs)198 int mpc5xxx_get_irq(struct pt_regs *regs)
199 {
200 	u32 status;
201 	int irq = -1;
202 
203 	status = in_be32(&intr->enc_status);
204 
205 	if (status & 0x00000400) {	/* critical */
206 		irq = (status >> 8) & 0x3;
207 		if (irq == 2)	/* high priority peripheral */
208 			goto peripheral;
209 		irq += MPC5XXX_CRIT_IRQ_BASE;
210 	} else if (status & 0x00200000) {	/* main */
211 		irq = (status >> 16) & 0x1f;
212 		if (irq == 4)	/* low priority peripheral */
213 			goto peripheral;
214 		irq += MPC5XXX_MAIN_IRQ_BASE;
215 	} else if (status & 0x20000000) {	/* peripheral */
216 	      peripheral:
217 		irq = (status >> 24) & 0x1f;
218 		if (irq == 0) {	/* bestcomm */
219 			status = in_be32(&sdma->IntPend);
220 			irq = ffs(status) + MPC5XXX_SDMA_IRQ_BASE - 1;
221 		} else
222 			irq += MPC5XXX_PERP_IRQ_BASE;
223 	}
224 
225 	return irq;
226 }
227 
228 /****************************************************************************/
229 
interrupt_init_cpu(ulong * decrementer_count)230 int interrupt_init_cpu(ulong * decrementer_count)
231 {
232 	*decrementer_count = get_tbclk() / CONFIG_SYS_HZ;
233 
234 	mpc5xxx_init_irq();
235 
236 	return (0);
237 }
238 
239 /****************************************************************************/
240 
241 /*
242  * Handle external interrupts
243  */
external_interrupt(struct pt_regs * regs)244 void external_interrupt(struct pt_regs *regs)
245 {
246 	int irq, unmask = 1;
247 
248 	irq = mpc5xxx_get_irq(regs);
249 
250 	mpc5xxx_ic_disable_and_ack(irq);
251 
252 	enable_interrupts();
253 
254 	if (irq_handlers[irq].handler != NULL)
255 		(*irq_handlers[irq].handler) (irq_handlers[irq].arg);
256 	else {
257 		printf("\nBogus External Interrupt IRQ %d\n", irq);
258 		/*
259 		 * turn off the bogus interrupt, otherwise it
260 		 * might repeat forever
261 		 */
262 		unmask = 0;
263 	}
264 
265 	if (unmask)
266 		mpc5xxx_ic_end(irq);
267 }
268 
timer_interrupt_cpu(struct pt_regs * regs)269 void timer_interrupt_cpu(struct pt_regs *regs)
270 {
271 	/* nothing to do here */
272 	return;
273 }
274 
275 /****************************************************************************/
276 
277 /*
278  * Install and free a interrupt handler.
279  */
280 
irq_install_handler(int irq,interrupt_handler_t * handler,void * arg)281 void irq_install_handler(int irq, interrupt_handler_t * handler, void *arg)
282 {
283 	if (irq < 0 || irq >= NR_IRQS) {
284 		printf("irq_install_handler: bad irq number %d\n", irq);
285 		return;
286 	}
287 
288 	if (irq_handlers[irq].handler != NULL)
289 		printf("irq_install_handler: 0x%08lx replacing 0x%08lx\n",
290 		       (ulong) handler, (ulong) irq_handlers[irq].handler);
291 
292 	irq_handlers[irq].handler = handler;
293 	irq_handlers[irq].arg = arg;
294 
295 	mpc5xxx_ic_enable(irq);
296 }
297 
irq_free_handler(int irq)298 void irq_free_handler(int irq)
299 {
300 	if (irq < 0 || irq >= NR_IRQS) {
301 		printf("irq_free_handler: bad irq number %d\n", irq);
302 		return;
303 	}
304 
305 	mpc5xxx_ic_disable(irq);
306 
307 	irq_handlers[irq].handler = NULL;
308 	irq_handlers[irq].arg = NULL;
309 }
310 
311 /****************************************************************************/
312 
313 #if defined(CONFIG_CMD_IRQ)
do_irqinfo(cmd_tbl_t * cmdtp,bd_t * bd,int flag,int argc,char * argv[])314 void do_irqinfo(cmd_tbl_t * cmdtp, bd_t * bd, int flag, int argc, char *argv[])
315 {
316 	int irq, re_enable;
317 	u32 intr_ctrl;
318 	char *irq_config[] = { "level sensitive, active high",
319 		"edge sensitive, rising active edge",
320 		"edge sensitive, falling active edge",
321 		"level sensitive, active low"
322 	};
323 
324 	re_enable = disable_interrupts();
325 
326 	intr_ctrl = in_be32(&intr->ctrl);
327 	printf("Interrupt configuration:\n");
328 
329 	for (irq = 0; irq <= 3; irq++) {
330 		printf("IRQ%d: %s\n", irq,
331 		       irq_config[(intr_ctrl >> (22 - 2 * irq)) & 0x3]);
332 	}
333 
334 	puts("\nInterrupt-Information:\n" "Nr  Routine   Arg       Count\n");
335 
336 	for (irq = 0; irq < NR_IRQS; irq++)
337 		if (irq_handlers[irq].handler != NULL)
338 			printf("%02d  %08lx  %08lx  %ld\n", irq,
339 			       (ulong) irq_handlers[irq].handler,
340 			       (ulong) irq_handlers[irq].arg,
341 			       irq_handlers[irq].count);
342 
343 	if (re_enable)
344 		enable_interrupts();
345 }
346 #endif
347