Commit | Line | Data |
---|---|---|
9ae6f740 TP |
1 | /* |
2 | * Marvell Armada 370 and Armada XP SoC IRQ handling | |
3 | * | |
4 | * Copyright (C) 2012 Marvell | |
5 | * | |
6 | * Lior Amsalem <alior@marvell.com> | |
7 | * Gregory CLEMENT <gregory.clement@free-electrons.com> | |
8 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | |
9 | * Ben Dooks <ben.dooks@codethink.co.uk> | |
10 | * | |
11 | * This file is licensed under the terms of the GNU General Public | |
12 | * License version 2. This program is licensed "as is" without any | |
13 | * warranty of any kind, whether express or implied. | |
14 | */ | |
15 | ||
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/irq.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/io.h> | |
22 | #include <linux/of_address.h> | |
23 | #include <linux/of_irq.h> | |
24 | #include <linux/irqdomain.h> | |
25 | #include <asm/mach/arch.h> | |
26 | #include <asm/exception.h> | |
27 | ||
28 | /* Interrupt Controller Registers Map */ | |
29 | #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) | |
30 | #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) | |
31 | ||
f3e16ccd | 32 | #define ARMADA_370_XP_INT_CONTROL (0x00) |
9ae6f740 TP |
33 | #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) |
34 | #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34) | |
35 | ||
36 | #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) | |
37 | ||
9ae6f740 TP |
38 | static void __iomem *per_cpu_int_base; |
39 | static void __iomem *main_int_base; | |
40 | static struct irq_domain *armada_370_xp_mpic_domain; | |
41 | ||
42 | static void armada_370_xp_irq_mask(struct irq_data *d) | |
43 | { | |
44 | writel(irqd_to_hwirq(d), | |
45 | per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); | |
46 | } | |
47 | ||
48 | static void armada_370_xp_irq_unmask(struct irq_data *d) | |
49 | { | |
50 | writel(irqd_to_hwirq(d), | |
51 | per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); | |
52 | } | |
53 | ||
54 | static struct irq_chip armada_370_xp_irq_chip = { | |
55 | .name = "armada_370_xp_irq", | |
56 | .irq_mask = armada_370_xp_irq_mask, | |
57 | .irq_mask_ack = armada_370_xp_irq_mask, | |
58 | .irq_unmask = armada_370_xp_irq_unmask, | |
59 | }; | |
60 | ||
61 | static int armada_370_xp_mpic_irq_map(struct irq_domain *h, | |
62 | unsigned int virq, irq_hw_number_t hw) | |
63 | { | |
64 | armada_370_xp_irq_mask(irq_get_irq_data(virq)); | |
65 | writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); | |
66 | ||
67 | irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, | |
68 | handle_level_irq); | |
69 | irq_set_status_flags(virq, IRQ_LEVEL); | |
70 | set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { | |
76 | .map = armada_370_xp_mpic_irq_map, | |
77 | .xlate = irq_domain_xlate_onecell, | |
78 | }; | |
79 | ||
80 | static int __init armada_370_xp_mpic_of_init(struct device_node *node, | |
81 | struct device_node *parent) | |
82 | { | |
f3e16ccd BD |
83 | u32 control; |
84 | ||
9ae6f740 TP |
85 | main_int_base = of_iomap(node, 0); |
86 | per_cpu_int_base = of_iomap(node, 1); | |
87 | ||
88 | BUG_ON(!main_int_base); | |
89 | BUG_ON(!per_cpu_int_base); | |
90 | ||
f3e16ccd BD |
91 | control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); |
92 | ||
9ae6f740 | 93 | armada_370_xp_mpic_domain = |
f3e16ccd | 94 | irq_domain_add_linear(node, (control >> 2) & 0x3ff, |
9ae6f740 TP |
95 | &armada_370_xp_mpic_irq_ops, NULL); |
96 | ||
97 | if (!armada_370_xp_mpic_domain) | |
98 | panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n"); | |
99 | ||
100 | irq_set_default_host(armada_370_xp_mpic_domain); | |
101 | return 0; | |
102 | } | |
103 | ||
104 | asmlinkage void __exception_irq_entry armada_370_xp_handle_irq(struct pt_regs | |
105 | *regs) | |
106 | { | |
107 | u32 irqstat, irqnr; | |
108 | ||
109 | do { | |
110 | irqstat = readl_relaxed(per_cpu_int_base + | |
111 | ARMADA_370_XP_CPU_INTACK_OFFS); | |
112 | irqnr = irqstat & 0x3FF; | |
113 | ||
114 | if (irqnr < 1023) { | |
115 | irqnr = | |
116 | irq_find_mapping(armada_370_xp_mpic_domain, irqnr); | |
117 | handle_IRQ(irqnr, regs); | |
118 | continue; | |
119 | } | |
120 | ||
121 | break; | |
122 | } while (1); | |
123 | } | |
124 | ||
125 | static const struct of_device_id mpic_of_match[] __initconst = { | |
126 | {.compatible = "marvell,mpic", .data = armada_370_xp_mpic_of_init}, | |
127 | {}, | |
128 | }; | |
129 | ||
130 | void __init armada_370_xp_init_irq(void) | |
131 | { | |
132 | of_irq_init(mpic_of_match); | |
133 | } |