linux/arch/powerpc/platforms/86xx/gef_pic.c
<<
>>
Prefs
   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
  50static DEFINE_RAW_SPINLOCK(gef_pic_lock);
  51
  52static void __iomem *gef_pic_irq_reg_base;
  53static struct irq_host *gef_pic_irq_host;
  54static int gef_pic_cascade_irq;
  55
  56/*
  57 * Interrupt Controller Handling
  58 *
  59 * The interrupt controller handles interrupts for most on board interrupts,
  60 * apart from PCI interrupts. For example on SBC610:
  61 *
  62 * 17:31 RO Reserved
  63 * 16    RO PCI Express Doorbell 3 Status
  64 * 15    RO PCI Express Doorbell 2 Status
  65 * 14    RO PCI Express Doorbell 1 Status
  66 * 13    RO PCI Express Doorbell 0 Status
  67 * 12    RO Real Time Clock Interrupt Status
  68 * 11    RO Temperature Interrupt Status
  69 * 10    RO Temperature Critical Interrupt Status
  70 * 9     RO Ethernet PHY1 Interrupt Status
  71 * 8     RO Ethernet PHY3 Interrupt Status
  72 * 7     RO PEX8548 Interrupt Status
  73 * 6     RO Reserved
  74 * 5     RO Watchdog 0 Interrupt Status
  75 * 4     RO Watchdog 1 Interrupt Status
  76 * 3     RO AXIS Message FIFO A Interrupt Status
  77 * 2     RO AXIS Message FIFO B Interrupt Status
  78 * 1     RO AXIS Message FIFO C Interrupt Status
  79 * 0     RO AXIS Message FIFO D Interrupt Status
  80 *
  81 * Interrupts can be forwarded to one of two output lines. Nothing
  82 * clever is done, so if the masks are incorrectly set, a single input
  83 * interrupt could generate interrupts on both output lines!
  84 *
  85 * The dual lines are there to allow the chained interrupts to be easily
  86 * passed into two different cores. We currently do not use this functionality
  87 * in this driver.
  88 *
  89 * Controller can also be configured to generate Machine checks (MCP), again on
  90 * two lines, to be attached to two different cores. It is suggested that these
  91 * should be masked out.
  92 */
  93
  94void gef_pic_cascade(unsigned int irq, struct irq_desc *desc)
  95{
  96        struct irq_chip *chip = irq_desc_get_chip(desc);
  97        unsigned int cascade_irq;
  98
  99        /*
 100         * See if we actually have an interrupt, call generic handling code if
 101         * we do.
 102         */
 103        cascade_irq = gef_pic_get_irq();
 104
 105        if (cascade_irq != NO_IRQ)
 106                generic_handle_irq(cascade_irq);
 107
 108        chip->irq_eoi(&desc->irq_data);
 109}
 110
 111static void gef_pic_mask(struct irq_data *d)
 112{
 113        unsigned long flags;
 114        unsigned int hwirq = irqd_to_hwirq(d);
 115        u32 mask;
 116
 117        raw_spin_lock_irqsave(&gef_pic_lock, flags);
 118        mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
 119        mask &= ~(1 << hwirq);
 120        out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask);
 121        raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
 122}
 123
 124static void gef_pic_mask_ack(struct irq_data *d)
 125{
 126        /* Don't think we actually have to do anything to ack an interrupt,
 127         * we just need to clear down the devices interrupt and it will go away
 128         */
 129        gef_pic_mask(d);
 130}
 131
 132static void gef_pic_unmask(struct irq_data *d)
 133{
 134        unsigned long flags;
 135        unsigned int hwirq = irqd_to_hwirq(d);
 136        u32 mask;
 137
 138        raw_spin_lock_irqsave(&gef_pic_lock, flags);
 139        mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
 140        mask |= (1 << hwirq);
 141        out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask);
 142        raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
 143}
 144
 145static struct irq_chip gef_pic_chip = {
 146        .name           = "gefp",
 147        .irq_mask       = gef_pic_mask,
 148        .irq_mask_ack   = gef_pic_mask_ack,
 149        .irq_unmask     = gef_pic_unmask,
 150};
 151
 152
 153/* When an interrupt is being configured, this call allows some flexibilty
 154 * in deciding which irq_chip structure is used
 155 */
 156static int gef_pic_host_map(struct irq_host *h, unsigned int virq,
 157                          irq_hw_number_t hwirq)
 158{
 159        /* All interrupts are LEVEL sensitive */
 160        irq_set_status_flags(virq, IRQ_LEVEL);
 161        irq_set_chip_and_handler(virq, &gef_pic_chip, handle_level_irq);
 162
 163        return 0;
 164}
 165
 166static int gef_pic_host_xlate(struct irq_host *h, struct device_node *ct,
 167                            const u32 *intspec, unsigned int intsize,
 168                            irq_hw_number_t *out_hwirq, unsigned int *out_flags)
 169{
 170
 171        *out_hwirq = intspec[0];
 172        if (intsize > 1)
 173                *out_flags = intspec[1];
 174        else
 175                *out_flags = IRQ_TYPE_LEVEL_HIGH;
 176
 177        return 0;
 178}
 179
 180static struct irq_host_ops gef_pic_host_ops = {
 181        .map    = gef_pic_host_map,
 182        .xlate  = gef_pic_host_xlate,
 183};
 184
 185
 186/*
 187 * Initialisation of PIC, this should be called in BSP
 188 */
 189void __init gef_pic_init(struct device_node *np)
 190{
 191        unsigned long flags;
 192
 193        /* Map the devices registers into memory */
 194        gef_pic_irq_reg_base = of_iomap(np, 0);
 195
 196        raw_spin_lock_irqsave(&gef_pic_lock, flags);
 197
 198        /* Initialise everything as masked. */
 199        out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0);
 200        out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0);
 201
 202        out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0);
 203        out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0);
 204
 205        raw_spin_unlock_irqrestore(&gef_pic_lock, flags);
 206
 207        /* Map controller */
 208        gef_pic_cascade_irq = irq_of_parse_and_map(np, 0);
 209        if (gef_pic_cascade_irq == NO_IRQ) {
 210                printk(KERN_ERR "SBC610: failed to map cascade interrupt");
 211                return;
 212        }
 213
 214        /* Setup an irq_host structure */
 215        gef_pic_irq_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR,
 216                                          GEF_PIC_NUM_IRQS,
 217                                          &gef_pic_host_ops, NO_IRQ);
 218        if (gef_pic_irq_host == NULL)
 219                return;
 220
 221        /* Chain with parent controller */
 222        irq_set_chained_handler(gef_pic_cascade_irq, gef_pic_cascade);
 223}
 224
 225/*
 226 * This is called when we receive an interrupt with apparently comes from this
 227 * chip - check, returning the highest interrupt generated or return NO_IRQ
 228 */
 229unsigned int gef_pic_get_irq(void)
 230{
 231        u32 cause, mask, active;
 232        unsigned int virq = NO_IRQ;
 233        int hwirq;
 234
 235        cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS);
 236
 237        mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0));
 238
 239        active = cause & mask;
 240
 241        if (active) {
 242                for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) {
 243                        if (active & (0x1 << hwirq))
 244                                break;
 245                }
 246                virq = irq_linear_revmap(gef_pic_irq_host,
 247                        (irq_hw_number_t)hwirq);
 248        }
 249
 250        return virq;
 251}
 252
 253