1/* 2 * Copyright (C) 2007-2009 Michal Simek <monstr@monstr.eu> 3 * Copyright (C) 2007-2009 PetaLogix 4 * Copyright (C) 2006 Atmark Techno, Inc. 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file "COPYING" in the main directory of this archive 8 * for more details. 9 */ 10 11#include <linux/init.h> 12#include <linux/irq.h> 13#include <asm/page.h> 14#include <linux/io.h> 15#include <linux/bug.h> 16 17#include <asm/prom.h> 18#include <asm/irq.h> 19 20#ifdef CONFIG_SELFMOD_INTC 21#include <asm/selfmod.h> 22#define INTC_BASE BARRIER_BASE_ADDR 23#else 24static unsigned int intc_baseaddr; 25#define INTC_BASE intc_baseaddr 26#endif 27 28unsigned int nr_irq; 29 30/* No one else should require these constants, so define them locally here. */ 31#define ISR 0x00 /* Interrupt Status Register */ 32#define IPR 0x04 /* Interrupt Pending Register */ 33#define IER 0x08 /* Interrupt Enable Register */ 34#define IAR 0x0c /* Interrupt Acknowledge Register */ 35#define SIE 0x10 /* Set Interrupt Enable bits */ 36#define CIE 0x14 /* Clear Interrupt Enable bits */ 37#define IVR 0x18 /* Interrupt Vector Register */ 38#define MER 0x1c /* Master Enable Register */ 39 40#define MER_ME (1<<0) 41#define MER_HIE (1<<1) 42 43static void intc_enable_or_unmask(unsigned int irq) 44{ 45 unsigned long mask = 1 << irq; 46 pr_debug("enable_or_unmask: %d\n", irq); 47 out_be32(INTC_BASE + SIE, mask); 48 49 /* ack level irqs because they can't be acked during 50 * ack function since the handle_level_irq function 51 * acks the irq before calling the interrupt handler 52 */ 53 if (irq_desc[irq].status & IRQ_LEVEL) 54 out_be32(INTC_BASE + IAR, mask); 55} 56 57static void intc_disable_or_mask(unsigned int irq) 58{ 59 pr_debug("disable: %d\n", irq); 60 out_be32(INTC_BASE + CIE, 1 << irq); 61} 62 63static void intc_ack(unsigned int irq) 64{ 65 pr_debug("ack: %d\n", irq); 66 out_be32(INTC_BASE + IAR, 1 << irq); 67} 68 69static void intc_mask_ack(unsigned int irq) 70{ 71 unsigned long mask = 1 << irq; 72 pr_debug("disable_and_ack: %d\n", irq); 73 out_be32(INTC_BASE + CIE, mask); 74 out_be32(INTC_BASE + IAR, mask); 75} 76 77static void intc_end(unsigned int irq) 78{ 79 unsigned long mask = 1 << irq; 80 pr_debug("end: %d\n", irq); 81 if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { 82 out_be32(INTC_BASE + SIE, mask); 83 /* ack level sensitive intr */ 84 if (irq_desc[irq].status & IRQ_LEVEL) 85 out_be32(INTC_BASE + IAR, mask); 86 } 87} 88 89static struct irq_chip intc_dev = { 90 .name = "Xilinx INTC", 91 .unmask = intc_enable_or_unmask, 92 .mask = intc_disable_or_mask, 93 .ack = intc_ack, 94 .mask_ack = intc_mask_ack, 95 .end = intc_end, 96}; 97 98unsigned int get_irq(struct pt_regs *regs) 99{ 100 int irq; 101 102 /* 103 * NOTE: This function is the one that needs to be improved in 104 * order to handle multiple interrupt controllers. It currently 105 * is hardcoded to check for interrupts only on the first INTC. 106 */ 107 irq = in_be32(INTC_BASE + IVR); 108 pr_debug("get_irq: %d\n", irq); 109 110 return irq; 111} 112 113void __init init_IRQ(void) 114{ 115 u32 i, j, intr_type; 116 struct device_node *intc = NULL; 117#ifdef CONFIG_SELFMOD_INTC 118 unsigned int intc_baseaddr = 0; 119 static int arr_func[] = { 120 (int)&get_irq, 121 (int)&intc_enable_or_unmask, 122 (int)&intc_disable_or_mask, 123 (int)&intc_mask_ack, 124 (int)&intc_ack, 125 (int)&intc_end, 126 0 127 }; 128#endif 129 static char *intc_list[] = { 130 "xlnx,xps-intc-1.00.a", 131 "xlnx,opb-intc-1.00.c", 132 "xlnx,opb-intc-1.00.b", 133 "xlnx,opb-intc-1.00.a", 134 NULL 135 }; 136 137 for (j = 0; intc_list[j] != NULL; j++) { 138 intc = of_find_compatible_node(NULL, NULL, intc_list[j]); 139 if (intc) 140 break; 141 } 142 BUG_ON(!intc); 143 144 intc_baseaddr = *(int *) of_get_property(intc, "reg", NULL); 145 intc_baseaddr = (unsigned long) ioremap(intc_baseaddr, PAGE_SIZE); 146 nr_irq = *(int *) of_get_property(intc, "xlnx,num-intr-inputs", NULL); 147 148 intr_type = 149 *(int *) of_get_property(intc, "xlnx,kind-of-intr", NULL); 150 if (intr_type >= (1 << (nr_irq + 1))) 151 printk(KERN_INFO " ERROR: Mismatch in kind-of-intr param\n"); 152 153#ifdef CONFIG_SELFMOD_INTC 154 selfmod_function((int *) arr_func, intc_baseaddr); 155#endif 156 printk(KERN_INFO "%s #0 at 0x%08x, num_irq=%d, edge=0x%x\n", 157 intc_list[j], intc_baseaddr, nr_irq, intr_type); 158 159 /* 160 * Disable all external interrupts until they are 161 * explicity requested. 162 */ 163 out_be32(intc_baseaddr + IER, 0); 164 165 /* Acknowledge any pending interrupts just in case. */ 166 out_be32(intc_baseaddr + IAR, 0xffffffff); 167 168 /* Turn on the Master Enable. */ 169 out_be32(intc_baseaddr + MER, MER_HIE | MER_ME); 170 171 for (i = 0; i < nr_irq; ++i) { 172 if (intr_type & (0x00000001 << i)) { 173 set_irq_chip_and_handler_name(i, &intc_dev, 174 handle_edge_irq, intc_dev.name); 175 irq_desc[i].status &= ~IRQ_LEVEL; 176 } else { 177 set_irq_chip_and_handler_name(i, &intc_dev, 178 handle_level_irq, intc_dev.name); 179 irq_desc[i].status |= IRQ_LEVEL; 180 } 181 } 182} 183