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