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, Universitaetstrasse 6, 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