1/**
2 * \file
3 * \brief Classic 8259A PIC driver.
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 2010, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <kernel.h>
16#include <arch/x86/pic.h>
17#include <dev/lpc_pic_dev.h>
18
19/// The dual PIC
20static lpc_pic_t pic;
21
22/**
23 * \brief Send end of interrupt.
24 */
25void pic_eoi(int irq)
26{
27    // Send specific end of interrupt message
28    lpc_pic_ocw2_t eoi = lpc_pic_ocw2_default;
29    eoi = lpc_pic_ocw2_rsleoi_insert(eoi, lpc_pic_seoi);
30
31    if(irq < 8) {
32        eoi = lpc_pic_ocw2_level_insert(eoi, irq);
33        lpc_pic_master_ocw2_wr(&pic, eoi);
34    } else {
35        eoi = lpc_pic_ocw2_level_insert(eoi, irq - 8);
36        lpc_pic_slave_ocw2_wr(&pic, eoi);
37    }
38}
39
40/**
41 * \brief returns true iff the PIC has an interrupt pending
42 */
43bool pic_have_interrupt(int irq)
44{
45    if(irq < 8) {
46        // send read ISR command
47        lpc_pic_master_ocw3_rrc_wrf(&pic, lpc_pic_read_is);
48        // read ISR and check bit
49        return (lpc_pic_master_ocw3rd_rd(&pic) & (1 << irq)) != 0;
50    } else {
51        lpc_pic_slave_ocw3_rrc_wrf(&pic, lpc_pic_read_is);
52        return (lpc_pic_slave_ocw3rd_rd(&pic) & (1 << (irq -8))) != 0;
53    }
54}
55
56static int mask_to_interrupt(uint8_t mask)
57{
58    for (int i = 0; i < 8; i++) {
59        if (mask & (1 << i)) {
60            return i;
61        }
62    }
63    return -1;
64}
65
66/**
67 * \brief Queries the PIC for pending interrupts
68 *
69 * \returns IRQ number of pending interrupt, or -1 if nothing is pending
70 */
71int pic_pending_interrupt(void)
72{
73    uint8_t isr;
74
75    // try master first
76    lpc_pic_master_ocw3_rrc_wrf(&pic, lpc_pic_read_is);
77    isr = lpc_pic_master_ocw3rd_rd(&pic);
78    if (isr != 0) {
79        return mask_to_interrupt(isr);
80    }
81
82    // try slave
83    lpc_pic_slave_ocw3_rrc_wrf(&pic, lpc_pic_read_is);
84    isr = lpc_pic_slave_ocw3rd_rd(&pic);
85    if (isr != 0) {
86        return mask_to_interrupt(isr) + 8;
87    }
88
89    return -1;
90}
91
92/**
93 * \brief Initialize 8259A.
94 *
95 * Initializes both master and slave 8259A in the standard cascaded
96 * way (slave attached to IR line 2 of master). Sets off interrupts by
97 * 32, leaving the lower 32 IRQs reserved for processor exceptions, as
98 * required by protected mode. Sets all interrupts to edge
99 * triggered. Finally, masks out all interrupts. If an interrupt is
100 * expected by the OS, it has to be unmasked individually.
101 */
102void pic_init(void)
103{
104    // setup mackerel state
105    lpc_pic_initialize(&pic, 0);
106
107    // Setup 8259A PIC for proper protected mode interrupt delivery
108    /* ICW1 */
109    lpc_pic_master_icw1_ltim_wrf(&pic, 0);
110    lpc_pic_slave_icw1_ltim_wrf( &pic, 0);
111
112    /* ICW2 */
113    lpc_pic_master_icw2_rawwr(&pic, 0x20); // IDT offset 0x20
114    lpc_pic_slave_icw2_rawwr(&pic, 0x28);  // IDT offset 0x28
115
116    /* ICW3 */
117    lpc_pic_master_icw3_cascade_wrf(&pic, 1);
118    lpc_pic_slave_icw3_slave_id_wrf(&pic, 2);
119
120    /* ICW4 */
121    lpc_pic_icw4_t icw4 = lpc_pic_icw4_default;
122    icw4 = lpc_pic_icw4_aeoi_insert(icw4, 0);
123    icw4 = lpc_pic_icw4_sfnm_insert(icw4, 0);
124    lpc_pic_master_icw4_wr(&pic, icw4);
125    lpc_pic_slave_icw4_wr(&pic, icw4);
126
127    if (CPU_IS_M5_SIMULATOR) {
128        printf("Warning: not setting elcr1 elcr2 on M5\n");
129    } else {
130        // Set all interrupts to be edge triggered (i.e. 0)
131        lpc_pic_master_trigger_rawwr(&pic, 0);
132        lpc_pic_slave_trigger_rawwr( &pic, 0);
133    }
134
135    // Mask all interrupts (except cascade IRQ 2)
136    lpc_pic_slave_ocw1_wr(&pic, 0xff);
137    lpc_pic_master_ocw1_wr(&pic, ~(1 << 2));
138}
139
140/**
141 * \brief Enable/Disable interrupt 'irq'.
142 *
143 * Be careful to serialize calls to this function on a
144 * multiprocessor. In general, the classic 8259A should not be used on
145 * a multiprocessor.
146 */
147void pic_toggle_irq(int irq, bool enable)
148{
149    assert(irq >= 0 && irq <= 15);
150
151    if(irq < 8) {
152        // Master controller
153        uint8_t mask = 1 << irq;
154        uint8_t val = lpc_pic_master_ocw1_rd(&pic);
155
156        if(enable) {
157            val &= ~mask;
158        } else {
159            val |= mask;
160        }
161
162        lpc_pic_master_ocw1_wr(&pic, val);
163    } else {
164        // Slave controller
165        uint8_t mask = 1 << (irq - 8);
166        uint8_t val = lpc_pic_slave_ocw1_rd(&pic);
167
168        if(enable) {
169            val &= ~mask;
170        } else {
171            val |= mask;
172        }
173
174        lpc_pic_slave_ocw1_wr(&pic, val);
175    }
176}
177