linux/drivers/irqchip/irq-mscc-ocelot.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
   2/*
   3 * Microsemi Ocelot IRQ controller driver
   4 *
   5 * Copyright (c) 2017 Microsemi Corporation
   6 */
   7#include <linux/bitops.h>
   8#include <linux/irq.h>
   9#include <linux/of_address.h>
  10#include <linux/of_irq.h>
  11#include <linux/irqchip.h>
  12#include <linux/irqchip/chained_irq.h>
  13#include <linux/interrupt.h>
  14
  15#define ICPU_CFG_INTR_DST_INTR_IDENT(_p, x) ((_p)->reg_off_ident + 0x4 * (x))
  16#define ICPU_CFG_INTR_INTR_TRIGGER(_p, x)   ((_p)->reg_off_trigger + 0x4 * (x))
  17
  18#define FLAGS_HAS_TRIGGER       BIT(0)
  19#define FLAGS_NEED_INIT_ENABLE  BIT(1)
  20
  21struct chip_props {
  22        u8 flags;
  23        u8 reg_off_sticky;
  24        u8 reg_off_ena;
  25        u8 reg_off_ena_clr;
  26        u8 reg_off_ena_set;
  27        u8 reg_off_ident;
  28        u8 reg_off_trigger;
  29        u8 reg_off_ena_irq0;
  30        u8 n_irq;
  31};
  32
  33static struct chip_props ocelot_props = {
  34        .flags                  = FLAGS_HAS_TRIGGER,
  35        .reg_off_sticky         = 0x10,
  36        .reg_off_ena            = 0x18,
  37        .reg_off_ena_clr        = 0x1c,
  38        .reg_off_ena_set        = 0x20,
  39        .reg_off_ident          = 0x38,
  40        .reg_off_trigger        = 0x5c,
  41        .n_irq                  = 24,
  42};
  43
  44static struct chip_props serval_props = {
  45        .flags                  = FLAGS_HAS_TRIGGER,
  46        .reg_off_sticky         = 0xc,
  47        .reg_off_ena            = 0x14,
  48        .reg_off_ena_clr        = 0x18,
  49        .reg_off_ena_set        = 0x1c,
  50        .reg_off_ident          = 0x20,
  51        .reg_off_trigger        = 0x4,
  52        .n_irq                  = 24,
  53};
  54
  55static struct chip_props luton_props = {
  56        .flags                  = FLAGS_NEED_INIT_ENABLE,
  57        .reg_off_sticky         = 0,
  58        .reg_off_ena            = 0x4,
  59        .reg_off_ena_clr        = 0x8,
  60        .reg_off_ena_set        = 0xc,
  61        .reg_off_ident          = 0x18,
  62        .reg_off_ena_irq0       = 0x14,
  63        .n_irq                  = 28,
  64};
  65
  66static struct chip_props jaguar2_props = {
  67        .flags                  = FLAGS_HAS_TRIGGER,
  68        .reg_off_sticky         = 0x10,
  69        .reg_off_ena            = 0x18,
  70        .reg_off_ena_clr        = 0x1c,
  71        .reg_off_ena_set        = 0x20,
  72        .reg_off_ident          = 0x38,
  73        .reg_off_trigger        = 0x5c,
  74        .n_irq                  = 29,
  75};
  76
  77static void ocelot_irq_unmask(struct irq_data *data)
  78{
  79        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
  80        struct irq_domain *d = data->domain;
  81        struct chip_props *p = d->host_data;
  82        struct irq_chip_type *ct = irq_data_get_chip_type(data);
  83        unsigned int mask = data->mask;
  84        u32 val;
  85
  86        irq_gc_lock(gc);
  87        val = irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(p, 0)) |
  88                irq_reg_readl(gc, ICPU_CFG_INTR_INTR_TRIGGER(p, 1));
  89        if (!(val & mask))
  90                irq_reg_writel(gc, mask, p->reg_off_sticky);
  91
  92        *ct->mask_cache &= ~mask;
  93        irq_reg_writel(gc, mask, p->reg_off_ena_set);
  94        irq_gc_unlock(gc);
  95}
  96
  97static void ocelot_irq_handler(struct irq_desc *desc)
  98{
  99        struct irq_chip *chip = irq_desc_get_chip(desc);
 100        struct irq_domain *d = irq_desc_get_handler_data(desc);
 101        struct chip_props *p = d->host_data;
 102        struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0);
 103        u32 reg = irq_reg_readl(gc, ICPU_CFG_INTR_DST_INTR_IDENT(p, 0));
 104
 105        chained_irq_enter(chip, desc);
 106
 107        while (reg) {
 108                u32 hwirq = __fls(reg);
 109
 110                generic_handle_domain_irq(d, hwirq);
 111                reg &= ~(BIT(hwirq));
 112        }
 113
 114        chained_irq_exit(chip, desc);
 115}
 116
 117static int __init vcoreiii_irq_init(struct device_node *node,
 118                                    struct device_node *parent,
 119                                    struct chip_props *p)
 120{
 121        struct irq_domain *domain;
 122        struct irq_chip_generic *gc;
 123        int parent_irq, ret;
 124
 125        parent_irq = irq_of_parse_and_map(node, 0);
 126        if (!parent_irq)
 127                return -EINVAL;
 128
 129        domain = irq_domain_add_linear(node, p->n_irq,
 130                                       &irq_generic_chip_ops, NULL);
 131        if (!domain) {
 132                pr_err("%pOFn: unable to add irq domain\n", node);
 133                return -ENOMEM;
 134        }
 135
 136        ret = irq_alloc_domain_generic_chips(domain, p->n_irq, 1,
 137                                             "icpu", handle_level_irq,
 138                                             0, 0, 0);
 139        if (ret) {
 140                pr_err("%pOFn: unable to alloc irq domain gc\n", node);
 141                goto err_domain_remove;
 142        }
 143
 144        gc = irq_get_domain_generic_chip(domain, 0);
 145        gc->reg_base = of_iomap(node, 0);
 146        if (!gc->reg_base) {
 147                pr_err("%pOFn: unable to map resource\n", node);
 148                ret = -ENOMEM;
 149                goto err_gc_free;
 150        }
 151
 152        gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
 153        gc->chip_types[0].regs.ack = p->reg_off_sticky;
 154        if (p->flags & FLAGS_HAS_TRIGGER) {
 155                gc->chip_types[0].regs.mask = p->reg_off_ena_clr;
 156                gc->chip_types[0].chip.irq_unmask = ocelot_irq_unmask;
 157                gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
 158        } else {
 159                gc->chip_types[0].regs.enable = p->reg_off_ena_set;
 160                gc->chip_types[0].regs.disable = p->reg_off_ena_clr;
 161                gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
 162                gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
 163        }
 164
 165        /* Mask and ack all interrupts */
 166        irq_reg_writel(gc, 0, p->reg_off_ena);
 167        irq_reg_writel(gc, 0xffffffff, p->reg_off_sticky);
 168
 169        /* Overall init */
 170        if (p->flags & FLAGS_NEED_INIT_ENABLE)
 171                irq_reg_writel(gc, BIT(0), p->reg_off_ena_irq0);
 172
 173        domain->host_data = p;
 174        irq_set_chained_handler_and_data(parent_irq, ocelot_irq_handler,
 175                                         domain);
 176
 177        return 0;
 178
 179err_gc_free:
 180        irq_free_generic_chip(gc);
 181
 182err_domain_remove:
 183        irq_domain_remove(domain);
 184
 185        return ret;
 186}
 187
 188static int __init ocelot_irq_init(struct device_node *node,
 189                                  struct device_node *parent)
 190{
 191        return vcoreiii_irq_init(node, parent, &ocelot_props);
 192}
 193
 194IRQCHIP_DECLARE(ocelot_icpu, "mscc,ocelot-icpu-intr", ocelot_irq_init);
 195
 196static int __init serval_irq_init(struct device_node *node,
 197                                  struct device_node *parent)
 198{
 199        return vcoreiii_irq_init(node, parent, &serval_props);
 200}
 201
 202IRQCHIP_DECLARE(serval_icpu, "mscc,serval-icpu-intr", serval_irq_init);
 203
 204static int __init luton_irq_init(struct device_node *node,
 205                                 struct device_node *parent)
 206{
 207        return vcoreiii_irq_init(node, parent, &luton_props);
 208}
 209
 210IRQCHIP_DECLARE(luton_icpu, "mscc,luton-icpu-intr", luton_irq_init);
 211
 212static int __init jaguar2_irq_init(struct device_node *node,
 213                                   struct device_node *parent)
 214{
 215        return vcoreiii_irq_init(node, parent, &jaguar2_props);
 216}
 217
 218IRQCHIP_DECLARE(jaguar2_icpu, "mscc,jaguar2-icpu-intr", jaguar2_irq_init);
 219