root/drivers/irqchip/irq-davinci-cp-intc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. davinci_cp_intc_read
  2. davinci_cp_intc_write
  3. davinci_cp_intc_ack_irq
  4. davinci_cp_intc_mask_irq
  5. davinci_cp_intc_unmask_irq
  6. davinci_cp_intc_set_irq_type
  7. davinci_cp_intc_handle_irq
  8. davinci_cp_intc_host_map
  9. davinci_cp_intc_do_init
  10. davinci_cp_intc_init
  11. davinci_cp_intc_of_init

   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 
  41 static void __iomem *davinci_cp_intc_base;
  42 static struct irq_domain *davinci_cp_intc_irq_domain;
  43 
  44 static inline unsigned int davinci_cp_intc_read(unsigned int offset)
  45 {
  46         return readl_relaxed(davinci_cp_intc_base + offset);
  47 }
  48 
  49 static 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 
  55 static 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 
  60 static 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 
  68 static 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 
  73 static 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 
 110 static 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 
 119 static asmlinkage void __exception_irq_entry
 120 davinci_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 
 141 static 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 
 153 static 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 
 158 static int __init
 159 davinci_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 
 233 int __init davinci_cp_intc_init(const struct davinci_cp_intc_config *config)
 234 {
 235         return davinci_cp_intc_do_init(config, NULL);
 236 }
 237 
 238 static 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 }
 260 IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", davinci_cp_intc_of_init);

/* [<][>][^][v][top][bottom][index][help] */