linux/arch/powerpc/platforms/85xx/socrates_fpga_pic.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2008 Ilya Yanok, Emcraft Systems
   3 *
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 *
   9 */
  10
  11#include <linux/irq.h>
  12#include <linux/of_platform.h>
  13#include <linux/io.h>
  14
  15/*
  16 * The FPGA supports 9 interrupt sources, which can be routed to 3
  17 * interrupt request lines of the MPIC. The line to be used can be
  18 * specified through the third cell of FDT property  "interrupts".
  19 */
  20
  21#define SOCRATES_FPGA_NUM_IRQS  9
  22
  23#define FPGA_PIC_IRQCFG         (0x0)
  24#define FPGA_PIC_IRQMASK(n)     (0x4 + 0x4 * (n))
  25
  26#define SOCRATES_FPGA_IRQ_MASK  ((1 << SOCRATES_FPGA_NUM_IRQS) - 1)
  27
  28struct socrates_fpga_irq_info {
  29        unsigned int irq_line;
  30        int type;
  31};
  32
  33/*
  34 * Interrupt routing and type table
  35 *
  36 * IRQ_TYPE_NONE means the interrupt type is configurable,
  37 * otherwise it's fixed to the specified value.
  38 */
  39static struct socrates_fpga_irq_info fpga_irqs[SOCRATES_FPGA_NUM_IRQS] = {
  40        [0] = {0, IRQ_TYPE_NONE},
  41        [1] = {0, IRQ_TYPE_LEVEL_HIGH},
  42        [2] = {0, IRQ_TYPE_LEVEL_LOW},
  43        [3] = {0, IRQ_TYPE_NONE},
  44        [4] = {0, IRQ_TYPE_NONE},
  45        [5] = {0, IRQ_TYPE_NONE},
  46        [6] = {0, IRQ_TYPE_NONE},
  47        [7] = {0, IRQ_TYPE_NONE},
  48        [8] = {0, IRQ_TYPE_LEVEL_HIGH},
  49};
  50
  51static DEFINE_RAW_SPINLOCK(socrates_fpga_pic_lock);
  52
  53static void __iomem *socrates_fpga_pic_iobase;
  54static struct irq_host *socrates_fpga_pic_irq_host;
  55static unsigned int socrates_fpga_irqs[3];
  56
  57static inline uint32_t socrates_fpga_pic_read(int reg)
  58{
  59        return in_be32(socrates_fpga_pic_iobase + reg);
  60}
  61
  62static inline void socrates_fpga_pic_write(int reg, uint32_t val)
  63{
  64        out_be32(socrates_fpga_pic_iobase + reg, val);
  65}
  66
  67static inline unsigned int socrates_fpga_pic_get_irq(unsigned int irq)
  68{
  69        uint32_t cause;
  70        unsigned long flags;
  71        int i;
  72
  73        /* Check irq line routed to the MPIC */
  74        for (i = 0; i < 3; i++) {
  75                if (irq == socrates_fpga_irqs[i])
  76                        break;
  77        }
  78        if (i == 3)
  79                return NO_IRQ;
  80
  81        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
  82        cause = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(i));
  83        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
  84        for (i = SOCRATES_FPGA_NUM_IRQS - 1; i >= 0; i--) {
  85                if (cause >> (i + 16))
  86                        break;
  87        }
  88        return irq_linear_revmap(socrates_fpga_pic_irq_host,
  89                        (irq_hw_number_t)i);
  90}
  91
  92void socrates_fpga_pic_cascade(unsigned int irq, struct irq_desc *desc)
  93{
  94        struct irq_chip *chip = irq_desc_get_chip(desc);
  95        unsigned int cascade_irq;
  96
  97        /*
  98         * See if we actually have an interrupt, call generic handling code if
  99         * we do.
 100         */
 101        cascade_irq = socrates_fpga_pic_get_irq(irq);
 102
 103        if (cascade_irq != NO_IRQ)
 104                generic_handle_irq(cascade_irq);
 105        chip->irq_eoi(&desc->irq_data);
 106}
 107
 108static void socrates_fpga_pic_ack(struct irq_data *d)
 109{
 110        unsigned long flags;
 111        unsigned int irq_line, hwirq = irqd_to_hwirq(d);
 112        uint32_t mask;
 113
 114        irq_line = fpga_irqs[hwirq].irq_line;
 115        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 116        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 117                & SOCRATES_FPGA_IRQ_MASK;
 118        mask |= (1 << (hwirq + 16));
 119        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 120        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 121}
 122
 123static void socrates_fpga_pic_mask(struct irq_data *d)
 124{
 125        unsigned long flags;
 126        unsigned int hwirq = irqd_to_hwirq(d);
 127        int irq_line;
 128        u32 mask;
 129
 130        irq_line = fpga_irqs[hwirq].irq_line;
 131        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 132        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 133                & SOCRATES_FPGA_IRQ_MASK;
 134        mask &= ~(1 << hwirq);
 135        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 136        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 137}
 138
 139static void socrates_fpga_pic_mask_ack(struct irq_data *d)
 140{
 141        unsigned long flags;
 142        unsigned int hwirq = irqd_to_hwirq(d);
 143        int irq_line;
 144        u32 mask;
 145
 146        irq_line = fpga_irqs[hwirq].irq_line;
 147        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 148        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 149                & SOCRATES_FPGA_IRQ_MASK;
 150        mask &= ~(1 << hwirq);
 151        mask |= (1 << (hwirq + 16));
 152        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 153        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 154}
 155
 156static void socrates_fpga_pic_unmask(struct irq_data *d)
 157{
 158        unsigned long flags;
 159        unsigned int hwirq = irqd_to_hwirq(d);
 160        int irq_line;
 161        u32 mask;
 162
 163        irq_line = fpga_irqs[hwirq].irq_line;
 164        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 165        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 166                & SOCRATES_FPGA_IRQ_MASK;
 167        mask |= (1 << hwirq);
 168        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 169        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 170}
 171
 172static void socrates_fpga_pic_eoi(struct irq_data *d)
 173{
 174        unsigned long flags;
 175        unsigned int hwirq = irqd_to_hwirq(d);
 176        int irq_line;
 177        u32 mask;
 178
 179        irq_line = fpga_irqs[hwirq].irq_line;
 180        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 181        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 182                & SOCRATES_FPGA_IRQ_MASK;
 183        mask |= (1 << (hwirq + 16));
 184        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 185        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 186}
 187
 188static int socrates_fpga_pic_set_type(struct irq_data *d,
 189                unsigned int flow_type)
 190{
 191        unsigned long flags;
 192        unsigned int hwirq = irqd_to_hwirq(d);
 193        int polarity;
 194        u32 mask;
 195
 196        if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
 197                return -EINVAL;
 198
 199        switch (flow_type & IRQ_TYPE_SENSE_MASK) {
 200        case IRQ_TYPE_LEVEL_HIGH:
 201                polarity = 1;
 202                break;
 203        case IRQ_TYPE_LEVEL_LOW:
 204                polarity = 0;
 205                break;
 206        default:
 207                return -EINVAL;
 208        }
 209        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 210        mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);
 211        if (polarity)
 212                mask |= (1 << hwirq);
 213        else
 214                mask &= ~(1 << hwirq);
 215        socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);
 216        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 217        return 0;
 218}
 219
 220static struct irq_chip socrates_fpga_pic_chip = {
 221        .name           = "FPGA-PIC",
 222        .irq_ack        = socrates_fpga_pic_ack,
 223        .irq_mask       = socrates_fpga_pic_mask,
 224        .irq_mask_ack   = socrates_fpga_pic_mask_ack,
 225        .irq_unmask     = socrates_fpga_pic_unmask,
 226        .irq_eoi        = socrates_fpga_pic_eoi,
 227        .irq_set_type   = socrates_fpga_pic_set_type,
 228};
 229
 230static int socrates_fpga_pic_host_map(struct irq_host *h, unsigned int virq,
 231                irq_hw_number_t hwirq)
 232{
 233        /* All interrupts are LEVEL sensitive */
 234        irq_set_status_flags(virq, IRQ_LEVEL);
 235        irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,
 236                                 handle_fasteoi_irq);
 237
 238        return 0;
 239}
 240
 241static int socrates_fpga_pic_host_xlate(struct irq_host *h,
 242                struct device_node *ct, const u32 *intspec, unsigned int intsize,
 243                irq_hw_number_t *out_hwirq, unsigned int *out_flags)
 244{
 245        struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];
 246
 247        *out_hwirq = intspec[0];
 248        if  (fpga_irq->type == IRQ_TYPE_NONE) {
 249                /* type is configurable */
 250                if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&
 251                    intspec[1] != IRQ_TYPE_LEVEL_HIGH) {
 252                        pr_warning("FPGA PIC: invalid irq type, "
 253                                   "setting default active low\n");
 254                        *out_flags = IRQ_TYPE_LEVEL_LOW;
 255                } else {
 256                        *out_flags = intspec[1];
 257                }
 258        } else {
 259                /* type is fixed */
 260                *out_flags = fpga_irq->type;
 261        }
 262
 263        /* Use specified interrupt routing */
 264        if (intspec[2] <= 2)
 265                fpga_irq->irq_line = intspec[2];
 266        else
 267                pr_warning("FPGA PIC: invalid irq routing\n");
 268
 269        return 0;
 270}
 271
 272static struct irq_host_ops socrates_fpga_pic_host_ops = {
 273        .map    = socrates_fpga_pic_host_map,
 274        .xlate  = socrates_fpga_pic_host_xlate,
 275};
 276
 277void socrates_fpga_pic_init(struct device_node *pic)
 278{
 279        unsigned long flags;
 280        int i;
 281
 282        /* Setup an irq_host structure */
 283        socrates_fpga_pic_irq_host = irq_alloc_host(pic, IRQ_HOST_MAP_LINEAR,
 284                        SOCRATES_FPGA_NUM_IRQS, &socrates_fpga_pic_host_ops,
 285                        SOCRATES_FPGA_NUM_IRQS);
 286        if (socrates_fpga_pic_irq_host == NULL) {
 287                pr_err("FPGA PIC: Unable to allocate host\n");
 288                return;
 289        }
 290
 291        for (i = 0; i < 3; i++) {
 292                socrates_fpga_irqs[i] = irq_of_parse_and_map(pic, i);
 293                if (socrates_fpga_irqs[i] == NO_IRQ) {
 294                        pr_warning("FPGA PIC: can't get irq%d.\n", i);
 295                        continue;
 296                }
 297                irq_set_chained_handler(socrates_fpga_irqs[i],
 298                                        socrates_fpga_pic_cascade);
 299        }
 300
 301        socrates_fpga_pic_iobase = of_iomap(pic, 0);
 302
 303        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 304        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(0),
 305                        SOCRATES_FPGA_IRQ_MASK << 16);
 306        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(1),
 307                        SOCRATES_FPGA_IRQ_MASK << 16);
 308        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(2),
 309                        SOCRATES_FPGA_IRQ_MASK << 16);
 310        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 311
 312        pr_info("FPGA PIC: Setting up Socrates FPGA PIC\n");
 313}
 314