165 lines
4.6 KiB
C
165 lines
4.6 KiB
C
/// @file pic8259.c
|
|
/// @brief pic8259 definitions.
|
|
/// @copyright (c) 2014-2023 This file is distributed under the MIT License.
|
|
/// See LICENSE.md for details.
|
|
|
|
// Setup the logging for this file (do this before any other include).
|
|
#include "sys/kernel_levels.h" // Include kernel log levels.
|
|
#define __DEBUG_HEADER__ "[PIC ]" ///< Change header.
|
|
#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE ///< Set log level.
|
|
#include "io/debug.h" // Include debugging functions.
|
|
|
|
#include "hardware/pic8259.h"
|
|
#include "io/port_io.h"
|
|
#include "stddef.h"
|
|
|
|
/// End-of-interrupt command code.
|
|
#define EOI 0x20
|
|
/// IO base address for master PIC.
|
|
#define MASTER_PORT_COMMAND 0x20
|
|
/// I/O address for data to master.
|
|
#define MASTER_PORT_DATA (MASTER_PORT_COMMAND + 1)
|
|
/// IO base address for slave PIC.
|
|
#define SLAVE_PORT_COMMAND 0xA0
|
|
/// I/O address for data to slave.
|
|
#define SLAVE_PORT_DATA (SLAVE_PORT_COMMAND + 1)
|
|
/// ICW4 (not) needed.
|
|
#define ICW1_ICW4 0x01
|
|
/// Single (cascade) mode.
|
|
#define ICW1_SINGLE 0x02
|
|
/// Call address interval 4 (8).
|
|
#define ICW1_INTERVAL4 0x04
|
|
/// Level triggered (edge) mode.
|
|
#define ICW1_LEVEL 0x08
|
|
/// Initialization - required.
|
|
#define ICW1_INIT 0x10
|
|
/// 8086/88 (MCS-80/85) mode
|
|
#define ICW4_8086 0x01
|
|
/// Auto (normal) EOI.
|
|
#define ICW4_AUTO 0x02
|
|
/// Buffered mode/slave.
|
|
#define ICW4_BUF_SLAVE 0x08
|
|
/// Buffered mode/master.
|
|
#define ICW4_BUF_MASTER 0x0C
|
|
/// Special fully nested (not).
|
|
#define ICW4_SFNM 0x10
|
|
/// OCW3 irq ready next CMD read.
|
|
#define PIC_READ_IRR 0x0A
|
|
/// OCW3 irq service next CMD read.
|
|
#define PIC_READ_ISR 0x0B
|
|
|
|
/// The current mask of the master.
|
|
static byte_t master_cur_mask;
|
|
/// The current mask of the slave.
|
|
static byte_t slave_cur_mask;
|
|
|
|
void pic8259_init_irq()
|
|
{
|
|
// Set the masks for the master and slave.
|
|
master_cur_mask = 0xFF;
|
|
slave_cur_mask = 0xFF;
|
|
|
|
// Starts the initialization sequence (in cascade mode).
|
|
outportb(MASTER_PORT_COMMAND, ICW1_INIT + ICW1_ICW4);
|
|
outportb(SLAVE_PORT_COMMAND, ICW1_INIT + ICW1_ICW4);
|
|
|
|
// ICW2: Master PIC vector offset.
|
|
outportb(MASTER_PORT_DATA, 0x20);
|
|
// ICW2: Slave PIC vector offset.
|
|
outportb(SLAVE_PORT_DATA, 0x28);
|
|
|
|
// ICW3: Tell Master PIC that there is a slave PIC at IRQ2 (0000 0100).
|
|
outportb(MASTER_PORT_DATA, 4);
|
|
// ICW3: Tell Slave PIC its cascade identity (0000 0010).
|
|
outportb(SLAVE_PORT_DATA, 2);
|
|
|
|
outportb(MASTER_PORT_DATA, ICW4_8086);
|
|
outportb(SLAVE_PORT_DATA, ICW4_8086);
|
|
|
|
// Restore saved masks.
|
|
outportb(MASTER_PORT_DATA, master_cur_mask);
|
|
outportb(SLAVE_PORT_DATA, slave_cur_mask);
|
|
|
|
pic8259_irq_enable(IRQ_TO_SLAVE_PIC);
|
|
|
|
outportb(0xFF, MASTER_PORT_DATA);
|
|
outportb(0xFF, SLAVE_PORT_DATA);
|
|
}
|
|
|
|
int pic8259_irq_enable(uint32_t irq)
|
|
{
|
|
if (irq >= IRQ_NUM) {
|
|
return -1;
|
|
}
|
|
|
|
byte_t cur_mask;
|
|
byte_t new_mask;
|
|
|
|
if (irq < 8) {
|
|
new_mask = ~(1 << irq);
|
|
cur_mask = inportb(MASTER_PORT_DATA);
|
|
outportb(MASTER_PORT_DATA, (new_mask & cur_mask));
|
|
master_cur_mask = (new_mask & cur_mask);
|
|
} else {
|
|
irq -= 8;
|
|
new_mask = ~(1 << irq);
|
|
cur_mask = inportb(SLAVE_PORT_DATA);
|
|
outportb(SLAVE_PORT_DATA, (new_mask & cur_mask));
|
|
slave_cur_mask = (new_mask & cur_mask);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int pic8259_irq_disable(uint32_t irq)
|
|
{
|
|
if (irq >= IRQ_NUM) {
|
|
return -1;
|
|
}
|
|
|
|
uint8_t cur_mask;
|
|
if (irq < 8) {
|
|
cur_mask = inportb(MASTER_PORT_DATA);
|
|
cur_mask |= (1 << irq);
|
|
outportb(MASTER_PORT_DATA, cur_mask & 0xFF);
|
|
} else {
|
|
irq = irq - 8;
|
|
cur_mask = inportb(SLAVE_PORT_DATA);
|
|
cur_mask |= (1 << irq);
|
|
outportb(SLAVE_PORT_DATA, cur_mask & 0xFF);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void pic8259_send_eoi(uint32_t irq)
|
|
{
|
|
// Perhaps the most common command issued to the PIC chips is the end of
|
|
// interrupt (EOI) command (code 0x20). This is issued to the PIC chips
|
|
// at the end of an IRQ-based interrupt routine. If the IRQ came from the
|
|
// Master PIC, it is sufficient to issue this command only to the Master
|
|
// PIC; however if the IRQ came from the Slave PIC, it is necessary to
|
|
// issue the command to both PIC chips.
|
|
if (irq >= 8) {
|
|
outportb(SLAVE_PORT_COMMAND, EOI);
|
|
}
|
|
outportb(MASTER_PORT_COMMAND, EOI);
|
|
}
|
|
|
|
/*
|
|
* int pic8259_irq_get_current()
|
|
* {
|
|
* outportb(MASTER_PORT_COMMAND, PIC_READ_ISR);
|
|
* uint8_t cur_irq = inportb(MASTER_PORT_COMMAND);
|
|
*
|
|
* if (cur_irq == 4)
|
|
* {
|
|
* outportb(SLAVE_PORT_COMMAND, PIC_READ_ISR);
|
|
* cur_irq = inportb(SLAVE_PORT_COMMAND);
|
|
*
|
|
* return 8 + find_first_bit(cur_irq);
|
|
* }
|
|
*
|
|
* return find_first_bit(cur_irq);
|
|
* }
|
|
*/
|