blob: 1cb49b1206c0cc7d86e3bf9c8e69a2de12215aa0 [file] [log] [blame]
Stefan Reinauer4d933dd2009-07-21 21:36:41 +00001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2009 coresystems GmbH
Mike Loptience740c42014-01-03 16:54:56 -07005 * Copyright (C) 2013 Sage Electronic Engineering, LLC.
Stefan Reinauer4d933dd2009-07-21 21:36:41 +00006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000015 */
16
Ronald G. Minniche5ac2952004-10-14 22:44:26 +000017#include <arch/io.h>
Stefan Reinauera829bfe2009-01-20 21:38:17 +000018#include <pc80/i8259.h>
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000019#include <console/console.h>
20
Mike Loptience740c42014-01-03 16:54:56 -070021/* Read the current PIC IRQ mask */
22u16 pic_read_irq_mask(void)
23{
24 u16 mask;
25 int i;
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000026
Mike Loptience740c42014-01-03 16:54:56 -070027 mask = inb(MASTER_PIC_OCW1) | (inb(SLAVE_PIC_OCW1) << 8);
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000028
Mike Loptience740c42014-01-03 16:54:56 -070029 printk(BIOS_DEBUG, "8259 PIC: OCW1 IRQ Mask: 0x%x\n", mask);
30 printk(BIOS_SPEW, "\tEnabled IRQs (0 = Unmasked, 1 = Masked off):\n"
31 "\t\tMaster\t\tSlave\n");
32 for(i = 0; i <= 7; i++) {
33 printk(BIOS_SPEW, "\t\tIRQ%X: %x\t\tIRQ%X: %x\n",
34 i, (mask >> i) & 1, i + 8, (mask >> (i + 8)) & 1);
35 }
36 return mask;
37}
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000038
Mike Loptience740c42014-01-03 16:54:56 -070039/*
40 * Write an IRQ mask to the PIC:
41 * IRQA is bit 0xA in the 16 bit bitmask (OCW1)
42 */
43void pic_write_irq_mask(u16 mask)
44{
45 outb(mask, MASTER_PIC_OCW1);
46 outb(mask >> 8, SLAVE_PIC_OCW1);
47}
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000048
Mike Loptience740c42014-01-03 16:54:56 -070049/*
50 * The PIC IRQs default to masked off
51 * Allow specific IRQs to be enabled (1)
52 * or disabled by (0) the user
53 */
54void pic_irq_enable(u8 int_num, u8 mask)
55{
56 pic_write_irq_mask(pic_read_irq_mask() & ~(mask << int_num));
57 pic_read_irq_mask();
58}
Ronald G. Minniche5ac2952004-10-14 22:44:26 +000059
60void setup_i8259(void)
61{
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000062 /* A write to ICW1 starts the Interrupt Controller Initialization
63 * Sequence. This implicitly causes the following to happen:
64 * - Interrupt Mask register is cleared
65 * - Priority 7 is assigned to IRQ7 input
66 * - Slave mode address is set to 7
67 * - Special mask mode is cleared
68 *
69 * We send the initialization sequence to both the master and
70 * slave i8259 controller.
71 */
72 outb(ICW_SELECT|IC4, MASTER_PIC_ICW1);
73 outb(ICW_SELECT|IC4, SLAVE_PIC_ICW1);
74
75 /* Now the interrupt controller expects us to write to ICW2. */
76 outb(INT_VECTOR_MASTER | IRQ0, MASTER_PIC_ICW2);
77 outb(INT_VECTOR_SLAVE | IRQ8, SLAVE_PIC_ICW2);
78
Stefan Reinauer14e22772010-04-27 06:56:47 +000079 /* Now the interrupt controller expects us to write to ICW3.
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000080 *
81 * The normal scenario is to set up cascading on IRQ2 on the master
82 * i8259 and assign the slave ID 2 to the slave i8259.
83 */
84 outb(CASCADED_PIC, MASTER_PIC_ICW3);
85 outb(SLAVE_ID, SLAVE_PIC_ICW3);
86
87 /* Now the interrupt controller expects us to write to ICW4.
88 *
89 * We switch both i8259 to microprocessor mode because they're
90 * operating as part of an x86 architecture based chipset
91 */
92 outb(MICROPROCESSOR_MODE, MASTER_PIC_ICW2);
Stefan Reinauer14e22772010-04-27 06:56:47 +000093 outb(MICROPROCESSOR_MODE, SLAVE_PIC_ICW2);
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000094
Stefan Reinauer14e22772010-04-27 06:56:47 +000095 /* Now clear the interrupts through OCW1.
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000096 * First we mask off all interrupts on the slave interrupt controller
97 * then we mask off all interrupts but interrupt 2 on the master
Martin Roth56889792013-07-09 21:39:46 -060098 * controller. This way the cascading stays alive.
Stefan Reinauer4d933dd2009-07-21 21:36:41 +000099 */
100 outb(ALL_IRQS, SLAVE_PIC_OCW1);
101 outb(ALL_IRQS & ~IRQ2, MASTER_PIC_OCW1);
Ronald G. Minniche5ac2952004-10-14 22:44:26 +0000102}
103
Stefan Reinauer4d933dd2009-07-21 21:36:41 +0000104/**
105 * @brief Configure IRQ triggering in the i8259 compatible Interrupt Controller.
106 *
107 * Switch a certain interrupt to be level / edge triggered.
108 *
109 * @param int_num legacy interrupt number (3-7, 9-15)
110 * @param is_level_triggered 1 for level triggered interrupt, 0 for edge
111 * triggered interrupt
112 */
113void i8259_configure_irq_trigger(int int_num, int is_level_triggered)
114{
115 u16 int_bits = inb(ELCR1) | (((u16)inb(ELCR2)) << 8);
116
Stefan Reinauer4d933dd2009-07-21 21:36:41 +0000117 if (is_level_triggered)
118 int_bits |= (1 << int_num);
119 else
120 int_bits &= ~(1 << int_num);
121
122 /* Write new values */
Stefan Reinauer4d933dd2009-07-21 21:36:41 +0000123 outb((u8)(int_bits & 0xff), ELCR1);
124 outb((u8)(int_bits >> 8), ELCR2);
125
126#ifdef PARANOID_IRQ_TRIGGERS
127 /* Try reading back the new values. This seems like an error but is not ... */
128 if (inb(ELCR1) != (int_bits & 0xff)) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000129 printk(BIOS_ERR, "%s: lower order bits are wrong: want 0x%x, got 0x%x\n",
Stefan Reinauer4d933dd2009-07-21 21:36:41 +0000130 __func__, (int_bits & 0xff), inb(ELCR1));
131 }
132
133 if (inb(ELCR2) != (int_bits >> 8)) {
Stefan Reinauerc02b4fc2010-03-22 11:42:32 +0000134 printk(BIOS_ERR, "%s: higher order bits are wrong: want 0x%x, got 0x%x\n",
Stefan Reinauer4d933dd2009-07-21 21:36:41 +0000135 __func__, (int_bits>>8), inb(ELCR2));
136 }
137#endif
138}