1/* 2 * Interrupt handling for GE FPGA based PIC 3 * 4 * Author: Martyn Welch <martyn.welch@ge.com> 5 * 6 * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. 7 * 8 * This file is licensed under the terms of the GNU General Public License 9 * version 2. This program is licensed "as is" without any warranty of any 10 * kind, whether express or implied. 11 */ 12 13#include <linux/stddef.h> 14#include <linux/kernel.h> 15#include <linux/init.h> 16#include <linux/irq.h> 17#include <linux/interrupt.h> 18#include <linux/spinlock.h> 19 20#include <asm/byteorder.h> 21#include <asm/io.h> 22#include <asm/prom.h> 23#include <asm/irq.h> 24 25#include "gef_pic.h" 26 27#define DEBUG 28#undef DEBUG 29 30#ifdef DEBUG 31#define DBG(fmt...) do { printk(KERN_DEBUG "gef_pic: " fmt); } while (0) 32#else 33#define DBG(fmt...) do { } while (0) 34#endif 35 36#define GEF_PIC_NUM_IRQS 32 37 38/* Interrupt Controller Interface Registers */ 39#define GEF_PIC_INTR_STATUS 0x0000 40 41#define GEF_PIC_INTR_MASK(cpu) (0x0010 + (0x4 * cpu)) 42#define GEF_PIC_CPU0_INTR_MASK GEF_PIC_INTR_MASK(0) 43#define GEF_PIC_CPU1_INTR_MASK GEF_PIC_INTR_MASK(1) 44 45#define GEF_PIC_MCP_MASK(cpu) (0x0018 + (0x4 * cpu)) 46#define GEF_PIC_CPU0_MCP_MASK GEF_PIC_MCP_MASK(0) 47#define GEF_PIC_CPU1_MCP_MASK GEF_PIC_MCP_MASK(1) 48 49#define gef_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq) 50 51 52static DEFINE_RAW_SPINLOCK(gef_pic_lock); 53 54static void __iomem *gef_pic_irq_reg_base; 55static struct irq_host *gef_pic_irq_host; 56static int gef_pic_cascade_irq; 57 58/* 59 * Interrupt Controller Handling 60 * 61 * The interrupt controller handles interrupts for most on board interrupts, 62 * apart from PCI interrupts. For example on SBC610: 63 * 64 * 17:31 RO Reserved 65 * 16 RO PCI Express Doorbell 3 Status 66 * 15 RO PCI Express Doorbell 2 Status 67 * 14 RO PCI Express Doorbell 1 Status 68 * 13 RO PCI Express Doorbell 0 Status 69 * 12 RO Real Time Clock Interrupt Status 70 * 11 RO Temperature Interrupt Status 71 * 10 RO Temperature Critical Interrupt Status 72 * 9 RO Ethernet PHY1 Interrupt Status 73 * 8 RO Ethernet PHY3 Interrupt Status 74 * 7 RO PEX8548 Interrupt Status 75 * 6 RO Reserved 76 * 5 RO Watchdog 0 Interrupt Status 77 * 4 RO Watchdog 1 Interrupt Status 78 * 3 RO AXIS Message FIFO A Interrupt Status 79 * 2 RO AXIS Message FIFO B Interrupt Status 80 * 1 RO AXIS Message FIFO C Interrupt Status 81 * 0 RO AXIS Message FIFO D Interrupt Status 82 * 83 * Interrupts can be forwarded to one of two output lines. Nothing 84 * clever is done, so if the masks are incorrectly set, a single input 85 * interrupt could generate interrupts on both output lines! 86 * 87 * The dual lines are there to allow the chained interrupts to be easily 88 * passed into two different cores. We currently do not use this functionality 89 * in this driver. 90 * 91 * Controller can also be configured to generate Machine checks (MCP), again on 92 * two lines, to be attached to two different cores. It is suggested that these 93 * should be masked out. 94 */ 95 96void gef_pic_cascade(unsigned int irq, struct irq_desc *desc) 97{ 98 unsigned int cascade_irq; 99 100 /* 101 * See if we actually have an interrupt, call generic handling code if 102 * we do. 103 */ 104 cascade_irq = gef_pic_get_irq(); 105 106 if (cascade_irq != NO_IRQ) 107 generic_handle_irq(cascade_irq); 108 109 desc->chip->eoi(irq); 110 111} 112 113static void gef_pic_mask(unsigned int virq) 114{ 115 unsigned long flags; 116 unsigned int hwirq; 117 u32 mask; 118 119 hwirq = gef_irq_to_hw(virq); 120 121 raw_spin_lock_irqsave(&gef_pic_lock, flags); 122 mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); 123 mask &= ~(1 << hwirq); 124 out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); 125 raw_spin_unlock_irqrestore(&gef_pic_lock, flags); 126} 127 128static void gef_pic_mask_ack(unsigned int virq) 129{ 130 /* Don't think we actually have to do anything to ack an interrupt, 131 * we just need to clear down the devices interrupt and it will go away 132 */ 133 gef_pic_mask(virq); 134} 135 136static void gef_pic_unmask(unsigned int virq) 137{ 138 unsigned long flags; 139 unsigned int hwirq; 140 u32 mask; 141 142 hwirq = gef_irq_to_hw(virq); 143 144 raw_spin_lock_irqsave(&gef_pic_lock, flags); 145 mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); 146 mask |= (1 << hwirq); 147 out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); 148 raw_spin_unlock_irqrestore(&gef_pic_lock, flags); 149} 150 151static struct irq_chip gef_pic_chip = { 152 .name = "gefp", 153 .mask = gef_pic_mask, 154 .mask_ack = gef_pic_mask_ack, 155 .unmask = gef_pic_unmask, 156}; 157 158 159/* When an interrupt is being configured, this call allows some flexibilty 160 * in deciding which irq_chip structure is used 161 */ 162static int gef_pic_host_map(struct irq_host *h, unsigned int virq, 163 irq_hw_number_t hwirq) 164{ 165 /* All interrupts are LEVEL sensitive */ 166 irq_to_desc(virq)->status |= IRQ_LEVEL; 167 set_irq_chip_and_handler(virq, &gef_pic_chip, handle_level_irq); 168 169 return 0; 170} 171 172static int gef_pic_host_xlate(struct irq_host *h, struct device_node *ct, 173 const u32 *intspec, unsigned int intsize, 174 irq_hw_number_t *out_hwirq, unsigned int *out_flags) 175{ 176 177 *out_hwirq = intspec[0]; 178 if (intsize > 1) 179 *out_flags = intspec[1]; 180 else 181 *out_flags = IRQ_TYPE_LEVEL_HIGH; 182 183 return 0; 184} 185 186static struct irq_host_ops gef_pic_host_ops = { 187 .map = gef_pic_host_map, 188 .xlate = gef_pic_host_xlate, 189}; 190 191 192/* 193 * Initialisation of PIC, this should be called in BSP 194 */ 195void __init gef_pic_init(struct device_node *np) 196{ 197 unsigned long flags; 198 199 /* Map the devices registers into memory */ 200 gef_pic_irq_reg_base = of_iomap(np, 0); 201 202 raw_spin_lock_irqsave(&gef_pic_lock, flags); 203 204 /* Initialise everything as masked. */ 205 out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0); 206 out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0); 207 208 out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0); 209 out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0); 210 211 raw_spin_unlock_irqrestore(&gef_pic_lock, flags); 212 213 /* Map controller */ 214 gef_pic_cascade_irq = irq_of_parse_and_map(np, 0); 215 if (gef_pic_cascade_irq == NO_IRQ) { 216 printk(KERN_ERR "SBC610: failed to map cascade interrupt"); 217 return; 218 } 219 220 /* Setup an irq_host structure */ 221 gef_pic_irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 222 GEF_PIC_NUM_IRQS, 223 &gef_pic_host_ops, NO_IRQ); 224 if (gef_pic_irq_host == NULL) 225 return; 226 227 /* Chain with parent controller */ 228 set_irq_chained_handler(gef_pic_cascade_irq, gef_pic_cascade); 229} 230 231/* 232 * This is called when we receive an interrupt with apparently comes from this 233 * chip - check, returning the highest interrupt generated or return NO_IRQ 234 */ 235unsigned int gef_pic_get_irq(void) 236{ 237 u32 cause, mask, active; 238 unsigned int virq = NO_IRQ; 239 int hwirq; 240 241 cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS); 242 243 mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); 244 245 active = cause & mask; 246 247 if (active) { 248 for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) { 249 if (active & (0x1 << hwirq)) 250 break; 251 } 252 virq = irq_linear_revmap(gef_pic_irq_host, 253 (irq_hw_number_t)hwirq); 254 } 255 256 return virq; 257} 258