Branch data Line data Source code
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/irq-bcm2836.h>
14 : :
15 : : #include <asm/exception.h>
16 : :
17 : : struct bcm2836_arm_irqchip_intc {
18 : : struct irq_domain *domain;
19 : : void __iomem *base;
20 : : };
21 : :
22 : : static struct bcm2836_arm_irqchip_intc intc __read_mostly;
23 : :
24 : : void __iomem *arm_local_intc;
25 : : EXPORT_SYMBOL_GPL(arm_local_intc);
26 : :
27 : : static void bcm2836_arm_irqchip_mask_per_cpu_irq(unsigned int reg_offset,
28 : : unsigned int bit,
29 : : int cpu)
30 : : {
31 : 0 : void __iomem *reg = intc.base + reg_offset + 4 * cpu;
32 : :
33 : 0 : writel(readl(reg) & ~BIT(bit), reg);
34 : : }
35 : :
36 : : static void bcm2836_arm_irqchip_unmask_per_cpu_irq(unsigned int reg_offset,
37 : : unsigned int bit,
38 : : int cpu)
39 : : {
40 : 3232 : void __iomem *reg = intc.base + reg_offset + 4 * cpu;
41 : :
42 : 6464 : writel(readl(reg) | BIT(bit), reg);
43 : : }
44 : :
45 : 0 : static void bcm2836_arm_irqchip_mask_timer_irq(struct irq_data *d)
46 : : {
47 : 0 : bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0,
48 : 0 : d->hwirq - LOCAL_IRQ_CNTPSIRQ,
49 : 0 : smp_processor_id());
50 : 0 : }
51 : :
52 : 1616 : static void bcm2836_arm_irqchip_unmask_timer_irq(struct irq_data *d)
53 : : {
54 : 1616 : bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0,
55 : 1616 : d->hwirq - LOCAL_IRQ_CNTPSIRQ,
56 : 1616 : smp_processor_id());
57 : 1616 : }
58 : :
59 : : static struct irq_chip bcm2836_arm_irqchip_timer = {
60 : : .name = "bcm2836-timer",
61 : : .irq_mask = bcm2836_arm_irqchip_mask_timer_irq,
62 : : .irq_unmask = bcm2836_arm_irqchip_unmask_timer_irq,
63 : : };
64 : :
65 : 0 : static void bcm2836_arm_irqchip_mask_pmu_irq(struct irq_data *d)
66 : : {
67 : 0 : writel(1 << smp_processor_id(), intc.base + LOCAL_PM_ROUTING_CLR);
68 : 0 : }
69 : :
70 : 1616 : static void bcm2836_arm_irqchip_unmask_pmu_irq(struct irq_data *d)
71 : : {
72 : 3232 : writel(1 << smp_processor_id(), intc.base + LOCAL_PM_ROUTING_SET);
73 : 1616 : }
74 : :
75 : : static struct irq_chip bcm2836_arm_irqchip_pmu = {
76 : : .name = "bcm2836-pmu",
77 : : .irq_mask = bcm2836_arm_irqchip_mask_pmu_irq,
78 : : .irq_unmask = bcm2836_arm_irqchip_unmask_pmu_irq,
79 : : };
80 : :
81 : 0 : static void bcm2836_arm_irqchip_mask_gpu_irq(struct irq_data *d)
82 : : {
83 : 0 : }
84 : :
85 : 404 : static void bcm2836_arm_irqchip_unmask_gpu_irq(struct irq_data *d)
86 : : {
87 : 404 : }
88 : :
89 : : #ifdef CONFIG_ARM64
90 : :
91 : : void bcm2836_arm_irqchip_spin_gpu_irq(void)
92 : : {
93 : : u32 i;
94 : : void __iomem *gpurouting = (intc.base + LOCAL_GPU_ROUTING);
95 : : u32 routing_val = readl(gpurouting);
96 : :
97 : : for (i = 1; i <= 3; i++) {
98 : : u32 new_routing_val = (routing_val + i) & 3;
99 : :
100 : : if (cpu_active(new_routing_val)) {
101 : : writel(new_routing_val, gpurouting);
102 : : return;
103 : : }
104 : : }
105 : : }
106 : : EXPORT_SYMBOL(bcm2836_arm_irqchip_spin_gpu_irq);
107 : :
108 : : #endif
109 : :
110 : : static struct irq_chip bcm2836_arm_irqchip_gpu = {
111 : : .name = "bcm2836-gpu",
112 : : .irq_mask = bcm2836_arm_irqchip_mask_gpu_irq,
113 : : .irq_unmask = bcm2836_arm_irqchip_unmask_gpu_irq,
114 : : };
115 : :
116 : 2424 : static int bcm2836_map(struct irq_domain *d, unsigned int irq,
117 : : irq_hw_number_t hw)
118 : : {
119 : : struct irq_chip *chip;
120 : :
121 [ + + - + ]: 2424 : switch (hw) {
122 : : case LOCAL_IRQ_CNTPSIRQ:
123 : : case LOCAL_IRQ_CNTPNSIRQ:
124 : : case LOCAL_IRQ_CNTHPIRQ:
125 : : case LOCAL_IRQ_CNTVIRQ:
126 : : chip = &bcm2836_arm_irqchip_timer;
127 : : break;
128 : : case LOCAL_IRQ_GPU_FAST:
129 : : chip = &bcm2836_arm_irqchip_gpu;
130 : 404 : break;
131 : : case LOCAL_IRQ_PMU_FAST:
132 : : chip = &bcm2836_arm_irqchip_pmu;
133 : 404 : break;
134 : : default:
135 [ # # ]: 0 : pr_warn_once("Unexpected hw irq: %lu\n", hw);
136 : : return -EINVAL;
137 : : }
138 : :
139 : 2424 : irq_set_percpu_devid(irq);
140 : 2424 : irq_domain_set_info(d, irq, hw, chip, d->host_data,
141 : : handle_percpu_devid_irq, NULL, NULL);
142 : : irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_TYPE_LEVEL_LOW);
143 : :
144 : 2424 : return 0;
145 : : }
146 : :
147 : : static void
148 : 77992340 : __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
149 : : {
150 : 77992340 : int cpu = smp_processor_id();
151 : : u32 stat;
152 : :
153 : 77992340 : stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu);
154 [ + + ]: 78306482 : if (stat & BIT(LOCAL_IRQ_MAILBOX0)) {
155 : : #ifdef CONFIG_SMP
156 : 9013246 : void __iomem *mailbox0 = (intc.base +
157 : 9013246 : LOCAL_MAILBOX0_CLR0 + 16 * cpu);
158 : 9014004 : u32 mbox_val = readl(mailbox0);
159 : 18028020 : u32 ipi = ffs(mbox_val) - 1;
160 : :
161 : 9014010 : writel(1 << ipi, mailbox0);
162 : 9014012 : dsb(sy);
163 : 9014012 : handle_IPI(ipi, regs);
164 : : #endif
165 [ + + ]: 69293236 : } else if (stat) {
166 : 138550952 : u32 hwirq = ffs(stat) - 1;
167 : :
168 : 69275476 : handle_domain_irq(intc.domain, hwirq, regs);
169 : : }
170 : 78331636 : }
171 : :
172 : : #ifdef CONFIG_SMP
173 : 9010686 : static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
174 : : unsigned int ipi)
175 : : {
176 : : int cpu;
177 : 9010686 : void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0;
178 : :
179 : : /*
180 : : * Ensure that stores to normal memory are visible to the
181 : : * other CPUs before issuing the IPI.
182 : : */
183 : 9010686 : smp_wmb();
184 : :
185 [ + + ]: 27038024 : for_each_cpu(cpu, mask) {
186 : 9016538 : writel(1 << ipi, mailbox0_base + 16 * cpu);
187 : : }
188 : 9010744 : }
189 : :
190 : 1616 : static int bcm2836_cpu_starting(unsigned int cpu)
191 : : {
192 : 1616 : bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0,
193 : : cpu);
194 : 1616 : return 0;
195 : : }
196 : :
197 : 0 : static int bcm2836_cpu_dying(unsigned int cpu)
198 : : {
199 : 0 : bcm2836_arm_irqchip_mask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0,
200 : : cpu);
201 : 0 : return 0;
202 : : }
203 : : #endif
204 : :
205 : : static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
206 : : .xlate = irq_domain_xlate_onetwocell,
207 : : .map = bcm2836_map,
208 : : };
209 : :
210 : : static void
211 : 404 : bcm2836_arm_irqchip_smp_init(void)
212 : : {
213 : : #ifdef CONFIG_SMP
214 : : /* Unmask IPIs to the boot CPU. */
215 : : cpuhp_setup_state(CPUHP_AP_IRQ_BCM2836_STARTING,
216 : : "irqchip/bcm2836:starting", bcm2836_cpu_starting,
217 : : bcm2836_cpu_dying);
218 : :
219 : 404 : set_smp_cross_call(bcm2836_arm_irqchip_send_ipi);
220 : : #endif
221 : 404 : }
222 : :
223 : : /*
224 : : * The LOCAL_IRQ_CNT* timer firings are based off of the external
225 : : * oscillator with some scaling. The firmware sets up CNTFRQ to
226 : : * report 19.2Mhz, but doesn't set up the scaling registers.
227 : : */
228 : : static void bcm2835_init_local_timer_frequency(void)
229 : : {
230 : : /*
231 : : * Set the timer to source from the 19.2Mhz crystal clock (bit
232 : : * 8 unset), and only increment by 1 instead of 2 (bit 9
233 : : * unset).
234 : : */
235 : 404 : writel(0, intc.base + LOCAL_CONTROL);
236 : :
237 : : /*
238 : : * Set the timer prescaler to 1:1 (timer freq = input freq *
239 : : * 2**31 / prescaler)
240 : : */
241 : 404 : writel(0x80000000, intc.base + LOCAL_PRESCALER);
242 : : }
243 : :
244 : 404 : static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
245 : : struct device_node *parent)
246 : : {
247 : 404 : intc.base = of_iomap(node, 0);
248 [ - + ]: 404 : if (!intc.base) {
249 : 0 : panic("%pOF: unable to map local interrupt registers\n", node);
250 : : }
251 : :
252 : 404 : arm_local_intc = intc.base;
253 : :
254 : : bcm2835_init_local_timer_frequency();
255 : :
256 : 404 : intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1,
257 : : &bcm2836_arm_irqchip_intc_ops,
258 : : NULL);
259 [ - + ]: 404 : if (!intc.domain)
260 : 0 : panic("%pOF: unable to create IRQ domain\n", node);
261 : :
262 : 404 : bcm2836_arm_irqchip_smp_init();
263 : :
264 : 404 : set_handle_irq(bcm2836_arm_irqchip_handle_irq);
265 : 404 : return 0;
266 : : }
267 : :
268 : : IRQCHIP_DECLARE(bcm2836_arm_irqchip_l1_intc, "brcm,bcm2836-l1-intc",
269 : : bcm2836_arm_irqchip_l1_intc_of_init);
|