1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Root interrupt controller for the BCM2836 (Raspberry Pi 2). |
4 | * |
5 | * Copyright 2015 Broadcom |
6 | */ |
7 | |
8 | #include <linux/cpu.h> |
9 | #include <linux/of_address.h> |
10 | #include <linux/of_irq.h> |
11 | #include <linux/irqchip.h> |
12 | #include <linux/irqdomain.h> |
13 | #include <linux/irqchip/chained_irq.h> |
14 | #include <linux/irqchip/irq-bcm2836.h> |
15 | |
16 | #include <asm/exception.h> |
17 | |
18 | struct bcm2836_arm_irqchip_intc { |
19 | struct irq_domain *domain; |
20 | void __iomem *base; |
21 | }; |
22 | |
23 | static struct bcm2836_arm_irqchip_intc intc __read_mostly; |
24 | |
25 | static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset, |
26 | unsigned int bit, |
27 | int cpu) |
28 | { |
29 | void __iomem *reg = intc.base + reg_offset + 4 * cpu; |
30 | |
31 | writel(readl(addr: reg) & ~BIT(bit), addr: reg); |
32 | } |
33 | |
34 | static void bcm2836_arm_irqchip_unmask_per_cpu_irq(unsigned int reg_offset, |
35 | unsigned int bit, |
36 | int cpu) |
37 | { |
38 | void __iomem *reg = intc.base + reg_offset + 4 * cpu; |
39 | |
40 | writel(readl(addr: reg) | BIT(bit), addr: reg); |
41 | } |
42 | |
43 | static void bcm2836_arm_irqchip_mask_timer_irq(struct irq_data *d) |
44 | { |
45 | bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0, |
46 | bit: d->hwirq - LOCAL_IRQ_CNTPSIRQ, |
47 | smp_processor_id()); |
48 | } |
49 | |
50 | static void bcm2836_arm_irqchip_unmask_timer_irq(struct irq_data *d) |
51 | { |
52 | bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0, |
53 | bit: d->hwirq - LOCAL_IRQ_CNTPSIRQ, |
54 | smp_processor_id()); |
55 | } |
56 | |
57 | static struct irq_chip bcm2836_arm_irqchip_timer = { |
58 | .name = "bcm2836-timer" , |
59 | .irq_mask = bcm2836_arm_irqchip_mask_timer_irq, |
60 | .irq_unmask = bcm2836_arm_irqchip_unmask_timer_irq, |
61 | }; |
62 | |
63 | static void bcm2836_arm_irqchip_mask_pmu_irq(struct irq_data *d) |
64 | { |
65 | writel(val: 1 << smp_processor_id(), addr: intc.base + LOCAL_PM_ROUTING_CLR); |
66 | } |
67 | |
68 | static void bcm2836_arm_irqchip_unmask_pmu_irq(struct irq_data *d) |
69 | { |
70 | writel(val: 1 << smp_processor_id(), addr: intc.base + LOCAL_PM_ROUTING_SET); |
71 | } |
72 | |
73 | static struct irq_chip bcm2836_arm_irqchip_pmu = { |
74 | .name = "bcm2836-pmu" , |
75 | .irq_mask = bcm2836_arm_irqchip_mask_pmu_irq, |
76 | .irq_unmask = bcm2836_arm_irqchip_unmask_pmu_irq, |
77 | }; |
78 | |
79 | static void bcm2836_arm_irqchip_mask_gpu_irq(struct irq_data *d) |
80 | { |
81 | } |
82 | |
83 | static void bcm2836_arm_irqchip_unmask_gpu_irq(struct irq_data *d) |
84 | { |
85 | } |
86 | |
87 | static struct irq_chip bcm2836_arm_irqchip_gpu = { |
88 | .name = "bcm2836-gpu" , |
89 | .irq_mask = bcm2836_arm_irqchip_mask_gpu_irq, |
90 | .irq_unmask = bcm2836_arm_irqchip_unmask_gpu_irq, |
91 | }; |
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 | |
102 | static int bcm2836_map(struct irq_domain *d, unsigned int irq, |
103 | irq_hw_number_t hw) |
104 | { |
105 | struct irq_chip *chip; |
106 | |
107 | switch (hw) { |
108 | case LOCAL_IRQ_MAILBOX0: |
109 | chip = &bcm2836_arm_irqchip_dummy; |
110 | break; |
111 | case LOCAL_IRQ_CNTPSIRQ: |
112 | case LOCAL_IRQ_CNTPNSIRQ: |
113 | case LOCAL_IRQ_CNTHPIRQ: |
114 | case LOCAL_IRQ_CNTVIRQ: |
115 | chip = &bcm2836_arm_irqchip_timer; |
116 | break; |
117 | case LOCAL_IRQ_GPU_FAST: |
118 | chip = &bcm2836_arm_irqchip_gpu; |
119 | break; |
120 | case LOCAL_IRQ_PMU_FAST: |
121 | chip = &bcm2836_arm_irqchip_pmu; |
122 | break; |
123 | default: |
124 | pr_warn_once("Unexpected hw irq: %lu\n" , hw); |
125 | return -EINVAL; |
126 | } |
127 | |
128 | irq_set_percpu_devid(irq); |
129 | irq_domain_set_info(domain: d, virq: irq, hwirq: hw, chip, chip_data: d->host_data, |
130 | handler: handle_percpu_devid_irq, NULL, NULL); |
131 | irq_set_status_flags(irq, set: IRQ_NOAUTOEN); |
132 | |
133 | return 0; |
134 | } |
135 | |
136 | static void |
137 | __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs) |
138 | { |
139 | int cpu = smp_processor_id(); |
140 | u32 stat; |
141 | |
142 | stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu); |
143 | if (stat) { |
144 | u32 hwirq = ffs(stat) - 1; |
145 | |
146 | generic_handle_domain_irq(intc.domain, hwirq); |
147 | } |
148 | } |
149 | |
150 | #ifdef CONFIG_SMP |
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_domain_irq(domain: ipi_domain, hwirq); |
165 | } |
166 | |
167 | chained_irq_exit(chip, desc); |
168 | } |
169 | |
170 | static void bcm2836_arm_irqchip_ipi_ack(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) |
180 | { |
181 | int cpu; |
182 | void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0; |
183 | |
184 | /* |
185 | * Ensure that stores to normal memory are visible to the |
186 | * other CPUs before issuing the IPI. |
187 | */ |
188 | smp_wmb(); |
189 | |
190 | for_each_cpu(cpu, mask) |
191 | writel_relaxed(BIT(d->hwirq), mailbox0_base + 16 * cpu); |
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_ack = bcm2836_arm_irqchip_ipi_ack, |
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(irq: virq + i); |
210 | irq_domain_set_info(domain: d, virq: virq + i, hwirq: i, chip: &bcm2836_arm_irqchip_ipi, |
211 | chip_data: d->host_data, |
212 | handler: handle_percpu_devid_irq, |
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 | }; |
230 | |
231 | static int bcm2836_cpu_starting(unsigned int cpu) |
232 | { |
233 | bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, bit: 0, |
234 | cpu); |
235 | return 0; |
236 | } |
237 | |
238 | static int bcm2836_cpu_dying(unsigned int cpu) |
239 | { |
240 | bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, bit: 0, |
241 | cpu); |
242 | return 0; |
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(fwspec: &ipi_fwspec); |
259 | if (WARN_ON(mux_irq <= 0)) |
260 | return; |
261 | |
262 | ipi_domain = irq_domain_create_linear(fwnode: intc.domain->fwnode, |
263 | BITS_PER_MBOX, ops: &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(domain: ipi_domain, bus_token: DOMAIN_BUS_IPI); |
270 | |
271 | base_ipi = irq_domain_alloc_irqs(domain: ipi_domain, BITS_PER_MBOX, NUMA_NO_NODE, NULL); |
272 | if (WARN_ON(!base_ipi)) |
273 | return; |
274 | |
275 | set_smp_ipi_range(base_ipi, BITS_PER_MBOX); |
276 | |
277 | irq_set_chained_handler_and_data(irq: mux_irq, |
278 | handle: bcm2836_arm_irqchip_handle_ipi, NULL); |
279 | |
280 | /* Unmask IPIs to the boot CPU. */ |
281 | cpuhp_setup_state(state: CPUHP_AP_IRQ_BCM2836_STARTING, |
282 | name: "irqchip/bcm2836:starting" , startup: bcm2836_cpu_starting, |
283 | teardown: bcm2836_cpu_dying); |
284 | } |
285 | #else |
286 | #define bcm2836_arm_irqchip_smp_init() do { } while(0) |
287 | #endif |
288 | |
289 | static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = { |
290 | .xlate = irq_domain_xlate_onetwocell, |
291 | .map = bcm2836_map, |
292 | }; |
293 | |
294 | /* |
295 | * The LOCAL_IRQ_CNT* timer firings are based off of the external |
296 | * oscillator with some scaling. The firmware sets up CNTFRQ to |
297 | * report 19.2Mhz, but doesn't set up the scaling registers. |
298 | */ |
299 | static void bcm2835_init_local_timer_frequency(void) |
300 | { |
301 | /* |
302 | * Set the timer to source from the 19.2Mhz crystal clock (bit |
303 | * 8 unset), and only increment by 1 instead of 2 (bit 9 |
304 | * unset). |
305 | */ |
306 | writel(val: 0, addr: intc.base + LOCAL_CONTROL); |
307 | |
308 | /* |
309 | * Set the timer prescaler to 1:1 (timer freq = input freq * |
310 | * 2**31 / prescaler) |
311 | */ |
312 | writel(val: 0x80000000, addr: intc.base + LOCAL_PRESCALER); |
313 | } |
314 | |
315 | static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node, |
316 | struct device_node *parent) |
317 | { |
318 | intc.base = of_iomap(node, index: 0); |
319 | if (!intc.base) { |
320 | panic(fmt: "%pOF: unable to map local interrupt registers\n" , node); |
321 | } |
322 | |
323 | bcm2835_init_local_timer_frequency(); |
324 | |
325 | intc.domain = irq_domain_add_linear(of_node: node, LAST_IRQ + 1, |
326 | ops: &bcm2836_arm_irqchip_intc_ops, |
327 | NULL); |
328 | if (!intc.domain) |
329 | panic(fmt: "%pOF: unable to create IRQ domain\n" , node); |
330 | |
331 | irq_domain_update_bus_token(domain: intc.domain, bus_token: DOMAIN_BUS_WIRED); |
332 | |
333 | bcm2836_arm_irqchip_smp_init(); |
334 | |
335 | set_handle_irq(bcm2836_arm_irqchip_handle_irq); |
336 | return 0; |
337 | } |
338 | |
339 | IRQCHIP_DECLARE(bcm2836_arm_irqchip_l1_intc, "brcm,bcm2836-l1-intc" , |
340 | bcm2836_arm_irqchip_l1_intc_of_init); |
341 | |