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