linux/drivers/irqchip/irq-al-fic.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
   4 */
   5
   6#include <linux/bitfield.h>
   7#include <linux/irq.h>
   8#include <linux/irqchip.h>
   9#include <linux/irqchip/chained_irq.h>
  10#include <linux/irqdomain.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/of_address.h>
  14#include <linux/of_irq.h>
  15
  16/* FIC Registers */
  17#define AL_FIC_CAUSE            0x00
  18#define AL_FIC_SET_CAUSE        0x08
  19#define AL_FIC_MASK             0x10
  20#define AL_FIC_CONTROL          0x28
  21
  22#define CONTROL_TRIGGER_RISING  BIT(3)
  23#define CONTROL_MASK_MSI_X      BIT(5)
  24
  25#define NR_FIC_IRQS 32
  26
  27MODULE_AUTHOR("Talel Shenhar");
  28MODULE_DESCRIPTION("Amazon's Annapurna Labs Interrupt Controller Driver");
  29MODULE_LICENSE("GPL v2");
  30
  31enum al_fic_state {
  32        AL_FIC_UNCONFIGURED = 0,
  33        AL_FIC_CONFIGURED_LEVEL,
  34        AL_FIC_CONFIGURED_RISING_EDGE,
  35};
  36
  37struct al_fic {
  38        void __iomem *base;
  39        struct irq_domain *domain;
  40        const char *name;
  41        unsigned int parent_irq;
  42        enum al_fic_state state;
  43};
  44
  45static void al_fic_set_trigger(struct al_fic *fic,
  46                               struct irq_chip_generic *gc,
  47                               enum al_fic_state new_state)
  48{
  49        irq_flow_handler_t handler;
  50        u32 control = readl_relaxed(fic->base + AL_FIC_CONTROL);
  51
  52        if (new_state == AL_FIC_CONFIGURED_LEVEL) {
  53                handler = handle_level_irq;
  54                control &= ~CONTROL_TRIGGER_RISING;
  55        } else {
  56                handler = handle_edge_irq;
  57                control |= CONTROL_TRIGGER_RISING;
  58        }
  59        gc->chip_types->handler = handler;
  60        fic->state = new_state;
  61        writel_relaxed(control, fic->base + AL_FIC_CONTROL);
  62}
  63
  64static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type)
  65{
  66        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
  67        struct al_fic *fic = gc->private;
  68        enum al_fic_state new_state;
  69        int ret = 0;
  70
  71        irq_gc_lock(gc);
  72
  73        if (((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) &&
  74            ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)) {
  75                pr_debug("fic doesn't support flow type %d\n", flow_type);
  76                ret = -EINVAL;
  77                goto err;
  78        }
  79
  80        new_state = (flow_type & IRQ_TYPE_LEVEL_HIGH) ?
  81                AL_FIC_CONFIGURED_LEVEL : AL_FIC_CONFIGURED_RISING_EDGE;
  82
  83        /*
  84         * A given FIC instance can be either all level or all edge triggered.
  85         * This is generally fixed depending on what pieces of HW it's wired up
  86         * to.
  87         *
  88         * We configure it based on the sensitivity of the first source
  89         * being setup, and reject any subsequent attempt at configuring it in a
  90         * different way.
  91         */
  92        if (fic->state == AL_FIC_UNCONFIGURED) {
  93                al_fic_set_trigger(fic, gc, new_state);
  94        } else if (fic->state != new_state) {
  95                pr_debug("fic %s state already configured to %d\n",
  96                         fic->name, fic->state);
  97                ret = -EINVAL;
  98                goto err;
  99        }
 100
 101err:
 102        irq_gc_unlock(gc);
 103
 104        return ret;
 105}
 106
 107static void al_fic_irq_handler(struct irq_desc *desc)
 108{
 109        struct al_fic *fic = irq_desc_get_handler_data(desc);
 110        struct irq_domain *domain = fic->domain;
 111        struct irq_chip *irqchip = irq_desc_get_chip(desc);
 112        struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
 113        unsigned long pending;
 114        unsigned int irq;
 115        u32 hwirq;
 116
 117        chained_irq_enter(irqchip, desc);
 118
 119        pending = readl_relaxed(fic->base + AL_FIC_CAUSE);
 120        pending &= ~gc->mask_cache;
 121
 122        for_each_set_bit(hwirq, &pending, NR_FIC_IRQS) {
 123                irq = irq_find_mapping(domain, hwirq);
 124                generic_handle_irq(irq);
 125        }
 126
 127        chained_irq_exit(irqchip, desc);
 128}
 129
 130static int al_fic_irq_retrigger(struct irq_data *data)
 131{
 132        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
 133        struct al_fic *fic = gc->private;
 134
 135        writel_relaxed(BIT(data->hwirq), fic->base + AL_FIC_SET_CAUSE);
 136
 137        return 1;
 138}
 139
 140static int al_fic_register(struct device_node *node,
 141                           struct al_fic *fic)
 142{
 143        struct irq_chip_generic *gc;
 144        int ret;
 145
 146        fic->domain = irq_domain_add_linear(node,
 147                                            NR_FIC_IRQS,
 148                                            &irq_generic_chip_ops,
 149                                            fic);
 150        if (!fic->domain) {
 151                pr_err("fail to add irq domain\n");
 152                return -ENOMEM;
 153        }
 154
 155        ret = irq_alloc_domain_generic_chips(fic->domain,
 156                                             NR_FIC_IRQS,
 157                                             1, fic->name,
 158                                             handle_level_irq,
 159                                             0, 0, IRQ_GC_INIT_MASK_CACHE);
 160        if (ret) {
 161                pr_err("fail to allocate generic chip (%d)\n", ret);
 162                goto err_domain_remove;
 163        }
 164
 165        gc = irq_get_domain_generic_chip(fic->domain, 0);
 166        gc->reg_base = fic->base;
 167        gc->chip_types->regs.mask = AL_FIC_MASK;
 168        gc->chip_types->regs.ack = AL_FIC_CAUSE;
 169        gc->chip_types->chip.irq_mask = irq_gc_mask_set_bit;
 170        gc->chip_types->chip.irq_unmask = irq_gc_mask_clr_bit;
 171        gc->chip_types->chip.irq_ack = irq_gc_ack_clr_bit;
 172        gc->chip_types->chip.irq_set_type = al_fic_irq_set_type;
 173        gc->chip_types->chip.irq_retrigger = al_fic_irq_retrigger;
 174        gc->chip_types->chip.flags = IRQCHIP_SKIP_SET_WAKE;
 175        gc->private = fic;
 176
 177        irq_set_chained_handler_and_data(fic->parent_irq,
 178                                         al_fic_irq_handler,
 179                                         fic);
 180        return 0;
 181
 182err_domain_remove:
 183        irq_domain_remove(fic->domain);
 184
 185        return ret;
 186}
 187
 188/*
 189 * al_fic_wire_init() - initialize and configure fic in wire mode
 190 * @of_node: optional pointer to interrupt controller's device tree node.
 191 * @base: mmio to fic register
 192 * @name: name of the fic
 193 * @parent_irq: interrupt of parent
 194 *
 195 * This API will configure the fic hardware to to work in wire mode.
 196 * In wire mode, fic hardware is generating a wire ("wired") interrupt.
 197 * Interrupt can be generated based on positive edge or level - configuration is
 198 * to be determined based on connected hardware to this fic.
 199 */
 200static struct al_fic *al_fic_wire_init(struct device_node *node,
 201                                       void __iomem *base,
 202                                       const char *name,
 203                                       unsigned int parent_irq)
 204{
 205        struct al_fic *fic;
 206        int ret;
 207        u32 control = CONTROL_MASK_MSI_X;
 208
 209        fic = kzalloc(sizeof(*fic), GFP_KERNEL);
 210        if (!fic)
 211                return ERR_PTR(-ENOMEM);
 212
 213        fic->base = base;
 214        fic->parent_irq = parent_irq;
 215        fic->name = name;
 216
 217        /* mask out all interrupts */
 218        writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_MASK);
 219
 220        /* clear any pending interrupt */
 221        writel_relaxed(0, fic->base + AL_FIC_CAUSE);
 222
 223        writel_relaxed(control, fic->base + AL_FIC_CONTROL);
 224
 225        ret = al_fic_register(node, fic);
 226        if (ret) {
 227                pr_err("fail to register irqchip\n");
 228                goto err_free;
 229        }
 230
 231        pr_debug("%s initialized successfully in Legacy mode (parent-irq=%u)\n",
 232                 fic->name, parent_irq);
 233
 234        return fic;
 235
 236err_free:
 237        kfree(fic);
 238        return ERR_PTR(ret);
 239}
 240
 241static int __init al_fic_init_dt(struct device_node *node,
 242                                 struct device_node *parent)
 243{
 244        int ret;
 245        void __iomem *base;
 246        unsigned int parent_irq;
 247        struct al_fic *fic;
 248
 249        if (!parent) {
 250                pr_err("%s: unsupported - device require a parent\n",
 251                       node->name);
 252                return -EINVAL;
 253        }
 254
 255        base = of_iomap(node, 0);
 256        if (!base) {
 257                pr_err("%s: fail to map memory\n", node->name);
 258                return -ENOMEM;
 259        }
 260
 261        parent_irq = irq_of_parse_and_map(node, 0);
 262        if (!parent_irq) {
 263                pr_err("%s: fail to map irq\n", node->name);
 264                ret = -EINVAL;
 265                goto err_unmap;
 266        }
 267
 268        fic = al_fic_wire_init(node,
 269                               base,
 270                               node->name,
 271                               parent_irq);
 272        if (IS_ERR(fic)) {
 273                pr_err("%s: fail to initialize irqchip (%lu)\n",
 274                       node->name,
 275                       PTR_ERR(fic));
 276                ret = PTR_ERR(fic);
 277                goto err_irq_dispose;
 278        }
 279
 280        return 0;
 281
 282err_irq_dispose:
 283        irq_dispose_mapping(parent_irq);
 284err_unmap:
 285        iounmap(base);
 286
 287        return ret;
 288}
 289
 290IRQCHIP_DECLARE(al_fic, "amazon,al-fic", al_fic_init_dt);
 291