summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-mips-cpu.c
diff options
context:
space:
mode:
authorPaul Burton <paul.burton@imgtec.com>2017-03-30 15:06:11 -0400
committerRalf Baechle <ralf@linux-mips.org>2017-04-12 17:12:35 -0400
commit3838a547fda22a37faab5770d01acd72aaeabbf6 (patch)
tree3a2db574074ad780b0dbae00a8cd3b57f54ae22a /drivers/irqchip/irq-mips-cpu.c
parent131735afc1838997da2c151b614b13f0352cf448 (diff)
irqchip: mips-cpu: Introduce IPI IRQ domain support
Introduce support for registering an IPI IRQ domain suitable for use by systems using the MIPS MT (multithreading) ASE within a single core. This will allow for such systems to be supported generically, without the current kludge of IPI code split between the MIPS arch & the malta board support code. Signed-off-by: Paul Burton <paul.burton@imgtec.com> Acked-by: Thomas Gleixner <tglx@linutronix.de> Cc: Jason Cooper <jason@lakedaemon.net> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/15836/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers/irqchip/irq-mips-cpu.c')
-rw-r--r--drivers/irqchip/irq-mips-cpu.c125
1 files changed, 117 insertions, 8 deletions
diff --git a/drivers/irqchip/irq-mips-cpu.c b/drivers/irqchip/irq-mips-cpu.c
index 338de924b269..b247f3c743ac 100644
--- a/drivers/irqchip/irq-mips-cpu.c
+++ b/drivers/irqchip/irq-mips-cpu.c
@@ -17,15 +17,14 @@
17/* 17/*
18 * Almost all MIPS CPUs define 8 interrupt sources. They are typically 18 * Almost all MIPS CPUs define 8 interrupt sources. They are typically
19 * level triggered (i.e., cannot be cleared from CPU; must be cleared from 19 * level triggered (i.e., cannot be cleared from CPU; must be cleared from
20 * device). The first two are software interrupts which we don't really 20 * device).
21 * use or support. The last one is usually the CPU timer interrupt if
22 * counter register is present or, for CPUs with an external FPU, by
23 * convention it's the FPU exception interrupt.
24 * 21 *
25 * Don't even think about using this on SMP. You have been warned. 22 * The first two are software interrupts (i.e. not exposed as pins) which
23 * may be used for IPIs in multi-threaded single-core systems.
26 * 24 *
27 * This file exports one global function: 25 * The last one is usually the CPU timer interrupt if the counter register
28 * void mips_cpu_irq_init(void); 26 * is present, or for old CPUs with an external FPU by convention it's the
27 * FPU exception interrupt.
29 */ 28 */
30#include <linux/init.h> 29#include <linux/init.h>
31#include <linux/interrupt.h> 30#include <linux/interrupt.h>
@@ -40,6 +39,7 @@
40#include <asm/setup.h> 39#include <asm/setup.h>
41 40
42static struct irq_domain *irq_domain; 41static struct irq_domain *irq_domain;
42static struct irq_domain *ipi_domain;
43 43
44static inline void unmask_mips_irq(struct irq_data *d) 44static inline void unmask_mips_irq(struct irq_data *d)
45{ 45{
@@ -90,6 +90,29 @@ static void mips_mt_cpu_irq_ack(struct irq_data *d)
90 mask_mips_irq(d); 90 mask_mips_irq(d);
91} 91}
92 92
93#ifdef CONFIG_GENERIC_IRQ_IPI
94
95static void mips_mt_send_ipi(struct irq_data *d, unsigned int cpu)
96{
97 irq_hw_number_t hwirq = irqd_to_hwirq(d);
98 unsigned long flags;
99 int vpflags;
100
101 local_irq_save(flags);
102
103 /* We can only send IPIs to VPEs within the local core */
104 WARN_ON(cpu_data[cpu].core != current_cpu_data.core);
105
106 vpflags = dvpe();
107 settc(cpu_vpe_id(&cpu_data[cpu]));
108 write_vpe_c0_cause(read_vpe_c0_cause() | (C_SW0 << hwirq));
109 evpe(vpflags);
110
111 local_irq_restore(flags);
112}
113
114#endif /* CONFIG_GENERIC_IRQ_IPI */
115
93static struct irq_chip mips_mt_cpu_irq_controller = { 116static struct irq_chip mips_mt_cpu_irq_controller = {
94 .name = "MIPS", 117 .name = "MIPS",
95 .irq_startup = mips_mt_cpu_irq_startup, 118 .irq_startup = mips_mt_cpu_irq_startup,
@@ -100,6 +123,9 @@ static struct irq_chip mips_mt_cpu_irq_controller = {
100 .irq_eoi = unmask_mips_irq, 123 .irq_eoi = unmask_mips_irq,
101 .irq_disable = mask_mips_irq, 124 .irq_disable = mask_mips_irq,
102 .irq_enable = unmask_mips_irq, 125 .irq_enable = unmask_mips_irq,
126#ifdef CONFIG_GENERIC_IRQ_IPI
127 .ipi_send_single = mips_mt_send_ipi,
128#endif
103}; 129};
104 130
105asmlinkage void __weak plat_irq_dispatch(void) 131asmlinkage void __weak plat_irq_dispatch(void)
@@ -116,7 +142,10 @@ asmlinkage void __weak plat_irq_dispatch(void)
116 pending >>= CAUSEB_IP; 142 pending >>= CAUSEB_IP;
117 while (pending) { 143 while (pending) {
118 irq = fls(pending) - 1; 144 irq = fls(pending) - 1;
119 virq = irq_linear_revmap(irq_domain, irq); 145 if (IS_ENABLED(CONFIG_GENERIC_IRQ_IPI) && irq < 2)
146 virq = irq_linear_revmap(ipi_domain, irq);
147 else
148 virq = irq_linear_revmap(irq_domain, irq);
120 do_IRQ(virq); 149 do_IRQ(virq);
121 pending &= ~BIT(irq); 150 pending &= ~BIT(irq);
122 } 151 }
@@ -147,6 +176,79 @@ static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = {
147 .xlate = irq_domain_xlate_onecell, 176 .xlate = irq_domain_xlate_onecell,
148}; 177};
149 178
179#ifdef CONFIG_GENERIC_IRQ_IPI
180
181struct cpu_ipi_domain_state {
182 DECLARE_BITMAP(allocated, 2);
183};
184
185static int mips_cpu_ipi_alloc(struct irq_domain *domain, unsigned int virq,
186 unsigned int nr_irqs, void *arg)
187{
188 struct cpu_ipi_domain_state *state = domain->host_data;
189 unsigned int i, hwirq;
190 int ret;
191
192 for (i = 0; i < nr_irqs; i++) {
193 hwirq = find_first_zero_bit(state->allocated, 2);
194 if (hwirq == 2)
195 return -EBUSY;
196 bitmap_set(state->allocated, hwirq, 1);
197
198 ret = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq,
199 &mips_mt_cpu_irq_controller,
200 NULL);
201 if (ret)
202 return ret;
203
204 ret = irq_set_irq_type(virq + i, IRQ_TYPE_LEVEL_HIGH);
205 if (ret)
206 return ret;
207 }
208
209 return 0;
210}
211
212static int mips_cpu_ipi_match(struct irq_domain *d, struct device_node *node,
213 enum irq_domain_bus_token bus_token)
214{
215 bool is_ipi;
216
217 switch (bus_token) {
218 case DOMAIN_BUS_IPI:
219 is_ipi = d->bus_token == bus_token;
220 return (!node || (to_of_node(d->fwnode) == node)) && is_ipi;
221 default:
222 return 0;
223 }
224}
225
226static const struct irq_domain_ops mips_cpu_ipi_chip_ops = {
227 .alloc = mips_cpu_ipi_alloc,
228 .match = mips_cpu_ipi_match,
229};
230
231static void mips_cpu_register_ipi_domain(struct device_node *of_node)
232{
233 struct cpu_ipi_domain_state *ipi_domain_state;
234
235 ipi_domain_state = kzalloc(sizeof(*ipi_domain_state), GFP_KERNEL);
236 ipi_domain = irq_domain_add_hierarchy(irq_domain,
237 IRQ_DOMAIN_FLAG_IPI_SINGLE,
238 2, of_node,
239 &mips_cpu_ipi_chip_ops,
240 ipi_domain_state);
241 if (!ipi_domain)
242 panic("Failed to add MIPS CPU IPI domain");
243 ipi_domain->bus_token = DOMAIN_BUS_IPI;
244}
245
246#else /* !CONFIG_GENERIC_IRQ_IPI */
247
248static inline void mips_cpu_register_ipi_domain(struct device_node *of_node) {}
249
250#endif /* !CONFIG_GENERIC_IRQ_IPI */
251
150static void __init __mips_cpu_irq_init(struct device_node *of_node) 252static void __init __mips_cpu_irq_init(struct device_node *of_node)
151{ 253{
152 /* Mask interrupts. */ 254 /* Mask interrupts. */
@@ -158,6 +260,13 @@ static void __init __mips_cpu_irq_init(struct device_node *of_node)
158 NULL); 260 NULL);
159 if (!irq_domain) 261 if (!irq_domain)
160 panic("Failed to add irqdomain for MIPS CPU"); 262 panic("Failed to add irqdomain for MIPS CPU");
263
264 /*
265 * Only proceed to register the software interrupt IPI implementation
266 * for CPUs which implement the MIPS MT (multi-threading) ASE.
267 */
268 if (cpu_has_mipsmt)
269 mips_cpu_register_ipi_domain(of_node);
161} 270}
162 271
163void __init mips_cpu_irq_init(void) 272void __init mips_cpu_irq_init(void)