416 lines
15 KiB
C
416 lines
15 KiB
C
/* loApicIntr.c - Intel Pentium[234] Local APIC/xAPIC driver */
|
|
|
|
/*
|
|
* Copyright (c) 1984-2008, 2011-2015 Wind River Systems, Inc.
|
|
*
|
|
* 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.
|
|
*
|
|
* 3) Neither the name of Wind River Systems nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
This module is a driver for the local APIC/xAPIC (Advanced Programmable
|
|
Interrupt Controller) in P6 (PentiumPro, II, III) family processors
|
|
and P7 (Pentium4) family processors. The local APIC/xAPIC is included
|
|
in selected P6 (PentiumPro, II, III) and P7 (Pentium4) family processors.
|
|
Beginning with the P6 family processors, the presence or absence of an
|
|
on-chip local APIC can be detected using the CPUID instruction. When the
|
|
CPUID instruction is executed, bit 9 of the feature flags returned in the
|
|
EDX register indicates the presence (set) or absence (clear) of an on-chip
|
|
local APIC.
|
|
|
|
The local APIC performs two main functions for the processor:
|
|
- It processes local external interrupts that the processor receives at its
|
|
interrupt pins and local internal interrupts that software generates.
|
|
- It communicates with an external IO APIC
|
|
chip. The external IO APIC receives external interrupt events from
|
|
peripheral and direct them to the local APIC. The IO APIC is
|
|
part of Intel's system chip set.
|
|
The local APIC controls the dispatching of interrupts (to its associated
|
|
processor) that it receives either locally or from the IO APIC. It provides
|
|
facilities for queuing, nesting and masking of interrupts. It handles the
|
|
interrupt delivery protocol with its local processor and accesses to APIC
|
|
registers.
|
|
A timer on the local APIC allows local generation of interrupts, and
|
|
local interrupt pins permit local reception of processor-specific interrupts.
|
|
The local APIC can be disabled and used in conjunction with a standard 8259A
|
|
style interrupt controller. Disabling the local APIC can be done in hardware
|
|
for the Pentium processors or in software for the P6 and P7 (Pentium4) family
|
|
processors.
|
|
|
|
The local APIC in the Pentium4 processors (called the xAPIC) is an extension
|
|
of the local APIC found in the P6 family processors. The primary difference
|
|
between the APIC architecture and xAPIC architecture is that with Pentium4
|
|
processors, the local xAPICs and IO xAPIC communicate with one another through
|
|
the processors system bus; whereas, with the P6 family processors, communication
|
|
between the local APICs and the IO APIC is handled through a dedicated 3-wire
|
|
APIC bus. Also, some of the architectural features of the local APIC have been
|
|
extended and/or modified in the local xAPIC.
|
|
|
|
The base address of the local APIC and IO APIC is taken from the BSP board.h.
|
|
It uses LOAPIC_BASE_ADRS and IOAPIC_BASE_ADRS.
|
|
This driver contains three routines for use. They are:
|
|
_loapic_init() initializes the Local APIC for the interrupt mode chosen.
|
|
_loapic_enable()/disable() enables / disables the Local APIC.
|
|
|
|
Local APIC is used in the Virtual Wire Mode: delivery mode ExtINT.
|
|
|
|
Virtual Wire Mode is one of three interrupt modes defined by the MP
|
|
specification. In this mode, interrupts are generated by the 8259A equivalent
|
|
PICs (if present) and delivered to the Boot Strap Processor by
|
|
the local APIC that is programmed to act as a "virtual Wire"; that
|
|
is, the local APIC is logically indistinguishable from a hardware
|
|
connection. This is a uniprocessor compatibility mode.
|
|
|
|
The local and IO APICs support interrupts in the range of 32 to 255.
|
|
Interrupt priority is implied by its vector, according to the following
|
|
relationship: "priority = vector / 16".
|
|
Here the quotient is rounded down to the nearest integer value to determine
|
|
the priority, with 1 being the lowest and 15 is the highest. Because vectors
|
|
0 through 31 are reserved for exclusive use by the processor, the priority of
|
|
user defined interrupts range from 2 to 15. A value of 15 in the Interrupt
|
|
Class field of the Task Priority Register (TPR) will mask off all interrupts,
|
|
which require interrupt service.
|
|
The P6 family processor's local APIC includes an in-service entry and a holding
|
|
entry for each priority level. To avoid losing interrupts, software should
|
|
allocate no more than 2 interrupt vectors per priority. The Pentium4 processor
|
|
expands this support of all acceptance of two interrupts per vector rather than
|
|
per priority level.
|
|
|
|
INCLUDE FILES: loapic.h
|
|
*/
|
|
|
|
#include <nanokernel.h>
|
|
#include <arch/cpu.h>
|
|
#include <stdint.h>
|
|
|
|
#include "board.h"
|
|
#include <toolchain.h>
|
|
#include <sections.h>
|
|
#include <drivers/loapic.h> /* public API declarations */
|
|
|
|
/* IA32_APIC_BASE MSR Bits */
|
|
|
|
#define LOAPIC_BASE_MASK 0xfffff000 /* LO APIC Base Addr mask */
|
|
#define LOAPIC_GLOBAL_ENABLE 0x00000800 /* LO APIC Global Enable */
|
|
#define LOAPIC_BSP 0x00000100 /* LO APIC BSP */
|
|
|
|
/* Local APIC ID Register Bits */
|
|
|
|
#define LOAPIC_ID_MASK 0x0f000000 /* LO APIC ID mask */
|
|
|
|
/* Local APIC Version Register Bits */
|
|
|
|
#define LOAPIC_VERSION_MASK 0x000000ff /* LO APIC Version mask */
|
|
#define LOAPIC_MAXLVT_MASK 0x00ff0000 /* LO APIC Max LVT mask */
|
|
#define LOAPIC_PENTIUM4 0x00000014 /* LO APIC in Pentium4 */
|
|
#define LOAPIC_LVT_PENTIUM4 5 /* LO APIC LVT - Pentium4 */
|
|
#define LOAPIC_LVT_P6 4 /* LO APIC LVT - P6 */
|
|
#define LOAPIC_LVT_P5 3 /* LO APIC LVT - P5 */
|
|
|
|
/* Local APIC Vector Table Bits */
|
|
|
|
#define LOAPIC_VECTOR 0x000000ff /* vectorNo */
|
|
#define LOAPIC_MODE 0x00000700 /* delivery mode */
|
|
#define LOAPIC_FIXED 0x00000000 /* delivery mode: FIXED */
|
|
#define LOAPIC_SMI 0x00000200 /* delivery mode: SMI */
|
|
#define LOAPIC_NMI 0x00000400 /* delivery mode: NMI */
|
|
#define LOAPIC_EXT 0x00000700 /* delivery mode: ExtINT */
|
|
#define LOAPIC_IDLE 0x00000000 /* delivery status: Idle */
|
|
#define LOAPIC_PEND 0x00001000 /* delivery status: Pend */
|
|
#define LOAPIC_HIGH 0x00000000 /* polarity: High */
|
|
#define LOAPIC_LOW 0x00002000 /* polarity: Low */
|
|
#define LOAPIC_REMOTE 0x00004000 /* remote IRR */
|
|
#define LOAPIC_EDGE 0x00000000 /* trigger mode: Edge */
|
|
#define LOAPIC_LEVEL 0x00008000 /* trigger mode: Level */
|
|
|
|
/* Local APIC Spurious-Interrupt Register Bits */
|
|
|
|
#define LOAPIC_ENABLE 0x100 /* APIC Enabled */
|
|
#define LOAPIC_FOCUS_DISABLE 0x200 /* Focus Processor Checking */
|
|
|
|
/* Local Vector's lock-unlock macro used in loApicIntLock/Unlock */
|
|
|
|
#define LOCKED_TIMER 0x01
|
|
#define LOCKED_PMC 0x02
|
|
#define LOCKED_LINT0 0x04
|
|
#define LOCKED_LINT1 0x08
|
|
#define LOCKED_ERROR 0x10
|
|
#define LOCKED_THERMAL 0x20
|
|
|
|
/* Interrupt Command Register: delivery mode and status */
|
|
|
|
#define MODE_FIXED 0x0 /* delivery mode: Fixed */
|
|
#define MODE_LOWEST 0x1 /* delivery mode: Lowest */
|
|
#define MODE_SMI 0x2 /* delivery mode: SMI */
|
|
#define MODE_NMI 0x4 /* delivery mode: NMI */
|
|
#define MODE_INIT 0x5 /* delivery mode: INIT */
|
|
#define MODE_STARTUP 0x6 /* delivery mode: StartUp */
|
|
#define STATUS_PEND 0x1000 /* delivery status: Pend */
|
|
|
|
/* MP Configuration Table Entries */
|
|
|
|
#define MP_ENTRY_CPU 0 /* Entry Type: CPU */
|
|
#define MP_ENTRY_BUS 1 /* Entry Type: BUS */
|
|
#define MP_ENTRY_IOAPIC 2 /* Entry Type: IO APIC */
|
|
#define MP_ENTRY_IOINTERRUPT 3 /* Entry Type: IO INT */
|
|
#define MP_ENTRY_LOINTERRUPT 4 /* Entry Type: LO INT */
|
|
|
|
/* Extended MP Configuration Table Entries */
|
|
|
|
#define EXT_MP_ENTRY_SASM 128 /* Entry Type: System Address Space Map */
|
|
#define EXT_MP_ENTRY_BHD 129 /* Entry Type: Bus Hierarchy Descriptor */
|
|
#define EXT_MP_ENTRY_CBASM 130 /* Entry Type: Comp Address Space Modifier */
|
|
|
|
/* MP Configuration Table CPU Flags */
|
|
|
|
#define MP_CPU_FLAGS_BP 0x02
|
|
|
|
/* IMCR related bits */
|
|
|
|
#define IMCR_ADRS 0x22 /* IMCR addr reg */
|
|
#define IMCR_DATA 0x23 /* IMCR data reg */
|
|
#define IMCR_REG_SEL 0x70 /* IMCR reg select */
|
|
#define IMCR_IOAPIC_ON 0x01 /* IMCR IOAPIC route enable */
|
|
#define IMCR_IOAPIC_OFF 0x00 /* IMCR IOAPIC route disable */
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _loapic_init - initialize the Local APIC or xAPIC
|
|
*
|
|
* This routine initializes Local APIC or xAPIC.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
*/
|
|
|
|
void _loapic_init(void)
|
|
{
|
|
int32_t loApicMaxLvt; /* local APIC Max LVT */
|
|
|
|
/* enable the Local APIC */
|
|
|
|
_loapic_enable();
|
|
|
|
loApicMaxLvt = (*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_VER) &
|
|
LOAPIC_MAXLVT_MASK) >>
|
|
16;
|
|
|
|
/* reset the DFR, TPR, TIMER_CONFIG, and TIMER_ICR */
|
|
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_DFR) = (int)0xffffffff;
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_TPR) = (int)0x0;
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_TIMER_CONFIG) = (int)0x0;
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_TIMER_ICR) = (int)0x0;
|
|
|
|
/* program Local Vector Table for the Virtual Wire Mode */
|
|
|
|
/* set LINT0: extInt, high-polarity, edge-trigger, not-masked */
|
|
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_LINT0) =
|
|
(*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_LINT0) &
|
|
~(LOAPIC_MODE | LOAPIC_LOW | LOAPIC_LEVEL | LOAPIC_LVT_MASKED)) |
|
|
(LOAPIC_EXT | LOAPIC_HIGH | LOAPIC_EDGE);
|
|
|
|
/* set LINT1: NMI, high-polarity, edge-trigger, not-masked */
|
|
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_LINT1) =
|
|
(*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_LINT1) &
|
|
~(LOAPIC_MODE | LOAPIC_LOW | LOAPIC_LEVEL | LOAPIC_LVT_MASKED)) |
|
|
(LOAPIC_NMI | LOAPIC_HIGH | LOAPIC_EDGE);
|
|
|
|
/* lock the Local APIC interrupts */
|
|
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_TIMER) = LOAPIC_LVT_MASKED;
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_ERROR) = LOAPIC_LVT_MASKED;
|
|
|
|
if (loApicMaxLvt >= LOAPIC_LVT_P6)
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_PMC) = LOAPIC_LVT_MASKED;
|
|
|
|
if (loApicMaxLvt >= LOAPIC_LVT_PENTIUM4)
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_THERMAL) =
|
|
LOAPIC_LVT_MASKED;
|
|
|
|
/* discard a pending interrupt if any */
|
|
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_EOI) = 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _loapic_enable - enable the Local xAPIC
|
|
*
|
|
* This routine enables the Local xAPIC.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void _loapic_enable(void)
|
|
{
|
|
int32_t oldLevel = irq_lock(); /* LOCK INTERRUPTS */
|
|
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_SVR) |= LOAPIC_ENABLE;
|
|
|
|
irq_unlock(oldLevel); /* UNLOCK INTERRUPTS */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _loapic_disable - disable the Local xAPIC
|
|
*
|
|
* This routine disables the Local xAPIC.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void _loapic_disable(void)
|
|
{
|
|
int32_t oldLevel = irq_lock(); /* LOCK INTERRUPTS */
|
|
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_SVR) &= ~LOAPIC_ENABLE;
|
|
|
|
irq_unlock(oldLevel); /* UNLOCK INTERRUPTS */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _loapic_eoi - send EOI (End Of Interrupt) signal to Local APIC
|
|
*
|
|
* This routine sends an EOI signal to the Local APIC's interrupting source.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void _loapic_eoi(unsigned int irq)
|
|
{
|
|
ARG_UNUSED(irq);
|
|
*(volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_EOI) = 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _loapic_int_vec_set - set the vector field in the specified RTE
|
|
*
|
|
* This routine is utilized by the BSP provided routined _SysIntVecAllocate()
|
|
* which in turn is provided to support the irq_connect() API. Once
|
|
* a vector has been allocated, this routine is invoked to update the LVT
|
|
* entry associated with <irq> with the vector.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void _loapic_int_vec_set(unsigned int irq, /* IRQ number of the
|
|
interrupt */
|
|
unsigned int vector /* vector to copy
|
|
into the LVT */
|
|
)
|
|
{
|
|
volatile int *pLvt; /* pointer to local vector table */
|
|
int32_t oldLevel; /* previous interrupt lock level */
|
|
|
|
/*
|
|
* The following mappings are used:
|
|
*
|
|
* IRQ0 -> LOAPIC_TIMER
|
|
* IRQ1 -> LOAPIC_THERMAL
|
|
* IRQ2 -> LOAPIC_PMC
|
|
* IRQ3 -> LOAPIC_LINT0
|
|
* IRQ4 -> LOAPIC_LINT1
|
|
* IRQ5 -> LOAPIC_ERROR
|
|
*
|
|
* It's assumed that LVTs are spaced by 0x10 bytes
|
|
*/
|
|
|
|
pLvt = (volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_TIMER + (irq * 0x10));
|
|
|
|
/* update the 'vector' bits in the LVT */
|
|
|
|
oldLevel = irq_lock();
|
|
*pLvt = (*pLvt & ~LOAPIC_VECTOR) | vector;
|
|
irq_unlock(oldLevel);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _loapic_irq_enable - enable an individual LOAPIC interrupt (IRQ)
|
|
*
|
|
* This routine clears the interrupt mask bit in the LVT for the specified IRQ
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void _loapic_irq_enable(unsigned int irq /* IRQ number of
|
|
the interrupt */
|
|
)
|
|
{
|
|
volatile int *pLvt; /* pointer to local vector table */
|
|
int32_t oldLevel; /* previous interrupt lock level */
|
|
|
|
/*
|
|
* See the comments in _LoApicLvtVecSet() regarding IRQ to LVT mappings
|
|
* and ths assumption concerning LVT spacing.
|
|
*/
|
|
|
|
pLvt = (volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_TIMER + (irq * 0x10));
|
|
|
|
/* clear the mask bit in the LVT */
|
|
|
|
oldLevel = irq_lock();
|
|
*pLvt = *pLvt & ~LOAPIC_LVT_MASKED;
|
|
irq_unlock(oldLevel);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _loapic_irq_disable - disable an individual LOAPIC interrupt (IRQ)
|
|
*
|
|
* This routine clears the interrupt mask bit in the LVT for the specified IRQ
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void _loapic_irq_disable(unsigned int irq /* IRQ number of the
|
|
interrupt */
|
|
)
|
|
{
|
|
volatile int *pLvt; /* pointer to local vector table */
|
|
int32_t oldLevel; /* previous interrupt lock level */
|
|
|
|
/*
|
|
* See the comments in _LoApicLvtVecSet() regarding IRQ to LVT mappings
|
|
* and ths assumption concerning LVT spacing.
|
|
*/
|
|
|
|
pLvt = (volatile int *)(LOAPIC_BASE_ADRS + LOAPIC_TIMER + (irq * 0x10));
|
|
|
|
/* set the mask bit in the LVT */
|
|
|
|
oldLevel = irq_lock();
|
|
*pLvt = *pLvt | LOAPIC_LVT_MASKED;
|
|
irq_unlock(oldLevel);
|
|
}
|