linux/drivers/irqchip/irq-sunxi-nmi.c
<<
>>
Prefs
   1/*
   2 * Allwinner A20/A31 SoCs NMI IRQ chip driver.
   3 *
   4 * Carlo Caione <carlo.caione@gmail.com>
   5 *
   6 * This file is licensed under the terms of the GNU General Public
   7 * License version 2.  This program is licensed "as is" without any
   8 * warranty of any kind, whether express or implied.
   9 */
  10
  11#define DRV_NAME        "sunxi-nmi"
  12#define pr_fmt(fmt)     DRV_NAME ": " fmt
  13
  14#include <linux/bitops.h>
  15#include <linux/device.h>
  16#include <linux/io.h>
  17#include <linux/irq.h>
  18#include <linux/interrupt.h>
  19#include <linux/irqdomain.h>
  20#include <linux/of_irq.h>
  21#include <linux/of_address.h>
  22#include <linux/of_platform.h>
  23#include <linux/irqchip.h>
  24#include <linux/irqchip/chained_irq.h>
  25
  26#define SUNXI_NMI_SRC_TYPE_MASK 0x00000003
  27
  28#define SUNXI_NMI_IRQ_BIT       BIT(0)
  29
  30/*
  31 * For deprecated sun6i-a31-sc-nmi compatible.
  32 */
  33#define SUN6I_NMI_CTRL          0x00
  34#define SUN6I_NMI_PENDING       0x04
  35#define SUN6I_NMI_ENABLE        0x34
  36
  37#define SUN7I_NMI_CTRL          0x00
  38#define SUN7I_NMI_PENDING       0x04
  39#define SUN7I_NMI_ENABLE        0x08
  40
  41#define SUN9I_NMI_CTRL          0x00
  42#define SUN9I_NMI_ENABLE        0x04
  43#define SUN9I_NMI_PENDING       0x08
  44
  45enum {
  46        SUNXI_SRC_TYPE_LEVEL_LOW = 0,
  47        SUNXI_SRC_TYPE_EDGE_FALLING,
  48        SUNXI_SRC_TYPE_LEVEL_HIGH,
  49        SUNXI_SRC_TYPE_EDGE_RISING,
  50};
  51
  52struct sunxi_sc_nmi_reg_offs {
  53        u32 ctrl;
  54        u32 pend;
  55        u32 enable;
  56};
  57
  58static const struct sunxi_sc_nmi_reg_offs sun6i_reg_offs __initconst = {
  59        .ctrl   = SUN6I_NMI_CTRL,
  60        .pend   = SUN6I_NMI_PENDING,
  61        .enable = SUN6I_NMI_ENABLE,
  62};
  63
  64static const struct sunxi_sc_nmi_reg_offs sun7i_reg_offs __initconst = {
  65        .ctrl   = SUN7I_NMI_CTRL,
  66        .pend   = SUN7I_NMI_PENDING,
  67        .enable = SUN7I_NMI_ENABLE,
  68};
  69
  70static const struct sunxi_sc_nmi_reg_offs sun9i_reg_offs __initconst = {
  71        .ctrl   = SUN9I_NMI_CTRL,
  72        .pend   = SUN9I_NMI_PENDING,
  73        .enable = SUN9I_NMI_ENABLE,
  74};
  75
  76static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off,
  77                                      u32 val)
  78{
  79        irq_reg_writel(gc, val, off);
  80}
  81
  82static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off)
  83{
  84        return irq_reg_readl(gc, off);
  85}
  86
  87static void sunxi_sc_nmi_handle_irq(struct irq_desc *desc)
  88{
  89        struct irq_domain *domain = irq_desc_get_handler_data(desc);
  90        struct irq_chip *chip = irq_desc_get_chip(desc);
  91        unsigned int virq = irq_find_mapping(domain, 0);
  92
  93        chained_irq_enter(chip, desc);
  94        generic_handle_irq(virq);
  95        chained_irq_exit(chip, desc);
  96}
  97
  98static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
  99{
 100        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
 101        struct irq_chip_type *ct = gc->chip_types;
 102        u32 src_type_reg;
 103        u32 ctrl_off = ct->regs.type;
 104        unsigned int src_type;
 105        unsigned int i;
 106
 107        irq_gc_lock(gc);
 108
 109        switch (flow_type & IRQF_TRIGGER_MASK) {
 110        case IRQ_TYPE_EDGE_FALLING:
 111                src_type = SUNXI_SRC_TYPE_EDGE_FALLING;
 112                break;
 113        case IRQ_TYPE_EDGE_RISING:
 114                src_type = SUNXI_SRC_TYPE_EDGE_RISING;
 115                break;
 116        case IRQ_TYPE_LEVEL_HIGH:
 117                src_type = SUNXI_SRC_TYPE_LEVEL_HIGH;
 118                break;
 119        case IRQ_TYPE_NONE:
 120        case IRQ_TYPE_LEVEL_LOW:
 121                src_type = SUNXI_SRC_TYPE_LEVEL_LOW;
 122                break;
 123        default:
 124                irq_gc_unlock(gc);
 125                pr_err("Cannot assign multiple trigger modes to IRQ %d.\n",
 126                        data->irq);
 127                return -EBADR;
 128        }
 129
 130        irqd_set_trigger_type(data, flow_type);
 131        irq_setup_alt_chip(data, flow_type);
 132
 133        for (i = 0; i < gc->num_ct; i++, ct++)
 134                if (ct->type & flow_type)
 135                        ctrl_off = ct->regs.type;
 136
 137        src_type_reg = sunxi_sc_nmi_read(gc, ctrl_off);
 138        src_type_reg &= ~SUNXI_NMI_SRC_TYPE_MASK;
 139        src_type_reg |= src_type;
 140        sunxi_sc_nmi_write(gc, ctrl_off, src_type_reg);
 141
 142        irq_gc_unlock(gc);
 143
 144        return IRQ_SET_MASK_OK;
 145}
 146
 147static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
 148                                        const struct sunxi_sc_nmi_reg_offs *reg_offs)
 149{
 150        struct irq_domain *domain;
 151        struct irq_chip_generic *gc;
 152        unsigned int irq;
 153        unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
 154        int ret;
 155
 156
 157        domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL);
 158        if (!domain) {
 159                pr_err("Could not register interrupt domain.\n");
 160                return -ENOMEM;
 161        }
 162
 163        ret = irq_alloc_domain_generic_chips(domain, 1, 2, DRV_NAME,
 164                                             handle_fasteoi_irq, clr, 0,
 165                                             IRQ_GC_INIT_MASK_CACHE);
 166        if (ret) {
 167                pr_err("Could not allocate generic interrupt chip.\n");
 168                goto fail_irqd_remove;
 169        }
 170
 171        irq = irq_of_parse_and_map(node, 0);
 172        if (irq <= 0) {
 173                pr_err("unable to parse irq\n");
 174                ret = -EINVAL;
 175                goto fail_irqd_remove;
 176        }
 177
 178        gc = irq_get_domain_generic_chip(domain, 0);
 179        gc->reg_base = of_io_request_and_map(node, 0, of_node_full_name(node));
 180        if (IS_ERR(gc->reg_base)) {
 181                pr_err("unable to map resource\n");
 182                ret = PTR_ERR(gc->reg_base);
 183                goto fail_irqd_remove;
 184        }
 185
 186        gc->chip_types[0].type                  = IRQ_TYPE_LEVEL_MASK;
 187        gc->chip_types[0].chip.irq_mask         = irq_gc_mask_clr_bit;
 188        gc->chip_types[0].chip.irq_unmask       = irq_gc_mask_set_bit;
 189        gc->chip_types[0].chip.irq_eoi          = irq_gc_ack_set_bit;
 190        gc->chip_types[0].chip.irq_set_type     = sunxi_sc_nmi_set_type;
 191        gc->chip_types[0].chip.flags            = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED;
 192        gc->chip_types[0].regs.ack              = reg_offs->pend;
 193        gc->chip_types[0].regs.mask             = reg_offs->enable;
 194        gc->chip_types[0].regs.type             = reg_offs->ctrl;
 195
 196        gc->chip_types[1].type                  = IRQ_TYPE_EDGE_BOTH;
 197        gc->chip_types[1].chip.name             = gc->chip_types[0].chip.name;
 198        gc->chip_types[1].chip.irq_ack          = irq_gc_ack_set_bit;
 199        gc->chip_types[1].chip.irq_mask         = irq_gc_mask_clr_bit;
 200        gc->chip_types[1].chip.irq_unmask       = irq_gc_mask_set_bit;
 201        gc->chip_types[1].chip.irq_set_type     = sunxi_sc_nmi_set_type;
 202        gc->chip_types[1].regs.ack              = reg_offs->pend;
 203        gc->chip_types[1].regs.mask             = reg_offs->enable;
 204        gc->chip_types[1].regs.type             = reg_offs->ctrl;
 205        gc->chip_types[1].handler               = handle_edge_irq;
 206
 207        /* Disable any active interrupts */
 208        sunxi_sc_nmi_write(gc, reg_offs->enable, 0);
 209
 210        /* Clear any pending NMI interrupts */
 211        sunxi_sc_nmi_write(gc, reg_offs->pend, SUNXI_NMI_IRQ_BIT);
 212
 213        irq_set_chained_handler_and_data(irq, sunxi_sc_nmi_handle_irq, domain);
 214
 215        return 0;
 216
 217fail_irqd_remove:
 218        irq_domain_remove(domain);
 219
 220        return ret;
 221}
 222
 223static int __init sun6i_sc_nmi_irq_init(struct device_node *node,
 224                                        struct device_node *parent)
 225{
 226        return sunxi_sc_nmi_irq_init(node, &sun6i_reg_offs);
 227}
 228IRQCHIP_DECLARE(sun6i_sc_nmi, "allwinner,sun6i-a31-sc-nmi", sun6i_sc_nmi_irq_init);
 229
 230static int __init sun7i_sc_nmi_irq_init(struct device_node *node,
 231                                        struct device_node *parent)
 232{
 233        return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs);
 234}
 235IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init);
 236
 237static int __init sun9i_nmi_irq_init(struct device_node *node,
 238                                     struct device_node *parent)
 239{
 240        return sunxi_sc_nmi_irq_init(node, &sun9i_reg_offs);
 241}
 242IRQCHIP_DECLARE(sun9i_nmi, "allwinner,sun9i-a80-nmi", sun9i_nmi_irq_init);
 243