diff options
author | Paul Burton <paul.burton@imgtec.com> | 2017-03-30 15:06:11 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2017-04-12 17:12:35 -0400 |
commit | 3838a547fda22a37faab5770d01acd72aaeabbf6 (patch) | |
tree | 3a2db574074ad780b0dbae00a8cd3b57f54ae22a /drivers/irqchip/irq-mips-cpu.c | |
parent | 131735afc1838997da2c151b614b13f0352cf448 (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.c | 125 |
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 | ||
42 | static struct irq_domain *irq_domain; | 41 | static struct irq_domain *irq_domain; |
42 | static struct irq_domain *ipi_domain; | ||
43 | 43 | ||
44 | static inline void unmask_mips_irq(struct irq_data *d) | 44 | static 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 | |||
95 | static 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 | |||
93 | static struct irq_chip mips_mt_cpu_irq_controller = { | 116 | static 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 | ||
105 | asmlinkage void __weak plat_irq_dispatch(void) | 131 | asmlinkage 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 | |||
181 | struct cpu_ipi_domain_state { | ||
182 | DECLARE_BITMAP(allocated, 2); | ||
183 | }; | ||
184 | |||
185 | static 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 | |||
212 | static 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 | |||
226 | static const struct irq_domain_ops mips_cpu_ipi_chip_ops = { | ||
227 | .alloc = mips_cpu_ipi_alloc, | ||
228 | .match = mips_cpu_ipi_match, | ||
229 | }; | ||
230 | |||
231 | static 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 | |||
248 | static inline void mips_cpu_register_ipi_domain(struct device_node *of_node) {} | ||
249 | |||
250 | #endif /* !CONFIG_GENERIC_IRQ_IPI */ | ||
251 | |||
150 | static void __init __mips_cpu_irq_init(struct device_node *of_node) | 252 | static 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 | ||
163 | void __init mips_cpu_irq_init(void) | 272 | void __init mips_cpu_irq_init(void) |