linux/arch/powerpc/sysdev/xilinx_intc.c
<<
>>
Prefs
   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 <linux/of_address.h>
  27#include <linux/of_irq.h>
  28#include <asm/io.h>
  29#include <asm/processor.h>
  30#include <asm/i8259.h>
  31#include <asm/irq.h>
  32
  33/*
  34 * INTC Registers
  35 */
  36#define XINTC_ISR       0       /* Interrupt Status */
  37#define XINTC_IPR       4       /* Interrupt Pending */
  38#define XINTC_IER       8       /* Interrupt Enable */
  39#define XINTC_IAR       12      /* Interrupt Acknowledge */
  40#define XINTC_SIE       16      /* Set Interrupt Enable bits */
  41#define XINTC_CIE       20      /* Clear Interrupt Enable bits */
  42#define XINTC_IVR       24      /* Interrupt Vector */
  43#define XINTC_MER       28      /* Master Enable */
  44
  45static struct irq_domain *master_irqhost;
  46
  47#define XILINX_INTC_MAXIRQS     (32)
  48
  49/* The following table allows the interrupt type, edge or level,
  50 * to be cached after being read from the device tree until the interrupt
  51 * is mapped
  52 */
  53static int xilinx_intc_typetable[XILINX_INTC_MAXIRQS];
  54
  55/* Map the interrupt type from the device tree to the interrupt types
  56 * used by the interrupt subsystem
  57 */
  58static unsigned char xilinx_intc_map_senses[] = {
  59        IRQ_TYPE_EDGE_RISING,
  60        IRQ_TYPE_EDGE_FALLING,
  61        IRQ_TYPE_LEVEL_HIGH,
  62        IRQ_TYPE_LEVEL_LOW,
  63};
  64
  65/*
  66 * The interrupt controller is setup such that it doesn't work well with
  67 * the level interrupt handler in the kernel because the handler acks the
  68 * interrupt before calling the application interrupt handler. To deal with
  69 * that, we use 2 different irq chips so that different functions can be
  70 * used for level and edge type interrupts.
  71 *
  72 * IRQ Chip common (across level and edge) operations
  73 */
  74static void xilinx_intc_mask(struct irq_data *d)
  75{
  76        int irq = irqd_to_hwirq(d);
  77        void * regs = irq_data_get_irq_chip_data(d);
  78        pr_debug("mask: %d\n", irq);
  79        out_be32(regs + XINTC_CIE, 1 << irq);
  80}
  81
  82static int xilinx_intc_set_type(struct irq_data *d, unsigned int flow_type)
  83{
  84        return 0;
  85}
  86
  87/*
  88 * IRQ Chip level operations
  89 */
  90static void xilinx_intc_level_unmask(struct irq_data *d)
  91{
  92        int irq = irqd_to_hwirq(d);
  93        void * regs = irq_data_get_irq_chip_data(d);
  94        pr_debug("unmask: %d\n", irq);
  95        out_be32(regs + XINTC_SIE, 1 << irq);
  96
  97        /* ack level irqs because they can't be acked during
  98         * ack function since the handle_level_irq function
  99         * acks the irq before calling the inerrupt handler
 100         */
 101        out_be32(regs + XINTC_IAR, 1 << irq);
 102}
 103
 104static struct irq_chip xilinx_intc_level_irqchip = {
 105        .name = "Xilinx Level INTC",
 106        .irq_mask = xilinx_intc_mask,
 107        .irq_mask_ack = xilinx_intc_mask,
 108        .irq_unmask = xilinx_intc_level_unmask,
 109        .irq_set_type = xilinx_intc_set_type,
 110};
 111
 112/*
 113 * IRQ Chip edge operations
 114 */
 115static void xilinx_intc_edge_unmask(struct irq_data *d)
 116{
 117        int irq = irqd_to_hwirq(d);
 118        void *regs = irq_data_get_irq_chip_data(d);
 119        pr_debug("unmask: %d\n", irq);
 120        out_be32(regs + XINTC_SIE, 1 << irq);
 121}
 122
 123static void xilinx_intc_edge_ack(struct irq_data *d)
 124{
 125        int irq = irqd_to_hwirq(d);
 126        void * regs = irq_data_get_irq_chip_data(d);
 127        pr_debug("ack: %d\n", irq);
 128        out_be32(regs + XINTC_IAR, 1 << irq);
 129}
 130
 131static struct irq_chip xilinx_intc_edge_irqchip = {
 132        .name = "Xilinx Edge  INTC",
 133        .irq_mask = xilinx_intc_mask,
 134        .irq_unmask = xilinx_intc_edge_unmask,
 135        .irq_ack = xilinx_intc_edge_ack,
 136        .irq_set_type = xilinx_intc_set_type,
 137};
 138
 139/*
 140 * IRQ Host operations
 141 */
 142
 143/**
 144 * xilinx_intc_xlate - translate virq# from device tree interrupts property
 145 */
 146static int xilinx_intc_xlate(struct irq_domain *h, struct device_node *ct,
 147                                const u32 *intspec, unsigned int intsize,
 148                                irq_hw_number_t *out_hwirq,
 149                                unsigned int *out_flags)
 150{
 151        if ((intsize < 2) || (intspec[0] >= XILINX_INTC_MAXIRQS))
 152                return -EINVAL;
 153
 154        /* keep a copy of the interrupt type til the interrupt is mapped
 155         */
 156        xilinx_intc_typetable[intspec[0]] = xilinx_intc_map_senses[intspec[1]];
 157
 158        /* Xilinx uses 2 interrupt entries, the 1st being the h/w
 159         * interrupt number, the 2nd being the interrupt type, edge or level
 160         */
 161        *out_hwirq = intspec[0];
 162        *out_flags = xilinx_intc_map_senses[intspec[1]];
 163
 164        return 0;
 165}
 166static int xilinx_intc_map(struct irq_domain *h, unsigned int virq,
 167                                  irq_hw_number_t irq)
 168{
 169        irq_set_chip_data(virq, h->host_data);
 170
 171        if (xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_HIGH ||
 172            xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_LOW) {
 173                irq_set_chip_and_handler(virq, &xilinx_intc_level_irqchip,
 174                                         handle_level_irq);
 175        } else {
 176                irq_set_chip_and_handler(virq, &xilinx_intc_edge_irqchip,
 177                                         handle_edge_irq);
 178        }
 179        return 0;
 180}
 181
 182static struct irq_domain_ops xilinx_intc_ops = {
 183        .map = xilinx_intc_map,
 184        .xlate = xilinx_intc_xlate,
 185};
 186
 187struct irq_domain * __init
 188xilinx_intc_init(struct device_node *np)
 189{
 190        struct irq_domain * irq;
 191        void * regs;
 192
 193        /* Find and map the intc registers */
 194        regs = of_iomap(np, 0);
 195        if (!regs) {
 196                pr_err("xilinx_intc: could not map registers\n");
 197                return NULL;
 198        }
 199
 200        /* Setup interrupt controller */
 201        out_be32(regs + XINTC_IER, 0); /* disable all irqs */
 202        out_be32(regs + XINTC_IAR, ~(u32) 0); /* Acknowledge pending irqs */
 203        out_be32(regs + XINTC_MER, 0x3UL); /* Turn on the Master Enable. */
 204
 205        /* Allocate and initialize an irq_domain structure. */
 206        irq = irq_domain_add_linear(np, XILINX_INTC_MAXIRQS, &xilinx_intc_ops,
 207                                    regs);
 208        if (!irq)
 209                panic(__FILE__ ": Cannot allocate IRQ host\n");
 210
 211        return irq;
 212}
 213
 214int xilinx_intc_get_irq(void)
 215{
 216        void * regs = master_irqhost->host_data;
 217        pr_debug("get_irq:\n");
 218        return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR));
 219}
 220
 221#if defined(CONFIG_PPC_I8259)
 222/*
 223 * Support code for cascading to 8259 interrupt controllers
 224 */
 225static void xilinx_i8259_cascade(unsigned int irq, struct irq_desc *desc)
 226{
 227        struct irq_chip *chip = irq_desc_get_chip(desc);
 228        unsigned int cascade_irq = i8259_irq();
 229
 230        if (cascade_irq)
 231                generic_handle_irq(cascade_irq);
 232
 233        /* Let xilinx_intc end the interrupt */
 234        chip->irq_unmask(&desc->irq_data);
 235}
 236
 237static void __init xilinx_i8259_setup_cascade(void)
 238{
 239        struct device_node *cascade_node;
 240        int cascade_irq;
 241
 242        /* Initialize i8259 controller */
 243        cascade_node = of_find_compatible_node(NULL, NULL, "chrp,iic");
 244        if (!cascade_node)
 245                return;
 246
 247        cascade_irq = irq_of_parse_and_map(cascade_node, 0);
 248        if (!cascade_irq) {
 249                pr_err("virtex_ml510: Failed to map cascade interrupt\n");
 250                goto out;
 251        }
 252
 253        i8259_init(cascade_node, 0);
 254        irq_set_chained_handler(cascade_irq, xilinx_i8259_cascade);
 255
 256        /* Program irq 7 (usb/audio), 14/15 (ide) to level sensitive */
 257        /* This looks like a dirty hack to me --gcl */
 258        outb(0xc0, 0x4d0);
 259        outb(0xc0, 0x4d1);
 260
 261 out:
 262        of_node_put(cascade_node);
 263}
 264#else
 265static inline void xilinx_i8259_setup_cascade(void) { return; }
 266#endif /* defined(CONFIG_PPC_I8259) */
 267
 268static const struct of_device_id xilinx_intc_match[] __initconst = {
 269        { .compatible = "xlnx,opb-intc-1.00.c", },
 270        { .compatible = "xlnx,xps-intc-1.00.a", },
 271        {}
 272};
 273
 274/*
 275 * Initialize master Xilinx interrupt controller
 276 */
 277void __init xilinx_intc_init_tree(void)
 278{
 279        struct device_node *np;
 280
 281        /* find top level interrupt controller */
 282        for_each_matching_node(np, xilinx_intc_match) {
 283                if (!of_get_property(np, "interrupts", NULL))
 284                        break;
 285        }
 286        BUG_ON(!np);
 287
 288        master_irqhost = xilinx_intc_init(np);
 289        BUG_ON(!master_irqhost);
 290
 291        irq_set_default_host(master_irqhost);
 292        of_node_put(np);
 293
 294        xilinx_i8259_setup_cascade();
 295}
 296