.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
---|
1 | 2 | /* |
---|
2 | 3 | * Root interrupt controller for the BCM2836 (Raspberry Pi 2). |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright 2015 Broadcom |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | 6 | */ |
---|
16 | 7 | |
---|
17 | 8 | #include <linux/cpu.h> |
---|
.. | .. |
---|
19 | 10 | #include <linux/of_irq.h> |
---|
20 | 11 | #include <linux/irqchip.h> |
---|
21 | 12 | #include <linux/irqdomain.h> |
---|
| 13 | +#include <linux/irqchip/chained_irq.h> |
---|
22 | 14 | #include <linux/irqchip/irq-bcm2836.h> |
---|
23 | 15 | |
---|
24 | 16 | #include <asm/exception.h> |
---|
.. | .. |
---|
98 | 90 | .irq_unmask = bcm2836_arm_irqchip_unmask_gpu_irq, |
---|
99 | 91 | }; |
---|
100 | 92 | |
---|
| 93 | +static void bcm2836_arm_irqchip_dummy_op(struct irq_data *d) |
---|
| 94 | +{ |
---|
| 95 | +} |
---|
| 96 | + |
---|
| 97 | +static struct irq_chip bcm2836_arm_irqchip_dummy = { |
---|
| 98 | + .name = "bcm2836-dummy", |
---|
| 99 | + .irq_eoi = bcm2836_arm_irqchip_dummy_op, |
---|
| 100 | +}; |
---|
| 101 | + |
---|
101 | 102 | static int bcm2836_map(struct irq_domain *d, unsigned int irq, |
---|
102 | 103 | irq_hw_number_t hw) |
---|
103 | 104 | { |
---|
104 | 105 | struct irq_chip *chip; |
---|
105 | 106 | |
---|
106 | 107 | switch (hw) { |
---|
| 108 | + case LOCAL_IRQ_MAILBOX0: |
---|
| 109 | + chip = &bcm2836_arm_irqchip_dummy; |
---|
| 110 | + break; |
---|
107 | 111 | case LOCAL_IRQ_CNTPSIRQ: |
---|
108 | 112 | case LOCAL_IRQ_CNTPNSIRQ: |
---|
109 | 113 | case LOCAL_IRQ_CNTHPIRQ: |
---|
.. | .. |
---|
136 | 140 | u32 stat; |
---|
137 | 141 | |
---|
138 | 142 | stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu); |
---|
139 | | - if (stat & BIT(LOCAL_IRQ_MAILBOX0)) { |
---|
140 | | -#ifdef CONFIG_SMP |
---|
141 | | - void __iomem *mailbox0 = (intc.base + |
---|
142 | | - LOCAL_MAILBOX0_CLR0 + 16 * cpu); |
---|
143 | | - u32 mbox_val = readl(mailbox0); |
---|
144 | | - u32 ipi = ffs(mbox_val) - 1; |
---|
145 | | - |
---|
146 | | - writel(1 << ipi, mailbox0); |
---|
147 | | - handle_IPI(ipi, regs); |
---|
148 | | -#endif |
---|
149 | | - } else if (stat) { |
---|
| 143 | + if (stat) { |
---|
150 | 144 | u32 hwirq = ffs(stat) - 1; |
---|
151 | 145 | |
---|
152 | 146 | handle_domain_irq(intc.domain, hwirq, regs); |
---|
.. | .. |
---|
154 | 148 | } |
---|
155 | 149 | |
---|
156 | 150 | #ifdef CONFIG_SMP |
---|
157 | | -static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask, |
---|
158 | | - unsigned int ipi) |
---|
| 151 | +static struct irq_domain *ipi_domain; |
---|
| 152 | + |
---|
| 153 | +static void bcm2836_arm_irqchip_handle_ipi(struct irq_desc *desc) |
---|
| 154 | +{ |
---|
| 155 | + struct irq_chip *chip = irq_desc_get_chip(desc); |
---|
| 156 | + int cpu = smp_processor_id(); |
---|
| 157 | + u32 mbox_val; |
---|
| 158 | + |
---|
| 159 | + chained_irq_enter(chip, desc); |
---|
| 160 | + |
---|
| 161 | + mbox_val = readl_relaxed(intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu); |
---|
| 162 | + if (mbox_val) { |
---|
| 163 | + int hwirq = ffs(mbox_val) - 1; |
---|
| 164 | + generic_handle_irq(irq_find_mapping(ipi_domain, hwirq)); |
---|
| 165 | + } |
---|
| 166 | + |
---|
| 167 | + chained_irq_exit(chip, desc); |
---|
| 168 | +} |
---|
| 169 | + |
---|
| 170 | +static void bcm2836_arm_irqchip_ipi_eoi(struct irq_data *d) |
---|
| 171 | +{ |
---|
| 172 | + int cpu = smp_processor_id(); |
---|
| 173 | + |
---|
| 174 | + writel_relaxed(BIT(d->hwirq), |
---|
| 175 | + intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu); |
---|
| 176 | +} |
---|
| 177 | + |
---|
| 178 | +static void bcm2836_arm_irqchip_ipi_send_mask(struct irq_data *d, |
---|
| 179 | + const struct cpumask *mask) |
---|
159 | 180 | { |
---|
160 | 181 | int cpu; |
---|
161 | 182 | void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0; |
---|
.. | .. |
---|
166 | 187 | */ |
---|
167 | 188 | smp_wmb(); |
---|
168 | 189 | |
---|
169 | | - for_each_cpu(cpu, mask) { |
---|
170 | | - writel(1 << ipi, mailbox0_base + 16 * cpu); |
---|
171 | | - } |
---|
| 190 | + for_each_cpu(cpu, mask) |
---|
| 191 | + writel_relaxed(BIT(d->hwirq), mailbox0_base + 16 * cpu); |
---|
172 | 192 | } |
---|
| 193 | + |
---|
| 194 | +static struct irq_chip bcm2836_arm_irqchip_ipi = { |
---|
| 195 | + .name = "IPI", |
---|
| 196 | + .irq_mask = bcm2836_arm_irqchip_dummy_op, |
---|
| 197 | + .irq_unmask = bcm2836_arm_irqchip_dummy_op, |
---|
| 198 | + .irq_eoi = bcm2836_arm_irqchip_ipi_eoi, |
---|
| 199 | + .ipi_send_mask = bcm2836_arm_irqchip_ipi_send_mask, |
---|
| 200 | +}; |
---|
| 201 | + |
---|
| 202 | +static int bcm2836_arm_irqchip_ipi_alloc(struct irq_domain *d, |
---|
| 203 | + unsigned int virq, |
---|
| 204 | + unsigned int nr_irqs, void *args) |
---|
| 205 | +{ |
---|
| 206 | + int i; |
---|
| 207 | + |
---|
| 208 | + for (i = 0; i < nr_irqs; i++) { |
---|
| 209 | + irq_set_percpu_devid(virq + i); |
---|
| 210 | + irq_domain_set_info(d, virq + i, i, &bcm2836_arm_irqchip_ipi, |
---|
| 211 | + d->host_data, |
---|
| 212 | + handle_percpu_devid_fasteoi_ipi, |
---|
| 213 | + NULL, NULL); |
---|
| 214 | + } |
---|
| 215 | + |
---|
| 216 | + return 0; |
---|
| 217 | +} |
---|
| 218 | + |
---|
| 219 | +static void bcm2836_arm_irqchip_ipi_free(struct irq_domain *d, |
---|
| 220 | + unsigned int virq, |
---|
| 221 | + unsigned int nr_irqs) |
---|
| 222 | +{ |
---|
| 223 | + /* Not freeing IPIs */ |
---|
| 224 | +} |
---|
| 225 | + |
---|
| 226 | +static const struct irq_domain_ops ipi_domain_ops = { |
---|
| 227 | + .alloc = bcm2836_arm_irqchip_ipi_alloc, |
---|
| 228 | + .free = bcm2836_arm_irqchip_ipi_free, |
---|
| 229 | +}; |
---|
173 | 230 | |
---|
174 | 231 | static int bcm2836_cpu_starting(unsigned int cpu) |
---|
175 | 232 | { |
---|
.. | .. |
---|
184 | 241 | cpu); |
---|
185 | 242 | return 0; |
---|
186 | 243 | } |
---|
| 244 | + |
---|
| 245 | +#define BITS_PER_MBOX 32 |
---|
| 246 | + |
---|
| 247 | +static void __init bcm2836_arm_irqchip_smp_init(void) |
---|
| 248 | +{ |
---|
| 249 | + struct irq_fwspec ipi_fwspec = { |
---|
| 250 | + .fwnode = intc.domain->fwnode, |
---|
| 251 | + .param_count = 1, |
---|
| 252 | + .param = { |
---|
| 253 | + [0] = LOCAL_IRQ_MAILBOX0, |
---|
| 254 | + }, |
---|
| 255 | + }; |
---|
| 256 | + int base_ipi, mux_irq; |
---|
| 257 | + |
---|
| 258 | + mux_irq = irq_create_fwspec_mapping(&ipi_fwspec); |
---|
| 259 | + if (WARN_ON(mux_irq <= 0)) |
---|
| 260 | + return; |
---|
| 261 | + |
---|
| 262 | + ipi_domain = irq_domain_create_linear(intc.domain->fwnode, |
---|
| 263 | + BITS_PER_MBOX, &ipi_domain_ops, |
---|
| 264 | + NULL); |
---|
| 265 | + if (WARN_ON(!ipi_domain)) |
---|
| 266 | + return; |
---|
| 267 | + |
---|
| 268 | + ipi_domain->flags |= IRQ_DOMAIN_FLAG_IPI_SINGLE; |
---|
| 269 | + irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI); |
---|
| 270 | + |
---|
| 271 | + base_ipi = __irq_domain_alloc_irqs(ipi_domain, -1, BITS_PER_MBOX, |
---|
| 272 | + NUMA_NO_NODE, NULL, |
---|
| 273 | + false, NULL); |
---|
| 274 | + |
---|
| 275 | + if (WARN_ON(!base_ipi)) |
---|
| 276 | + return; |
---|
| 277 | + |
---|
| 278 | + set_smp_ipi_range(base_ipi, BITS_PER_MBOX); |
---|
| 279 | + |
---|
| 280 | + irq_set_chained_handler_and_data(mux_irq, |
---|
| 281 | + bcm2836_arm_irqchip_handle_ipi, NULL); |
---|
| 282 | + |
---|
| 283 | + /* Unmask IPIs to the boot CPU. */ |
---|
| 284 | + cpuhp_setup_state(CPUHP_AP_IRQ_BCM2836_STARTING, |
---|
| 285 | + "irqchip/bcm2836:starting", bcm2836_cpu_starting, |
---|
| 286 | + bcm2836_cpu_dying); |
---|
| 287 | +} |
---|
| 288 | +#else |
---|
| 289 | +#define bcm2836_arm_irqchip_smp_init() do { } while(0) |
---|
187 | 290 | #endif |
---|
188 | 291 | |
---|
189 | 292 | static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = { |
---|
190 | 293 | .xlate = irq_domain_xlate_onetwocell, |
---|
191 | 294 | .map = bcm2836_map, |
---|
192 | 295 | }; |
---|
193 | | - |
---|
194 | | -static void |
---|
195 | | -bcm2836_arm_irqchip_smp_init(void) |
---|
196 | | -{ |
---|
197 | | -#ifdef CONFIG_SMP |
---|
198 | | - /* Unmask IPIs to the boot CPU. */ |
---|
199 | | - cpuhp_setup_state(CPUHP_AP_IRQ_BCM2836_STARTING, |
---|
200 | | - "irqchip/bcm2836:starting", bcm2836_cpu_starting, |
---|
201 | | - bcm2836_cpu_dying); |
---|
202 | | - |
---|
203 | | - set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); |
---|
204 | | -#endif |
---|
205 | | -} |
---|
206 | 296 | |
---|
207 | 297 | /* |
---|
208 | 298 | * The LOCAL_IRQ_CNT* timer firings are based off of the external |
---|
.. | .. |
---|
241 | 331 | if (!intc.domain) |
---|
242 | 332 | panic("%pOF: unable to create IRQ domain\n", node); |
---|
243 | 333 | |
---|
| 334 | + irq_domain_update_bus_token(intc.domain, DOMAIN_BUS_WIRED); |
---|
| 335 | + |
---|
244 | 336 | bcm2836_arm_irqchip_smp_init(); |
---|
245 | 337 | |
---|
246 | 338 | set_handle_irq(bcm2836_arm_irqchip_handle_irq); |
---|