aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-cpucfg.txt14
-rw-r--r--Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-ir.txt33
-rw-r--r--arch/arm/mach-imx/Kconfig1
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-vf610-mscm-ir.c212
5 files changed, 261 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-cpucfg.txt b/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-cpucfg.txt
new file mode 100644
index 000000000000..44aa3c451ccf
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-cpucfg.txt
@@ -0,0 +1,14 @@
1Freescale Vybrid Miscellaneous System Control - CPU Configuration
2
3The MSCM IP contains multiple sub modules, this binding describes the first
4block of registers which contains CPU configuration information.
5
6Required properties:
7- compatible: "fsl,vf610-mscm-cpucfg", "syscon"
8- reg: the register range of the MSCM CPU configuration registers
9
10Example:
11 mscm_cpucfg: cpucfg@40001000 {
12 compatible = "fsl,vf610-mscm-cpucfg", "syscon";
13 reg = <0x40001000 0x800>;
14 }
diff --git a/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-ir.txt b/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-ir.txt
new file mode 100644
index 000000000000..669808b2af49
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/freescale/fsl,vf610-mscm-ir.txt
@@ -0,0 +1,33 @@
1Freescale Vybrid Miscellaneous System Control - Interrupt Router
2
3The MSCM IP contains multiple sub modules, this binding describes the second
4block of registers which control the interrupt router. The interrupt router
5allows to configure the recipient of each peripheral interrupt. Furthermore
6it controls the directed processor interrupts. The module is available in all
7Vybrid SoC's but is only really useful in dual core configurations (VF6xx
8which comes with a Cortex-A5/Cortex-M4 combination).
9
10Required properties:
11- compatible: "fsl,vf610-mscm-ir"
12- reg: the register range of the MSCM Interrupt Router
13- fsl,cpucfg: The handle to the MSCM CPU configuration node, required
14 to get the current CPU ID
15- interrupt-controller: Identifies the node as an interrupt controller
16- #interrupt-cells: Two cells, interrupt number and cells.
17 The hardware interrupt number according to interrupt
18 assignment of the interrupt router is required.
19 Flags get passed only when using GIC as parent. Flags
20 encoding as documented by the GIC bindings.
21- interrupt-parent: Should be the phandle for the interrupt controller of
22 the CPU the device tree is intended to be used on. This
23 is either the node of the GIC or NVIC controller.
24
25Example:
26 mscm_ir: interrupt-controller@40001800 {
27 compatible = "fsl,vf610-mscm-ir";
28 reg = <0x40001800 0x400>;
29 fsl,cpucfg = <&mscm_cpucfg>;
30 interrupt-controller;
31 #interrupt-cells = <2>;
32 interrupt-parent = <&intc>;
33 }
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 93aa90413c12..3a3d3e9d7bfd 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -584,6 +584,7 @@ config SOC_IMX6SX
584 584
585config SOC_VF610 585config SOC_VF610
586 bool "Vybrid Family VF610 support" 586 bool "Vybrid Family VF610 support"
587 select IRQ_DOMAIN_HIERARCHY
587 select ARM_GIC 588 select ARM_GIC
588 select PINCTRL_VF610 589 select PINCTRL_VF610
589 select PL310_ERRATA_769419 if CACHE_L2X0 590 select PL310_ERRATA_769419 if CACHE_L2X0
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 42965d2476bb..9176c76eb164 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
37obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 37obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
38obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 38obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
39obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o 39obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
40obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o
40obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o 41obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
41obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o 42obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
42obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o 43obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
diff --git a/drivers/irqchip/irq-vf610-mscm-ir.c b/drivers/irqchip/irq-vf610-mscm-ir.c
new file mode 100644
index 000000000000..9521057d4744
--- /dev/null
+++ b/drivers/irqchip/irq-vf610-mscm-ir.c
@@ -0,0 +1,212 @@
1/*
2 * Copyright (C) 2014-2015 Toradex AG
3 * Author: Stefan Agner <stefan@agner.ch>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 *
10 * IRQ chip driver for MSCM interrupt router available on Vybrid SoC's.
11 * The interrupt router is between the CPU's interrupt controller and the
12 * peripheral. The router allows to route the peripheral interrupts to
13 * one of the two available CPU's on Vybrid VF6xx SoC's (Cortex-A5 or
14 * Cortex-M4). The router will be configured transparently on a IRQ
15 * request.
16 *
17 * o All peripheral interrupts of the Vybrid SoC can be routed to
18 * CPU 0, CPU 1 or both. The routing is useful for dual-core
19 * variants of Vybrid SoC such as VF6xx. This driver routes the
20 * requested interrupt to the CPU currently running on.
21 *
22 * o It is required to setup the interrupt router even on single-core
23 * variants of Vybrid.
24 */
25
26#include <linux/cpu_pm.h>
27#include <linux/io.h>
28#include <linux/irq.h>
29#include <linux/irqdomain.h>
30#include <linux/mfd/syscon.h>
31#include <dt-bindings/interrupt-controller/arm-gic.h>
32#include <linux/of.h>
33#include <linux/of_address.h>
34#include <linux/slab.h>
35#include <linux/regmap.h>
36
37#include "irqchip.h"
38
39#define MSCM_CPxNUM 0x4
40
41#define MSCM_IRSPRC(n) (0x80 + 2 * (n))
42#define MSCM_IRSPRC_CPEN_MASK 0x3
43
44#define MSCM_IRSPRC_NUM 112
45
46struct vf610_mscm_ir_chip_data {
47 void __iomem *mscm_ir_base;
48 u16 cpu_mask;
49 u16 saved_irsprc[MSCM_IRSPRC_NUM];
50};
51
52static struct vf610_mscm_ir_chip_data *mscm_ir_data;
53
54static inline void vf610_mscm_ir_save(struct vf610_mscm_ir_chip_data *data)
55{
56 int i;
57
58 for (i = 0; i < MSCM_IRSPRC_NUM; i++)
59 data->saved_irsprc[i] = readw_relaxed(data->mscm_ir_base + MSCM_IRSPRC(i));
60}
61
62static inline void vf610_mscm_ir_restore(struct vf610_mscm_ir_chip_data *data)
63{
64 int i;
65
66 for (i = 0; i < MSCM_IRSPRC_NUM; i++)
67 writew_relaxed(data->saved_irsprc[i], data->mscm_ir_base + MSCM_IRSPRC(i));
68}
69
70static int vf610_mscm_ir_notifier(struct notifier_block *self,
71 unsigned long cmd, void *v)
72{
73 switch (cmd) {
74 case CPU_CLUSTER_PM_ENTER:
75 vf610_mscm_ir_save(mscm_ir_data);
76 break;
77 case CPU_CLUSTER_PM_ENTER_FAILED:
78 case CPU_CLUSTER_PM_EXIT:
79 vf610_mscm_ir_restore(mscm_ir_data);
80 break;
81 }
82
83 return NOTIFY_OK;
84}
85
86static struct notifier_block mscm_ir_notifier_block = {
87 .notifier_call = vf610_mscm_ir_notifier,
88};
89
90static void vf610_mscm_ir_enable(struct irq_data *data)
91{
92 irq_hw_number_t hwirq = data->hwirq;
93 struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
94 u16 irsprc;
95
96 irsprc = readw_relaxed(chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
97 irsprc &= MSCM_IRSPRC_CPEN_MASK;
98
99 WARN_ON(irsprc & ~chip_data->cpu_mask);
100
101 writew_relaxed(chip_data->cpu_mask,
102 chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
103
104 irq_chip_unmask_parent(data);
105}
106
107static void vf610_mscm_ir_disable(struct irq_data *data)
108{
109 irq_hw_number_t hwirq = data->hwirq;
110 struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
111
112 writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
113
114 irq_chip_mask_parent(data);
115}
116
117static struct irq_chip vf610_mscm_ir_irq_chip = {
118 .name = "mscm-ir",
119 .irq_mask = irq_chip_mask_parent,
120 .irq_unmask = irq_chip_unmask_parent,
121 .irq_eoi = irq_chip_eoi_parent,
122 .irq_enable = vf610_mscm_ir_enable,
123 .irq_disable = vf610_mscm_ir_disable,
124 .irq_retrigger = irq_chip_retrigger_hierarchy,
125 .irq_set_affinity = irq_chip_set_affinity_parent,
126};
127
128static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int virq,
129 unsigned int nr_irqs, void *arg)
130{
131 int i;
132 irq_hw_number_t hwirq;
133 struct of_phandle_args *irq_data = arg;
134 struct of_phandle_args gic_data;
135
136 if (irq_data->args_count != 2)
137 return -EINVAL;
138
139 hwirq = irq_data->args[0];
140 for (i = 0; i < nr_irqs; i++)
141 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
142 &vf610_mscm_ir_irq_chip,
143 domain->host_data);
144
145 gic_data.np = domain->parent->of_node;
146 gic_data.args_count = 3;
147 gic_data.args[0] = GIC_SPI;
148 gic_data.args[1] = irq_data->args[0];
149 gic_data.args[2] = irq_data->args[1];
150 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
151}
152
153static const struct irq_domain_ops mscm_irq_domain_ops = {
154 .xlate = irq_domain_xlate_twocell,
155 .alloc = vf610_mscm_ir_domain_alloc,
156 .free = irq_domain_free_irqs_common,
157};
158
159static int __init vf610_mscm_ir_of_init(struct device_node *node,
160 struct device_node *parent)
161{
162 struct irq_domain *domain, *domain_parent;
163 struct regmap *mscm_cp_regmap;
164 int ret, cpuid;
165
166 domain_parent = irq_find_host(parent);
167 if (!domain_parent) {
168 pr_err("vf610_mscm_ir: interrupt-parent not found\n");
169 return -EINVAL;
170 }
171
172 mscm_ir_data = kzalloc(sizeof(*mscm_ir_data), GFP_KERNEL);
173 if (!mscm_ir_data)
174 return -ENOMEM;
175
176 mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir");
177
178 if (!mscm_ir_data->mscm_ir_base) {
179 pr_err("vf610_mscm_ir: unable to map mscm register\n");
180 ret = -ENOMEM;
181 goto out_free;
182 }
183
184 mscm_cp_regmap = syscon_regmap_lookup_by_phandle(node, "fsl,cpucfg");
185 if (IS_ERR(mscm_cp_regmap)) {
186 ret = PTR_ERR(mscm_cp_regmap);
187 pr_err("vf610_mscm_ir: regmap lookup for cpucfg failed\n");
188 goto out_unmap;
189 }
190
191 regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid);
192 mscm_ir_data->cpu_mask = 0x1 << cpuid;
193
194 domain = irq_domain_add_hierarchy(domain_parent, 0,
195 MSCM_IRSPRC_NUM, node,
196 &mscm_irq_domain_ops, mscm_ir_data);
197 if (!domain) {
198 ret = -ENOMEM;
199 goto out_unmap;
200 }
201
202 cpu_pm_register_notifier(&mscm_ir_notifier_block);
203
204 return 0;
205
206out_unmap:
207 iounmap(mscm_ir_data->mscm_ir_base);
208out_free:
209 kfree(mscm_ir_data);
210 return ret;
211}
212IRQCHIP_DECLARE(vf610_mscm_ir, "fsl,vf610-mscm-ir", vf610_mscm_ir_of_init);