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