summaryrefslogtreecommitdiff
path: root/arch/x86/machine/pic.c
blob: 440ca3a7d542aae3a8bdc3f08d4f6963fcd52adb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*
 * Copyright (c) 2012-2014 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 <http://www.gnu.org/licenses/>.
 */

#include <kern/assert.h>
#include <kern/init.h>
#include <kern/panic.h>
#include <kern/stdint.h>
#include <machine/io.h>
#include <machine/cpu.h>
#include <machine/pic.h>
#include <machine/trap.h>

/*
 * 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
#define PIC_NR_INTRS        8

void __init
pic_setup(void)
{
    /* 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_PIC_BASE);
    io_write_byte(PIC_SLAVE_IMR, TRAP_PIC_BASE + 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, 0xff);
    io_write_byte(PIC_SLAVE_IMR, 0xff);
}

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);
}

void
pic_spurious_intr(struct trap_frame *frame)
{
    unsigned long intr;
    uint8_t isr;

    intr = frame->vector - TRAP_PIC_BASE;
    assert((intr == PIC_SPURIOUS_INTR)
           || (intr == (PIC_NR_INTRS + PIC_SPURIOUS_INTR)));

    if (intr == PIC_SPURIOUS_INTR) {
        isr = pic_read_isr(PIC_MASTER_CMD);

        if (isr & (1 << PIC_SPURIOUS_INTR)) {
            panic("pic: real interrupt %lu", intr);
        }
    } else {
        isr = pic_read_isr(PIC_SLAVE_CMD);

        if (isr & (1 << PIC_SPURIOUS_INTR)) {
            panic("pic: real interrupt %lu", intr);
        }

        pic_eoi(PIC_SLAVE_INTR);
    }
}