1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * This software may be distributed and modified according to the terms of
5 * the GNU General Public License version 2. Note that NO WARRANTY is provided.
6 * See "LICENSE_GPLv2.txt" for details.
7 *
8 * @TAG(GD_GPL)
9 */
10
11#include <linker.h>
12#include <machine/io.h>
13#include <plat/machine/hardware.h>
14#include <plat/machine/pic.h>
15
16/* PIC (i8259) base registers */
17#define PIC1_BASE 0x20
18#define PIC2_BASE 0xa0
19
20/* Program PIC (i8259) to remap IRQs 0-15 to interrupt vectors starting at 'interrupt' */
21BOOT_CODE void
22pic_remap_irqs(interrupt_t interrupt)
23{
24    out8(PIC1_BASE, 0x11);
25    out8(PIC2_BASE, 0x11);
26    out8(PIC1_BASE + 1, interrupt);
27    out8(PIC2_BASE + 1, interrupt + 8);
28    out8(PIC1_BASE + 1, 0x04);
29    out8(PIC2_BASE + 1, 0x02);
30    out8(PIC1_BASE + 1, 0x01);
31    out8(PIC2_BASE + 1, 0x01);
32    out8(PIC1_BASE + 1, 0x0);
33    out8(PIC2_BASE + 1, 0x0);
34}
35
36BOOT_CODE void pic_disable(void)
37{
38    /* We assume that pic_remap_irqs has already been called and
39     * just mask all the irqs */
40    out8(PIC1_BASE + 1, 0xff);
41    out8(PIC2_BASE + 1, 0xff);
42}
43
44void pic_mask_irq(bool_t mask, irq_t irq)
45{
46    uint8_t  bit_mask;
47    uint16_t pic_port;
48
49    assert(irq >= irq_isa_min);
50    assert(irq <= irq_isa_max);
51
52    if (irq < 8) {
53        bit_mask = BIT(irq);
54        pic_port = PIC1_BASE + 1;
55    } else {
56        bit_mask = BIT(irq - 8);
57        pic_port = PIC2_BASE + 1;
58    }
59
60    if (mask) {
61        /* Disables the interrupt */
62        out8(pic_port, (in8(pic_port) | bit_mask));
63    } else {
64        /* Enables the interrupt */
65        out8(pic_port, (in8(pic_port) & ~bit_mask));
66    }
67}
68
69bool_t pic_is_irq_pending(void)
70{
71    /* Interrupt Request Register (IRR) - holds pending IRQs */
72    uint8_t irr;
73
74    /* Send to PIC1's OCW3, in order to read IRR from next inb instruction */
75    out8(PIC1_BASE, 0x0a);
76
77    /* Read IRR */
78    irr = in8(PIC1_BASE);
79
80    /* Since slave PIC is connected to IRQ2 of master PIC,
81     * there is no need to check IRR of slave PIC.
82     */
83    return irr != 0;
84}
85
86static uint16_t pic_get_isr(void)
87{
88    out8(PIC1_BASE, 0x0b);
89    out8(PIC2_BASE, 0x0b);
90    return (((uint16_t)in8(PIC2_BASE)) << 8) | in8(PIC1_BASE);
91}
92
93void pic_ack_active_irq(void)
94{
95    irq_t irq = getActiveIRQ();
96    if (irq >= irq_isa_min + 8) {
97        /* ack slave PIC, unless we got a spurious irq 15
98         * It is spurious if the bit is not set in the ISR
99         * Even if it was spurious we will still need to
100         * acknowledge the master PIC */
101        if (irq != irq_isa_min + 15 || (pic_get_isr() & BIT(15))) {
102            out8(PIC2_BASE, 0x20);
103        }
104    }
105    /* ack master PIC, unless we got a spurious IRQ 7
106     * It is spurious if the bit is not set in the ISR */
107    if (irq != irq_isa_min + 7 || (pic_get_isr() & BIT(7))) {
108        out8(PIC1_BASE, 0x20);
109    }
110}
111