linux/drivers/irqchip/irq-sni-exiu.c
<<
>>
Prefs
   1/*
   2 * Driver for Socionext External Interrupt Unit (EXIU)
   3 *
   4 * Copyright (c) 2017 Linaro, Ltd. <ard.biesheuvel@linaro.org>
   5 *
   6 * Based on irq-tegra.c:
   7 *   Copyright (C) 2011 Google, Inc.
   8 *   Copyright (C) 2010,2013, NVIDIA Corporation
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License version 2 as
  12 * published by the Free Software Foundation.
  13 */
  14
  15#include <linux/interrupt.h>
  16#include <linux/io.h>
  17#include <linux/irq.h>
  18#include <linux/irqchip.h>
  19#include <linux/irqdomain.h>
  20#include <linux/of.h>
  21#include <linux/of_address.h>
  22#include <linux/of_irq.h>
  23
  24#include <dt-bindings/interrupt-controller/arm-gic.h>
  25
  26#define NUM_IRQS        32
  27
  28#define EIMASK          0x00
  29#define EISRCSEL        0x04
  30#define EIREQSTA        0x08
  31#define EIRAWREQSTA     0x0C
  32#define EIREQCLR        0x10
  33#define EILVL           0x14
  34#define EIEDG           0x18
  35#define EISIR           0x1C
  36
  37struct exiu_irq_data {
  38        void __iomem    *base;
  39        u32             spi_base;
  40};
  41
  42static void exiu_irq_eoi(struct irq_data *d)
  43{
  44        struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  45
  46        writel(BIT(d->hwirq), data->base + EIREQCLR);
  47        irq_chip_eoi_parent(d);
  48}
  49
  50static void exiu_irq_mask(struct irq_data *d)
  51{
  52        struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  53        u32 val;
  54
  55        val = readl_relaxed(data->base + EIMASK) | BIT(d->hwirq);
  56        writel_relaxed(val, data->base + EIMASK);
  57        irq_chip_mask_parent(d);
  58}
  59
  60static void exiu_irq_unmask(struct irq_data *d)
  61{
  62        struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  63        u32 val;
  64
  65        val = readl_relaxed(data->base + EIMASK) & ~BIT(d->hwirq);
  66        writel_relaxed(val, data->base + EIMASK);
  67        irq_chip_unmask_parent(d);
  68}
  69
  70static void exiu_irq_enable(struct irq_data *d)
  71{
  72        struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  73        u32 val;
  74
  75        /* clear interrupts that were latched while disabled */
  76        writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
  77
  78        val = readl_relaxed(data->base + EIMASK) & ~BIT(d->hwirq);
  79        writel_relaxed(val, data->base + EIMASK);
  80        irq_chip_enable_parent(d);
  81}
  82
  83static int exiu_irq_set_type(struct irq_data *d, unsigned int type)
  84{
  85        struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  86        u32 val;
  87
  88        val = readl_relaxed(data->base + EILVL);
  89        if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
  90                val |= BIT(d->hwirq);
  91        else
  92                val &= ~BIT(d->hwirq);
  93        writel_relaxed(val, data->base + EILVL);
  94
  95        val = readl_relaxed(data->base + EIEDG);
  96        if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
  97                val &= ~BIT(d->hwirq);
  98        else
  99                val |= BIT(d->hwirq);
 100        writel_relaxed(val, data->base + EIEDG);
 101
 102        writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
 103
 104        return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
 105}
 106
 107static struct irq_chip exiu_irq_chip = {
 108        .name                   = "EXIU",
 109        .irq_eoi                = exiu_irq_eoi,
 110        .irq_enable             = exiu_irq_enable,
 111        .irq_mask               = exiu_irq_mask,
 112        .irq_unmask             = exiu_irq_unmask,
 113        .irq_set_type           = exiu_irq_set_type,
 114        .irq_set_affinity       = irq_chip_set_affinity_parent,
 115        .flags                  = IRQCHIP_SET_TYPE_MASKED |
 116                                  IRQCHIP_SKIP_SET_WAKE |
 117                                  IRQCHIP_EOI_THREADED |
 118                                  IRQCHIP_MASK_ON_SUSPEND,
 119};
 120
 121static int exiu_domain_translate(struct irq_domain *domain,
 122                                 struct irq_fwspec *fwspec,
 123                                 unsigned long *hwirq,
 124                                 unsigned int *type)
 125{
 126        struct exiu_irq_data *info = domain->host_data;
 127
 128        if (is_of_node(fwspec->fwnode)) {
 129                if (fwspec->param_count != 3)
 130                        return -EINVAL;
 131
 132                if (fwspec->param[0] != GIC_SPI)
 133                        return -EINVAL; /* No PPI should point to this domain */
 134
 135                *hwirq = fwspec->param[1] - info->spi_base;
 136                *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
 137                return 0;
 138        }
 139        return -EINVAL;
 140}
 141
 142static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq,
 143                             unsigned int nr_irqs, void *data)
 144{
 145        struct irq_fwspec *fwspec = data;
 146        struct irq_fwspec parent_fwspec;
 147        struct exiu_irq_data *info = dom->host_data;
 148        irq_hw_number_t hwirq;
 149
 150        if (fwspec->param_count != 3)
 151                return -EINVAL; /* Not GIC compliant */
 152        if (fwspec->param[0] != GIC_SPI)
 153                return -EINVAL; /* No PPI should point to this domain */
 154
 155        WARN_ON(nr_irqs != 1);
 156        hwirq = fwspec->param[1] - info->spi_base;
 157        irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &exiu_irq_chip, info);
 158
 159        parent_fwspec = *fwspec;
 160        parent_fwspec.fwnode = dom->parent->fwnode;
 161        return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec);
 162}
 163
 164static const struct irq_domain_ops exiu_domain_ops = {
 165        .translate      = exiu_domain_translate,
 166        .alloc          = exiu_domain_alloc,
 167        .free           = irq_domain_free_irqs_common,
 168};
 169
 170static int __init exiu_init(struct device_node *node,
 171                            struct device_node *parent)
 172{
 173        struct irq_domain *parent_domain, *domain;
 174        struct exiu_irq_data *data;
 175        int err;
 176
 177        if (!parent) {
 178                pr_err("%pOF: no parent, giving up\n", node);
 179                return -ENODEV;
 180        }
 181
 182        parent_domain = irq_find_host(parent);
 183        if (!parent_domain) {
 184                pr_err("%pOF: unable to obtain parent domain\n", node);
 185                return -ENXIO;
 186        }
 187
 188        data = kzalloc(sizeof(*data), GFP_KERNEL);
 189        if (!data)
 190                return -ENOMEM;
 191
 192        if (of_property_read_u32(node, "socionext,spi-base", &data->spi_base)) {
 193                pr_err("%pOF: failed to parse 'spi-base' property\n", node);
 194                err = -ENODEV;
 195                goto out_free;
 196        }
 197
 198        data->base = of_iomap(node, 0);
 199        if (!data->base) {
 200                err = -ENODEV;
 201                goto out_free;
 202        }
 203
 204        /* clear and mask all interrupts */
 205        writel_relaxed(0xFFFFFFFF, data->base + EIREQCLR);
 206        writel_relaxed(0xFFFFFFFF, data->base + EIMASK);
 207
 208        domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_IRQS, node,
 209                                          &exiu_domain_ops, data);
 210        if (!domain) {
 211                pr_err("%pOF: failed to allocate domain\n", node);
 212                err = -ENOMEM;
 213                goto out_unmap;
 214        }
 215
 216        pr_info("%pOF: %d interrupts forwarded to %pOF\n", node, NUM_IRQS,
 217                parent);
 218
 219        return 0;
 220
 221out_unmap:
 222        iounmap(data->base);
 223out_free:
 224        kfree(data);
 225        return err;
 226}
 227IRQCHIP_DECLARE(exiu, "socionext,synquacer-exiu", exiu_init);
 228