158 lines
3.9 KiB
C
158 lines
3.9 KiB
C
|
/*
|
||
|
* arch/ppc/syslib/xilinx_pic.c
|
||
|
*
|
||
|
* Interrupt controller driver for Xilinx Virtex-II Pro.
|
||
|
*
|
||
|
* Author: MontaVista Software, Inc.
|
||
|
* source@mvista.com
|
||
|
*
|
||
|
* 2002-2004 (c) MontaVista Software, Inc. This file is licensed under
|
||
|
* the terms of the GNU General Public License version 2. This program
|
||
|
* is licensed "as is" without any warranty of any kind, whether express
|
||
|
* or implied.
|
||
|
*/
|
||
|
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/irq.h>
|
||
|
#include <asm/io.h>
|
||
|
#include <asm/xparameters.h>
|
||
|
#include <asm/ibm4xx.h>
|
||
|
|
||
|
/* No one else should require these constants, so define them locally here. */
|
||
|
#define ISR 0 /* Interrupt Status Register */
|
||
|
#define IPR 1 /* Interrupt Pending Register */
|
||
|
#define IER 2 /* Interrupt Enable Register */
|
||
|
#define IAR 3 /* Interrupt Acknowledge Register */
|
||
|
#define SIE 4 /* Set Interrupt Enable bits */
|
||
|
#define CIE 5 /* Clear Interrupt Enable bits */
|
||
|
#define IVR 6 /* Interrupt Vector Register */
|
||
|
#define MER 7 /* Master Enable Register */
|
||
|
|
||
|
#if XPAR_XINTC_USE_DCR == 0
|
||
|
static volatile u32 *intc;
|
||
|
#define intc_out_be32(addr, mask) out_be32((addr), (mask))
|
||
|
#define intc_in_be32(addr) in_be32((addr))
|
||
|
#else
|
||
|
#define intc XPAR_INTC_0_BASEADDR
|
||
|
#define intc_out_be32(addr, mask) mtdcr((addr), (mask))
|
||
|
#define intc_in_be32(addr) mfdcr((addr))
|
||
|
#endif
|
||
|
|
||
|
static void
|
||
|
xilinx_intc_enable(unsigned int irq)
|
||
|
{
|
||
|
unsigned long mask = (0x00000001 << (irq & 31));
|
||
|
pr_debug("enable: %d\n", irq);
|
||
|
intc_out_be32(intc + SIE, mask);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
xilinx_intc_disable(unsigned int irq)
|
||
|
{
|
||
|
unsigned long mask = (0x00000001 << (irq & 31));
|
||
|
pr_debug("disable: %d\n", irq);
|
||
|
intc_out_be32(intc + CIE, mask);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
xilinx_intc_disable_and_ack(unsigned int irq)
|
||
|
{
|
||
|
unsigned long mask = (0x00000001 << (irq & 31));
|
||
|
pr_debug("disable_and_ack: %d\n", irq);
|
||
|
intc_out_be32(intc + CIE, mask);
|
||
|
if (!(irq_desc[irq].status & IRQ_LEVEL))
|
||
|
intc_out_be32(intc + IAR, mask); /* ack edge triggered intr */
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
xilinx_intc_end(unsigned int irq)
|
||
|
{
|
||
|
unsigned long mask = (0x00000001 << (irq & 31));
|
||
|
|
||
|
pr_debug("end: %d\n", irq);
|
||
|
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
|
||
|
intc_out_be32(intc + SIE, mask);
|
||
|
/* ack level sensitive intr */
|
||
|
if (irq_desc[irq].status & IRQ_LEVEL)
|
||
|
intc_out_be32(intc + IAR, mask);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static struct hw_interrupt_type xilinx_intc = {
|
||
|
"Xilinx Interrupt Controller",
|
||
|
NULL,
|
||
|
NULL,
|
||
|
xilinx_intc_enable,
|
||
|
xilinx_intc_disable,
|
||
|
xilinx_intc_disable_and_ack,
|
||
|
xilinx_intc_end,
|
||
|
0
|
||
|
};
|
||
|
|
||
|
int
|
||
|
xilinx_pic_get_irq(struct pt_regs *regs)
|
||
|
{
|
||
|
int irq;
|
||
|
|
||
|
/*
|
||
|
* NOTE: This function is the one that needs to be improved in
|
||
|
* order to handle multiple interrupt controllers. It currently
|
||
|
* is hardcoded to check for interrupts only on the first INTC.
|
||
|
*/
|
||
|
|
||
|
irq = intc_in_be32(intc + IVR);
|
||
|
if (irq != -1)
|
||
|
irq = irq;
|
||
|
|
||
|
pr_debug("get_irq: %d\n", irq);
|
||
|
|
||
|
return (irq);
|
||
|
}
|
||
|
|
||
|
void __init
|
||
|
ppc4xx_pic_init(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/*
|
||
|
* NOTE: The assumption here is that NR_IRQS is 32 or less
|
||
|
* (NR_IRQS is 32 for PowerPC 405 cores by default).
|
||
|
*/
|
||
|
#if (NR_IRQS > 32)
|
||
|
#error NR_IRQS > 32 not supported
|
||
|
#endif
|
||
|
|
||
|
#if XPAR_XINTC_USE_DCR == 0
|
||
|
intc = ioremap(XPAR_INTC_0_BASEADDR, 32);
|
||
|
|
||
|
printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n",
|
||
|
(unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc);
|
||
|
#else
|
||
|
printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n",
|
||
|
(unsigned long) XPAR_INTC_0_BASEADDR);
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Disable all external interrupts until they are
|
||
|
* explicity requested.
|
||
|
*/
|
||
|
intc_out_be32(intc + IER, 0);
|
||
|
|
||
|
/* Acknowledge any pending interrupts just in case. */
|
||
|
intc_out_be32(intc + IAR, ~(u32) 0);
|
||
|
|
||
|
/* Turn on the Master Enable. */
|
||
|
intc_out_be32(intc + MER, 0x3UL);
|
||
|
|
||
|
ppc_md.get_irq = xilinx_pic_get_irq;
|
||
|
|
||
|
for (i = 0; i < NR_IRQS; ++i) {
|
||
|
irq_desc[i].handler = &xilinx_intc;
|
||
|
|
||
|
if (XPAR_INTC_0_KIND_OF_INTR & (0x00000001 << i))
|
||
|
irq_desc[i].status &= ~IRQ_LEVEL;
|
||
|
else
|
||
|
irq_desc[i].status |= IRQ_LEVEL;
|
||
|
}
|
||
|
}
|