root/drivers/irqchip/irq-vf610-mscm-ir.c

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

DEFINITIONS

This source file includes following definitions.
  1. vf610_mscm_ir_save
  2. vf610_mscm_ir_restore
  3. vf610_mscm_ir_notifier
  4. vf610_mscm_ir_enable
  5. vf610_mscm_ir_disable
  6. vf610_mscm_ir_domain_alloc
  7. vf610_mscm_ir_domain_translate
  8. vf610_mscm_ir_of_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright (C) 2014-2015 Toradex AG
   4  * Author: Stefan Agner <stefan@agner.ch>
   5  *
   6  * IRQ chip driver for MSCM interrupt router available on Vybrid SoC's.
   7  * The interrupt router is between the CPU's interrupt controller and the
   8  * peripheral. The router allows to route the peripheral interrupts to
   9  * one of the two available CPU's on Vybrid VF6xx SoC's (Cortex-A5 or
  10  * Cortex-M4). The router will be configured transparently on a IRQ
  11  * request.
  12  *
  13  * o All peripheral interrupts of the Vybrid SoC can be routed to
  14  *   CPU 0, CPU 1 or both. The routing is useful for dual-core
  15  *   variants of Vybrid SoC such as VF6xx. This driver routes the
  16  *   requested interrupt to the CPU currently running on.
  17  *
  18  * o It is required to setup the interrupt router even on single-core
  19  *   variants of Vybrid.
  20  */
  21 
  22 #include <linux/cpu_pm.h>
  23 #include <linux/io.h>
  24 #include <linux/irq.h>
  25 #include <linux/irqchip.h>
  26 #include <linux/irqdomain.h>
  27 #include <linux/mfd/syscon.h>
  28 #include <dt-bindings/interrupt-controller/arm-gic.h>
  29 #include <linux/of.h>
  30 #include <linux/of_address.h>
  31 #include <linux/slab.h>
  32 #include <linux/regmap.h>
  33 
  34 #define MSCM_CPxNUM             0x4
  35 
  36 #define MSCM_IRSPRC(n)          (0x80 + 2 * (n))
  37 #define MSCM_IRSPRC_CPEN_MASK   0x3
  38 
  39 #define MSCM_IRSPRC_NUM         112
  40 
  41 struct vf610_mscm_ir_chip_data {
  42         void __iomem *mscm_ir_base;
  43         u16 cpu_mask;
  44         u16 saved_irsprc[MSCM_IRSPRC_NUM];
  45         bool is_nvic;
  46 };
  47 
  48 static struct vf610_mscm_ir_chip_data *mscm_ir_data;
  49 
  50 static inline void vf610_mscm_ir_save(struct vf610_mscm_ir_chip_data *data)
  51 {
  52         int i;
  53 
  54         for (i = 0; i < MSCM_IRSPRC_NUM; i++)
  55                 data->saved_irsprc[i] = readw_relaxed(data->mscm_ir_base + MSCM_IRSPRC(i));
  56 }
  57 
  58 static inline void vf610_mscm_ir_restore(struct vf610_mscm_ir_chip_data *data)
  59 {
  60         int i;
  61 
  62         for (i = 0; i < MSCM_IRSPRC_NUM; i++)
  63                 writew_relaxed(data->saved_irsprc[i], data->mscm_ir_base + MSCM_IRSPRC(i));
  64 }
  65 
  66 static int vf610_mscm_ir_notifier(struct notifier_block *self,
  67                                   unsigned long cmd, void *v)
  68 {
  69         switch (cmd) {
  70         case CPU_CLUSTER_PM_ENTER:
  71                 vf610_mscm_ir_save(mscm_ir_data);
  72                 break;
  73         case CPU_CLUSTER_PM_ENTER_FAILED:
  74         case CPU_CLUSTER_PM_EXIT:
  75                 vf610_mscm_ir_restore(mscm_ir_data);
  76                 break;
  77         }
  78 
  79         return NOTIFY_OK;
  80 }
  81 
  82 static struct notifier_block mscm_ir_notifier_block = {
  83         .notifier_call = vf610_mscm_ir_notifier,
  84 };
  85 
  86 static void vf610_mscm_ir_enable(struct irq_data *data)
  87 {
  88         irq_hw_number_t hwirq = data->hwirq;
  89         struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
  90         u16 irsprc;
  91 
  92         irsprc = readw_relaxed(chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
  93         irsprc &= MSCM_IRSPRC_CPEN_MASK;
  94 
  95         WARN_ON(irsprc & ~chip_data->cpu_mask);
  96 
  97         writew_relaxed(chip_data->cpu_mask,
  98                        chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
  99 
 100         irq_chip_enable_parent(data);
 101 }
 102 
 103 static void vf610_mscm_ir_disable(struct irq_data *data)
 104 {
 105         irq_hw_number_t hwirq = data->hwirq;
 106         struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
 107 
 108         writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
 109 
 110         irq_chip_disable_parent(data);
 111 }
 112 
 113 static struct irq_chip vf610_mscm_ir_irq_chip = {
 114         .name                   = "mscm-ir",
 115         .irq_mask               = irq_chip_mask_parent,
 116         .irq_unmask             = irq_chip_unmask_parent,
 117         .irq_eoi                = irq_chip_eoi_parent,
 118         .irq_enable             = vf610_mscm_ir_enable,
 119         .irq_disable            = vf610_mscm_ir_disable,
 120         .irq_retrigger          = irq_chip_retrigger_hierarchy,
 121         .irq_set_affinity       = irq_chip_set_affinity_parent,
 122 };
 123 
 124 static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int virq,
 125                                       unsigned int nr_irqs, void *arg)
 126 {
 127         int i;
 128         irq_hw_number_t hwirq;
 129         struct irq_fwspec *fwspec = arg;
 130         struct irq_fwspec parent_fwspec;
 131 
 132         if (!irq_domain_get_of_node(domain->parent))
 133                 return -EINVAL;
 134 
 135         if (fwspec->param_count != 2)
 136                 return -EINVAL;
 137 
 138         hwirq = fwspec->param[0];
 139         for (i = 0; i < nr_irqs; i++)
 140                 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
 141                                               &vf610_mscm_ir_irq_chip,
 142                                               domain->host_data);
 143 
 144         parent_fwspec.fwnode = domain->parent->fwnode;
 145 
 146         if (mscm_ir_data->is_nvic) {
 147                 parent_fwspec.param_count = 1;
 148                 parent_fwspec.param[0] = fwspec->param[0];
 149         } else {
 150                 parent_fwspec.param_count = 3;
 151                 parent_fwspec.param[0] = GIC_SPI;
 152                 parent_fwspec.param[1] = fwspec->param[0];
 153                 parent_fwspec.param[2] = fwspec->param[1];
 154         }
 155 
 156         return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
 157                                             &parent_fwspec);
 158 }
 159 
 160 static int vf610_mscm_ir_domain_translate(struct irq_domain *d,
 161                                           struct irq_fwspec *fwspec,
 162                                           unsigned long *hwirq,
 163                                           unsigned int *type)
 164 {
 165         if (WARN_ON(fwspec->param_count < 2))
 166                 return -EINVAL;
 167         *hwirq = fwspec->param[0];
 168         *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
 169         return 0;
 170 }
 171 
 172 static const struct irq_domain_ops mscm_irq_domain_ops = {
 173         .translate = vf610_mscm_ir_domain_translate,
 174         .alloc = vf610_mscm_ir_domain_alloc,
 175         .free = irq_domain_free_irqs_common,
 176 };
 177 
 178 static int __init vf610_mscm_ir_of_init(struct device_node *node,
 179                                struct device_node *parent)
 180 {
 181         struct irq_domain *domain, *domain_parent;
 182         struct regmap *mscm_cp_regmap;
 183         int ret, cpuid;
 184 
 185         domain_parent = irq_find_host(parent);
 186         if (!domain_parent) {
 187                 pr_err("vf610_mscm_ir: interrupt-parent not found\n");
 188                 return -EINVAL;
 189         }
 190 
 191         mscm_ir_data = kzalloc(sizeof(*mscm_ir_data), GFP_KERNEL);
 192         if (!mscm_ir_data)
 193                 return -ENOMEM;
 194 
 195         mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir");
 196         if (IS_ERR(mscm_ir_data->mscm_ir_base)) {
 197                 pr_err("vf610_mscm_ir: unable to map mscm register\n");
 198                 ret = PTR_ERR(mscm_ir_data->mscm_ir_base);
 199                 goto out_free;
 200         }
 201 
 202         mscm_cp_regmap = syscon_regmap_lookup_by_phandle(node, "fsl,cpucfg");
 203         if (IS_ERR(mscm_cp_regmap)) {
 204                 ret = PTR_ERR(mscm_cp_regmap);
 205                 pr_err("vf610_mscm_ir: regmap lookup for cpucfg failed\n");
 206                 goto out_unmap;
 207         }
 208 
 209         regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid);
 210         mscm_ir_data->cpu_mask = 0x1 << cpuid;
 211 
 212         domain = irq_domain_add_hierarchy(domain_parent, 0,
 213                                           MSCM_IRSPRC_NUM, node,
 214                                           &mscm_irq_domain_ops, mscm_ir_data);
 215         if (!domain) {
 216                 ret = -ENOMEM;
 217                 goto out_unmap;
 218         }
 219 
 220         if (of_device_is_compatible(irq_domain_get_of_node(domain->parent),
 221                                     "arm,armv7m-nvic"))
 222                 mscm_ir_data->is_nvic = true;
 223 
 224         cpu_pm_register_notifier(&mscm_ir_notifier_block);
 225 
 226         return 0;
 227 
 228 out_unmap:
 229         iounmap(mscm_ir_data->mscm_ir_base);
 230 out_free:
 231         kfree(mscm_ir_data);
 232         return ret;
 233 }
 234 IRQCHIP_DECLARE(vf610_mscm_ir, "fsl,vf610-mscm-ir", vf610_mscm_ir_of_init);

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