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