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--Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt4
-rw-r--r--arch/arm/mach-imx/Kconfig1
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c89
-rw-r--r--drivers/irqchip/irq-digicolor.c4
-rw-r--r--drivers/irqchip/irq-gic.c2
-rw-r--r--drivers/irqchip/irq-mips-gic.c21
-rw-r--r--drivers/irqchip/irq-renesas-irqc.c54
-rw-r--r--drivers/irqchip/irq-vf610-mscm-ir.c212
-rw-r--r--include/linux/irqchip/mips-gic.h2
12 files changed, 396 insertions, 41 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/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt
index 1a88e62228e5..63633bdea7e4 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt
@@ -4,7 +4,7 @@ Required properties:
4 4
5- compatible: has to be "renesas,irqc-<soctype>", "renesas,irqc" as fallback. 5- compatible: has to be "renesas,irqc-<soctype>", "renesas,irqc" as fallback.
6 Examples with soctypes are: 6 Examples with soctypes are:
7 - "renesas,irqc-r8a73a4" (R-Mobile AP6) 7 - "renesas,irqc-r8a73a4" (R-Mobile APE6)
8 - "renesas,irqc-r8a7790" (R-Car H2) 8 - "renesas,irqc-r8a7790" (R-Car H2)
9 - "renesas,irqc-r8a7791" (R-Car M2-W) 9 - "renesas,irqc-r8a7791" (R-Car M2-W)
10 - "renesas,irqc-r8a7792" (R-Car V2H) 10 - "renesas,irqc-r8a7792" (R-Car V2H)
@@ -12,6 +12,7 @@ Required properties:
12 - "renesas,irqc-r8a7794" (R-Car E2) 12 - "renesas,irqc-r8a7794" (R-Car E2)
13- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in 13- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
14 interrupts.txt in this directory 14 interrupts.txt in this directory
15- clocks: Must contain a reference to the functional clock.
15 16
16Optional properties: 17Optional properties:
17 18
@@ -29,4 +30,5 @@ Example:
29 <0 1 IRQ_TYPE_LEVEL_HIGH>, 30 <0 1 IRQ_TYPE_LEVEL_HIGH>,
30 <0 2 IRQ_TYPE_LEVEL_HIGH>, 31 <0 2 IRQ_TYPE_LEVEL_HIGH>,
31 <0 3 IRQ_TYPE_LEVEL_HIGH>; 32 <0 3 IRQ_TYPE_LEVEL_HIGH>;
33 clocks = <&mstp4_clks R8A7790_CLK_IRQC>;
32 }; 34 };
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index e8627e04e1e6..c8dffcee9736 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -631,6 +631,7 @@ config SOC_IMX6SX
631 631
632config SOC_VF610 632config SOC_VF610
633 bool "Vybrid Family VF610 support" 633 bool "Vybrid Family VF610 support"
634 select IRQ_DOMAIN_HIERARCHY
634 select ARM_GIC 635 select ARM_GIC
635 select PINCTRL_VF610 636 select PINCTRL_VF610
636 select PL310_ERRATA_769419 if CACHE_L2X0 637 select PL310_ERRATA_769419 if CACHE_L2X0
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 9bb4fd191e94..f117092ae014 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
38obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 38obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
39obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 39obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
40obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o 40obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
41obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o
41obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o 42obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
42obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o 43obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
43obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o 44obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 4387dae14e45..daccc8bdbb42 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -38,6 +38,8 @@
38/* Interrupt Controller Registers Map */ 38/* Interrupt Controller Registers Map */
39#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) 39#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
40#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) 40#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
41#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54)
42#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu)
41 43
42#define ARMADA_370_XP_INT_CONTROL (0x00) 44#define ARMADA_370_XP_INT_CONTROL (0x00)
43#define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) 45#define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
@@ -56,6 +58,7 @@
56#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) 58#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
57 59
58#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) 60#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5)
61#define ARMADA_370_XP_FABRIC_IRQ (3)
59 62
60#define IPI_DOORBELL_START (0) 63#define IPI_DOORBELL_START (0)
61#define IPI_DOORBELL_END (8) 64#define IPI_DOORBELL_END (8)
@@ -77,6 +80,17 @@ static DEFINE_MUTEX(msi_used_lock);
77static phys_addr_t msi_doorbell_addr; 80static phys_addr_t msi_doorbell_addr;
78#endif 81#endif
79 82
83static inline bool is_percpu_irq(irq_hw_number_t irq)
84{
85 switch (irq) {
86 case ARMADA_370_XP_TIMER0_PER_CPU_IRQ:
87 case ARMADA_370_XP_FABRIC_IRQ:
88 return true;
89 default:
90 return false;
91 }
92}
93
80/* 94/*
81 * In SMP mode: 95 * In SMP mode:
82 * For shared global interrupts, mask/unmask global enable bit 96 * For shared global interrupts, mask/unmask global enable bit
@@ -86,7 +100,7 @@ static void armada_370_xp_irq_mask(struct irq_data *d)
86{ 100{
87 irq_hw_number_t hwirq = irqd_to_hwirq(d); 101 irq_hw_number_t hwirq = irqd_to_hwirq(d);
88 102
89 if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 103 if (!is_percpu_irq(hwirq))
90 writel(hwirq, main_int_base + 104 writel(hwirq, main_int_base +
91 ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 105 ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
92 else 106 else
@@ -98,7 +112,7 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
98{ 112{
99 irq_hw_number_t hwirq = irqd_to_hwirq(d); 113 irq_hw_number_t hwirq = irqd_to_hwirq(d);
100 114
101 if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 115 if (!is_percpu_irq(hwirq))
102 writel(hwirq, main_int_base + 116 writel(hwirq, main_int_base +
103 ARMADA_370_XP_INT_SET_ENABLE_OFFS); 117 ARMADA_370_XP_INT_SET_ENABLE_OFFS);
104 else 118 else
@@ -281,20 +295,21 @@ static struct irq_chip armada_370_xp_irq_chip = {
281#ifdef CONFIG_SMP 295#ifdef CONFIG_SMP
282 .irq_set_affinity = armada_xp_set_affinity, 296 .irq_set_affinity = armada_xp_set_affinity,
283#endif 297#endif
298 .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
284}; 299};
285 300
286static int armada_370_xp_mpic_irq_map(struct irq_domain *h, 301static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
287 unsigned int virq, irq_hw_number_t hw) 302 unsigned int virq, irq_hw_number_t hw)
288{ 303{
289 armada_370_xp_irq_mask(irq_get_irq_data(virq)); 304 armada_370_xp_irq_mask(irq_get_irq_data(virq));
290 if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 305 if (!is_percpu_irq(hw))
291 writel(hw, per_cpu_int_base + 306 writel(hw, per_cpu_int_base +
292 ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 307 ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
293 else 308 else
294 writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); 309 writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
295 irq_set_status_flags(virq, IRQ_LEVEL); 310 irq_set_status_flags(virq, IRQ_LEVEL);
296 311
297 if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) { 312 if (is_percpu_irq(hw)) {
298 irq_set_percpu_devid(virq); 313 irq_set_percpu_devid(virq);
299 irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 314 irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
300 handle_percpu_devid_irq); 315 handle_percpu_devid_irq);
@@ -308,28 +323,6 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
308 return 0; 323 return 0;
309} 324}
310 325
311#ifdef CONFIG_SMP
312static void armada_mpic_send_doorbell(const struct cpumask *mask,
313 unsigned int irq)
314{
315 int cpu;
316 unsigned long map = 0;
317
318 /* Convert our logical CPU mask into a physical one. */
319 for_each_cpu(cpu, mask)
320 map |= 1 << cpu_logical_map(cpu);
321
322 /*
323 * Ensure that stores to Normal memory are visible to the
324 * other CPUs before issuing the IPI.
325 */
326 dsb();
327
328 /* submit softirq */
329 writel((map << 8) | irq, main_int_base +
330 ARMADA_370_XP_SW_TRIG_INT_OFFS);
331}
332
333static void armada_xp_mpic_smp_cpu_init(void) 326static void armada_xp_mpic_smp_cpu_init(void)
334{ 327{
335 u32 control; 328 u32 control;
@@ -352,11 +345,44 @@ static void armada_xp_mpic_smp_cpu_init(void)
352 writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 345 writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
353} 346}
354 347
348static void armada_xp_mpic_perf_init(void)
349{
350 unsigned long cpuid = cpu_logical_map(smp_processor_id());
351
352 /* Enable Performance Counter Overflow interrupts */
353 writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid),
354 per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS);
355}
356
357#ifdef CONFIG_SMP
358static void armada_mpic_send_doorbell(const struct cpumask *mask,
359 unsigned int irq)
360{
361 int cpu;
362 unsigned long map = 0;
363
364 /* Convert our logical CPU mask into a physical one. */
365 for_each_cpu(cpu, mask)
366 map |= 1 << cpu_logical_map(cpu);
367
368 /*
369 * Ensure that stores to Normal memory are visible to the
370 * other CPUs before issuing the IPI.
371 */
372 dsb();
373
374 /* submit softirq */
375 writel((map << 8) | irq, main_int_base +
376 ARMADA_370_XP_SW_TRIG_INT_OFFS);
377}
378
355static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, 379static int armada_xp_mpic_secondary_init(struct notifier_block *nfb,
356 unsigned long action, void *hcpu) 380 unsigned long action, void *hcpu)
357{ 381{
358 if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) 382 if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) {
383 armada_xp_mpic_perf_init();
359 armada_xp_mpic_smp_cpu_init(); 384 armada_xp_mpic_smp_cpu_init();
385 }
360 386
361 return NOTIFY_OK; 387 return NOTIFY_OK;
362} 388}
@@ -369,8 +395,10 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = {
369static int mpic_cascaded_secondary_init(struct notifier_block *nfb, 395static int mpic_cascaded_secondary_init(struct notifier_block *nfb,
370 unsigned long action, void *hcpu) 396 unsigned long action, void *hcpu)
371{ 397{
372 if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) 398 if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) {
399 armada_xp_mpic_perf_init();
373 enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); 400 enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
401 }
374 402
375 return NOTIFY_OK; 403 return NOTIFY_OK;
376} 404}
@@ -379,7 +407,6 @@ static struct notifier_block mpic_cascaded_cpu_notifier = {
379 .notifier_call = mpic_cascaded_secondary_init, 407 .notifier_call = mpic_cascaded_secondary_init,
380 .priority = 100, 408 .priority = 100,
381}; 409};
382
383#endif /* CONFIG_SMP */ 410#endif /* CONFIG_SMP */
384 411
385static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { 412static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
@@ -588,9 +615,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
588 615
589 BUG_ON(!armada_370_xp_mpic_domain); 616 BUG_ON(!armada_370_xp_mpic_domain);
590 617
591#ifdef CONFIG_SMP 618 /* Setup for the boot CPU */
619 armada_xp_mpic_perf_init();
592 armada_xp_mpic_smp_cpu_init(); 620 armada_xp_mpic_smp_cpu_init();
593#endif
594 621
595 armada_370_xp_msi_init(node, main_int_res.start); 622 armada_370_xp_msi_init(node, main_int_res.start);
596 623
diff --git a/drivers/irqchip/irq-digicolor.c b/drivers/irqchip/irq-digicolor.c
index 930a2a2fac7f..3cbc658afe27 100644
--- a/drivers/irqchip/irq-digicolor.c
+++ b/drivers/irqchip/irq-digicolor.c
@@ -55,8 +55,8 @@ static void __exception_irq_entry digicolor_handle_irq(struct pt_regs *regs)
55 } while (1); 55 } while (1);
56} 56}
57 57
58static void digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, 58static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base,
59 unsigned en_reg, unsigned ack_reg) 59 unsigned en_reg, unsigned ack_reg)
60{ 60{
61 struct irq_chip_generic *gc; 61 struct irq_chip_generic *gc;
62 62
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 0b4a4d0238e7..d6d6b74801d4 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -414,7 +414,7 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic)
414 break; 414 break;
415 } 415 }
416 416
417 if (!mask) 417 if (!mask && num_possible_cpus() > 1)
418 pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); 418 pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
419 419
420 return mask; 420 return mask;
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index 9acdc080e7ec..f2d269bca789 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -166,6 +166,27 @@ cycle_t gic_read_compare(void)
166 166
167 return (((cycle_t) hi) << 32) + lo; 167 return (((cycle_t) hi) << 32) + lo;
168} 168}
169
170void gic_start_count(void)
171{
172 u32 gicconfig;
173
174 /* Start the counter */
175 gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
176 gicconfig &= ~(1 << GIC_SH_CONFIG_COUNTSTOP_SHF);
177 gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
178}
179
180void gic_stop_count(void)
181{
182 u32 gicconfig;
183
184 /* Stop the counter */
185 gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
186 gicconfig |= 1 << GIC_SH_CONFIG_COUNTSTOP_SHF;
187 gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
188}
189
169#endif 190#endif
170 191
171static bool gic_local_irq_is_routable(int intr) 192static bool gic_local_irq_is_routable(int intr)
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
index 2ea3412fdf8c..cdf80b7794cd 100644
--- a/drivers/irqchip/irq-renesas-irqc.c
+++ b/drivers/irqchip/irq-renesas-irqc.c
@@ -17,6 +17,7 @@
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */ 18 */
19 19
20#include <linux/clk.h>
20#include <linux/init.h> 21#include <linux/init.h>
21#include <linux/platform_device.h> 22#include <linux/platform_device.h>
22#include <linux/spinlock.h> 23#include <linux/spinlock.h>
@@ -29,15 +30,26 @@
29#include <linux/slab.h> 30#include <linux/slab.h>
30#include <linux/module.h> 31#include <linux/module.h>
31#include <linux/platform_data/irq-renesas-irqc.h> 32#include <linux/platform_data/irq-renesas-irqc.h>
33#include <linux/pm_runtime.h>
32 34
33#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */ 35#define IRQC_IRQ_MAX 32 /* maximum 32 interrupts per driver instance */
34 36
35#define IRQC_REQ_STS 0x00 37#define IRQC_REQ_STS 0x00 /* Interrupt Request Status Register */
36#define IRQC_EN_STS 0x04 38#define IRQC_EN_STS 0x04 /* Interrupt Enable Status Register */
37#define IRQC_EN_SET 0x08 39#define IRQC_EN_SET 0x08 /* Interrupt Enable Set Register */
38#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10)) 40#define IRQC_INT_CPU_BASE(n) (0x000 + ((n) * 0x10))
39#define DETECT_STATUS 0x100 41 /* SYS-CPU vs. RT-CPU */
42#define DETECT_STATUS 0x100 /* IRQn Detect Status Register */
43#define MONITOR 0x104 /* IRQn Signal Level Monitor Register */
44#define HLVL_STS 0x108 /* IRQn High Level Detect Status Register */
45#define LLVL_STS 0x10c /* IRQn Low Level Detect Status Register */
46#define S_R_EDGE_STS 0x110 /* IRQn Sync Rising Edge Detect Status Reg. */
47#define S_F_EDGE_STS 0x114 /* IRQn Sync Falling Edge Detect Status Reg. */
48#define A_R_EDGE_STS 0x118 /* IRQn Async Rising Edge Detect Status Reg. */
49#define A_F_EDGE_STS 0x11c /* IRQn Async Falling Edge Detect Status Reg. */
50#define CHTEN_STS 0x120 /* Chattering Reduction Status Register */
40#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04)) 51#define IRQC_CONFIG(n) (0x180 + ((n) * 0x04))
52 /* IRQn Configuration Register */
41 53
42struct irqc_irq { 54struct irqc_irq {
43 int hw_irq; 55 int hw_irq;
@@ -55,6 +67,7 @@ struct irqc_priv {
55 struct platform_device *pdev; 67 struct platform_device *pdev;
56 struct irq_chip irq_chip; 68 struct irq_chip irq_chip;
57 struct irq_domain *irq_domain; 69 struct irq_domain *irq_domain;
70 struct clk *clk;
58}; 71};
59 72
60static void irqc_dbg(struct irqc_irq *i, char *str) 73static void irqc_dbg(struct irqc_irq *i, char *str)
@@ -108,6 +121,21 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
108 return 0; 121 return 0;
109} 122}
110 123
124static int irqc_irq_set_wake(struct irq_data *d, unsigned int on)
125{
126 struct irqc_priv *p = irq_data_get_irq_chip_data(d);
127
128 if (!p->clk)
129 return 0;
130
131 if (on)
132 clk_enable(p->clk);
133 else
134 clk_disable(p->clk);
135
136 return 0;
137}
138
111static irqreturn_t irqc_irq_handler(int irq, void *dev_id) 139static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
112{ 140{
113 struct irqc_irq *i = dev_id; 141 struct irqc_irq *i = dev_id;
@@ -170,6 +198,15 @@ static int irqc_probe(struct platform_device *pdev)
170 p->pdev = pdev; 198 p->pdev = pdev;
171 platform_set_drvdata(pdev, p); 199 platform_set_drvdata(pdev, p);
172 200
201 p->clk = devm_clk_get(&pdev->dev, NULL);
202 if (IS_ERR(p->clk)) {
203 dev_warn(&pdev->dev, "unable to get clock\n");
204 p->clk = NULL;
205 }
206
207 pm_runtime_enable(&pdev->dev);
208 pm_runtime_get_sync(&pdev->dev);
209
173 /* get hold of manadatory IOMEM */ 210 /* get hold of manadatory IOMEM */
174 io = platform_get_resource(pdev, IORESOURCE_MEM, 0); 211 io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
175 if (!io) { 212 if (!io) {
@@ -210,7 +247,8 @@ static int irqc_probe(struct platform_device *pdev)
210 irq_chip->irq_mask = irqc_irq_disable; 247 irq_chip->irq_mask = irqc_irq_disable;
211 irq_chip->irq_unmask = irqc_irq_enable; 248 irq_chip->irq_unmask = irqc_irq_enable;
212 irq_chip->irq_set_type = irqc_irq_set_type; 249 irq_chip->irq_set_type = irqc_irq_set_type;
213 irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; 250 irq_chip->irq_set_wake = irqc_irq_set_wake;
251 irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
214 252
215 p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, 253 p->irq_domain = irq_domain_add_simple(pdev->dev.of_node,
216 p->number_of_irqs, 254 p->number_of_irqs,
@@ -250,6 +288,8 @@ err3:
250err2: 288err2:
251 iounmap(p->iomem); 289 iounmap(p->iomem);
252err1: 290err1:
291 pm_runtime_put(&pdev->dev);
292 pm_runtime_disable(&pdev->dev);
253 kfree(p); 293 kfree(p);
254err0: 294err0:
255 return ret; 295 return ret;
@@ -265,6 +305,8 @@ static int irqc_remove(struct platform_device *pdev)
265 305
266 irq_domain_remove(p->irq_domain); 306 irq_domain_remove(p->irq_domain);
267 iounmap(p->iomem); 307 iounmap(p->iomem);
308 pm_runtime_put(&pdev->dev);
309 pm_runtime_disable(&pdev->dev);
268 kfree(p); 310 kfree(p);
269 return 0; 311 return 0;
270} 312}
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);
diff --git a/include/linux/irqchip/mips-gic.h b/include/linux/irqchip/mips-gic.h
index e6a6aac451db..3ea2e4754c40 100644
--- a/include/linux/irqchip/mips-gic.h
+++ b/include/linux/irqchip/mips-gic.h
@@ -240,6 +240,8 @@ extern unsigned int gic_get_count_width(void);
240extern cycle_t gic_read_compare(void); 240extern cycle_t gic_read_compare(void);
241extern void gic_write_compare(cycle_t cnt); 241extern void gic_write_compare(cycle_t cnt);
242extern void gic_write_cpu_compare(cycle_t cnt, int cpu); 242extern void gic_write_cpu_compare(cycle_t cnt, int cpu);
243extern void gic_start_count(void);
244extern void gic_stop_count(void);
243extern void gic_send_ipi(unsigned int intr); 245extern void gic_send_ipi(unsigned int intr);
244extern unsigned int plat_ipi_call_int_xlate(unsigned int); 246extern unsigned int plat_ipi_call_int_xlate(unsigned int);
245extern unsigned int plat_ipi_resched_int_xlate(unsigned int); 247extern unsigned int plat_ipi_resched_int_xlate(unsigned int);