linux/drivers/irqchip/irq-pic32-evic.c
<<
>>
Prefs
   1/*
   2 * Cristian Birsan <cristian.birsan@microchip.com>
   3 * Joshua Henderson <joshua.henderson@microchip.com>
   4 * Copyright (C) 2016 Microchip Technology Inc.  All rights reserved.
   5 *
   6 * This program is free software; you can redistribute  it and/or modify it
   7 * under  the terms of  the GNU General  Public License as published by the
   8 * Free Software Foundation;  either version 2 of the  License, or (at your
   9 * option) any later version.
  10 */
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/interrupt.h>
  14#include <linux/irqdomain.h>
  15#include <linux/of_address.h>
  16#include <linux/slab.h>
  17#include <linux/io.h>
  18#include <linux/irqchip.h>
  19#include <linux/irq.h>
  20
  21#include <asm/irq.h>
  22#include <asm/traps.h>
  23#include <asm/mach-pic32/pic32.h>
  24
  25#define REG_INTCON      0x0000
  26#define REG_INTSTAT     0x0020
  27#define REG_IFS_OFFSET  0x0040
  28#define REG_IEC_OFFSET  0x00C0
  29#define REG_IPC_OFFSET  0x0140
  30#define REG_OFF_OFFSET  0x0540
  31
  32#define MAJPRI_MASK     0x07
  33#define SUBPRI_MASK     0x03
  34#define PRIORITY_MASK   0x1F
  35
  36#define PIC32_INT_PRI(pri, subpri)                              \
  37        ((((pri) & MAJPRI_MASK) << 2) | ((subpri) & SUBPRI_MASK))
  38
  39struct evic_chip_data {
  40        u32 irq_types[NR_IRQS];
  41        u32 ext_irqs[8];
  42};
  43
  44static struct irq_domain *evic_irq_domain;
  45static void __iomem *evic_base;
  46
  47asmlinkage void __weak plat_irq_dispatch(void)
  48{
  49        unsigned int irq, hwirq;
  50
  51        hwirq = readl(evic_base + REG_INTSTAT) & 0xFF;
  52        irq = irq_linear_revmap(evic_irq_domain, hwirq);
  53        do_IRQ(irq);
  54}
  55
  56static struct evic_chip_data *irqd_to_priv(struct irq_data *data)
  57{
  58        return (struct evic_chip_data *)data->domain->host_data;
  59}
  60
  61static int pic32_set_ext_polarity(int bit, u32 type)
  62{
  63        /*
  64         * External interrupts can be either edge rising or edge falling,
  65         * but not both.
  66         */
  67        switch (type) {
  68        case IRQ_TYPE_EDGE_RISING:
  69                writel(BIT(bit), evic_base + PIC32_SET(REG_INTCON));
  70                break;
  71        case IRQ_TYPE_EDGE_FALLING:
  72                writel(BIT(bit), evic_base + PIC32_CLR(REG_INTCON));
  73                break;
  74        default:
  75                return -EINVAL;
  76        }
  77
  78        return 0;
  79}
  80
  81static int pic32_set_type_edge(struct irq_data *data,
  82                               unsigned int flow_type)
  83{
  84        struct evic_chip_data *priv = irqd_to_priv(data);
  85        int ret;
  86        int i;
  87
  88        if (!(flow_type & IRQ_TYPE_EDGE_BOTH))
  89                return -EBADR;
  90
  91        /* set polarity for external interrupts only */
  92        for (i = 0; i < ARRAY_SIZE(priv->ext_irqs); i++) {
  93                if (priv->ext_irqs[i] == data->hwirq) {
  94                        ret = pic32_set_ext_polarity(i, flow_type);
  95                        if (ret)
  96                                return ret;
  97                }
  98        }
  99
 100        irqd_set_trigger_type(data, flow_type);
 101
 102        return IRQ_SET_MASK_OK;
 103}
 104
 105static void pic32_bind_evic_interrupt(int irq, int set)
 106{
 107        writel(set, evic_base + REG_OFF_OFFSET + irq * 4);
 108}
 109
 110static void pic32_set_irq_priority(int irq, int priority)
 111{
 112        u32 reg, shift;
 113
 114        reg = irq / 4;
 115        shift = (irq % 4) * 8;
 116
 117        writel(PRIORITY_MASK << shift,
 118                evic_base + PIC32_CLR(REG_IPC_OFFSET + reg * 0x10));
 119        writel(priority << shift,
 120                evic_base + PIC32_SET(REG_IPC_OFFSET + reg * 0x10));
 121}
 122
 123#define IRQ_REG_MASK(_hwirq, _reg, _mask)                      \
 124        do {                                                   \
 125                _reg = _hwirq / 32;                            \
 126                _mask = 1 << (_hwirq % 32);                    \
 127        } while (0)
 128
 129static int pic32_irq_domain_map(struct irq_domain *d, unsigned int virq,
 130                                irq_hw_number_t hw)
 131{
 132        struct evic_chip_data *priv = d->host_data;
 133        struct irq_data *data;
 134        int ret;
 135        u32 iecclr, ifsclr;
 136        u32 reg, mask;
 137
 138        ret = irq_map_generic_chip(d, virq, hw);
 139        if (ret)
 140                return ret;
 141
 142        /*
 143         * Piggyback on xlate function to move to an alternate chip as necessary
 144         * at time of mapping instead of allowing the flow handler/chip to be
 145         * changed later. This requires all interrupts to be configured through
 146         * DT.
 147         */
 148        if (priv->irq_types[hw] & IRQ_TYPE_SENSE_MASK) {
 149                data = irq_domain_get_irq_data(d, virq);
 150                irqd_set_trigger_type(data, priv->irq_types[hw]);
 151                irq_setup_alt_chip(data, priv->irq_types[hw]);
 152        }
 153
 154        IRQ_REG_MASK(hw, reg, mask);
 155
 156        iecclr = PIC32_CLR(REG_IEC_OFFSET + reg * 0x10);
 157        ifsclr = PIC32_CLR(REG_IFS_OFFSET + reg * 0x10);
 158
 159        /* mask and clear flag */
 160        writel(mask, evic_base + iecclr);
 161        writel(mask, evic_base + ifsclr);
 162
 163        /* default priority is required */
 164        pic32_set_irq_priority(hw, PIC32_INT_PRI(2, 0));
 165
 166        return ret;
 167}
 168
 169int pic32_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
 170                           const u32 *intspec, unsigned int intsize,
 171                           irq_hw_number_t *out_hwirq, unsigned int *out_type)
 172{
 173        struct evic_chip_data *priv = d->host_data;
 174
 175        if (WARN_ON(intsize < 2))
 176                return -EINVAL;
 177
 178        if (WARN_ON(intspec[0] >= NR_IRQS))
 179                return -EINVAL;
 180
 181        *out_hwirq = intspec[0];
 182        *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
 183
 184        priv->irq_types[intspec[0]] = intspec[1] & IRQ_TYPE_SENSE_MASK;
 185
 186        return 0;
 187}
 188
 189static const struct irq_domain_ops pic32_irq_domain_ops = {
 190        .map    = pic32_irq_domain_map,
 191        .xlate  = pic32_irq_domain_xlate,
 192};
 193
 194static void __init pic32_ext_irq_of_init(struct irq_domain *domain)
 195{
 196        struct device_node *node = irq_domain_get_of_node(domain);
 197        struct evic_chip_data *priv = domain->host_data;
 198        struct property *prop;
 199        const __le32 *p;
 200        u32 hwirq;
 201        int i = 0;
 202        const char *pname = "microchip,external-irqs";
 203
 204        of_property_for_each_u32(node, pname, prop, p, hwirq) {
 205                if (i >= ARRAY_SIZE(priv->ext_irqs)) {
 206                        pr_warn("More than %d external irq, skip rest\n",
 207                                ARRAY_SIZE(priv->ext_irqs));
 208                        break;
 209                }
 210
 211                priv->ext_irqs[i] = hwirq;
 212                i++;
 213        }
 214}
 215
 216static int __init pic32_of_init(struct device_node *node,
 217                                struct device_node *parent)
 218{
 219        struct irq_chip_generic *gc;
 220        struct evic_chip_data *priv;
 221        unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
 222        int nchips, ret;
 223        int i;
 224
 225        nchips = DIV_ROUND_UP(NR_IRQS, 32);
 226
 227        evic_base = of_iomap(node, 0);
 228        if (!evic_base)
 229                return -ENOMEM;
 230
 231        priv = kcalloc(nchips, sizeof(*priv), GFP_KERNEL);
 232        if (!priv) {
 233                ret = -ENOMEM;
 234                goto err_iounmap;
 235        }
 236
 237        evic_irq_domain = irq_domain_add_linear(node, nchips * 32,
 238                                                &pic32_irq_domain_ops,
 239                                                priv);
 240        if (!evic_irq_domain) {
 241                ret = -ENOMEM;
 242                goto err_free_priv;
 243        }
 244
 245        /*
 246         * The PIC32 EVIC has a linear list of irqs and the type of each
 247         * irq is determined by the hardware peripheral the EVIC is arbitrating.
 248         * These irq types are defined in the datasheet as "persistent" and
 249         * "non-persistent" which are mapped here to level and edge
 250         * respectively. To manage the different flow handler requirements of
 251         * each irq type, different chip_types are used.
 252         */
 253        ret = irq_alloc_domain_generic_chips(evic_irq_domain, 32, 2,
 254                                             "evic-level", handle_level_irq,
 255                                             clr, 0, 0);
 256        if (ret)
 257                goto err_domain_remove;
 258
 259        board_bind_eic_interrupt = &pic32_bind_evic_interrupt;
 260
 261        for (i = 0; i < nchips; i++) {
 262                u32 ifsclr = PIC32_CLR(REG_IFS_OFFSET + (i * 0x10));
 263                u32 iec = REG_IEC_OFFSET + (i * 0x10);
 264
 265                gc = irq_get_domain_generic_chip(evic_irq_domain, i * 32);
 266
 267                gc->reg_base = evic_base;
 268                gc->unused = 0;
 269
 270                /*
 271                 * Level/persistent interrupts have a special requirement that
 272                 * the condition generating the interrupt be cleared before the
 273                 * interrupt flag (ifs) can be cleared. chip.irq_eoi is used to
 274                 * complete the interrupt with an ack.
 275                 */
 276                gc->chip_types[0].type                  = IRQ_TYPE_LEVEL_MASK;
 277                gc->chip_types[0].handler               = handle_fasteoi_irq;
 278                gc->chip_types[0].regs.ack              = ifsclr;
 279                gc->chip_types[0].regs.mask             = iec;
 280                gc->chip_types[0].chip.name             = "evic-level";
 281                gc->chip_types[0].chip.irq_eoi          = irq_gc_ack_set_bit;
 282                gc->chip_types[0].chip.irq_mask         = irq_gc_mask_clr_bit;
 283                gc->chip_types[0].chip.irq_unmask       = irq_gc_mask_set_bit;
 284                gc->chip_types[0].chip.flags            = IRQCHIP_SKIP_SET_WAKE;
 285
 286                /* Edge interrupts */
 287                gc->chip_types[1].type                  = IRQ_TYPE_EDGE_BOTH;
 288                gc->chip_types[1].handler               = handle_edge_irq;
 289                gc->chip_types[1].regs.ack              = ifsclr;
 290                gc->chip_types[1].regs.mask             = iec;
 291                gc->chip_types[1].chip.name             = "evic-edge";
 292                gc->chip_types[1].chip.irq_ack          = irq_gc_ack_set_bit;
 293                gc->chip_types[1].chip.irq_mask         = irq_gc_mask_clr_bit;
 294                gc->chip_types[1].chip.irq_unmask       = irq_gc_mask_set_bit;
 295                gc->chip_types[1].chip.irq_set_type     = pic32_set_type_edge;
 296                gc->chip_types[1].chip.flags            = IRQCHIP_SKIP_SET_WAKE;
 297
 298                gc->private = &priv[i];
 299        }
 300
 301        irq_set_default_host(evic_irq_domain);
 302
 303        /*
 304         * External interrupts have software configurable edge polarity. These
 305         * interrupts are defined in DT allowing polarity to be configured only
 306         * for these interrupts when requested.
 307         */
 308        pic32_ext_irq_of_init(evic_irq_domain);
 309
 310        return 0;
 311
 312err_domain_remove:
 313        irq_domain_remove(evic_irq_domain);
 314
 315err_free_priv:
 316        kfree(priv);
 317
 318err_iounmap:
 319        iounmap(evic_base);
 320
 321        return ret;
 322}
 323
 324IRQCHIP_DECLARE(pic32_evic, "microchip,pic32mzda-evic", pic32_of_init);
 325