bsps: Move interrupt controller support to bsps

This patch is a part of the BSP source reorganization.

Update #3285.
This commit is contained in:
Sebastian Huber
2018-04-23 09:50:39 +02:00
parent 276afd2b48
commit 8f8ccee0d9
116 changed files with 95 additions and 95 deletions

170
bsps/i386/shared/irq/elcr.c Normal file
View File

@@ -0,0 +1,170 @@
/*-
* Copyright (c) 2004 John Baldwin <jhb@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef __rtems__
__FBSDID("$FreeBSD$");
#endif /* __rtems__ */
/*
* The ELCR is a register that controls the trigger mode and polarity of
* EISA and ISA interrupts. In FreeBSD 3.x and 4.x, the ELCR was only
* consulted for determining the appropriate trigger mode of EISA
* interrupts when using an APIC. However, it seems that almost all
* systems that include PCI also include an ELCR that manages the ISA
* IRQs 0 through 15. Thus, we check for the presence of an ELCR on
* every machine by checking to see if the values found at bootup are
* sane. Note that the polarity of ISA and EISA IRQs are linked to the
* trigger mode. All edge triggered IRQs use active-hi polarity, and
* all level triggered interrupts use active-lo polarity.
*
* The format of the ELCR is simple: it is a 16-bit bitmap where bit 0
* controls IRQ 0, bit 1 controls IRQ 1, etc. If the bit is zero, the
* associated IRQ is edge triggered. If the bit is one, the IRQ is
* level triggered.
*/
#ifndef __rtems__
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/systm.h>
#include <machine/intr_machdep.h>
#endif /* __rtems__ */
#ifdef __rtems__
#include <bsp.h>
#include "i386_io.h"
#include <errno.h>
#include "elcr.h"
#endif /* __rtems__ */
#define ELCR_PORT 0x4d0
#define ELCR_MASK(irq) (1 << (irq))
static int elcr_status;
#ifdef __rtems__
static
#endif /* __rtems__ */
int elcr_found;
#ifdef __rtems__
#undef printf
#define printf printk
#define bootverbose 1
#define KASSERT(...)
#endif /* __rtems__ */
/*
* Check to see if we have what looks like a valid ELCR. We do this by
* verifying that IRQs 0, 1, 2, and 13 are all edge triggered.
*/
int
elcr_probe(void)
{
int i;
elcr_status = inb(ELCR_PORT) | inb(ELCR_PORT + 1) << 8;
if ((elcr_status & (ELCR_MASK(0) | ELCR_MASK(1) | ELCR_MASK(2) |
ELCR_MASK(8) | ELCR_MASK(13))) != 0)
return (ENXIO);
if (bootverbose) {
printf("ELCR Found. ISA IRQs programmed as:\n");
for (i = 0; i < 16; i++)
printf(" %2d", i);
printf("\n");
for (i = 0; i < 16; i++)
if (elcr_status & ELCR_MASK(i))
printf(" L");
else
printf(" E");
printf("\n");
}
#ifndef __rtems__
if (resource_disabled("elcr", 0))
return (ENXIO);
#endif /* __rtems__ */
elcr_found = 1;
return (0);
}
/*
* Returns 1 for level trigger, 0 for edge.
*/
enum intr_trigger
elcr_read_trigger(u_int irq)
{
#ifdef __rtems__
if (!elcr_found)
return INTR_TRIGGER_EDGE;
#endif /* __rtems__ */
KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
KASSERT(irq <= 15, ("%s: invalid IRQ %u", __func__, irq));
if (elcr_status & ELCR_MASK(irq))
return (INTR_TRIGGER_LEVEL);
else
return (INTR_TRIGGER_EDGE);
}
/*
* Set the trigger mode for a specified IRQ. Mode of 0 means edge triggered,
* and a mode of 1 means level triggered.
*/
void
elcr_write_trigger(u_int irq, enum intr_trigger trigger)
{
int new_status;
#ifdef __rtems__
if (!elcr_found)
return;
#endif /* __rtems__ */
KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
KASSERT(irq <= 15, ("%s: invalid IRQ %u", __func__, irq));
if (trigger == INTR_TRIGGER_LEVEL)
new_status = elcr_status | ELCR_MASK(irq);
else
new_status = elcr_status & ~ELCR_MASK(irq);
if (new_status == elcr_status)
return;
elcr_status = new_status;
if (irq >= 8)
outb(ELCR_PORT + 1, elcr_status >> 8);
else
outb(ELCR_PORT, elcr_status & 0xff);
}
void
elcr_resume(void)
{
#ifdef __rtems__
if (!elcr_found)
return;
#endif /* __rtems__ */
KASSERT(elcr_found, ("%s: no ELCR was found!", __func__));
outb(ELCR_PORT, elcr_status & 0xff);
outb(ELCR_PORT + 1, elcr_status >> 8);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2016 Chris Johns <chrisj@rtems.org>
*
* Header for the FreeBSD ported elcr.c
*/
#ifndef _IRQ_ELCR_H_
#define _IRQ_ELCR_H_
#include <sys/cdefs.h>
enum intr_trigger {
INTR_TRIGGER_EDGE,
INTR_TRIGGER_LEVEL
};
/*
* Check to see if we have what looks like a valid ELCR. We do this by
* verifying that IRQs 0, 1, 2, and 13 are all edge triggered.
*/
int elcr_probe(void);
/*
* Returns 1 for level trigger, 0 for edge.
*/
enum intr_trigger elcr_read_trigger(u_int irq);
/*
* Set the trigger mode for a specified IRQ. Mode of 0 means edge triggered,
* and a mode of 1 means level triggered.
*/
void elcr_write_trigger(u_int irq, enum intr_trigger trigger);
void elcr_resume(void);
#endif

381
bsps/i386/shared/irq/idt.c Normal file
View File

@@ -0,0 +1,381 @@
/*
* cpu.c - This file contains implementation of C function to
* instantiate IDT entries. More detailled information can be found
* on Intel site and more precisely in the following book :
*
* Pentium Processor family
* Developper's Manual
*
* Volume 3 : Architecture and Programming Manual
*
* Copyright (C) 1998 Eric Valette (valette@crf.canon.fr)
* Canon Centre Recherche France.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems/score/cpu.h>
#include <bsp/irq.h>
#include <bsp/tblsizes.h>
/*
* This locking is not enough if IDT is changed at runtime
* and entry can be changed for vector which is enabled
* at change time. But such use is broken anyway.
* Protect code only against concurrent changes.
* Even that is probably unnecessary if different
* entries are changed concurrently.
*/
RTEMS_INTERRUPT_LOCK_DEFINE( static, rtems_idt_access_lock, "rtems_idt_access_lock" );
static rtems_raw_irq_connect_data* raw_irq_table;
static rtems_raw_irq_connect_data default_raw_irq_entry;
static interrupt_gate_descriptor default_idt_entry;
static rtems_raw_irq_global_settings* local_settings;
void create_interrupt_gate_descriptor (interrupt_gate_descriptor* idtEntry,
rtems_raw_irq_hdl hdl)
{
idtEntry->low_offsets_bits = (((unsigned) hdl) & 0xffff);
idtEntry->segment_selector = i386_get_cs();
idtEntry->fixed_value_bits = 0;
idtEntry->gate_type = 0xe;
idtEntry->privilege = 0;
idtEntry->present = 1;
idtEntry->high_offsets_bits = ((((unsigned) hdl) >> 16) & 0xffff);
}
rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset index)
{
uint32_t hdl;
interrupt_gate_descriptor* idt_entry_tbl;
unsigned limit;
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
/* Convert limit into number of entries */
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
if(index >= limit) {
return 0;
}
hdl = (idt_entry_tbl[index].low_offsets_bits |
(idt_entry_tbl[index].high_offsets_bits << 16));
return (rtems_raw_irq_hdl) hdl;
}
int i386_set_idt_entry (const rtems_raw_irq_connect_data* irq)
{
interrupt_gate_descriptor* idt_entry_tbl;
unsigned limit;
rtems_interrupt_lock_context lock_context;
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
/* Convert limit into number of entries */
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
if (irq->idtIndex >= limit) {
return 0;
}
/*
* Check if default handler is actually connected. If not issue an error.
* You must first get the current handler via i386_get_current_idt_entry
* and then disconnect it using i386_delete_idt_entry.
* RATIONALE : to always have the same transition by forcing the user
* to get the previous handler before accepting to disconnect.
*/
if (get_hdl_from_vector(irq->idtIndex) != default_raw_irq_entry.hdl) {
return 0;
}
rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
raw_irq_table [irq->idtIndex] = *irq;
create_interrupt_gate_descriptor (&idt_entry_tbl[irq->idtIndex], irq->hdl);
if (irq->on)
irq->on(irq);
rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
return 1;
}
void _CPU_ISR_install_vector (uint32_t vector,
proc_ptr hdl,
proc_ptr * oldHdl)
{
interrupt_gate_descriptor* idt_entry_tbl;
unsigned limit;
interrupt_gate_descriptor new;
rtems_interrupt_lock_context lock_context;
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
/* Convert limit into number of entries */
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
if (vector >= limit) {
return;
}
rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
* ((unsigned int *) oldHdl) = idt_entry_tbl[vector].low_offsets_bits |
(idt_entry_tbl[vector].high_offsets_bits << 16);
create_interrupt_gate_descriptor(&new, hdl);
idt_entry_tbl[vector] = new;
rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
}
int i386_get_current_idt_entry (rtems_raw_irq_connect_data* irq)
{
interrupt_gate_descriptor* idt_entry_tbl;
unsigned limit;
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
/* Convert limit into number of entries */
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
if (irq->idtIndex >= limit) {
return 0;
}
raw_irq_table [irq->idtIndex].hdl = get_hdl_from_vector(irq->idtIndex);
*irq = raw_irq_table [irq->idtIndex];
return 1;
}
int i386_delete_idt_entry (const rtems_raw_irq_connect_data* irq)
{
interrupt_gate_descriptor* idt_entry_tbl;
unsigned limit;
rtems_interrupt_lock_context lock_context;
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
/* Convert limit into number of entries */
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
if (irq->idtIndex >= limit) {
return 0;
}
/*
* Check if handler passed is actually connected. If not issue an error.
* You must first get the current handler via i386_get_current_idt_entry
* and then disconnect it using i386_delete_idt_entry.
* RATIONALE : to always have the same transition by forcing the user
* to get the previous handler before accepting to disconnect.
*/
if (get_hdl_from_vector(irq->idtIndex) != irq->hdl){
return 0;
}
rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
idt_entry_tbl[irq->idtIndex] = default_idt_entry;
if (irq->off)
irq->off(irq);
raw_irq_table[irq->idtIndex] = default_raw_irq_entry;
raw_irq_table[irq->idtIndex].idtIndex = irq->idtIndex;
rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
return 1;
}
/*
* Caution this function assumes the IDTR has been already set.
*/
int i386_init_idt (rtems_raw_irq_global_settings* config)
{
unsigned limit;
unsigned i;
rtems_interrupt_lock_context lock_context;
interrupt_gate_descriptor* idt_entry_tbl;
i386_get_info_from_IDTR (&idt_entry_tbl, &limit);
/* Convert limit into number of entries */
limit = (limit + 1) / sizeof(interrupt_gate_descriptor);
if (config->idtSize != limit) {
return 0;
}
/*
* store various accelarators
*/
raw_irq_table = config->rawIrqHdlTbl;
local_settings = config;
default_raw_irq_entry = config->defaultRawEntry;
rtems_interrupt_lock_acquire(&rtems_idt_access_lock, &lock_context);
create_interrupt_gate_descriptor (&default_idt_entry, default_raw_irq_entry.hdl);
for (i=0; i < limit; i++) {
interrupt_gate_descriptor new;
create_interrupt_gate_descriptor (&new, raw_irq_table[i].hdl);
idt_entry_tbl[i] = new;
if (raw_irq_table[i].hdl != default_raw_irq_entry.hdl) {
raw_irq_table[i].on(&raw_irq_table[i]);
}
else {
raw_irq_table[i].off(&raw_irq_table[i]);
}
}
rtems_interrupt_lock_release(&rtems_idt_access_lock, &lock_context);
return 1;
}
int i386_get_idt_config (rtems_raw_irq_global_settings** config)
{
*config = local_settings;
return 1;
}
uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
segment_descriptors* sd)
{
uint16_t gdt_limit;
uint16_t tmp_segment = 0;
segment_descriptors* gdt_entry_tbl;
uint8_t present;
i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
if (segment_selector_index >= (gdt_limit+1)/8) {
/* index to GDT table out of bounds */
return 0;
}
if (segment_selector_index == 0) {
/* index 0 is not usable */
return 0;
}
/* put prepared descriptor into the GDT */
present = sd->present;
sd->present = 0;
gdt_entry_tbl[segment_selector_index].present = 0;
RTEMS_COMPILER_MEMORY_BARRIER();
gdt_entry_tbl[segment_selector_index] = *sd;
RTEMS_COMPILER_MEMORY_BARRIER();
gdt_entry_tbl[segment_selector_index].present = present;
sd->present = present;
/*
* Now, reload all segment registers so that the possible changes takes effect.
*/
__asm__ volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
"movw %%es,%0 ; movw %0,%%es\n\t"
"movw %%fs,%0 ; movw %0,%%fs\n\t"
"movw %%gs,%0 ; movw %0,%%gs\n\t"
"movw %%ss,%0 ; movw %0,%%ss"
: "=r" (tmp_segment)
: "0" (tmp_segment)
);
return 1;
}
void i386_fill_segment_desc_base(uint32_t base,
segment_descriptors* sd)
{
sd->base_address_15_0 = base & 0xffff;
sd->base_address_23_16 = (base >> 16) & 0xff;
sd->base_address_31_24 = (base >> 24) & 0xff;
}
void i386_fill_segment_desc_limit(uint32_t limit,
segment_descriptors* sd)
{
sd->granularity = 0;
if (limit > 65535) {
sd->granularity = 1;
limit /= 4096;
}
sd->limit_15_0 = limit & 0xffff;
sd->limit_19_16 = (limit >> 16) & 0xf;
}
/*
* Caution this function assumes the GDTR has been already set.
*/
uint32_t i386_set_gdt_entry (uint16_t segment_selector_index, uint32_t base,
uint32_t limit)
{
segment_descriptors gdt_entry;
memset(&gdt_entry, 0, sizeof(gdt_entry));
i386_fill_segment_desc_limit(limit, &gdt_entry);
i386_fill_segment_desc_base(base, &gdt_entry);
/*
* set up descriptor type (this may well becomes a parameter if needed)
*/
gdt_entry.type = 2; /* Data R/W */
gdt_entry.descriptor_type = 1; /* Code or Data */
gdt_entry.privilege = 0; /* ring 0 */
gdt_entry.present = 1; /* not present */
/*
* Now, reload all segment registers so the limit takes effect.
*/
return i386_raw_gdt_entry(segment_selector_index, &gdt_entry);
}
uint16_t i386_next_empty_gdt_entry ()
{
uint16_t gdt_limit;
segment_descriptors* gdt_entry_tbl;
/* initial amount of filled descriptors */
static uint16_t segment_selector_index = NUM_SYSTEM_GDT_DESCRIPTORS - 1;
segment_selector_index += 1;
i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
if (segment_selector_index >= (gdt_limit+1)/8) {
return 0;
}
return segment_selector_index;
}
uint16_t i386_cpy_gdt_entry(uint16_t segment_selector_index,
segment_descriptors* struct_to_fill)
{
uint16_t gdt_limit;
segment_descriptors* gdt_entry_tbl;
i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
if (segment_selector_index >= (gdt_limit+1)/8) {
return 0;
}
*struct_to_fill = gdt_entry_tbl[segment_selector_index];
return segment_selector_index;
}
segment_descriptors* i386_get_gdt_entry(uint16_t segment_selector_index)
{
uint16_t gdt_limit;
segment_descriptors* gdt_entry_tbl;
i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
if (segment_selector_index >= (gdt_limit+1)/8) {
return 0;
}
return &gdt_entry_tbl[segment_selector_index];
}
uint32_t i386_limit_gdt_entry(segment_descriptors* gdt_entry)
{
uint32_t lim = (gdt_entry->limit_15_0 + (gdt_entry->limit_19_16<<16));
if (gdt_entry->granularity) {
return lim*4096+4095;
}
return lim;
}

405
bsps/i386/shared/irq/irq.c Normal file
View File

@@ -0,0 +1,405 @@
/*
* This file contains the implementation of the function described in irq.h
*/
/*
* Copyright (c) 2009 embedded brains GmbH
* Copyright (C) 1998 valette@crf.canon.fr
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <bsp.h>
#include <bsp/irq.h>
#include <bsp/irq-generic.h>
#include <rtems/score/cpu.h>
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include "elcr.h"
RTEMS_INTERRUPT_LOCK_DEFINE( static, rtems_i8259_access_lock, "rtems_i8259_access_lock" );
/*
* pointer to the mask representing the additionnal irq vectors
* that must be disabled when a particular entry is activated.
* They will be dynamically computed from teh prioruty table given
* in BSP_rtems_irq_mngt_set();
* CAUTION : this table is accessed directly by interrupt routine
* prologue.
*/
static rtems_i8259_masks irq_mask_or_tbl[BSP_IRQ_LINES_NUMBER];
/*
* Stats of interrupts dispatched.
*/
static uint32_t irq_count[BSP_IRQ_VECTOR_NUMBER] = {0};
static uint32_t spurious_count;
/*
* Edge or level trigger interrupts.
*/
static enum intr_trigger irq_trigger[BSP_IRQ_LINES_NUMBER];
/*-------------------------------------------------------------------------+
| Cache for 1st and 2nd PIC IRQ line's mssk (enabled or disabled) register.
+--------------------------------------------------------------------------*/
/*
* lower byte is interrupt mask on the master PIC.
* while upper bits are interrupt on the slave PIC.
* This cache is initialized in ldseg.s
*/
static rtems_i8259_masks i8259a_imr_cache = 0xFFFB;
static rtems_i8259_masks i8259a_in_progress = 0;
static inline
void BSP_i8259a_irq_update_master_imr( void )
{
rtems_i8259_masks mask = i8259a_in_progress | i8259a_imr_cache;
outport_byte( PIC_MASTER_IMR_IO_PORT, mask & 0xff );
}
static inline
void BSP_i8259a_irq_update_slave_imr( void )
{
rtems_i8259_masks mask = i8259a_in_progress | i8259a_imr_cache;
outport_byte( PIC_SLAVE_IMR_IO_PORT, ( mask >> 8 ) & 0xff );
}
/*
* Print the stats.
*/
uint32_t BSP_irq_count_dump(FILE *f)
{
uint32_t tot = 0;
int i;
if ( !f )
f = stdout;
fprintf(f,"SPURIOUS: %9"PRIu32"\n", spurious_count);
for ( i = 0; i < BSP_IRQ_VECTOR_NUMBER; i++ ) {
char type = '-';
if (i < BSP_IRQ_LINES_NUMBER)
type = irq_trigger[i] == INTR_TRIGGER_EDGE ? 'E' : 'L';
tot += irq_count[i];
fprintf(f,"IRQ %2u: %c %9"PRIu32"\n", i, type, irq_count[i]);
}
return tot;
}
/*
* Is the IRQ valid?
*/
static inline bool BSP_i8259a_irq_valid(const rtems_irq_number irqLine)
{
return ((int)irqLine >= BSP_IRQ_VECTOR_LOWEST_OFFSET) &&
((int)irqLine <= BSP_IRQ_MAX_ON_i8259A);
}
/*
* Read the IRR register. The default.
*/
static inline uint8_t BSP_i8259a_irq_int_request_reg(uint32_t ioport)
{
uint8_t isr;
inport_byte(ioport, isr);
return isr;
}
/*
* Read the ISR register. Keep the default of the IRR.
*/
static inline uint8_t BSP_i8259a_irq_in_service_reg(uint32_t ioport)
{
uint8_t isr;
outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR | PIC_OCW3_RIS);
inport_byte(ioport, isr);
outport_byte(ioport, PIC_OCW3_SEL | PIC_OCW3_RR);
return isr;
}
/*-------------------------------------------------------------------------+
| Function: BSP_irq_disable_at_i8259a
| Description: Mask IRQ line in appropriate PIC chip.
| Global Variables: i8259a_imr_cache, i8259a_in_progress
| Arguments: vector_offset - number of IRQ line to mask.
| Returns: 0 is OK.
+--------------------------------------------------------------------------*/
static int BSP_irq_disable_at_i8259a(const rtems_irq_number irqLine)
{
unsigned short mask;
rtems_interrupt_lock_context lock_context;
rtems_interrupt_lock_acquire(&rtems_i8259_access_lock, &lock_context);
mask = 1 << irqLine;
i8259a_imr_cache |= mask;
if (irqLine < 8)
{
BSP_i8259a_irq_update_master_imr();
}
else
{
BSP_i8259a_irq_update_slave_imr();
}
rtems_interrupt_lock_release(&rtems_i8259_access_lock, &lock_context);
return 0;
}
/*-------------------------------------------------------------------------+
| Function: BSP_irq_enable_at_i8259a
| Description: Unmask IRQ line in appropriate PIC chip.
| Global Variables: i8259a_imr_cache, i8259a_in_progress
| Arguments: irqLine - number of IRQ line to mask.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
static int BSP_irq_enable_at_i8259a(const rtems_irq_number irqLine)
{
unsigned short mask;
rtems_interrupt_lock_context lock_context;
uint8_t isr;
uint8_t irr;
rtems_interrupt_lock_acquire(&rtems_i8259_access_lock, &lock_context);
mask = 1 << irqLine;
i8259a_imr_cache &= ~mask;
if (irqLine < 8)
{
isr = BSP_i8259a_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT);
irr = BSP_i8259a_irq_int_request_reg(PIC_MASTER_COMMAND_IO_PORT);
BSP_i8259a_irq_update_master_imr();
}
else
{
isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
irr = BSP_i8259a_irq_int_request_reg(PIC_SLAVE_COMMAND_IO_PORT);
BSP_i8259a_irq_update_slave_imr();
}
if (((isr ^ irr) & mask) != 0)
printk("i386: isr=%x irr=%x\n", isr, irr);
rtems_interrupt_lock_release(&rtems_i8259_access_lock, &lock_context);
return 0;
} /* mask_irq */
/*-------------------------------------------------------------------------+
| Function: BSP_irq_ack_at_i8259a
| Description: Signal generic End Of Interrupt (EOI) to appropriate PIC.
| Global Variables: None.
| Arguments: irqLine - number of IRQ line to acknowledge.
| Returns: Nothing.
+--------------------------------------------------------------------------*/
static int BSP_irq_ack_at_i8259a(const rtems_irq_number irqLine)
{
uint8_t slave_isr = 0;
if (irqLine >= 8) {
outport_byte(PIC_SLAVE_COMMAND_IO_PORT, PIC_EOI);
slave_isr = BSP_i8259a_irq_in_service_reg(PIC_SLAVE_COMMAND_IO_PORT);
}
/*
* Only issue the EOI to the master if there are no more interrupts in
* service for the slave. i8259a data sheet page 18, The Special Fully Nested
* Mode, b.
*/
if (slave_isr == 0)
outport_byte(PIC_MASTER_COMMAND_IO_PORT, PIC_EOI);
return 0;
} /* ackIRQ */
/*
* ------------------------ RTEMS Irq helper functions ----------------
*/
static rtems_irq_prio irqPrioTable[BSP_IRQ_LINES_NUMBER]={
/*
* actual priorities for each interrupt source:
* 0 means that only current interrupt is masked
* 255 means all other interrupts are masked
* The second entry has a priority of 255 because
* it is the slave pic entry and is should always remain
* unmasked.
*/
0,0,
255,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static void compute_i8259_masks_from_prio (void)
{
rtems_interrupt_lock_context lock_context;
unsigned int i;
unsigned int j;
rtems_interrupt_lock_acquire(&rtems_i8259_access_lock, &lock_context);
/*
* Always mask at least current interrupt to prevent re-entrance
*/
for (i=0; i < BSP_IRQ_LINES_NUMBER; i++) {
* ((unsigned short*) &irq_mask_or_tbl[i]) = (1 << i);
for (j = 0; j < BSP_IRQ_LINES_NUMBER; j++) {
/*
* Mask interrupts at i8259 level that have a lower priority
*/
if (irqPrioTable [i] > irqPrioTable [j]) {
* ((unsigned short*) &irq_mask_or_tbl[i]) |= (1 << j);
}
}
}
rtems_interrupt_lock_release(&rtems_i8259_access_lock, &lock_context);
}
static inline bool bsp_interrupt_vector_is_valid(rtems_vector_number vector)
{
return BSP_i8259a_irq_valid((const rtems_irq_number) vector);
}
void bsp_interrupt_vector_enable(rtems_vector_number vector)
{
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
BSP_irq_enable_at_i8259a(vector);
}
void bsp_interrupt_vector_disable(rtems_vector_number vector)
{
bsp_interrupt_assert(bsp_interrupt_is_valid_vector(vector));
BSP_irq_disable_at_i8259a(vector);
}
rtems_status_code bsp_interrupt_facility_initialize(void)
{
int i;
/*
* set up internal tables used by rtems interrupt prologue
*/
compute_i8259_masks_from_prio();
/*
* must enable slave pic anyway
*/
BSP_irq_enable_at_i8259a(2);
/*
* Probe the ELCR.
*/
elcr_probe();
for (i = 0; i < BSP_IRQ_LINES_NUMBER; i++)
irq_trigger[i] = elcr_read_trigger(i);
return RTEMS_SUCCESSFUL;
}
/*
* Global so the asm handler can call it.
*/
void BSP_dispatch_isr(int vector);
void BSP_dispatch_isr(int vector)
{
rtems_interrupt_lock_context lock_context;
rtems_i8259_masks in_progress_save = 0;
if (vector < BSP_IRQ_VECTOR_NUMBER) {
/*
* Hardware?
*/
if (vector <= BSP_IRQ_MAX_ON_i8259A) {
rtems_interrupt_lock_acquire_isr(&rtems_i8259_access_lock, &lock_context);
/*
* See if this is a spurious interrupt.
*/
if ((vector == 7 || vector == 15)) {
/*
* Only check it there no handler for 7 or 15.
*/
if (bsp_interrupt_handler_is_empty(vector)) {
/*
* Read the ISR register to see if IRQ 7/15 is really pending.
*/
uint8_t isr = BSP_i8259a_irq_in_service_reg(PIC_MASTER_COMMAND_IO_PORT);
if ((isr & (1 << 7)) == 0) {
++spurious_count;
rtems_interrupt_lock_release_isr(&rtems_i8259_access_lock, &lock_context);
return;
}
}
}
/*
* Save the current cached value for the IMR. It will have the bit for this
* vector clear.
*/
if (vector <= BSP_IRQ_MAX_ON_i8259A) {
in_progress_save = i8259a_in_progress;
i8259a_in_progress |= irq_mask_or_tbl[vector];
BSP_i8259a_irq_update_master_imr();
BSP_i8259a_irq_update_slave_imr();
}
/*
* Do not use auto-EOI as some slave PIC do not work correctly.
*/
BSP_irq_ack_at_i8259a(vector);
rtems_interrupt_lock_release_isr(&rtems_i8259_access_lock, &lock_context);
}
/*
* Count the interrupt.
*/
irq_count[vector]++;
RTEMS_COMPILER_MEMORY_BARRIER();
/*
* Allow nesting.
*/
__asm__ __volatile__("sti");
bsp_interrupt_handler_dispatch(vector);
/*
* Disallow nesting.
*/
__asm__ __volatile__("cli");
RTEMS_COMPILER_MEMORY_BARRIER();
if (vector <= BSP_IRQ_MAX_ON_i8259A) {
rtems_interrupt_lock_acquire_isr(&rtems_i8259_access_lock, &lock_context);
/*
* Put the mask back but keep this vector masked if the trigger type is
* level. The driver or a thread level interrupt server needs to enable it
* again.
*/
if (vector <= BSP_IRQ_MAX_ON_i8259A) {
i8259a_in_progress = in_progress_save;
BSP_i8259a_irq_update_master_imr();
BSP_i8259a_irq_update_slave_imr();
}
rtems_interrupt_lock_release_isr(&rtems_i8259_access_lock, &lock_context);
}
}
}

View File

@@ -0,0 +1,268 @@
/*
* This file contains the implementation of the function described in irq.h
*/
/*
* Copyright (C) 1998 valette@crf.canon.fr
*
* COPYRIGHT (c) 1989-2011.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems/asm.h>
#include <rtems/system.h>
#include <bspopts.h>
#include <rtems/score/cpu.h>
#include <rtems/score/percpu.h>
#include <bsp.h> /* to establish dependency on prototype */
#ifndef CPU_STACK_ALIGNMENT
#error "Missing header? CPU_STACK_ALIGNMENT is not defined here"
#endif
/* Stack frame we use for intermediate storage */
#define ARG_OFF 0
#define MSK_OFF 4 /* not used any more */
#define EBX_OFF 8 /* ebx */
#define EBP_OFF 12 /* code restoring ebp/esp relies on */
#define ESP_OFF 16 /* esp being on top of ebp! */
#ifdef __SSE__
/* need to be on 16 byte boundary for SSE, add 12 to do that */
#define FRM_SIZ (20+12+512)
#define SSE_OFF 32
#else
#define FRM_SIZ 20
#endif
BEGIN_CODE
SYM (_ISR_Handler):
/*
* Before this was point is reached the vectors unique
* entry point did the following:
*
* 1. saved scratch registers registers eax edx ecx"
* 2. put the vector number in ecx.
*
* BEGINNING OF ESTABLISH SEGMENTS
*
* WARNING: If an interrupt can occur when the segments are
* not correct, then this is where we should establish
* the segments. In addition to establishing the
* segments, it may be necessary to establish a stack
* in the current data area on the outermost interrupt.
*
* NOTE: If the previous values of the segment registers are
* pushed, do not forget to adjust SAVED_REGS.
*
* NOTE: Make sure the exit code which restores these
* when this type of code is needed.
*/
/***** ESTABLISH SEGMENTS CODE GOES HERE ******/
/*
* END OF ESTABLISH SEGMENTS
*/
/*
* Establish an aligned stack frame
* original sp
* saved ebx
* saved ebp
* saved irq mask
* vector arg to BSP_dispatch_isr <- aligned SP
*/
movl esp, eax
subl $FRM_SIZ, esp
andl $ - CPU_STACK_ALIGNMENT, esp
movl ebx, EBX_OFF(esp)
movl eax, ESP_OFF(esp)
movl ebp, EBP_OFF(esp)
/*
* GCC versions starting with 4.3 no longer place the cld
* instruction before string operations. We need to ensure
* it is set correctly for ISR handlers.
*/
cld
#ifdef __SSE__
/* NOTE: SSE only is supported if the BSP enables fxsave/fxrstor
* to save/restore SSE context! This is so far only implemented
* for pc386!.
*/
/* We save SSE here (on the task stack) because we possibly
* call other C-code (besides the ISR, namely _Thread_Dispatch())
*/
/* don't wait here; a possible exception condition will eventually be
* detected when the task resumes control and executes a FP instruction
fwait
*/
fxsave SSE_OFF(esp)
fninit /* clean-slate FPU */
movl $0x1f80, ARG_OFF(esp) /* use ARG_OFF as scratch space */
ldmxcsr ARG_OFF(esp) /* clean-slate MXCSR */
#endif
/*
* Now switch stacks if necessary
*/
PUBLIC (ISR_STOP)
ISR_STOP:
.check_stack_switch:
movl esp, ebp /* ebp = previous stack pointer */
#ifdef RTEMS_SMP
call SYM(_CPU_SMP_Get_current_processor)
sall $PER_CPU_CONTROL_SIZE_LOG2, eax
addl $SYM(_Per_CPU_Information), eax
movl eax, ebx
#else
movl $SYM(_Per_CPU_Information), ebx
#endif
/* is this the outermost interrupt? */
cmpl $0, PER_CPU_ISR_NEST_LEVEL(ebx)
jne nested /* No, then continue */
movl PER_CPU_INTERRUPT_STACK_HIGH(ebx), esp
/*
* We want to insure that the old stack pointer is in ebp
* By saving it on every interrupt, all we have to do is
* movl ebp->esp near the end of every interrupt.
*/
nested:
incl PER_CPU_ISR_NEST_LEVEL(ebx) /* one nest level deeper */
incl PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL(ebx) /* disable
multitasking */
/*
* ECX is preloaded with the vector number; store as arg
* on top of stack. Note that _CPU_Interrupt_stack_high
* was adjusted in _CPU_Interrupt_stack_setup() (score/rtems/cpu.h)
* to make sure there is space.
*/
movl ecx, ARG_OFF(esp) /* store vector arg in stack */
call BSP_dispatch_isr
movl ARG_OFF(esp), ecx /* grab vector arg from stack */
/*
* Restore stack. This moves back to the task stack
* when all interrupts are unnested.
*/
movl ebp, esp
decl PER_CPU_ISR_NEST_LEVEL(ebx) /* one less ISR nest level */
/* If interrupts are nested, */
/* then dispatching is disabled */
decl PER_CPU_THREAD_DISPATCH_DISABLE_LEVEL(ebx)
/* unnest multitasking */
/* Is dispatch disabled */
jne .exit /* Yes, then exit */
cmpb $0, PER_CPU_DISPATCH_NEEDED(ebx)
/* Is task switch necessary? */
jne .schedule /* Yes, then call the scheduler */
jmp .exit /* No, exit */
.schedule:
/*
* the scratch registers have already been saved and we are already
* back on the thread system stack. So we can call _Thread_Dispatch
* directly
*/
call _Thread_Dispatch
/*
* fall through exit to restore complete contex (scratch registers
* eip, CS, Flags).
*/
.exit:
#ifdef __SSE__
fwait
fxrstor SSE_OFF(esp)
#endif
/* restore ebx, ebp and original esp */
addl $EBX_OFF, esp
popl ebx
popl ebp
popl esp
/*
* BEGINNING OF DE-ESTABLISH SEGMENTS
*
* NOTE: Make sure there is code here if code is added to
* load the segment registers.
*
*/
/******* DE-ESTABLISH SEGMENTS CODE GOES HERE ********/
/*
* END OF DE-ESTABLISH SEGMENTS
*/
popl edx
popl ecx
popl eax
iret
#define DISTINCT_INTERRUPT_ENTRY(_vector) \
.p2align 4 ; \
PUBLIC (rtems_irq_prologue_ ## _vector ) ; \
SYM (rtems_irq_prologue_ ## _vector ): \
pushl eax ; \
pushl ecx ; \
pushl edx ; \
movl $ _vector, ecx ; \
jmp SYM (_ISR_Handler) ;
DISTINCT_INTERRUPT_ENTRY(0)
DISTINCT_INTERRUPT_ENTRY(1)
DISTINCT_INTERRUPT_ENTRY(2)
DISTINCT_INTERRUPT_ENTRY(3)
DISTINCT_INTERRUPT_ENTRY(4)
DISTINCT_INTERRUPT_ENTRY(5)
DISTINCT_INTERRUPT_ENTRY(6)
DISTINCT_INTERRUPT_ENTRY(7)
DISTINCT_INTERRUPT_ENTRY(8)
DISTINCT_INTERRUPT_ENTRY(9)
DISTINCT_INTERRUPT_ENTRY(10)
DISTINCT_INTERRUPT_ENTRY(11)
DISTINCT_INTERRUPT_ENTRY(12)
DISTINCT_INTERRUPT_ENTRY(13)
DISTINCT_INTERRUPT_ENTRY(14)
DISTINCT_INTERRUPT_ENTRY(15)
DISTINCT_INTERRUPT_ENTRY(16)
/*
* routine used to initialize the IDT by default
*/
PUBLIC (default_raw_idt_handler)
PUBLIC (raw_idt_notify)
SYM (default_raw_idt_handler):
pusha
cld
mov esp, ebp
andl $ - CPU_STACK_ALIGNMENT, esp
call raw_idt_notify
mov ebp, esp
popa
iret
END_CODE
END

View File

@@ -0,0 +1,191 @@
/* irq_init.c
*
* This file contains the implementation of rtems initialization
* related to interrupt handling.
*
* Copyright (c) 2009 embedded brains GmbH
* CopyRight (C) 1998 valette@crf.canon.fr
*
* COPYRIGHT (c) 2011.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <rtems/bspIo.h>
#include <rtems/score/cpu.h>
#include <bsp.h>
#include <bsp/irq.h>
#include <bsp/irq-generic.h>
/*
* rtems prologue generated in irq_asm.S
*/
extern void rtems_irq_prologue_0(void);
extern void rtems_irq_prologue_1(void);
extern void rtems_irq_prologue_2(void);
extern void rtems_irq_prologue_3(void);
extern void rtems_irq_prologue_4(void);
extern void rtems_irq_prologue_5(void);
extern void rtems_irq_prologue_6(void);
extern void rtems_irq_prologue_7(void);
extern void rtems_irq_prologue_8(void);
extern void rtems_irq_prologue_9(void);
extern void rtems_irq_prologue_10(void);
extern void rtems_irq_prologue_11(void);
extern void rtems_irq_prologue_12(void);
extern void rtems_irq_prologue_13(void);
extern void rtems_irq_prologue_14(void);
extern void rtems_irq_prologue_15(void);
extern void rtems_irq_prologue_16(void);
/*
* default vectors
*/
extern void default_raw_idt_handler(void);
/*
* default raw on/off function
*/
static void raw_nop_func(const struct __rtems_raw_irq_connect_data__ *unused)
{
}
/*
* default raw isOn function
*/
static int raw_not_connected(
const struct __rtems_raw_irq_connect_data__ *unused
)
{
return 0;
}
static rtems_raw_irq_connect_data idtHdl[IDT_SIZE];
static rtems_raw_irq_hdl rtemsIrq[BSP_IRQ_VECTOR_NUMBER] = {
rtems_irq_prologue_0,
rtems_irq_prologue_1,
rtems_irq_prologue_2,
rtems_irq_prologue_3,
rtems_irq_prologue_4,
rtems_irq_prologue_5,
rtems_irq_prologue_6,
rtems_irq_prologue_7,
rtems_irq_prologue_8,
rtems_irq_prologue_9,
rtems_irq_prologue_10,
rtems_irq_prologue_11,
rtems_irq_prologue_12,
rtems_irq_prologue_13,
rtems_irq_prologue_14,
rtems_irq_prologue_15,
rtems_irq_prologue_16,
};
static rtems_raw_irq_connect_data defaultRawIrq = {
0, /* vectorIdex */
default_raw_idt_handler, /* hdl */
raw_nop_func, /* on */
raw_nop_func, /* off */
raw_not_connected /* isOn */
};
static interrupt_gate_descriptor idtEntry;
static rtems_raw_irq_global_settings raw_initial_config;
/*
* This method is called from irq_asm.S and cannot be static.
*/
void raw_idt_notify(void)
{
printk("raw_idt_notify has been called \n");
}
void rtems_irq_mngt_init(void)
{
int i;
interrupt_gate_descriptor* idt_entry_tbl;
unsigned int limit;
rtems_interrupt_level level;
i386_get_info_from_IDTR(&idt_entry_tbl, &limit);
/* Convert into number of entries */
limit = (limit + 1)/sizeof(interrupt_gate_descriptor);
if(limit != IDT_SIZE) {
printk("IDT table size mismatch !!! System locked\n");
while(1);
}
/*
* The interrupt management can be initialized only once
* during system bootup and that should happen on boot
* CPU so there is no need to synchronize with others CPUs.
*/
rtems_interrupt_local_disable(level);
/*
* Init the complete IDT vector table with defaultRawIrq value
*/
for (i = 0; i < IDT_SIZE ; i++) {
idtHdl[i] = defaultRawIrq;
idtHdl[i].idtIndex = i;
}
raw_initial_config.idtSize = IDT_SIZE;
raw_initial_config.defaultRawEntry = defaultRawIrq;
raw_initial_config.rawIrqHdlTbl = idtHdl;
if (!i386_init_idt (&raw_initial_config)) {
/*
* put something here that will show the failure...
*/
printk("Unable to initialize IDT!!! System locked\n");
while (1);
}
/*
* Patch the entry that will be used by RTEMS for interrupt management
* with RTEMS prologue.
*/
for (i = 0; i < BSP_IRQ_VECTOR_NUMBER; i++) {
create_interrupt_gate_descriptor(&idtEntry, rtemsIrq[i]);
idt_entry_tbl[i + BSP_ASM_IRQ_VECTOR_BASE] = idtEntry;
}
/*
* At this point we have completed the initialization of IDT
* with raw handlers. We must now initialize the higher level
* interrupt management.
*/
/*
* Init initial Interrupt management config
*/
bsp_interrupt_initialize();
/*
* #define DEBUG
*/
#ifdef DEBUG
{
/*
* following adresses should be the same
*/
unsigned tmp;
printk("idt_entry_tbl = %x Interrupt_descriptor_table addr = %x\n",
idt_entry_tbl, &Interrupt_descriptor_table);
tmp = (unsigned) get_hdl_from_vector (BSP_ASM_IRQ_VECTOR_BASE + BSP_PERIODIC_TIMER);
printk("clock isr address from idt = %x should be %x\n",
tmp, (unsigned) rtems_irq_prologue_0);
}
printk("i8259s_cache = %x\n", * (unsigned short*) &i8259s_cache);
BSP_wait_polled_input();
#endif
}