-Add interrupt controller and timer support for qemu virt machine -Switch tty read to irq driven as well
131 lines
3.2 KiB
C
131 lines
3.2 KiB
C
/*
|
|
* Copyright (c) 2021 Travis Geiselbrecht
|
|
*
|
|
* Use of this source code is governed by a MIT-style
|
|
* license that can be found in the LICENSE file or at
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
#include "platform_p.h"
|
|
|
|
#include <assert.h>
|
|
#include <lk/bits.h>
|
|
#include <lk/err.h>
|
|
#include <lk/debug.h>
|
|
#include <lk/reg.h>
|
|
#include <lk/trace.h>
|
|
#include <kernel/debug.h>
|
|
#include <kernel/thread.h>
|
|
#include <platform/interrupts.h>
|
|
#include <platform/virt.h>
|
|
|
|
#define LOCAL_TRACE 0
|
|
|
|
// implementation of PIC at
|
|
// https://github.com/qemu/qemu/blob/master/hw/intc/goldfish_pic.c
|
|
|
|
enum {
|
|
REG_STATUS = 0x00,
|
|
REG_IRQ_PENDING = 0x04,
|
|
REG_IRQ_DISABLE_ALL = 0x08,
|
|
REG_DISABLE = 0x0c,
|
|
REG_ENABLE = 0x10,
|
|
};
|
|
|
|
volatile unsigned int * const goldfish_pic_base = (void *)VIRT_GF_PIC_MMIO_BASE;
|
|
|
|
static struct int_handlers {
|
|
int_handler handler;
|
|
void *arg;
|
|
} handlers[NUM_IRQS];
|
|
|
|
static void write_reg(int pic, int reg, uint32_t val) {
|
|
goldfish_pic_base[0x1000 * pic / 4 + reg / 4] = val;
|
|
}
|
|
|
|
static uint32_t read_reg(int pic, int reg) {
|
|
return goldfish_pic_base[0x1000 * pic / 4 + reg / 4];
|
|
}
|
|
|
|
static void dump_pic(int i) {
|
|
dprintf(INFO, "PIC %d: status %u pending %#x\n", i, read_reg(i, REG_STATUS), read_reg(i, REG_IRQ_PENDING));
|
|
}
|
|
|
|
static void dump_all_pics(void) {
|
|
for (int i = 0; i < NUM_PICS; i++) {
|
|
dump_pic(i);
|
|
}
|
|
}
|
|
|
|
static int irq_to_pic_num(unsigned int vector) {
|
|
return vector / 32;
|
|
}
|
|
|
|
static int irq_to_pic_vec(unsigned int vector) {
|
|
return vector % 32;
|
|
}
|
|
|
|
void pic_early_init(void) {
|
|
}
|
|
|
|
void pic_init(void) {
|
|
dump_all_pics();
|
|
}
|
|
|
|
status_t mask_interrupt(unsigned int vector) {
|
|
LTRACEF("vector %u\n", vector);
|
|
write_reg(irq_to_pic_num(vector), REG_DISABLE, 1U << irq_to_pic_vec(vector));
|
|
return NO_ERROR;
|
|
}
|
|
|
|
status_t unmask_interrupt(unsigned int vector) {
|
|
LTRACEF("vector %u\n", vector);
|
|
write_reg(irq_to_pic_num(vector), REG_ENABLE, 1U << irq_to_pic_vec(vector));
|
|
return NO_ERROR;
|
|
}
|
|
|
|
void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
|
|
LTRACEF("vector %u handler %p arg %p\n", vector, handler, arg);
|
|
|
|
DEBUG_ASSERT(vector < NUM_IRQS);
|
|
|
|
handlers[vector].handler = handler;
|
|
handlers[vector].arg = arg;
|
|
}
|
|
|
|
enum handler_return m68k_platform_irq(uint8_t m68k_irq) {
|
|
LTRACEF("m68k irq vector %d\n", m68k_irq);
|
|
|
|
// translate m68k irqs to pic numbers
|
|
int pic_num;
|
|
if (likely(m68k_irq >= 1 && m68k_irq <= 6)) {
|
|
pic_num = m68k_irq - 1;
|
|
} else {
|
|
panic("unhandled irq %d from cpu\n", m68k_irq);
|
|
}
|
|
|
|
// see what is pending
|
|
uint32_t pending = read_reg(pic_num, REG_IRQ_PENDING);
|
|
if (pending == 0) {
|
|
// spurious
|
|
return INT_NO_RESCHEDULE;
|
|
}
|
|
|
|
// find the lowest numbered bit set
|
|
uint vector = ctz(pending) + pic_num * 32;
|
|
LTRACEF("pic %d pending %#x vector %u\n", pic_num, pending, vector);
|
|
|
|
THREAD_STATS_INC(interrupts);
|
|
KEVLOG_IRQ_ENTER(vector);
|
|
|
|
enum handler_return ret = INT_NO_RESCHEDULE;
|
|
if (handlers[vector].handler) {
|
|
ret = handlers[vector].handler(handlers[vector].arg);
|
|
}
|
|
|
|
// no need to ack the interrupt controller since all irqs are implicitly level
|
|
KEVLOG_IRQ_EXIT(vector);
|
|
|
|
return ret;
|
|
}
|
|
|