/* * Copyright (c) 2012-2017 Richard Braun. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * I/O ports. */ #define PIC_MASTER_CMD 0x20 #define PIC_MASTER_IMR 0x21 #define PIC_SLAVE_CMD 0xa0 #define PIC_SLAVE_IMR 0xa1 /* * Register bits. */ #define PIC_ICW1_IC4 0x01 #define PIC_ICW1_INIT 0x10 #define PIC_ICW4_8086 0x01 #define PIC_OCW3_ISR 0x0b #define PIC_EOI 0x20 /* * Special interrupts. */ #define PIC_SLAVE_INTR 2 #define PIC_SPURIOUS_INTR 7 static unsigned int pic_nr_slave_intrs; static uint8_t pic_master_mask; static uint8_t pic_slave_mask; static uint8_t pic_master_spurious_intr; static uint8_t pic_slave_spurious_intr; static bool pic_is_slave_intr(unsigned int intr) { assert(intr <= PIC_MAX_INTR); return (intr >= PIC_NR_INTRS); } static void pic_inc_slave_intrs(void) { if (pic_nr_slave_intrs == 0) { pic_master_mask |= 1 << PIC_SLAVE_INTR; io_write_byte(PIC_MASTER_IMR, pic_master_mask); } pic_nr_slave_intrs++; assert(pic_nr_slave_intrs != 0); } static void pic_dec_slave_intrs(void) { assert(pic_nr_slave_intrs != 0); pic_nr_slave_intrs--; if (pic_nr_slave_intrs == 0) { pic_master_mask &= ~(1 << PIC_SLAVE_INTR); io_write_byte(PIC_MASTER_IMR, pic_master_mask); } } static void pic_eoi(unsigned long intr) { if (intr >= PIC_NR_INTRS) { io_write_byte(PIC_SLAVE_CMD, PIC_EOI); } io_write_byte(PIC_MASTER_CMD, PIC_EOI); } static uint8_t pic_read_isr(uint16_t port) { io_write_byte(port, PIC_OCW3_ISR); return io_read_byte(port); } static void pic_ops_enable(void *priv, unsigned int intr, unsigned int cpu) { (void)priv; (void)cpu; if (pic_is_slave_intr(intr)) { pic_slave_mask &= ~(1 << (intr - PIC_NR_INTRS)); io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); pic_inc_slave_intrs(); } else { pic_master_mask &= ~(1 << intr); io_write_byte(PIC_MASTER_IMR, pic_master_mask); } } static void pic_ops_disable(void *priv, unsigned int intr) { (void)priv; if (pic_is_slave_intr(intr)) { pic_dec_slave_intrs(); pic_slave_mask |= 1 << (intr - PIC_NR_INTRS); io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); } else { pic_master_mask |= 1 << intr; io_write_byte(PIC_MASTER_IMR, pic_master_mask); } } static void pic_ops_eoi(void *priv, unsigned int intr) { (void)priv; pic_eoi(intr); } static const struct intr_ops pic_ops = { .enable = pic_ops_enable, .disable = pic_ops_disable, .eoi = pic_ops_eoi, }; static void pic_intr(struct trap_frame *frame) { intr_handle(frame->vector - TRAP_INTR_FIRST); } static void __init pic_register(void) { unsigned int intr; intr_register_ctl(&pic_ops, NULL, 0, PIC_MAX_INTR); for (intr = 0; intr <= PIC_MAX_INTR; intr++) { trap_register(TRAP_INTR_FIRST + intr, pic_intr); } } static int pic_spurious_intr(void *arg) { uint8_t intr, isr; intr = *(const uint8_t *)arg; if (arg == &pic_master_spurious_intr) { isr = pic_read_isr(PIC_MASTER_CMD); if (isr & (1 << PIC_SPURIOUS_INTR)) { panic("pic: real interrupt %hhu", intr); } } else { isr = pic_read_isr(PIC_SLAVE_CMD); if (isr & (1 << PIC_SPURIOUS_INTR)) { panic("pic: real interrupt %hhu", intr); } pic_eoi(PIC_SLAVE_INTR); } return 0; } static void __init pic_setup_common(bool register_ctl) { int error; pic_nr_slave_intrs = 0; pic_master_mask = 0xff; pic_slave_mask = 0xff; /* ICW 1 - State that ICW 4 will be sent */ io_write_byte(PIC_MASTER_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); io_write_byte(PIC_SLAVE_CMD, PIC_ICW1_INIT | PIC_ICW1_IC4); /* ICW 2 */ io_write_byte(PIC_MASTER_IMR, TRAP_INTR_FIRST); io_write_byte(PIC_SLAVE_IMR, TRAP_INTR_FIRST + PIC_NR_INTRS); /* ICW 3 - Set up cascading */ io_write_byte(PIC_MASTER_IMR, 1 << PIC_SLAVE_INTR); io_write_byte(PIC_SLAVE_IMR, PIC_SLAVE_INTR); /* ICW 4 - Set 8086 mode */ io_write_byte(PIC_MASTER_IMR, PIC_ICW4_8086); io_write_byte(PIC_SLAVE_IMR, PIC_ICW4_8086); /* OCW 1 - Mask all interrupts */ io_write_byte(PIC_MASTER_IMR, pic_master_mask); io_write_byte(PIC_SLAVE_IMR, pic_slave_mask); if (register_ctl) { pic_register(); } pic_master_spurious_intr = PIC_SPURIOUS_INTR; error = intr_register(pic_master_spurious_intr, pic_spurious_intr, &pic_master_spurious_intr); error_check(error, __func__); pic_slave_spurious_intr = PIC_NR_INTRS + PIC_SPURIOUS_INTR; error = intr_register(pic_slave_spurious_intr, pic_spurious_intr, &pic_slave_spurious_intr); error_check(error, __func__); } void __init pic_setup(void) { pic_setup_common(true); } void __init pic_setup_disabled(void) { pic_setup_common(false); }