root/drivers/irqchip/irq-mxs.c

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

DEFINITIONS

This source file includes following definitions.
  1. icoll_intr_bitshift
  2. icoll_intr_reg
  3. icoll_ack_irq
  4. icoll_mask_irq
  5. icoll_unmask_irq
  6. asm9260_mask_irq
  7. asm9260_unmask_irq
  8. icoll_handle_irq
  9. icoll_irq_domain_map
  10. icoll_add_domain
  11. icoll_init_iobase
  12. icoll_of_init
  13. asm9260_of_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
   4  * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
   5  *      Add Alphascale ASM9260 support.
   6  */
   7 
   8 #include <linux/kernel.h>
   9 #include <linux/init.h>
  10 #include <linux/irq.h>
  11 #include <linux/irqchip.h>
  12 #include <linux/irqdomain.h>
  13 #include <linux/io.h>
  14 #include <linux/of.h>
  15 #include <linux/of_address.h>
  16 #include <linux/of_irq.h>
  17 #include <linux/stmp_device.h>
  18 #include <asm/exception.h>
  19 
  20 #include "alphascale_asm9260-icoll.h"
  21 
  22 /*
  23  * this device provide 4 offsets for each register:
  24  * 0x0 - plain read write mode
  25  * 0x4 - set mode, OR logic.
  26  * 0x8 - clr mode, XOR logic.
  27  * 0xc - togle mode.
  28  */
  29 #define SET_REG 4
  30 #define CLR_REG 8
  31 
  32 #define HW_ICOLL_VECTOR                         0x0000
  33 #define HW_ICOLL_LEVELACK                       0x0010
  34 #define HW_ICOLL_CTRL                           0x0020
  35 #define HW_ICOLL_STAT_OFFSET                    0x0070
  36 #define HW_ICOLL_INTERRUPT0                     0x0120
  37 #define HW_ICOLL_INTERRUPTn(n)                  ((n) * 0x10)
  38 #define BM_ICOLL_INTR_ENABLE                    BIT(2)
  39 #define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0   0x1
  40 
  41 #define ICOLL_NUM_IRQS          128
  42 
  43 enum icoll_type {
  44         ICOLL,
  45         ASM9260_ICOLL,
  46 };
  47 
  48 struct icoll_priv {
  49         void __iomem *vector;
  50         void __iomem *levelack;
  51         void __iomem *ctrl;
  52         void __iomem *stat;
  53         void __iomem *intr;
  54         void __iomem *clear;
  55         enum icoll_type type;
  56 };
  57 
  58 static struct icoll_priv icoll_priv;
  59 static struct irq_domain *icoll_domain;
  60 
  61 /* calculate bit offset depending on number of intterupt per register */
  62 static u32 icoll_intr_bitshift(struct irq_data *d, u32 bit)
  63 {
  64         /*
  65          * mask lower part of hwirq to convert it
  66          * in 0, 1, 2 or 3 and then multiply it by 8 (or shift by 3)
  67          */
  68         return bit << ((d->hwirq & 3) << 3);
  69 }
  70 
  71 /* calculate mem offset depending on number of intterupt per register */
  72 static void __iomem *icoll_intr_reg(struct irq_data *d)
  73 {
  74         /* offset = hwirq / intr_per_reg * 0x10 */
  75         return icoll_priv.intr + ((d->hwirq >> 2) * 0x10);
  76 }
  77 
  78 static void icoll_ack_irq(struct irq_data *d)
  79 {
  80         /*
  81          * The Interrupt Collector is able to prioritize irqs.
  82          * Currently only level 0 is used. So acking can use
  83          * BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally.
  84          */
  85         __raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0,
  86                         icoll_priv.levelack);
  87 }
  88 
  89 static void icoll_mask_irq(struct irq_data *d)
  90 {
  91         __raw_writel(BM_ICOLL_INTR_ENABLE,
  92                         icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
  93 }
  94 
  95 static void icoll_unmask_irq(struct irq_data *d)
  96 {
  97         __raw_writel(BM_ICOLL_INTR_ENABLE,
  98                         icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
  99 }
 100 
 101 static void asm9260_mask_irq(struct irq_data *d)
 102 {
 103         __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
 104                         icoll_intr_reg(d) + CLR_REG);
 105 }
 106 
 107 static void asm9260_unmask_irq(struct irq_data *d)
 108 {
 109         __raw_writel(ASM9260_BM_CLEAR_BIT(d->hwirq),
 110                      icoll_priv.clear +
 111                      ASM9260_HW_ICOLL_CLEARn(d->hwirq));
 112 
 113         __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
 114                         icoll_intr_reg(d) + SET_REG);
 115 }
 116 
 117 static struct irq_chip mxs_icoll_chip = {
 118         .irq_ack = icoll_ack_irq,
 119         .irq_mask = icoll_mask_irq,
 120         .irq_unmask = icoll_unmask_irq,
 121         .flags = IRQCHIP_MASK_ON_SUSPEND |
 122                  IRQCHIP_SKIP_SET_WAKE,
 123 };
 124 
 125 static struct irq_chip asm9260_icoll_chip = {
 126         .irq_ack = icoll_ack_irq,
 127         .irq_mask = asm9260_mask_irq,
 128         .irq_unmask = asm9260_unmask_irq,
 129         .flags = IRQCHIP_MASK_ON_SUSPEND |
 130                  IRQCHIP_SKIP_SET_WAKE,
 131 };
 132 
 133 asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
 134 {
 135         u32 irqnr;
 136 
 137         irqnr = __raw_readl(icoll_priv.stat);
 138         __raw_writel(irqnr, icoll_priv.vector);
 139         handle_domain_irq(icoll_domain, irqnr, regs);
 140 }
 141 
 142 static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
 143                                 irq_hw_number_t hw)
 144 {
 145         struct irq_chip *chip;
 146 
 147         if (icoll_priv.type == ICOLL)
 148                 chip = &mxs_icoll_chip;
 149         else
 150                 chip = &asm9260_icoll_chip;
 151 
 152         irq_set_chip_and_handler(virq, chip, handle_level_irq);
 153 
 154         return 0;
 155 }
 156 
 157 static const struct irq_domain_ops icoll_irq_domain_ops = {
 158         .map = icoll_irq_domain_map,
 159         .xlate = irq_domain_xlate_onecell,
 160 };
 161 
 162 static void __init icoll_add_domain(struct device_node *np,
 163                           int num)
 164 {
 165         icoll_domain = irq_domain_add_linear(np, num,
 166                                              &icoll_irq_domain_ops, NULL);
 167 
 168         if (!icoll_domain)
 169                 panic("%pOF: unable to create irq domain", np);
 170 }
 171 
 172 static void __iomem * __init icoll_init_iobase(struct device_node *np)
 173 {
 174         void __iomem *icoll_base;
 175 
 176         icoll_base = of_io_request_and_map(np, 0, np->name);
 177         if (IS_ERR(icoll_base))
 178                 panic("%pOF: unable to map resource", np);
 179         return icoll_base;
 180 }
 181 
 182 static int __init icoll_of_init(struct device_node *np,
 183                           struct device_node *interrupt_parent)
 184 {
 185         void __iomem *icoll_base;
 186 
 187         icoll_priv.type = ICOLL;
 188 
 189         icoll_base              = icoll_init_iobase(np);
 190         icoll_priv.vector       = icoll_base + HW_ICOLL_VECTOR;
 191         icoll_priv.levelack     = icoll_base + HW_ICOLL_LEVELACK;
 192         icoll_priv.ctrl         = icoll_base + HW_ICOLL_CTRL;
 193         icoll_priv.stat         = icoll_base + HW_ICOLL_STAT_OFFSET;
 194         icoll_priv.intr         = icoll_base + HW_ICOLL_INTERRUPT0;
 195         icoll_priv.clear        = NULL;
 196 
 197         /*
 198          * Interrupt Collector reset, which initializes the priority
 199          * for each irq to level 0.
 200          */
 201         stmp_reset_block(icoll_priv.ctrl);
 202 
 203         icoll_add_domain(np, ICOLL_NUM_IRQS);
 204 
 205         return 0;
 206 }
 207 IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
 208 
 209 static int __init asm9260_of_init(struct device_node *np,
 210                           struct device_node *interrupt_parent)
 211 {
 212         void __iomem *icoll_base;
 213         int i;
 214 
 215         icoll_priv.type = ASM9260_ICOLL;
 216 
 217         icoll_base = icoll_init_iobase(np);
 218         icoll_priv.vector       = icoll_base + ASM9260_HW_ICOLL_VECTOR;
 219         icoll_priv.levelack     = icoll_base + ASM9260_HW_ICOLL_LEVELACK;
 220         icoll_priv.ctrl         = icoll_base + ASM9260_HW_ICOLL_CTRL;
 221         icoll_priv.stat         = icoll_base + ASM9260_HW_ICOLL_STAT_OFFSET;
 222         icoll_priv.intr         = icoll_base + ASM9260_HW_ICOLL_INTERRUPT0;
 223         icoll_priv.clear        = icoll_base + ASM9260_HW_ICOLL_CLEAR0;
 224 
 225         writel_relaxed(ASM9260_BM_CTRL_IRQ_ENABLE,
 226                         icoll_priv.ctrl);
 227         /*
 228          * ASM9260 don't provide reset bit. So, we need to set level 0
 229          * manually.
 230          */
 231         for (i = 0; i < 16 * 0x10; i += 0x10)
 232                 writel(0, icoll_priv.intr + i);
 233 
 234         icoll_add_domain(np, ASM9260_NUM_IRQS);
 235         set_handle_irq(icoll_handle_irq);
 236 
 237         return 0;
 238 }
 239 IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);

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