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 0;
  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
  94static void socrates_fpga_pic_cascade(struct irq_desc *desc)
  95{
  96        struct irq_chip *chip = irq_desc_get_chip(desc);
  97        unsigned int irq = irq_desc_get_irq(desc);
  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 = socrates_fpga_pic_get_irq(irq);
 105
 106        if (cascade_irq)
 107                generic_handle_irq(cascade_irq);
 108        chip->irq_eoi(&desc->irq_data);
 109}
 110
 111static void socrates_fpga_pic_ack(struct irq_data *d)
 112{
 113        unsigned long flags;
 114        unsigned int irq_line, hwirq = irqd_to_hwirq(d);
 115        uint32_t mask;
 116
 117        irq_line = fpga_irqs[hwirq].irq_line;
 118        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 119        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 120                & SOCRATES_FPGA_IRQ_MASK;
 121        mask |= (1 << (hwirq + 16));
 122        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 123        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 124}
 125
 126static void socrates_fpga_pic_mask(struct irq_data *d)
 127{
 128        unsigned long flags;
 129        unsigned int hwirq = irqd_to_hwirq(d);
 130        int irq_line;
 131        u32 mask;
 132
 133        irq_line = fpga_irqs[hwirq].irq_line;
 134        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 135        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 136                & SOCRATES_FPGA_IRQ_MASK;
 137        mask &= ~(1 << hwirq);
 138        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 139        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 140}
 141
 142static void socrates_fpga_pic_mask_ack(struct irq_data *d)
 143{
 144        unsigned long flags;
 145        unsigned int hwirq = irqd_to_hwirq(d);
 146        int irq_line;
 147        u32 mask;
 148
 149        irq_line = fpga_irqs[hwirq].irq_line;
 150        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 151        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 152                & SOCRATES_FPGA_IRQ_MASK;
 153        mask &= ~(1 << hwirq);
 154        mask |= (1 << (hwirq + 16));
 155        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 156        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 157}
 158
 159static void socrates_fpga_pic_unmask(struct irq_data *d)
 160{
 161        unsigned long flags;
 162        unsigned int hwirq = irqd_to_hwirq(d);
 163        int irq_line;
 164        u32 mask;
 165
 166        irq_line = fpga_irqs[hwirq].irq_line;
 167        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 168        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 169                & SOCRATES_FPGA_IRQ_MASK;
 170        mask |= (1 << hwirq);
 171        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 172        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 173}
 174
 175static void socrates_fpga_pic_eoi(struct irq_data *d)
 176{
 177        unsigned long flags;
 178        unsigned int hwirq = irqd_to_hwirq(d);
 179        int irq_line;
 180        u32 mask;
 181
 182        irq_line = fpga_irqs[hwirq].irq_line;
 183        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 184        mask = socrates_fpga_pic_read(FPGA_PIC_IRQMASK(irq_line))
 185                & SOCRATES_FPGA_IRQ_MASK;
 186        mask |= (1 << (hwirq + 16));
 187        socrates_fpga_pic_write(FPGA_PIC_IRQMASK(irq_line), mask);
 188        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 189}
 190
 191static int socrates_fpga_pic_set_type(struct irq_data *d,
 192                unsigned int flow_type)
 193{
 194        unsigned long flags;
 195        unsigned int hwirq = irqd_to_hwirq(d);
 196        int polarity;
 197        u32 mask;
 198
 199        if (fpga_irqs[hwirq].type != IRQ_TYPE_NONE)
 200                return -EINVAL;
 201
 202        switch (flow_type & IRQ_TYPE_SENSE_MASK) {
 203        case IRQ_TYPE_LEVEL_HIGH:
 204                polarity = 1;
 205                break;
 206        case IRQ_TYPE_LEVEL_LOW:
 207                polarity = 0;
 208                break;
 209        default:
 210                return -EINVAL;
 211        }
 212        raw_spin_lock_irqsave(&socrates_fpga_pic_lock, flags);
 213        mask = socrates_fpga_pic_read(FPGA_PIC_IRQCFG);
 214        if (polarity)
 215                mask |= (1 << hwirq);
 216        else
 217                mask &= ~(1 << hwirq);
 218        socrates_fpga_pic_write(FPGA_PIC_IRQCFG, mask);
 219        raw_spin_unlock_irqrestore(&socrates_fpga_pic_lock, flags);
 220        return 0;
 221}
 222
 223static struct irq_chip socrates_fpga_pic_chip = {
 224        .name           = "FPGA-PIC",
 225        .irq_ack        = socrates_fpga_pic_ack,
 226        .irq_mask       = socrates_fpga_pic_mask,
 227        .irq_mask_ack   = socrates_fpga_pic_mask_ack,
 228        .irq_unmask     = socrates_fpga_pic_unmask,
 229        .irq_eoi        = socrates_fpga_pic_eoi,
 230        .irq_set_type   = socrates_fpga_pic_set_type,
 231};
 232
 233static int socrates_fpga_pic_host_map(struct irq_domain *h, unsigned int virq,
 234                irq_hw_number_t hwirq)
 235{
 236        /* All interrupts are LEVEL sensitive */
 237        irq_set_status_flags(virq, IRQ_LEVEL);
 238        irq_set_chip_and_handler(virq, &socrates_fpga_pic_chip,
 239                                 handle_fasteoi_irq);
 240
 241        return 0;
 242}
 243
 244static int socrates_fpga_pic_host_xlate(struct irq_domain *h,
 245                struct device_node *ct, const u32 *intspec, unsigned int intsize,
 246                irq_hw_number_t *out_hwirq, unsigned int *out_flags)
 247{
 248        struct socrates_fpga_irq_info *fpga_irq = &fpga_irqs[intspec[0]];
 249
 250        *out_hwirq = intspec[0];
 251        if  (fpga_irq->type == IRQ_TYPE_NONE) {
 252                /* type is configurable */
 253                if (intspec[1] != IRQ_TYPE_LEVEL_LOW &&
 254                    intspec[1] != IRQ_TYPE_LEVEL_HIGH) {
 255                        pr_warn("FPGA PIC: invalid irq type, 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_warn("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]) {
 295                        pr_warn("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