Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.30-rc7 254 lines 7.0 kB view raw
1/* 2 * Interrupt controller driver for Xilinx Virtex FPGAs 3 * 4 * Copyright (C) 2007 Secret Lab Technologies Ltd. 5 * 6 * This file is licensed under the terms of the GNU General Public License 7 * version 2. This program is licensed "as is" without any warranty of any 8 * kind, whether express or implied. 9 * 10 */ 11 12/* 13 * This is a driver for the interrupt controller typically found in 14 * Xilinx Virtex FPGA designs. 15 * 16 * The interrupt sense levels are hard coded into the FPGA design with 17 * typically a 1:1 relationship between irq lines and devices (no shared 18 * irq lines). Therefore, this driver does not attempt to handle edge 19 * and level interrupts differently. 20 */ 21#undef DEBUG 22 23#include <linux/kernel.h> 24#include <linux/irq.h> 25#include <linux/of.h> 26#include <asm/io.h> 27#include <asm/processor.h> 28#include <asm/irq.h> 29 30/* 31 * INTC Registers 32 */ 33#define XINTC_ISR 0 /* Interrupt Status */ 34#define XINTC_IPR 4 /* Interrupt Pending */ 35#define XINTC_IER 8 /* Interrupt Enable */ 36#define XINTC_IAR 12 /* Interrupt Acknowledge */ 37#define XINTC_SIE 16 /* Set Interrupt Enable bits */ 38#define XINTC_CIE 20 /* Clear Interrupt Enable bits */ 39#define XINTC_IVR 24 /* Interrupt Vector */ 40#define XINTC_MER 28 /* Master Enable */ 41 42static struct irq_host *master_irqhost; 43 44#define XILINX_INTC_MAXIRQS (32) 45 46/* The following table allows the interrupt type, edge or level, 47 * to be cached after being read from the device tree until the interrupt 48 * is mapped 49 */ 50static int xilinx_intc_typetable[XILINX_INTC_MAXIRQS]; 51 52/* Map the interrupt type from the device tree to the interrupt types 53 * used by the interrupt subsystem 54 */ 55static unsigned char xilinx_intc_map_senses[] = { 56 IRQ_TYPE_EDGE_RISING, 57 IRQ_TYPE_EDGE_FALLING, 58 IRQ_TYPE_LEVEL_HIGH, 59 IRQ_TYPE_LEVEL_LOW, 60}; 61 62/* 63 * The interrupt controller is setup such that it doesn't work well with 64 * the level interrupt handler in the kernel because the handler acks the 65 * interrupt before calling the application interrupt handler. To deal with 66 * that, we use 2 different irq chips so that different functions can be 67 * used for level and edge type interrupts. 68 * 69 * IRQ Chip common (across level and edge) operations 70 */ 71static void xilinx_intc_mask(unsigned int virq) 72{ 73 int irq = virq_to_hw(virq); 74 void * regs = get_irq_chip_data(virq); 75 pr_debug("mask: %d\n", irq); 76 out_be32(regs + XINTC_CIE, 1 << irq); 77} 78 79static int xilinx_intc_set_type(unsigned int virq, unsigned int flow_type) 80{ 81 struct irq_desc *desc = get_irq_desc(virq); 82 83 desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); 84 desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; 85 if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) 86 desc->status |= IRQ_LEVEL; 87 return 0; 88} 89 90/* 91 * IRQ Chip level operations 92 */ 93static void xilinx_intc_level_unmask(unsigned int virq) 94{ 95 int irq = virq_to_hw(virq); 96 void * regs = get_irq_chip_data(virq); 97 pr_debug("unmask: %d\n", irq); 98 out_be32(regs + XINTC_SIE, 1 << irq); 99 100 /* ack level irqs because they can't be acked during 101 * ack function since the handle_level_irq function 102 * acks the irq before calling the inerrupt handler 103 */ 104 out_be32(regs + XINTC_IAR, 1 << irq); 105} 106 107static struct irq_chip xilinx_intc_level_irqchip = { 108 .typename = "Xilinx Level INTC", 109 .mask = xilinx_intc_mask, 110 .mask_ack = xilinx_intc_mask, 111 .unmask = xilinx_intc_level_unmask, 112 .set_type = xilinx_intc_set_type, 113}; 114 115/* 116 * IRQ Chip edge operations 117 */ 118static void xilinx_intc_edge_unmask(unsigned int virq) 119{ 120 int irq = virq_to_hw(virq); 121 void *regs = get_irq_chip_data(virq); 122 pr_debug("unmask: %d\n", irq); 123 out_be32(regs + XINTC_SIE, 1 << irq); 124} 125 126static void xilinx_intc_edge_ack(unsigned int virq) 127{ 128 int irq = virq_to_hw(virq); 129 void * regs = get_irq_chip_data(virq); 130 pr_debug("ack: %d\n", irq); 131 out_be32(regs + XINTC_IAR, 1 << irq); 132} 133 134static struct irq_chip xilinx_intc_edge_irqchip = { 135 .typename = "Xilinx Edge INTC", 136 .mask = xilinx_intc_mask, 137 .unmask = xilinx_intc_edge_unmask, 138 .ack = xilinx_intc_edge_ack, 139 .set_type = xilinx_intc_set_type, 140}; 141 142/* 143 * IRQ Host operations 144 */ 145 146/** 147 * xilinx_intc_xlate - translate virq# from device tree interrupts property 148 */ 149static int xilinx_intc_xlate(struct irq_host *h, struct device_node *ct, 150 u32 *intspec, unsigned int intsize, 151 irq_hw_number_t *out_hwirq, 152 unsigned int *out_flags) 153{ 154 if ((intsize < 2) || (intspec[0] >= XILINX_INTC_MAXIRQS)) 155 return -EINVAL; 156 157 /* keep a copy of the interrupt type til the interrupt is mapped 158 */ 159 xilinx_intc_typetable[intspec[0]] = xilinx_intc_map_senses[intspec[1]]; 160 161 /* Xilinx uses 2 interrupt entries, the 1st being the h/w 162 * interrupt number, the 2nd being the interrupt type, edge or level 163 */ 164 *out_hwirq = intspec[0]; 165 *out_flags = xilinx_intc_map_senses[intspec[1]]; 166 167 return 0; 168} 169static int xilinx_intc_map(struct irq_host *h, unsigned int virq, 170 irq_hw_number_t irq) 171{ 172 set_irq_chip_data(virq, h->host_data); 173 174 if (xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_HIGH || 175 xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_LOW) { 176 set_irq_chip_and_handler(virq, &xilinx_intc_level_irqchip, 177 handle_level_irq); 178 } else { 179 set_irq_chip_and_handler(virq, &xilinx_intc_edge_irqchip, 180 handle_edge_irq); 181 } 182 return 0; 183} 184 185static struct irq_host_ops xilinx_intc_ops = { 186 .map = xilinx_intc_map, 187 .xlate = xilinx_intc_xlate, 188}; 189 190struct irq_host * __init 191xilinx_intc_init(struct device_node *np) 192{ 193 struct irq_host * irq; 194 struct resource res; 195 void * regs; 196 int rc; 197 198 /* Find and map the intc registers */ 199 rc = of_address_to_resource(np, 0, &res); 200 if (rc) { 201 printk(KERN_ERR __FILE__ ": of_address_to_resource() failed\n"); 202 return NULL; 203 } 204 regs = ioremap(res.start, 32); 205 206 printk(KERN_INFO "Xilinx intc at 0x%08llx mapped to 0x%p\n", 207 (unsigned long long) res.start, regs); 208 209 /* Setup interrupt controller */ 210 out_be32(regs + XINTC_IER, 0); /* disable all irqs */ 211 out_be32(regs + XINTC_IAR, ~(u32) 0); /* Acknowledge pending irqs */ 212 out_be32(regs + XINTC_MER, 0x3UL); /* Turn on the Master Enable. */ 213 214 /* Allocate and initialize an irq_host structure. */ 215 irq = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, XILINX_INTC_MAXIRQS, 216 &xilinx_intc_ops, -1); 217 if (!irq) 218 panic(__FILE__ ": Cannot allocate IRQ host\n"); 219 irq->host_data = regs; 220 return irq; 221} 222 223int xilinx_intc_get_irq(void) 224{ 225 void * regs = master_irqhost->host_data; 226 pr_debug("get_irq:\n"); 227 return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR)); 228} 229 230void __init xilinx_intc_init_tree(void) 231{ 232 struct device_node *np; 233 234 /* find top level interrupt controller */ 235 for_each_compatible_node(np, NULL, "xlnx,opb-intc-1.00.c") { 236 if (!of_get_property(np, "interrupts", NULL)) 237 break; 238 } 239 if (!np) { 240 for_each_compatible_node(np, NULL, "xlnx,xps-intc-1.00.a") { 241 if (!of_get_property(np, "interrupts", NULL)) 242 break; 243 } 244 } 245 246 /* xilinx interrupt controller needs to be top level */ 247 BUG_ON(!np); 248 249 master_irqhost = xilinx_intc_init(np); 250 BUG_ON(!master_irqhost); 251 252 irq_set_default_host(master_irqhost); 253 of_node_put(np); 254}