1 /*
2  * Copyright (c) 2021 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 "platform_p.h"
9 
10 #include <assert.h>
11 #include <lk/bits.h>
12 #include <lk/err.h>
13 #include <lk/debug.h>
14 #include <lk/reg.h>
15 #include <lk/trace.h>
16 #include <kernel/debug.h>
17 #include <kernel/thread.h>
18 #include <platform/interrupts.h>
19 #include <platform/virt.h>
20 
21 #define LOCAL_TRACE 0
22 
23 // implementation of PIC at
24 // https://github.com/qemu/qemu/blob/master/hw/intc/goldfish_pic.c
25 
26 enum {
27     REG_STATUS          = 0x00,
28     REG_IRQ_PENDING     = 0x04,
29     REG_IRQ_DISABLE_ALL = 0x08,
30     REG_DISABLE         = 0x0c,
31     REG_ENABLE          = 0x10,
32 };
33 
34 volatile unsigned int * const goldfish_pic_base = (void *)VIRT_GF_PIC_MMIO_BASE;
35 
36 static struct int_handlers {
37     int_handler handler;
38     void *arg;
39 } handlers[NUM_IRQS];
40 
write_reg(int pic,int reg,uint32_t val)41 static void write_reg(int pic, int reg, uint32_t val) {
42     goldfish_pic_base[0x1000 * pic / 4 + reg / 4] = val;
43 }
44 
read_reg(int pic,int reg)45 static uint32_t read_reg(int pic, int reg) {
46     return goldfish_pic_base[0x1000 * pic / 4 + reg / 4];
47 }
48 
dump_pic(int i)49 static void dump_pic(int i) {
50     dprintf(INFO, "PIC %d: status %u pending %#x\n", i, read_reg(i, REG_STATUS), read_reg(i, REG_IRQ_PENDING));
51 }
52 
dump_all_pics(void)53 static void dump_all_pics(void) {
54     for (int i = 0; i < NUM_PICS; i++) {
55         dump_pic(i);
56     }
57 }
58 
irq_to_pic_num(unsigned int vector)59 static int irq_to_pic_num(unsigned int vector) {
60     return vector / 32;
61 }
62 
irq_to_pic_vec(unsigned int vector)63 static int irq_to_pic_vec(unsigned int vector) {
64     return vector % 32;
65 }
66 
pic_early_init(void)67 void pic_early_init(void) {
68 }
69 
pic_init(void)70 void pic_init(void) {
71     dump_all_pics();
72 }
73 
mask_interrupt(unsigned int vector)74 status_t mask_interrupt(unsigned int vector) {
75     LTRACEF("vector %u\n", vector);
76     write_reg(irq_to_pic_num(vector), REG_DISABLE, 1U << irq_to_pic_vec(vector));
77     return NO_ERROR;
78 }
79 
unmask_interrupt(unsigned int vector)80 status_t unmask_interrupt(unsigned int vector) {
81     LTRACEF("vector %u\n", vector);
82     write_reg(irq_to_pic_num(vector), REG_ENABLE, 1U << irq_to_pic_vec(vector));
83     return NO_ERROR;
84 }
85 
register_int_handler(unsigned int vector,int_handler handler,void * arg)86 void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
87     LTRACEF("vector %u handler %p arg %p\n", vector, handler, arg);
88 
89     DEBUG_ASSERT(vector < NUM_IRQS);
90 
91     handlers[vector].handler = handler;
92     handlers[vector].arg = arg;
93 }
94 
m68k_platform_irq(uint8_t m68k_irq)95 enum handler_return m68k_platform_irq(uint8_t m68k_irq) {
96     LTRACEF("m68k irq vector %d\n", m68k_irq);
97 
98     // translate m68k irqs to pic numbers
99     int pic_num;
100     if (likely(m68k_irq >= 1 && m68k_irq <= 6)) {
101         pic_num = m68k_irq - 1;
102     } else {
103         panic("unhandled irq %d from cpu\n", m68k_irq);
104     }
105 
106     // see what is pending
107     uint32_t pending = read_reg(pic_num, REG_IRQ_PENDING);
108     if (pending == 0) {
109         // spurious
110         return INT_NO_RESCHEDULE;
111     }
112 
113     // find the lowest numbered bit set
114     uint vector = ctz(pending) + pic_num * 32;
115     LTRACEF("pic %d pending %#x vector %u\n", pic_num, pending, vector);
116 
117     THREAD_STATS_INC(interrupts);
118     KEVLOG_IRQ_ENTER(vector);
119 
120     enum handler_return ret = INT_NO_RESCHEDULE;
121     if (handlers[vector].handler) {
122         ret = handlers[vector].handler(handlers[vector].arg);
123     }
124 
125     // no need to ack the interrupt controller since all irqs are implicitly level
126     KEVLOG_IRQ_EXIT(vector);
127 
128     return ret;
129 }
130 
131