diff options
Diffstat (limited to 'arch/mips/ralink/irq.c')
-rw-r--r-- | arch/mips/ralink/irq.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c new file mode 100644 index 000000000..220ca0cd7 --- /dev/null +++ b/arch/mips/ralink/irq.c | |||
@@ -0,0 +1,204 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0-only | ||
2 | /* | ||
3 | * | ||
4 | * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> | ||
5 | * Copyright (C) 2013 John Crispin <john@phrozen.org> | ||
6 | */ | ||
7 | |||
8 | #include <linux/io.h> | ||
9 | #include <linux/bitops.h> | ||
10 | #include <linux/of_platform.h> | ||
11 | #include <linux/of_address.h> | ||
12 | #include <linux/of_irq.h> | ||
13 | #include <linux/irqdomain.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | |||
16 | #include <asm/irq_cpu.h> | ||
17 | #include <asm/mipsregs.h> | ||
18 | |||
19 | #include "common.h" | ||
20 | |||
21 | #define INTC_INT_GLOBAL BIT(31) | ||
22 | |||
23 | #define RALINK_CPU_IRQ_INTC (MIPS_CPU_IRQ_BASE + 2) | ||
24 | #define RALINK_CPU_IRQ_PCI (MIPS_CPU_IRQ_BASE + 4) | ||
25 | #define RALINK_CPU_IRQ_FE (MIPS_CPU_IRQ_BASE + 5) | ||
26 | #define RALINK_CPU_IRQ_WIFI (MIPS_CPU_IRQ_BASE + 6) | ||
27 | #define RALINK_CPU_IRQ_COUNTER (MIPS_CPU_IRQ_BASE + 7) | ||
28 | |||
29 | /* we have a cascade of 8 irqs */ | ||
30 | #define RALINK_INTC_IRQ_BASE 8 | ||
31 | |||
32 | /* we have 32 SoC irqs */ | ||
33 | #define RALINK_INTC_IRQ_COUNT 32 | ||
34 | |||
35 | #define RALINK_INTC_IRQ_PERFC (RALINK_INTC_IRQ_BASE + 9) | ||
36 | |||
37 | enum rt_intc_regs_enum { | ||
38 | INTC_REG_STATUS0 = 0, | ||
39 | INTC_REG_STATUS1, | ||
40 | INTC_REG_TYPE, | ||
41 | INTC_REG_RAW_STATUS, | ||
42 | INTC_REG_ENABLE, | ||
43 | INTC_REG_DISABLE, | ||
44 | }; | ||
45 | |||
46 | static u32 rt_intc_regs[] = { | ||
47 | [INTC_REG_STATUS0] = 0x00, | ||
48 | [INTC_REG_STATUS1] = 0x04, | ||
49 | [INTC_REG_TYPE] = 0x20, | ||
50 | [INTC_REG_RAW_STATUS] = 0x30, | ||
51 | [INTC_REG_ENABLE] = 0x34, | ||
52 | [INTC_REG_DISABLE] = 0x38, | ||
53 | }; | ||
54 | |||
55 | static void __iomem *rt_intc_membase; | ||
56 | |||
57 | static int rt_perfcount_irq; | ||
58 | |||
59 | static inline void rt_intc_w32(u32 val, unsigned reg) | ||
60 | { | ||
61 | __raw_writel(val, rt_intc_membase + rt_intc_regs[reg]); | ||
62 | } | ||
63 | |||
64 | static inline u32 rt_intc_r32(unsigned reg) | ||
65 | { | ||
66 | return __raw_readl(rt_intc_membase + rt_intc_regs[reg]); | ||
67 | } | ||
68 | |||
69 | static void ralink_intc_irq_unmask(struct irq_data *d) | ||
70 | { | ||
71 | rt_intc_w32(BIT(d->hwirq), INTC_REG_ENABLE); | ||
72 | } | ||
73 | |||
74 | static void ralink_intc_irq_mask(struct irq_data *d) | ||
75 | { | ||
76 | rt_intc_w32(BIT(d->hwirq), INTC_REG_DISABLE); | ||
77 | } | ||
78 | |||
79 | static struct irq_chip ralink_intc_irq_chip = { | ||
80 | .name = "INTC", | ||
81 | .irq_unmask = ralink_intc_irq_unmask, | ||
82 | .irq_mask = ralink_intc_irq_mask, | ||
83 | .irq_mask_ack = ralink_intc_irq_mask, | ||
84 | }; | ||
85 | |||
86 | int get_c0_perfcount_int(void) | ||
87 | { | ||
88 | return rt_perfcount_irq; | ||
89 | } | ||
90 | EXPORT_SYMBOL_GPL(get_c0_perfcount_int); | ||
91 | |||
92 | unsigned int get_c0_compare_int(void) | ||
93 | { | ||
94 | return CP0_LEGACY_COMPARE_IRQ; | ||
95 | } | ||
96 | |||
97 | static void ralink_intc_irq_handler(struct irq_desc *desc) | ||
98 | { | ||
99 | u32 pending = rt_intc_r32(INTC_REG_STATUS0); | ||
100 | |||
101 | if (pending) { | ||
102 | struct irq_domain *domain = irq_desc_get_handler_data(desc); | ||
103 | generic_handle_irq(irq_find_mapping(domain, __ffs(pending))); | ||
104 | } else { | ||
105 | spurious_interrupt(); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | asmlinkage void plat_irq_dispatch(void) | ||
110 | { | ||
111 | unsigned long pending; | ||
112 | |||
113 | pending = read_c0_status() & read_c0_cause() & ST0_IM; | ||
114 | |||
115 | if (pending & STATUSF_IP7) | ||
116 | do_IRQ(RALINK_CPU_IRQ_COUNTER); | ||
117 | |||
118 | else if (pending & STATUSF_IP5) | ||
119 | do_IRQ(RALINK_CPU_IRQ_FE); | ||
120 | |||
121 | else if (pending & STATUSF_IP6) | ||
122 | do_IRQ(RALINK_CPU_IRQ_WIFI); | ||
123 | |||
124 | else if (pending & STATUSF_IP4) | ||
125 | do_IRQ(RALINK_CPU_IRQ_PCI); | ||
126 | |||
127 | else if (pending & STATUSF_IP2) | ||
128 | do_IRQ(RALINK_CPU_IRQ_INTC); | ||
129 | |||
130 | else | ||
131 | spurious_interrupt(); | ||
132 | } | ||
133 | |||
134 | static int intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) | ||
135 | { | ||
136 | irq_set_chip_and_handler(irq, &ralink_intc_irq_chip, handle_level_irq); | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static const struct irq_domain_ops irq_domain_ops = { | ||
142 | .xlate = irq_domain_xlate_onecell, | ||
143 | .map = intc_map, | ||
144 | }; | ||
145 | |||
146 | static int __init intc_of_init(struct device_node *node, | ||
147 | struct device_node *parent) | ||
148 | { | ||
149 | struct resource res; | ||
150 | struct irq_domain *domain; | ||
151 | int irq; | ||
152 | |||
153 | if (!of_property_read_u32_array(node, "ralink,intc-registers", | ||
154 | rt_intc_regs, 6)) | ||
155 | pr_info("intc: using register map from devicetree\n"); | ||
156 | |||
157 | irq = irq_of_parse_and_map(node, 0); | ||
158 | if (!irq) | ||
159 | panic("Failed to get INTC IRQ"); | ||
160 | |||
161 | if (of_address_to_resource(node, 0, &res)) | ||
162 | panic("Failed to get intc memory range"); | ||
163 | |||
164 | if (!request_mem_region(res.start, resource_size(&res), | ||
165 | res.name)) | ||
166 | pr_err("Failed to request intc memory"); | ||
167 | |||
168 | rt_intc_membase = ioremap(res.start, | ||
169 | resource_size(&res)); | ||
170 | if (!rt_intc_membase) | ||
171 | panic("Failed to remap intc memory"); | ||
172 | |||
173 | /* disable all interrupts */ | ||
174 | rt_intc_w32(~0, INTC_REG_DISABLE); | ||
175 | |||
176 | /* route all INTC interrupts to MIPS HW0 interrupt */ | ||
177 | rt_intc_w32(0, INTC_REG_TYPE); | ||
178 | |||
179 | domain = irq_domain_add_legacy(node, RALINK_INTC_IRQ_COUNT, | ||
180 | RALINK_INTC_IRQ_BASE, 0, &irq_domain_ops, NULL); | ||
181 | if (!domain) | ||
182 | panic("Failed to add irqdomain"); | ||
183 | |||
184 | rt_intc_w32(INTC_INT_GLOBAL, INTC_REG_ENABLE); | ||
185 | |||
186 | irq_set_chained_handler_and_data(irq, ralink_intc_irq_handler, domain); | ||
187 | |||
188 | /* tell the kernel which irq is used for performance monitoring */ | ||
189 | rt_perfcount_irq = irq_create_mapping(domain, 9); | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static struct of_device_id __initdata of_irq_ids[] = { | ||
195 | { .compatible = "mti,cpu-interrupt-controller", .data = mips_cpu_irq_of_init }, | ||
196 | { .compatible = "ralink,rt2880-intc", .data = intc_of_init }, | ||
197 | {}, | ||
198 | }; | ||
199 | |||
200 | void __init arch_init_irq(void) | ||
201 | { | ||
202 | of_irq_init(of_irq_ids); | ||
203 | } | ||
204 | |||