linux/drivers/irqchip/irq-davinci-cp-intc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2//
   3// Author: Steve Chen <schen@mvista.com>
   4// Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
   5// Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
   6// Copyright (C) 2019, Texas Instruments
   7//
   8// TI Common Platform Interrupt Controller (cp_intc) driver
   9
  10#include <linux/export.h>
  11#include <linux/init.h>
  12#include <linux/irq.h>
  13#include <linux/irqchip.h>
  14#include <linux/irqchip/irq-davinci-cp-intc.h>
  15#include <linux/irqdomain.h>
  16#include <linux/io.h>
  17#include <linux/of.h>
  18#include <linux/of_address.h>
  19#include <linux/of_irq.h>
  20
  21#include <asm/exception.h>
  22
  23#define DAVINCI_CP_INTC_CTRL                    0x04
  24#define DAVINCI_CP_INTC_HOST_CTRL               0x0c
  25#define DAVINCI_CP_INTC_GLOBAL_ENABLE           0x10
  26#define DAVINCI_CP_INTC_SYS_STAT_IDX_CLR        0x24
  27#define DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET      0x28
  28#define DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR      0x2c
  29#define DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET     0x34
  30#define DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR     0x38
  31#define DAVINCI_CP_INTC_PRIO_IDX                0x80
  32#define DAVINCI_CP_INTC_SYS_STAT_CLR(n)         (0x0280 + (n << 2))
  33#define DAVINCI_CP_INTC_SYS_ENABLE_CLR(n)       (0x0380 + (n << 2))
  34#define DAVINCI_CP_INTC_CHAN_MAP(n)             (0x0400 + (n << 2))
  35#define DAVINCI_CP_INTC_SYS_POLARITY(n)         (0x0d00 + (n << 2))
  36#define DAVINCI_CP_INTC_SYS_TYPE(n)             (0x0d80 + (n << 2))
  37#define DAVINCI_CP_INTC_HOST_ENABLE(n)          (0x1500 + (n << 2))
  38#define DAVINCI_CP_INTC_PRI_INDX_MASK           GENMASK(9, 0)
  39#define DAVINCI_CP_INTC_GPIR_NONE               BIT(31)
  40
  41static void __iomem *davinci_cp_intc_base;
  42static struct irq_domain *davinci_cp_intc_irq_domain;
  43
  44static inline unsigned int davinci_cp_intc_read(unsigned int offset)
  45{
  46        return readl_relaxed(davinci_cp_intc_base + offset);
  47}
  48
  49static inline void davinci_cp_intc_write(unsigned long value,
  50                                         unsigned int offset)
  51{
  52        writel_relaxed(value, davinci_cp_intc_base + offset);
  53}
  54
  55static void davinci_cp_intc_ack_irq(struct irq_data *d)
  56{
  57        davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_STAT_IDX_CLR);
  58}
  59
  60static void davinci_cp_intc_mask_irq(struct irq_data *d)
  61{
  62        /* XXX don't know why we need to disable nIRQ here... */
  63        davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_CLR);
  64        davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_CLR);
  65        davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET);
  66}
  67
  68static void davinci_cp_intc_unmask_irq(struct irq_data *d)
  69{
  70        davinci_cp_intc_write(d->hwirq, DAVINCI_CP_INTC_SYS_ENABLE_IDX_SET);
  71}
  72
  73static int davinci_cp_intc_set_irq_type(struct irq_data *d,
  74                                        unsigned int flow_type)
  75{
  76        unsigned int reg, mask, polarity, type;
  77
  78        reg = BIT_WORD(d->hwirq);
  79        mask = BIT_MASK(d->hwirq);
  80        polarity = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_POLARITY(reg));
  81        type = davinci_cp_intc_read(DAVINCI_CP_INTC_SYS_TYPE(reg));
  82
  83        switch (flow_type) {
  84        case IRQ_TYPE_EDGE_RISING:
  85                polarity |= mask;
  86                type |= mask;
  87                break;
  88        case IRQ_TYPE_EDGE_FALLING:
  89                polarity &= ~mask;
  90                type |= mask;
  91                break;
  92        case IRQ_TYPE_LEVEL_HIGH:
  93                polarity |= mask;
  94                type &= ~mask;
  95                break;
  96        case IRQ_TYPE_LEVEL_LOW:
  97                polarity &= ~mask;
  98                type &= ~mask;
  99                break;
 100        default:
 101                return -EINVAL;
 102        }
 103
 104        davinci_cp_intc_write(polarity, DAVINCI_CP_INTC_SYS_POLARITY(reg));
 105        davinci_cp_intc_write(type, DAVINCI_CP_INTC_SYS_TYPE(reg));
 106
 107        return 0;
 108}
 109
 110static struct irq_chip davinci_cp_intc_irq_chip = {
 111        .name           = "cp_intc",
 112        .irq_ack        = davinci_cp_intc_ack_irq,
 113        .irq_mask       = davinci_cp_intc_mask_irq,
 114        .irq_unmask     = davinci_cp_intc_unmask_irq,
 115        .irq_set_type   = davinci_cp_intc_set_irq_type,
 116        .flags          = IRQCHIP_SKIP_SET_WAKE,
 117};
 118
 119static asmlinkage void __exception_irq_entry
 120davinci_cp_intc_handle_irq(struct pt_regs *regs)
 121{
 122        int gpir, irqnr, none;
 123
 124        /*
 125         * The interrupt number is in first ten bits. The NONE field set to 1
 126         * indicates a spurious irq.
 127         */
 128
 129        gpir = davinci_cp_intc_read(DAVINCI_CP_INTC_PRIO_IDX);
 130        irqnr = gpir & DAVINCI_CP_INTC_PRI_INDX_MASK;
 131        none = gpir & DAVINCI_CP_INTC_GPIR_NONE;
 132
 133        if (unlikely(none)) {
 134                pr_err_once("%s: spurious irq!\n", __func__);
 135                return;
 136        }
 137
 138        handle_domain_irq(davinci_cp_intc_irq_domain, irqnr, regs);
 139}
 140
 141static int davinci_cp_intc_host_map(struct irq_domain *h, unsigned int virq,
 142                          irq_hw_number_t hw)
 143{
 144        pr_debug("cp_intc_host_map(%d, 0x%lx)\n", virq, hw);
 145
 146        irq_set_chip(virq, &davinci_cp_intc_irq_chip);
 147        irq_set_probe(virq);
 148        irq_set_handler(virq, handle_edge_irq);
 149
 150        return 0;
 151}
 152
 153static const struct irq_domain_ops davinci_cp_intc_irq_domain_ops = {
 154        .map = davinci_cp_intc_host_map,
 155        .xlate = irq_domain_xlate_onetwocell,
 156};
 157
 158static int __init
 159davinci_cp_intc_do_init(const struct davinci_cp_intc_config *config,
 160                        struct device_node *node)
 161{
 162        unsigned int num_regs = BITS_TO_LONGS(config->num_irqs);
 163        int offset, irq_base;
 164        void __iomem *req;
 165
 166        req = request_mem_region(config->reg.start,
 167                                 resource_size(&config->reg),
 168                                 "davinci-cp-intc");
 169        if (!req) {
 170                pr_err("%s: register range busy\n", __func__);
 171                return -EBUSY;
 172        }
 173
 174        davinci_cp_intc_base = ioremap(config->reg.start,
 175                                       resource_size(&config->reg));
 176        if (!davinci_cp_intc_base) {
 177                pr_err("%s: unable to ioremap register range\n", __func__);
 178                return -EINVAL;
 179        }
 180
 181        davinci_cp_intc_write(0, DAVINCI_CP_INTC_GLOBAL_ENABLE);
 182
 183        /* Disable all host interrupts */
 184        davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_ENABLE(0));
 185
 186        /* Disable system interrupts */
 187        for (offset = 0; offset < num_regs; offset++)
 188                davinci_cp_intc_write(~0,
 189                        DAVINCI_CP_INTC_SYS_ENABLE_CLR(offset));
 190
 191        /* Set to normal mode, no nesting, no priority hold */
 192        davinci_cp_intc_write(0, DAVINCI_CP_INTC_CTRL);
 193        davinci_cp_intc_write(0, DAVINCI_CP_INTC_HOST_CTRL);
 194
 195        /* Clear system interrupt status */
 196        for (offset = 0; offset < num_regs; offset++)
 197                davinci_cp_intc_write(~0,
 198                        DAVINCI_CP_INTC_SYS_STAT_CLR(offset));
 199
 200        /* Enable nIRQ (what about nFIQ?) */
 201        davinci_cp_intc_write(1, DAVINCI_CP_INTC_HOST_ENABLE_IDX_SET);
 202
 203        /* Default all priorities to channel 7. */
 204        num_regs = (config->num_irqs + 3) >> 2; /* 4 channels per register */
 205        for (offset = 0; offset < num_regs; offset++)
 206                davinci_cp_intc_write(0x07070707,
 207                        DAVINCI_CP_INTC_CHAN_MAP(offset));
 208
 209        irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0);
 210        if (irq_base < 0) {
 211                pr_err("%s: unable to allocate interrupt descriptors: %d\n",
 212                       __func__, irq_base);
 213                return irq_base;
 214        }
 215
 216        davinci_cp_intc_irq_domain = irq_domain_add_legacy(
 217                                        node, config->num_irqs, irq_base, 0,
 218                                        &davinci_cp_intc_irq_domain_ops, NULL);
 219
 220        if (!davinci_cp_intc_irq_domain) {
 221                pr_err("%s: unable to create an interrupt domain\n", __func__);
 222                return -EINVAL;
 223        }
 224
 225        set_handle_irq(davinci_cp_intc_handle_irq);
 226
 227        /* Enable global interrupt */
 228        davinci_cp_intc_write(1, DAVINCI_CP_INTC_GLOBAL_ENABLE);
 229
 230        return 0;
 231}
 232
 233int __init davinci_cp_intc_init(const struct davinci_cp_intc_config *config)
 234{
 235        return davinci_cp_intc_do_init(config, NULL);
 236}
 237
 238static int __init davinci_cp_intc_of_init(struct device_node *node,
 239                                          struct device_node *parent)
 240{
 241        struct davinci_cp_intc_config config = { };
 242        int ret;
 243
 244        ret = of_address_to_resource(node, 0, &config.reg);
 245        if (ret) {
 246                pr_err("%s: unable to get the register range from device-tree\n",
 247                       __func__);
 248                return ret;
 249        }
 250
 251        ret = of_property_read_u32(node, "ti,intc-size", &config.num_irqs);
 252        if (ret) {
 253                pr_err("%s: unable to read the 'ti,intc-size' property\n",
 254                       __func__);
 255                return ret;
 256        }
 257
 258        return davinci_cp_intc_do_init(&config, node);
 259}
 260IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", davinci_cp_intc_of_init);
 261