1/*
2 * Interrupt controller driver for Xilinx Virtex-II Pro.
3 *
4 * Author: MontaVista Software, Inc.
5 *         source@mvista.com
6 *
7 * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under
8 * the terms of the GNU General Public License version 2. This program
9 * is licensed "as is" without any warranty of any kind, whether express
10 * or implied.
11 */
12
13#include <linux/init.h>
14#include <linux/irq.h>
15#include <asm/io.h>
16#include <platforms/4xx/xparameters/xparameters.h>
17#include <asm/ibm4xx.h>
18#include <asm/machdep.h>
19
20/* No one else should require these constants, so define them locally here. */
21#define ISR 0			/* Interrupt Status Register */
22#define IPR 1			/* Interrupt Pending Register */
23#define IER 2			/* Interrupt Enable Register */
24#define IAR 3			/* Interrupt Acknowledge Register */
25#define SIE 4			/* Set Interrupt Enable bits */
26#define CIE 5			/* Clear Interrupt Enable bits */
27#define IVR 6			/* Interrupt Vector Register */
28#define MER 7			/* Master Enable Register */
29
30#if XPAR_XINTC_USE_DCR == 0
31static volatile u32 *intc;
32#define intc_out_be32(addr, mask)     out_be32((addr), (mask))
33#define intc_in_be32(addr)            in_be32((addr))
34#else
35#define intc    XPAR_INTC_0_BASEADDR
36#define intc_out_be32(addr, mask)     mtdcr((addr), (mask))
37#define intc_in_be32(addr)            mfdcr((addr))
38#endif
39
40static void
41xilinx_intc_enable(unsigned int irq)
42{
43	unsigned long mask = (0x00000001 << (irq & 31));
44	pr_debug("enable: %d\n", irq);
45	intc_out_be32(intc + SIE, mask);
46}
47
48static void
49xilinx_intc_disable(unsigned int irq)
50{
51	unsigned long mask = (0x00000001 << (irq & 31));
52	pr_debug("disable: %d\n", irq);
53	intc_out_be32(intc + CIE, mask);
54}
55
56static void
57xilinx_intc_disable_and_ack(unsigned int irq)
58{
59	unsigned long mask = (0x00000001 << (irq & 31));
60	pr_debug("disable_and_ack: %d\n", irq);
61	intc_out_be32(intc + CIE, mask);
62	if (!(irq_desc[irq].status & IRQ_LEVEL))
63		intc_out_be32(intc + IAR, mask);	/* ack edge triggered intr */
64}
65
66static void
67xilinx_intc_end(unsigned int irq)
68{
69	unsigned long mask = (0x00000001 << (irq & 31));
70
71	pr_debug("end: %d\n", irq);
72	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
73		intc_out_be32(intc + SIE, mask);
74		/* ack level sensitive intr */
75		if (irq_desc[irq].status & IRQ_LEVEL)
76			intc_out_be32(intc + IAR, mask);
77	}
78}
79
80static struct hw_interrupt_type xilinx_intc = {
81	.typename = "Xilinx Interrupt Controller",
82	.enable = xilinx_intc_enable,
83	.disable = xilinx_intc_disable,
84	.ack = xilinx_intc_disable_and_ack,
85	.end = xilinx_intc_end,
86};
87
88int
89xilinx_pic_get_irq(void)
90{
91	int irq;
92
93	/*
94	 * NOTE: This function is the one that needs to be improved in
95	 * order to handle multiple interrupt controllers.  It currently
96	 * is hardcoded to check for interrupts only on the first INTC.
97	 */
98
99	irq = intc_in_be32(intc + IVR);
100	if (irq != -1)
101		irq = irq;
102
103	pr_debug("get_irq: %d\n", irq);
104
105	return (irq);
106}
107
108void __init
109ppc4xx_pic_init(void)
110{
111	int i;
112
113	/*
114	 * NOTE: The assumption here is that NR_IRQS is 32 or less
115	 * (NR_IRQS is 32 for PowerPC 405 cores by default).
116	 */
117#if (NR_IRQS > 32)
118#error NR_IRQS > 32 not supported
119#endif
120
121#if XPAR_XINTC_USE_DCR == 0
122	intc = ioremap(XPAR_INTC_0_BASEADDR, 32);
123
124	printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n",
125	       (unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc);
126#else
127	printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n",
128	       (unsigned long) XPAR_INTC_0_BASEADDR);
129#endif
130
131	/*
132	 * Disable all external interrupts until they are
133	 * explicitly requested.
134	 */
135	intc_out_be32(intc + IER, 0);
136
137	/* Acknowledge any pending interrupts just in case. */
138	intc_out_be32(intc + IAR, ~(u32) 0);
139
140	/* Turn on the Master Enable. */
141	intc_out_be32(intc + MER, 0x3UL);
142
143	ppc_md.get_irq = xilinx_pic_get_irq;
144
145	for (i = 0; i < NR_IRQS; ++i) {
146		irq_desc[i].chip = &xilinx_intc;
147
148		if (XPAR_INTC_0_KIND_OF_INTR & (0x00000001 << i))
149			irq_desc[i].status &= ~IRQ_LEVEL;
150		else
151			irq_desc[i].status |= IRQ_LEVEL;
152	}
153}
154