blob: 97688ab0ae2506abb46f7f58e429a82d3559c714 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <trace.h>
24#include <assert.h>
25#include <err.h>
26#include <bits.h>
27#include <arch/arm.h>
28#include <kernel/spinlock.h>
29#include <kernel/thread.h>
30#include <kernel/mp.h>
31#include <platform/interrupts.h>
32#include <platform/bcm2835.h>
33
34#define LOCAL_TRACE 0
35
36/* global interrupt controller */
37#define INTC_PEND0 (ARMCTRL_INTC_BASE + 0x0)
38#define INTC_PEND1 (ARMCTRL_INTC_BASE + 0x4)
39#define INTC_PEND2 (ARMCTRL_INTC_BASE + 0x8)
40#define INTC_FAST (ARMCTRL_INTC_BASE + 0xc)
41#define INTC_ENABLE1 (ARMCTRL_INTC_BASE + 0x10)
42#define INTC_ENABLE2 (ARMCTRL_INTC_BASE + 0x14)
43#define INTC_ENABLE3 (ARMCTRL_INTC_BASE + 0x18)
44#define INTC_DISABLE1 (ARMCTRL_INTC_BASE + 0x1c)
45#define INTC_DISABLE2 (ARMCTRL_INTC_BASE + 0x20)
46#define INTC_DISABLE3 (ARMCTRL_INTC_BASE + 0x24)
47
48/* per-cpu local interrupt controller bits.
49 * each is repeated 4 times, one per cpu.
50 */
51#define INTC_LOCAL_TIMER_INT_CONTROL0 (ARM_LOCAL_BASE + 0x40)
52#define INTC_LOCAL_TIMER_INT_CONTROL1 (ARM_LOCAL_BASE + 0x44)
53#define INTC_LOCAL_TIMER_INT_CONTROL2 (ARM_LOCAL_BASE + 0x48)
54#define INTC_LOCAL_TIMER_INT_CONTROL3 (ARM_LOCAL_BASE + 0x4c)
55
56#define INTC_LOCAL_MAILBOX_INT_CONTROL0 (ARM_LOCAL_BASE + 0x50)
57#define INTC_LOCAL_MAILBOX_INT_CONTROL1 (ARM_LOCAL_BASE + 0x54)
58#define INTC_LOCAL_MAILBOX_INT_CONTROL2 (ARM_LOCAL_BASE + 0x58)
59#define INTC_LOCAL_MAILBOX_INT_CONTROL3 (ARM_LOCAL_BASE + 0x5c)
60
61#define INTC_LOCAL_IRQ_PEND0 (ARM_LOCAL_BASE + 0x60)
62#define INTC_LOCAL_IRQ_PEND1 (ARM_LOCAL_BASE + 0x64)
63#define INTC_LOCAL_IRQ_PEND2 (ARM_LOCAL_BASE + 0x68)
64#define INTC_LOCAL_IRQ_PEND3 (ARM_LOCAL_BASE + 0x6c)
65
66#define INTC_LOCAL_FIQ_PEND0 (ARM_LOCAL_BASE + 0x70)
67#define INTC_LOCAL_FIQ_PEND1 (ARM_LOCAL_BASE + 0x74)
68#define INTC_LOCAL_FIQ_PEND2 (ARM_LOCAL_BASE + 0x78)
69#define INTC_LOCAL_FIQ_PEND3 (ARM_LOCAL_BASE + 0x7c)
70
71#define INTC_LOCAL_MAILBOX0_SET0 (ARM_LOCAL_BASE + 0x80)
72#define INTC_LOCAL_MAILBOX0_SET1 (ARM_LOCAL_BASE + 0x90)
73#define INTC_LOCAL_MAILBOX0_SET2 (ARM_LOCAL_BASE + 0xa0)
74#define INTC_LOCAL_MAILBOX0_SET3 (ARM_LOCAL_BASE + 0xb0)
75
76#define INTC_LOCAL_MAILBOX0_CLR0 (ARM_LOCAL_BASE + 0xc0)
77#define INTC_LOCAL_MAILBOX0_CLR1 (ARM_LOCAL_BASE + 0xd0)
78#define INTC_LOCAL_MAILBOX0_CLR2 (ARM_LOCAL_BASE + 0xe0)
79#define INTC_LOCAL_MAILBOX0_CLR3 (ARM_LOCAL_BASE + 0xf0)
80
81struct int_handler_struct {
82 int_handler handler;
83 void *arg;
84};
85
86static struct int_handler_struct int_handler_table[MAX_INT];
87
88static spin_lock_t lock = SPIN_LOCK_INITIAL_VALUE;
89
90status_t mask_interrupt(unsigned int vector)
91{
92 LTRACEF("vector %u\n", vector);
93
94 spin_lock_saved_state_t state;
95 spin_lock_irqsave(&lock, state);
96
97 if (vector >= INTERRUPT_ARM_LOCAL_CNTPSIRQ && vector <= INTERRUPT_ARM_LOCAL_CNTVIRQ) {
98 // local timer interrupts, mask on all cpus
99 for (uint cpu = 0; cpu < 4; cpu++) {
100 uintptr_t reg = INTC_LOCAL_TIMER_INT_CONTROL0 + cpu * 4;
101
102 *REG32(reg) &= (1 << (vector - INTERRUPT_ARM_LOCAL_CNTPSIRQ));
103 }
104 } else if (/* vector >= ARM_IRQ1_BASE && */ vector < (ARM_IRQ0_BASE + 32)) {
105 uintptr_t reg;
106 if (vector >= ARM_IRQ0_BASE)
107 reg = INTC_DISABLE3;
108 else if (vector >= ARM_IRQ2_BASE)
109 reg = INTC_DISABLE2;
110 else
111 reg = INTC_DISABLE1;
112
113 *REG32(reg) = 1 << (vector % 32);
114 } else {
115 PANIC_UNIMPLEMENTED;
116 }
117
118 spin_unlock_irqrestore(&lock, state);
119
120 return NO_ERROR;
121}
122
123status_t unmask_interrupt(unsigned int vector)
124{
125 LTRACEF("vector %u\n", vector);
126
127 spin_lock_saved_state_t state;
128 spin_lock_irqsave(&lock, state);
129
130 if (vector >= INTERRUPT_ARM_LOCAL_CNTPSIRQ && vector <= INTERRUPT_ARM_LOCAL_CNTVIRQ) {
131 // local timer interrupts, unmask for all cpus
132 for (uint cpu = 0; cpu < 4; cpu++) {
133 uintptr_t reg = INTC_LOCAL_TIMER_INT_CONTROL0 + cpu * 4;
134
135 *REG32(reg) |= (1 << (vector - INTERRUPT_ARM_LOCAL_CNTPSIRQ));
136 }
137 } else if (/* vector >= ARM_IRQ1_BASE && */ vector < (ARM_IRQ0_BASE + 32)) {
138 uintptr_t reg;
139 if (vector >= ARM_IRQ0_BASE)
140 reg = INTC_ENABLE3;
141 else if (vector >= ARM_IRQ2_BASE)
142 reg = INTC_ENABLE2;
143 else
144 reg = INTC_ENABLE1;
145
146 *REG32(reg) = 1 << (vector % 32);
147 } else {
148 PANIC_UNIMPLEMENTED;
149 }
150
151 spin_unlock_irqrestore(&lock, state);
152
153 return NO_ERROR;
154}
155
156void register_int_handler(unsigned int vector, int_handler handler, void *arg)
157{
158 if (vector >= MAX_INT)
159 panic("register_int_handler: vector out of range %d\n", vector);
160
161 spin_lock_saved_state_t state;
162 spin_lock_irqsave(&lock, state);
163
164 int_handler_table[vector].handler = handler;
165 int_handler_table[vector].arg = arg;
166
167 spin_unlock_irqrestore(&lock, state);
168}
169
170enum handler_return platform_irq(struct arm_iframe *frame)
171{
172 uint vector;
173 uint cpu = arch_curr_cpu_num();
174
175 THREAD_STATS_INC(interrupts);
176
177 // see what kind of irq it is
178 uint32_t pend = *REG32(INTC_LOCAL_IRQ_PEND0 + cpu * 4);
179
180 pend &= ~(1 << (INTERRUPT_ARM_LOCAL_GPU_FAST % 32)); // mask out gpu interrupts
181
182 if (pend != 0) {
183 // it's a local interrupt
184 LTRACEF("local pend 0x%x\n", pend);
185 vector = ARM_IRQ_LOCAL_BASE + ctz(pend);
186 goto decoded;
187 }
188
189 // XXX disable for now, since all of the interesting irqs are mirrored into the other banks
190#if 0
191 // look in bank 0 (ARM interrupts)
192 pend = *REG32(INTC_PEND0);
193 LTRACEF("pend0 0x%x\n", pend);
194 pend &= ~((1<<8)|(1<<9)); // mask out bit 8 and 9
195 if (pend != 0) {
196 // it's a bank 0 interrupt
197 vector = ARM_IRQ0_BASE + ctz(pend);
198 goto decoded;
199 }
200#endif
201
202 // look for VC interrupt bank 1
203 pend = *REG32(INTC_PEND1);
204 LTRACEF("pend1 0x%x\n", pend);
205 if (pend != 0) {
206 // it's a bank 1 interrupt
207 vector = ARM_IRQ1_BASE + ctz(pend);
208 goto decoded;
209 }
210
211 // look for VC interrupt bank 2
212 pend = *REG32(INTC_PEND2);
213 LTRACEF("pend2 0x%x\n", pend);
214 if (pend != 0) {
215 // it's a bank 2 interrupt
216 vector = ARM_IRQ2_BASE + ctz(pend);
217 goto decoded;
218 }
219
220 vector = 0xffffffff;
221
222decoded:
223 LTRACEF("cpu %u vector %u\n", cpu, vector);
224
225 // dispatch the irq
226 enum handler_return ret = INT_NO_RESCHEDULE;
227
228#if WITH_SMP
229 if (vector == INTERRUPT_ARM_LOCAL_MAILBOX0) {
230 pend = *REG32(INTC_LOCAL_MAILBOX0_CLR0 + 0x10 * cpu);
231 LTRACEF("mailbox0 clr 0x%x\n", pend);
232
233 // ack it
234 *REG32(INTC_LOCAL_MAILBOX0_CLR0 + 0x10 * cpu) = pend;
235
236 if (pend & (1 << MP_IPI_GENERIC)) {
237 PANIC_UNIMPLEMENTED;
238 }
239 if (pend & (1 << MP_IPI_RESCHEDULE)) {
240 ret = mp_mbx_reschedule_irq();
241 }
242 } else
243#endif // WITH_SMP
244 if (vector == 0xffffffff) {
245 ret = INT_NO_RESCHEDULE;
246 } else if (int_handler_table[vector].handler) {
247 ret = int_handler_table[vector].handler(int_handler_table[vector].arg);
248 } else {
249 panic("irq %u fired on cpu %u but no handler set!\n", vector, cpu);
250 }
251
252 return ret;
253}
254
255enum handler_return platform_fiq(struct arm_iframe *frame)
256{
257 PANIC_UNIMPLEMENTED;
258}
259
260void bcm2835_send_ipi(uint irq, uint cpu_mask)
261{
262 LTRACEF("irq %u, cpu_mask 0x%x\n", irq, cpu_mask);
263
264 for (uint i = 0; i < 4; i++) {
265 if (cpu_mask & (1<<i)) {
266 LTRACEF("sending to cpu %u\n", i);
267 *REG32(INTC_LOCAL_MAILBOX0_SET0 + 0x10 * i) = (1 << irq);
268 }
269 }
270}
271
272void intc_init(void)
273{
274 // mask everything
275 *REG32(INTC_DISABLE1) = 0xffffffff;
276 *REG32(INTC_DISABLE2) = 0xffffffff;
277 *REG32(INTC_DISABLE3) = 0xffffffff;
278
279#if WITH_SMP
280 // unable mailbox irqs on all cores
281 for (uint i = 0; i < 4; i++) {
282 *REG32(INTC_LOCAL_MAILBOX_INT_CONTROL0 + 0x4 * i) = 0x1;
283 }
284#endif
285}
286