1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2009 4 * Graeme Russ, <graeme.russ@gmail.com> 5 * 6 * (C) Copyright 2002 7 * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> 8 */ 9 10 /* 11 * This file provides the interrupt handling functionality for systems 12 * based on the standard PC/AT architecture using two cascaded i8259 13 * Programmable Interrupt Controllers. 14 */ 15 16 #include <common.h> 17 #include <asm/io.h> 18 #include <asm/i8259.h> 19 #include <asm/ibmpc.h> 20 #include <asm/interrupt.h> 21 22 int i8259_init(void) 23 { 24 u8 i; 25 26 /* Mask all interrupts */ 27 outb(0xff, MASTER_PIC + IMR); 28 outb(0xff, SLAVE_PIC + IMR); 29 30 /* 31 * Master PIC 32 * Place master PIC interrupts at INT20 33 */ 34 outb(ICW1_SEL | ICW1_EICW4, MASTER_PIC + ICW1); 35 outb(0x20, MASTER_PIC + ICW2); 36 outb(IR2, MASTER_PIC + ICW3); 37 outb(ICW4_PM, MASTER_PIC + ICW4); 38 39 for (i = 0; i < 8; i++) 40 outb(OCW2_SEOI | i, MASTER_PIC + OCW2); 41 42 /* 43 * Slave PIC 44 * Place slave PIC interrupts at INT28 45 */ 46 outb(ICW1_SEL | ICW1_EICW4, SLAVE_PIC + ICW1); 47 outb(0x28, SLAVE_PIC + ICW2); 48 outb(0x02, SLAVE_PIC + ICW3); 49 outb(ICW4_PM, SLAVE_PIC + ICW4); 50 51 for (i = 0; i < 8; i++) 52 outb(OCW2_SEOI | i, SLAVE_PIC + OCW2); 53 54 /* 55 * Enable cascaded interrupts by unmasking the cascade IRQ pin of 56 * the master PIC 57 */ 58 unmask_irq(2); 59 60 /* Interrupt 9 should be level triggered (SCI). The OS might do this */ 61 configure_irq_trigger(9, true); 62 63 return 0; 64 } 65 66 void mask_irq(int irq) 67 { 68 int imr_port; 69 70 if (irq >= SYS_NUM_IRQS) 71 return; 72 73 if (irq > 7) 74 imr_port = SLAVE_PIC + IMR; 75 else 76 imr_port = MASTER_PIC + IMR; 77 78 outb(inb(imr_port) | (1 << (irq & 7)), imr_port); 79 } 80 81 void unmask_irq(int irq) 82 { 83 int imr_port; 84 85 if (irq >= SYS_NUM_IRQS) 86 return; 87 88 if (irq > 7) 89 imr_port = SLAVE_PIC + IMR; 90 else 91 imr_port = MASTER_PIC + IMR; 92 93 outb(inb(imr_port) & ~(1 << (irq & 7)), imr_port); 94 } 95 96 void specific_eoi(int irq) 97 { 98 if (irq >= SYS_NUM_IRQS) 99 return; 100 101 if (irq > 7) { 102 /* 103 * IRQ is on the slave - Issue a corresponding EOI to the 104 * slave PIC and an EOI for IRQ2 (the cascade interrupt) 105 * on the master PIC 106 */ 107 outb(OCW2_SEOI | (irq & 7), SLAVE_PIC + OCW2); 108 irq = SEOI_IR2; 109 } 110 111 outb(OCW2_SEOI | irq, MASTER_PIC + OCW2); 112 } 113 114 void configure_irq_trigger(int int_num, bool is_level_triggered) 115 { 116 u16 int_bits = inb(ELCR1) | (((u16)inb(ELCR2)) << 8); 117 118 debug("%s: current interrupts are 0x%x\n", __func__, int_bits); 119 if (is_level_triggered) 120 int_bits |= (1 << int_num); 121 else 122 int_bits &= ~(1 << int_num); 123 124 /* Write new values */ 125 debug("%s: try to set interrupts 0x%x\n", __func__, int_bits); 126 outb((u8)(int_bits & 0xff), ELCR1); 127 outb((u8)(int_bits >> 8), ELCR2); 128 } 129