1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <pc80/i8259.h>
5 #include <console/console.h>
7 /* Read the current PIC IRQ mask */
8 u16
pic_read_irq_mask(void)
13 mask
= inb(MASTER_PIC_OCW1
) | (inb(SLAVE_PIC_OCW1
) << 8);
15 printk(BIOS_DEBUG
, "8259 PIC: OCW1 IRQ Mask: 0x%x\n", mask
);
16 printk(BIOS_SPEW
, "\tEnabled IRQs (0 = Unmasked, 1 = Masked off):\n"
17 "\t\tMaster\t\tSlave\n");
18 for (i
= 0; i
<= 7; i
++) {
19 printk(BIOS_SPEW
, "\t\tIRQ%X: %x\t\tIRQ%X: %x\n",
20 i
, (mask
>> i
) & 1, i
+ 8, (mask
>> (i
+ 8)) & 1);
26 * Write an IRQ mask to the PIC:
27 * IRQA is bit 0xA in the 16 bit bitmask (OCW1)
29 void pic_write_irq_mask(u16 mask
)
31 outb(mask
, MASTER_PIC_OCW1
);
32 outb(mask
>> 8, SLAVE_PIC_OCW1
);
36 * The PIC IRQs default to masked off
37 * Allow specific IRQs to be enabled (1)
38 * or disabled by (0) the user
40 void pic_irq_enable(u8 int_num
, u8 mask
)
42 pic_write_irq_mask(pic_read_irq_mask() & ~(mask
<< int_num
));
46 void setup_i8259(void)
48 /* A write to ICW1 starts the Interrupt Controller Initialization
49 * Sequence. This implicitly causes the following to happen:
50 * - Interrupt Mask register is cleared
51 * - Priority 7 is assigned to IRQ7 input
52 * - Slave mode address is set to 7
53 * - Special mask mode is cleared
55 * We send the initialization sequence to both the master and
56 * slave i8259 controller.
58 outb(ICW_SELECT
|IC4
, MASTER_PIC_ICW1
);
59 outb(ICW_SELECT
|IC4
, SLAVE_PIC_ICW1
);
61 /* Now the interrupt controller expects us to write to ICW2. */
62 outb(INT_VECTOR_MASTER
| IRQ0
, MASTER_PIC_ICW2
);
63 outb(INT_VECTOR_SLAVE
| IRQ8
, SLAVE_PIC_ICW2
);
65 /* Now the interrupt controller expects us to write to ICW3.
67 * The normal scenario is to set up cascading on IRQ2 on the master
68 * i8259 and assign the slave ID 2 to the slave i8259.
70 outb(CASCADED_PIC
, MASTER_PIC_ICW3
);
71 outb(SLAVE_ID
, SLAVE_PIC_ICW3
);
73 /* Now the interrupt controller expects us to write to ICW4.
75 * We switch both i8259 to microprocessor mode because they're
76 * operating as part of an x86 architecture based chipset
78 outb(MICROPROCESSOR_MODE
, MASTER_PIC_ICW2
);
79 outb(MICROPROCESSOR_MODE
, SLAVE_PIC_ICW2
);
81 /* Now clear the interrupts through OCW1.
82 * First we mask off all interrupts on the slave interrupt controller
83 * then we mask off all interrupts but interrupt 2 on the master
84 * controller. This way the cascading stays alive.
86 outb(ALL_IRQS
, SLAVE_PIC_OCW1
);
87 outb(ALL_IRQS
& ~IRQ2
, MASTER_PIC_OCW1
);
91 * @brief Configure IRQ triggering in the i8259 compatible Interrupt Controller.
93 * Switch a certain interrupt to be level / edge triggered.
95 * @param int_num legacy interrupt number (3-7, 9-15)
96 * @param is_level_triggered 1 for level triggered interrupt, 0 for edge
99 void i8259_configure_irq_trigger(int int_num
, int is_level_triggered
)
101 u16 int_bits
= inb(ELCR1
) | (((u16
)inb(ELCR2
)) << 8);
103 if (is_level_triggered
)
104 int_bits
|= (1 << int_num
);
106 int_bits
&= ~(1 << int_num
);
108 /* Write new values */
109 outb((u8
)(int_bits
& 0xff), ELCR1
);
110 outb((u8
)(int_bits
>> 8), ELCR2
);