1 /*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <lk/trace.h>
9 #include <assert.h>
10 #include <lk/err.h>
11 #include <lk/bits.h>
12 #include <kernel/spinlock.h>
13 #include <kernel/thread.h>
14 #include <kernel/mp.h>
15 #include <platform/interrupts.h>
16 #include <platform/bcm28xx.h>
17
18 #if defined (BCM2836)
19 #include <arch/arm.h>
20 typedef struct arm_iframe arm_platform_iframe_t;
21 #elif defined (BCM2837)
22 #include <arch/arm64.h>
23 typedef struct arm64_iframe_long arm_platform_iframe_t;
24 #else
25 #error Unknown BCM28XX Variant
26 #endif
27
28
29 #define LOCAL_TRACE 0
30
31 /* global interrupt controller */
32 #define INTC_PEND0 (ARMCTRL_INTC_BASE + 0x0)
33 #define INTC_PEND1 (ARMCTRL_INTC_BASE + 0x4)
34 #define INTC_PEND2 (ARMCTRL_INTC_BASE + 0x8)
35 #define INTC_FAST (ARMCTRL_INTC_BASE + 0xc)
36 #define INTC_ENABLE1 (ARMCTRL_INTC_BASE + 0x10)
37 #define INTC_ENABLE2 (ARMCTRL_INTC_BASE + 0x14)
38 #define INTC_ENABLE3 (ARMCTRL_INTC_BASE + 0x18)
39 #define INTC_DISABLE1 (ARMCTRL_INTC_BASE + 0x1c)
40 #define INTC_DISABLE2 (ARMCTRL_INTC_BASE + 0x20)
41 #define INTC_DISABLE3 (ARMCTRL_INTC_BASE + 0x24)
42
43 /* per-cpu local interrupt controller bits.
44 * each is repeated 4 times, one per cpu.
45 */
46 #define INTC_LOCAL_TIMER_INT_CONTROL0 (ARM_LOCAL_BASE + 0x40)
47 #define INTC_LOCAL_TIMER_INT_CONTROL1 (ARM_LOCAL_BASE + 0x44)
48 #define INTC_LOCAL_TIMER_INT_CONTROL2 (ARM_LOCAL_BASE + 0x48)
49 #define INTC_LOCAL_TIMER_INT_CONTROL3 (ARM_LOCAL_BASE + 0x4c)
50
51 #define INTC_LOCAL_MAILBOX_INT_CONTROL0 (ARM_LOCAL_BASE + 0x50)
52 #define INTC_LOCAL_MAILBOX_INT_CONTROL1 (ARM_LOCAL_BASE + 0x54)
53 #define INTC_LOCAL_MAILBOX_INT_CONTROL2 (ARM_LOCAL_BASE + 0x58)
54 #define INTC_LOCAL_MAILBOX_INT_CONTROL3 (ARM_LOCAL_BASE + 0x5c)
55
56 #define INTC_LOCAL_IRQ_PEND0 (ARM_LOCAL_BASE + 0x60)
57 #define INTC_LOCAL_IRQ_PEND1 (ARM_LOCAL_BASE + 0x64)
58 #define INTC_LOCAL_IRQ_PEND2 (ARM_LOCAL_BASE + 0x68)
59 #define INTC_LOCAL_IRQ_PEND3 (ARM_LOCAL_BASE + 0x6c)
60
61 #define INTC_LOCAL_FIQ_PEND0 (ARM_LOCAL_BASE + 0x70)
62 #define INTC_LOCAL_FIQ_PEND1 (ARM_LOCAL_BASE + 0x74)
63 #define INTC_LOCAL_FIQ_PEND2 (ARM_LOCAL_BASE + 0x78)
64 #define INTC_LOCAL_FIQ_PEND3 (ARM_LOCAL_BASE + 0x7c)
65
66 #define INTC_LOCAL_MAILBOX0_SET0 (ARM_LOCAL_BASE + 0x80)
67 #define INTC_LOCAL_MAILBOX0_SET1 (ARM_LOCAL_BASE + 0x90)
68 #define INTC_LOCAL_MAILBOX0_SET2 (ARM_LOCAL_BASE + 0xa0)
69 #define INTC_LOCAL_MAILBOX0_SET3 (ARM_LOCAL_BASE + 0xb0)
70
71 #define INTC_LOCAL_MAILBOX0_CLR0 (ARM_LOCAL_BASE + 0xc0)
72 #define INTC_LOCAL_MAILBOX0_CLR1 (ARM_LOCAL_BASE + 0xd0)
73 #define INTC_LOCAL_MAILBOX0_CLR2 (ARM_LOCAL_BASE + 0xe0)
74 #define INTC_LOCAL_MAILBOX0_CLR3 (ARM_LOCAL_BASE + 0xf0)
75
76 struct int_handler_struct {
77 int_handler handler;
78 void *arg;
79 };
80
81 static struct int_handler_struct int_handler_table[MAX_INT];
82
83 static spin_lock_t lock = SPIN_LOCK_INITIAL_VALUE;
84
mask_interrupt(unsigned int vector)85 status_t mask_interrupt(unsigned int vector) {
86 LTRACEF("vector %u\n", vector);
87
88 spin_lock_saved_state_t state;
89 spin_lock_irqsave(&lock, state);
90
91 if (vector >= INTERRUPT_ARM_LOCAL_CNTPSIRQ && vector <= INTERRUPT_ARM_LOCAL_CNTVIRQ) {
92 // local timer interrupts, mask on all cpus
93 for (uint cpu = 0; cpu < 4; cpu++) {
94 uintptr_t reg = INTC_LOCAL_TIMER_INT_CONTROL0 + cpu * 4;
95
96 *REG32(reg) &= (1 << (vector - INTERRUPT_ARM_LOCAL_CNTPSIRQ));
97 }
98 } else if (/* vector >= ARM_IRQ1_BASE && */ vector < (ARM_IRQ0_BASE + 32)) {
99 uintptr_t reg;
100 if (vector >= ARM_IRQ0_BASE)
101 reg = INTC_DISABLE3;
102 else if (vector >= ARM_IRQ2_BASE)
103 reg = INTC_DISABLE2;
104 else
105 reg = INTC_DISABLE1;
106
107 *REG32(reg) = 1 << (vector % 32);
108 } else {
109 PANIC_UNIMPLEMENTED;
110 }
111
112 spin_unlock_irqrestore(&lock, state);
113
114 return NO_ERROR;
115 }
116
unmask_interrupt(unsigned int vector)117 status_t unmask_interrupt(unsigned int vector) {
118 LTRACEF("vector %u\n", vector);
119
120 spin_lock_saved_state_t state;
121 spin_lock_irqsave(&lock, state);
122
123 if (vector >= INTERRUPT_ARM_LOCAL_CNTPSIRQ && vector <= INTERRUPT_ARM_LOCAL_CNTVIRQ) {
124 // local timer interrupts, unmask for all cpus
125 for (uint cpu = 0; cpu < 4; cpu++) {
126 uintptr_t reg = INTC_LOCAL_TIMER_INT_CONTROL0 + cpu * 4;
127
128 *REG32(reg) |= (1 << (vector - INTERRUPT_ARM_LOCAL_CNTPSIRQ));
129 }
130 } else if (/* vector >= ARM_IRQ1_BASE && */ vector < (ARM_IRQ0_BASE + 32)) {
131 uintptr_t reg;
132 if (vector >= ARM_IRQ0_BASE)
133 reg = INTC_ENABLE3;
134 else if (vector >= ARM_IRQ2_BASE)
135 reg = INTC_ENABLE2;
136 else
137 reg = INTC_ENABLE1;
138
139 *REG32(reg) = 1 << (vector % 32);
140 } else {
141 PANIC_UNIMPLEMENTED;
142 }
143
144 spin_unlock_irqrestore(&lock, state);
145
146 return NO_ERROR;
147 }
148
register_int_handler(unsigned int vector,int_handler handler,void * arg)149 void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
150 if (vector >= MAX_INT)
151 panic("register_int_handler: vector out of range %d\n", vector);
152
153 spin_lock_saved_state_t state;
154 spin_lock_irqsave(&lock, state);
155
156 int_handler_table[vector].handler = handler;
157 int_handler_table[vector].arg = arg;
158
159 spin_unlock_irqrestore(&lock, state);
160 }
161
platform_irq(arm_platform_iframe_t * frame)162 enum handler_return platform_irq(arm_platform_iframe_t *frame) {
163 uint vector;
164 uint cpu = arch_curr_cpu_num();
165
166 THREAD_STATS_INC(interrupts);
167
168 // see what kind of irq it is
169 uint32_t pend = *REG32(INTC_LOCAL_IRQ_PEND0 + cpu * 4);
170
171 pend &= ~(1 << (INTERRUPT_ARM_LOCAL_GPU_FAST % 32)); // mask out gpu interrupts
172
173 if (pend != 0) {
174 // it's a local interrupt
175 LTRACEF("local pend 0x%x\n", pend);
176 vector = ARM_IRQ_LOCAL_BASE + ctz(pend);
177 goto decoded;
178 }
179
180 // XXX disable for now, since all of the interesting irqs are mirrored into the other banks
181 #if 0
182 // look in bank 0 (ARM interrupts)
183 pend = *REG32(INTC_PEND0);
184 LTRACEF("pend0 0x%x\n", pend);
185 pend &= ~((1<<8)|(1<<9)); // mask out bit 8 and 9
186 if (pend != 0) {
187 // it's a bank 0 interrupt
188 vector = ARM_IRQ0_BASE + ctz(pend);
189 goto decoded;
190 }
191 #endif
192
193 // look for VC interrupt bank 1
194 pend = *REG32(INTC_PEND1);
195 LTRACEF("pend1 0x%x\n", pend);
196 if (pend != 0) {
197 // it's a bank 1 interrupt
198 vector = ARM_IRQ1_BASE + ctz(pend);
199 goto decoded;
200 }
201
202 // look for VC interrupt bank 2
203 pend = *REG32(INTC_PEND2);
204 LTRACEF("pend2 0x%x\n", pend);
205 if (pend != 0) {
206 // it's a bank 2 interrupt
207 vector = ARM_IRQ2_BASE + ctz(pend);
208 goto decoded;
209 }
210
211 vector = 0xffffffff;
212
213 decoded:
214 LTRACEF("cpu %u vector %u\n", cpu, vector);
215
216 // dispatch the irq
217 enum handler_return ret = INT_NO_RESCHEDULE;
218
219 #if WITH_SMP
220 if (vector == INTERRUPT_ARM_LOCAL_MAILBOX0) {
221 pend = *REG32(INTC_LOCAL_MAILBOX0_CLR0 + 0x10 * cpu);
222 LTRACEF("mailbox0 clr 0x%x\n", pend);
223
224 // ack it
225 *REG32(INTC_LOCAL_MAILBOX0_CLR0 + 0x10 * cpu) = pend;
226
227 if (pend & (1 << MP_IPI_GENERIC)) {
228 PANIC_UNIMPLEMENTED;
229 }
230 if (pend & (1 << MP_IPI_RESCHEDULE)) {
231 ret = mp_mbx_reschedule_irq();
232 }
233 } else
234 #endif // WITH_SMP
235 if (vector == 0xffffffff) {
236 ret = INT_NO_RESCHEDULE;
237 } else if (int_handler_table[vector].handler) {
238 ret = int_handler_table[vector].handler(int_handler_table[vector].arg);
239 } else {
240 panic("irq %u fired on cpu %u but no handler set!\n", vector, cpu);
241 }
242
243 return ret;
244 }
245
platform_fiq(arm_platform_iframe_t * frame)246 enum handler_return platform_fiq(arm_platform_iframe_t *frame) {
247 PANIC_UNIMPLEMENTED;
248 }
249
bcm28xx_send_ipi(uint irq,uint cpu_mask)250 void bcm28xx_send_ipi(uint irq, uint cpu_mask) {
251 LTRACEF("irq %u, cpu_mask 0x%x\n", irq, cpu_mask);
252
253 for (uint i = 0; i < 4; i++) {
254 if (cpu_mask & (1<<i)) {
255 LTRACEF("sending to cpu %u\n", i);
256 *REG32(INTC_LOCAL_MAILBOX0_SET0 + 0x10 * i) = (1 << irq);
257 }
258 }
259 }
260
intc_init(void)261 void intc_init(void) {
262 // mask everything
263 *REG32(INTC_DISABLE1) = 0xffffffff;
264 *REG32(INTC_DISABLE2) = 0xffffffff;
265 *REG32(INTC_DISABLE3) = 0xffffffff;
266
267 #if WITH_SMP
268 // unable mailbox irqs on all cores
269 for (uint i = 0; i < 4; i++) {
270 *REG32(INTC_LOCAL_MAILBOX_INT_CONTROL0 + 0x4 * i) = 0x1;
271 }
272 #endif
273 }
274
275