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