1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/x86/x86_pic.c

195 lines
4.5 KiB
C

/*
* Copyright (C) 2014 René Kijewski <rene.kijewski@fu-berlin.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @ingroup x86-irq
* @{
*
* @file
* @brief Configuration and interrupt handling for the Programmable Interrupt Controller (PIC).
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
*
* @}
*/
#include "x86_interrupts.h"
#include "x86_pic.h"
#include "x86_ports.h"
#include "cpu.h"
#include "irq.h"
#include <stdio.h>
static x86_irq_handler_t x86_registered_irqs[X86_IRQ_COUNT];
static const char *const IRQ_NAMES[X86_IRQ_COUNT] __attribute__((unused)) = {
"PIT",
"KBC 1",
NULL,
"COM 2/4",
"COM 1/3",
"LPT 2",
"FLOPPY",
"LPT 1",
"RTC",
"#9",
"ATA 4",
"ATA 3",
"KBC 2",
NULL,
"ATA 1",
"ATA 2",
};
static bool spurious_irq(uint8_t irq_num)
{
if (irq_num < 8) {
outb(PIC_MASTER + PIC_COMMAND, PIC_READ_ISR);
return (inb(PIC_MASTER + PIC_COMMAND) & (1 << irq_num)) == 0;
}
return false;
#if 0 /* TODO: does not work */
irq_num -= 8;
outb(PIC_SLAVE + PIC_COMMAND, PIC_READ_ISR);
if (inb(PIC_SLAVE + PIC_COMMAND) & (1 << irq_num)) {
outb(PIC_MASTER + PIC_COMMAND, PIC_EOI);
return true;
}
return false;
#endif
}
static void pic_interrupt_entry(uint8_t intr_num, struct x86_pushad *orig_ctx, unsigned long error_code)
{
(void) error_code;
(void) orig_ctx;
uint8_t irq_num = intr_num - PIC_MASTER_INTERRUPT_BASE;
if (spurious_irq(irq_num)) {
return;
}
x86_irq_handler_t handler = x86_registered_irqs[irq_num];
if (handler) {
handler(irq_num);
}
outb(PIC_MASTER + PIC_COMMAND, PIC_EOI);
if (irq_num > 7) {
outb(PIC_SLAVE + PIC_COMMAND, PIC_EOI);
}
}
static void pic_remap(void)
{
/* tell both PICs we want to initialize them */
outb(PIC_MASTER + PIC_COMMAND, PIC_ICW1_INIT + PIC_ICW1_ICW4);
io_wait();
outb(PIC_SLAVE + PIC_COMMAND, PIC_ICW1_INIT + PIC_ICW1_ICW4);
io_wait();
/* which ISR should be called if an IRQ occurs */
outb(PIC_MASTER + PIC_DATA, PIC_MASTER_INTERRUPT_BASE);
io_wait();
outb(PIC_SLAVE + PIC_DATA, PIC_SLAVE_INTERRUPT_BASE);
io_wait();
/* IRQ of the master the slave talks to */
outb(PIC_MASTER + PIC_DATA, 1 << PIC_NUM_SLAVE);
io_wait();
outb(PIC_SLAVE + PIC_DATA, PIC_NUM_SLAVE);
io_wait();
/* use PC mode */
outb(PIC_MASTER + PIC_DATA, PIC_ICW4_8086);
io_wait();
outb(PIC_SLAVE + PIC_DATA, PIC_ICW4_8086);
}
static void pic_register_handler(void)
{
for (unsigned i = 0; i < X86_IRQ_COUNT; ++i) {
x86_interrupt_handler_set(PIC_MASTER_INTERRUPT_BASE + i, &pic_interrupt_entry);
}
}
void x86_pic_set_enabled_irqs(uint16_t mask)
{
unsigned old_status = irq_disable();
mask |= PIC_MASK_SLAVE;
mask &= ~PIC_MASK_FPU;
outb(PIC_MASTER + PIC_IMR, ~(uint8_t) mask);
io_wait();
outb(PIC_SLAVE + PIC_IMR, ~(uint8_t) (mask >> 8));
irq_restore(old_status);
}
void x86_pic_enable_irq(unsigned num)
{
unsigned old_status = irq_disable();
uint16_t port;
if (num < 8) {
port = PIC_MASTER;
}
else {
num -= 8;
port = PIC_SLAVE;
}
uint8_t cur = inb(port + PIC_IMR);
outb(port + PIC_IMR, cur & ~(1 << num));
irq_restore(old_status);
}
void x86_pic_disable_irq(unsigned num)
{
unsigned old_status = irq_disable();
uint16_t port;
if (num < 8) {
port = PIC_MASTER;
}
else {
num -= 8;
port = PIC_SLAVE;
}
uint8_t cur = inb(port + PIC_IMR);
outb(port + PIC_IMR, cur | (1 << num));
irq_restore(old_status);
}
void x86_init_pic(void)
{
pic_register_handler();
pic_remap();
x86_pic_set_enabled_irqs(0);
puts("PIC initialized");
}
void x86_pic_set_handler(unsigned irq, x86_irq_handler_t handler)
{
x86_registered_irqs[irq] = handler;
}