562 lines
18 KiB
C
562 lines
18 KiB
C
/* loApicIntr.c - Intel Pentium[234] Local APIC/xAPIC driver */
|
|
|
|
/*
|
|
* Copyright 1984-2008 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute, modify or otherwise make use
|
|
* of this software may be licensed only pursuant to the terms
|
|
* of an applicable Wind River license agreement.
|
|
*/
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
01p,27aug08,jpb Renamed VSB header file
|
|
01o,18jun08,jpb Renamed _WRS_VX_SMP to _WRS_CONFIG_SMP. Added include path
|
|
for kernel configurations options set in vsb.
|
|
01n,15mar07,scm correct mask in loApicIpi...
|
|
01m,07dec06,scm smp:adjust loApicIpi for shorthand support...
|
|
01l,15nov06,scm adjust loApicIpi for shortHand delivery...
|
|
01k,13oct06,scm AP work...
|
|
01j,09oct06,scm remove sysBp dependencey for SMP...
|
|
01i,06sep06,scm debug for SMP...
|
|
01h,26jul06,scm update for pentium/xeon support...
|
|
01g,01apr04,dmh add support for multiple APICs
|
|
01f,15may02,hdn changed apicId 8 bit to 16 bit, added DBG_MSG.
|
|
added Error Status Register checking
|
|
01e,08mar02,hdn updated loApicInit() and loApicIpi()
|
|
added loApicEnable() for HTT (spr 73738)
|
|
01d,29nov01,hdn doc update for 5.5
|
|
01c,21jun01,hdn added support for Pentium4 Local xAPIC
|
|
moved show routines to loApicIntrShow.c
|
|
01b,25mar98,hdn re-written.
|
|
01a,20jun97,sub written.
|
|
*/
|
|
|
|
/*
|
|
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.
|
|
- In multiple-processor systems, it communicates with an external IO APIC
|
|
chip. The external IO APIC receives external interrupt events from the
|
|
system and interprocessor interrupts from the processors on the system bus
|
|
and distributes them to the processors on the system bus. 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, and also manages interprocessor interrupts and remote APIC register
|
|
reads. 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 MP configuration
|
|
table (see Intel MP Specification Version 1.4) or the IA32_APIC_BASE MSR.
|
|
It uses LOAPIC_BASE and IOAPIC_INDEX_BASE defined in the BSP, if it is not able
|
|
to find the addresses. This driver contains three routines for use. They are:
|
|
loApicInit() initializes the Local APIC for the interrupt mode chosen.
|
|
loApicEnable() enables or disables the Local APIC.
|
|
loApicIpi() delivers the inter processor interrupt to the specified local APIC.
|
|
|
|
Local APIC is used in the Virtual Wire Mode (define VIRTUAL_WIRE_MODE in the
|
|
BSP) and the Symmetric IO Mode (define SYMMETRIC_IO_MODE in the BSP), but not
|
|
in the PIC Mode which is the default interrupt mode and uses 8259A PIC.
|
|
|
|
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, but 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.
|
|
|
|
Symmetric IO Mode is one of three interrupt modes defined by the MP
|
|
specification. In this mode, the local and IO APICs are fully functional,
|
|
and interrupts are generated and delivered to the processors by the APICs.
|
|
Any interrupt can be delivered to any processor. This is the only
|
|
multiprocessor interrupt 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
|
|
*/
|
|
|
|
/* includes */
|
|
|
|
#include <vsbConfig.h>
|
|
|
|
#include "drv/intrCtl/loApic.h"
|
|
|
|
/* defines */
|
|
|
|
#undef LOAPIC_DBG
|
|
|
|
#ifdef LOAPIC_DBG
|
|
# define LOAPIC_DBG_MSG(STR,VALUE) logMsg(STR,VALUE,0,0,0,0,0);
|
|
#else
|
|
# define LOAPIC_DBG_MSG(STR,VALUE)
|
|
#endif /* IPI_DEBUG */
|
|
|
|
|
|
/* externs */
|
|
|
|
IMPORT CPUID sysCpuId; /* CPUID structure */
|
|
IMPORT UINT sysProcessor; /* CPU family */
|
|
|
|
#ifndef _WRS_CONFIG_SMP
|
|
IMPORT BOOL sysBp; /* TRUE(default) for BP, FALSE for AP */
|
|
#endif /* !_WRS_CONFIG_SMP */
|
|
|
|
/* globals */
|
|
|
|
UINT32 loApicBase = LOAPIC_BASE; /* def Local APIC addr */
|
|
UINT32 loApicId; /* local APIC Id */
|
|
UINT32 loApicVersion; /* local APIC Version */
|
|
UINT32 loApicMaxLvt; /* local APIC Max LVT */
|
|
UINT32 loApicBusy = 0; /* counter for not sending IPI */
|
|
|
|
/* locals */
|
|
|
|
LOCAL UINT32 sysIntMaskLocalApic = 0;
|
|
LOCAL UINT32 loApicOldSvr = 0; /* original SVR */
|
|
LOCAL UINT32 loApicOldLint0 = 0; /* original LINT0 */
|
|
LOCAL UINT32 loApicOldLint1 = 0; /* original LINT1 */
|
|
|
|
/* forward declarations */
|
|
|
|
LOCAL void loApicIntEoi (INT32 irq);
|
|
|
|
#ifdef _WRS_CONFIG_SMP
|
|
IMPORT int sysCpuNumGet (); /* define in sysLib.c of BSP */
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* loApicInit - initialize the Local APIC or xAPIC
|
|
*
|
|
* This routine initializes Local APIC or xAPIC.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
*/
|
|
|
|
void loApicInit (void)
|
|
{
|
|
UINT32 apicBase[2];
|
|
|
|
/* override loApicBase, sysBp from MSR if available */
|
|
|
|
if (sysCpuId.featuresEdx & CPUID_APIC)
|
|
{
|
|
pentiumMsrGet (IA32_APIC_BASE, (long long int *)&apicBase);
|
|
loApicBase = apicBase[0] & LOAPIC_BASE_MASK; /* override */
|
|
#ifndef _WRS_CONFIG_SMP
|
|
sysBp = apicBase[0] & LOAPIC_BSP ? TRUE : FALSE; /* override */
|
|
#endif /* !_WRS_CONFIG_SMP */
|
|
|
|
/* enable the Local APIC explicitly */
|
|
|
|
apicBase[0] |= LOAPIC_GLOBAL_ENABLE; /* set E(nable) bit */
|
|
pentiumMsrSet (IA32_APIC_BASE, (long long int *)&apicBase);
|
|
}
|
|
|
|
/* remember the original state : SVR, LINT0, LINT1 for now */
|
|
|
|
loApicOldSvr = *(int *)(loApicBase + LOAPIC_SVR);
|
|
loApicOldLint0 = *(int *)(loApicBase + LOAPIC_LINT0);
|
|
loApicOldLint1 = *(int *)(loApicBase + LOAPIC_LINT1);
|
|
|
|
/* enable the Local APIC */
|
|
|
|
loApicEnable (TRUE);
|
|
*(int *)(loApicBase + LOAPIC_SVR) |= LOAPIC_FOCUS_DISABLE |
|
|
INT_NUM_LOAPIC_SPURIOUS;
|
|
|
|
/* get the Local APIC ID from Local APIC ID register */
|
|
|
|
loApicId = (*(int *)(loApicBase + LOAPIC_ID) & LOAPIC_ID_MASK) >> 24;
|
|
|
|
/* get the Local APIC Version from Local APIC Version register */
|
|
|
|
loApicVersion = *(int *)(loApicBase + LOAPIC_VER) & LOAPIC_VERSION_MASK;
|
|
loApicMaxLvt = (*(int *)(loApicBase + LOAPIC_VER) & LOAPIC_MAXLVT_MASK)
|
|
>> 16;
|
|
|
|
/* reset the DFR, TPR, TIMER_CONFIG, and TIMER_ICR */
|
|
|
|
*(int *)(loApicBase + LOAPIC_DFR) = (int)0xffffffff;
|
|
*(int *)(loApicBase + LOAPIC_TPR) = (int)0x0;
|
|
*(int *)(loApicBase + LOAPIC_TIMER_CONFIG) = (int)0x0;
|
|
*(int *)(loApicBase + LOAPIC_TIMER_ICR) = (int)0x0;
|
|
|
|
#if defined(VIRTUAL_WIRE_MODE)
|
|
|
|
/* program Local Vector Table for the Virtual Wire Mode */
|
|
|
|
#ifndef _WRS_CONFIG_SMP
|
|
if (sysBp)
|
|
#else
|
|
if (sysCpuNumGet() == 0)
|
|
#endif /* !_WRS_CONFIG_SMP */
|
|
{
|
|
/* set LINT0: extInt, high-polarity, edge-trigger, not-masked */
|
|
|
|
*(int *)(loApicBase + LOAPIC_LINT0) =
|
|
(*(int *)(loApicBase + LOAPIC_LINT0) &
|
|
~(LOAPIC_MODE | LOAPIC_LOW | LOAPIC_LEVEL | LOAPIC_MASK)) |
|
|
(LOAPIC_EXT | LOAPIC_HIGH | LOAPIC_EDGE);
|
|
|
|
/* set LINT1: NMI, high-polarity, edge-trigger, not-masked */
|
|
|
|
*(int *)(loApicBase + LOAPIC_LINT1) =
|
|
(*(int *)(loApicBase + LOAPIC_LINT1) &
|
|
~(LOAPIC_MODE | LOAPIC_LOW | LOAPIC_LEVEL | LOAPIC_MASK)) |
|
|
(LOAPIC_NMI | LOAPIC_HIGH | LOAPIC_EDGE);
|
|
}
|
|
else
|
|
{
|
|
*(int *)(loApicBase + LOAPIC_LINT0) = LOAPIC_MASK;
|
|
*(int *)(loApicBase + LOAPIC_LINT1) = LOAPIC_MASK;
|
|
}
|
|
|
|
#elif defined(SYMMETRIC_IO_MODE)
|
|
|
|
/* program Local Vector Table for the Symmetric IO Mode */
|
|
|
|
*(int *)(loApicBase + LOAPIC_LINT0) = LOAPIC_MASK;
|
|
*(int *)(loApicBase + LOAPIC_LINT1) = LOAPIC_MASK;
|
|
|
|
#endif /* defined(VIRTUAL_WIRE_MODE) */
|
|
|
|
/* lock the Local APIC interrupts */
|
|
|
|
*(int *)(loApicBase + LOAPIC_TIMER) = LOAPIC_MASK;
|
|
*(int *)(loApicBase + LOAPIC_ERROR) = LOAPIC_MASK;
|
|
|
|
if (loApicMaxLvt >= LOAPIC_LVT_P6)
|
|
*(int *)(loApicBase + LOAPIC_PMC) = LOAPIC_MASK;
|
|
|
|
if (loApicMaxLvt >= LOAPIC_LVT_PENTIUM4)
|
|
*(int *)(loApicBase + LOAPIC_THERMAL) = LOAPIC_MASK;
|
|
|
|
/* discard a pending interrupt if any */
|
|
|
|
*(int *)(loApicBase + LOAPIC_EOI) = 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* loApicEnable - enable or disable the Local xAPIC
|
|
*
|
|
* This routine enables or disables the Local xAPIC. If the parameter is TRUE,
|
|
* the Local xAPIC is enabled. If the parameter is FALSE, the Local xAPIC
|
|
* is disabled.
|
|
*
|
|
* RETURNS: N/A
|
|
*/
|
|
|
|
void loApicEnable
|
|
(
|
|
BOOL enable /* enable the Local xAPIC */
|
|
)
|
|
{
|
|
INT32 oldLevel;
|
|
BOOL loApicImcr = FALSE; /* TRUE if IMCR exist (MP Table) */
|
|
MP_FPS *pFps = mpApicFps;
|
|
|
|
/* check if the IMCR exists or not */
|
|
|
|
loApicImcr = (pFps->featureByte[1] & 0x80) ? TRUE : FALSE;
|
|
|
|
oldLevel = intLock (); /* LOCK INTERRUPTS */
|
|
|
|
/* enable or disable the Local APIC */
|
|
|
|
if (enable)
|
|
{
|
|
/* if the original mode is PIC mode, enable the Local APIC */
|
|
|
|
if ((loApicOldSvr & LOAPIC_ENABLE) == 0)
|
|
{
|
|
*(int *)(loApicBase + LOAPIC_SVR) |= LOAPIC_ENABLE;
|
|
}
|
|
|
|
#if defined (SYMMETRIC_IO_MODE)
|
|
|
|
/* switch to SYMMETRIC_IO mode from PIC/VIRTUAL_WIRE mode */
|
|
|
|
if (loApicImcr)
|
|
{
|
|
sysOutByte (IMCR_ADRS, IMCR_REG_SEL);
|
|
sysOutByte (IMCR_DATA, IMCR_IOAPIC_ON);
|
|
}
|
|
|
|
#endif /* defined (SYMMETRIC_IO_MODE) */
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
#if defined (SYMMETRIC_IO_MODE)
|
|
|
|
/* switch from SYMMETRIC_IO mode to PIC/VIRTUAL_WIRE mode */
|
|
|
|
if (loApicImcr)
|
|
{
|
|
sysOutByte (IMCR_ADRS, IMCR_REG_SEL);
|
|
sysOutByte (IMCR_DATA, IMCR_IOAPIC_OFF);
|
|
}
|
|
|
|
#endif /* defined (SYMMETRIC_IO_MODE) */
|
|
|
|
/* if the original mode is PIC mode, disable the Local APIC */
|
|
|
|
if ((loApicOldSvr & LOAPIC_ENABLE) == 0)
|
|
{
|
|
*(int *)(loApicBase + LOAPIC_SVR) &= ~LOAPIC_ENABLE;
|
|
}
|
|
else
|
|
{
|
|
/* restore the original value */
|
|
|
|
*(int *)(loApicBase + LOAPIC_SVR) = loApicOldSvr;
|
|
*(int *)(loApicBase + LOAPIC_LINT0) = loApicOldLint0;
|
|
*(int *)(loApicBase + LOAPIC_LINT1) = loApicOldLint1;
|
|
}
|
|
}
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPTS */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* loApicIntLock - lock out all Local APIC interrupts
|
|
*
|
|
* This routine locks out all Local APIC interrupts
|
|
* It should be called in the interrupt disable state(IF bit is 0).
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* SEE ALSO: loApicIntUnlock()
|
|
*
|
|
* ARGSUSED0
|
|
*/
|
|
|
|
LOCAL void loApicIntLock (void)
|
|
{
|
|
|
|
if (!(*(int *)(loApicBase + LOAPIC_TIMER) & LOAPIC_MASK))
|
|
{
|
|
sysIntMaskLocalApic |= LOCKED_TIMER;
|
|
*(int *)(loApicBase + LOAPIC_TIMER) |= LOAPIC_MASK;
|
|
}
|
|
if (!(*(int *)(loApicBase + LOAPIC_LINT0) & LOAPIC_MASK))
|
|
{
|
|
sysIntMaskLocalApic |= LOCKED_LINT0;
|
|
*(int *)(loApicBase + LOAPIC_LINT0) |= LOAPIC_MASK;
|
|
}
|
|
if (!(*(int *)(loApicBase + LOAPIC_LINT1) & LOAPIC_MASK))
|
|
{
|
|
sysIntMaskLocalApic |= LOCKED_LINT1;
|
|
*(int *)(loApicBase + LOAPIC_LINT1) |= LOAPIC_MASK;
|
|
}
|
|
if (!(*(int *)(loApicBase + LOAPIC_ERROR) & LOAPIC_MASK))
|
|
{
|
|
sysIntMaskLocalApic |= LOCKED_ERROR;
|
|
*(int *)(loApicBase + LOAPIC_ERROR) |= LOAPIC_MASK;
|
|
}
|
|
if ((loApicMaxLvt >= LOAPIC_LVT_P6) &&
|
|
(!(*(int *)(loApicBase + LOAPIC_PMC) & LOAPIC_MASK)))
|
|
{
|
|
sysIntMaskLocalApic |= LOCKED_PMC;
|
|
*(int *)(loApicBase + LOAPIC_PMC) |= LOAPIC_MASK;
|
|
}
|
|
if ((loApicMaxLvt >= LOAPIC_LVT_PENTIUM4) &&
|
|
(!(*(int *)(loApicBase + LOAPIC_THERMAL) & LOAPIC_MASK)))
|
|
{
|
|
sysIntMaskLocalApic |= LOCKED_THERMAL;
|
|
*(int *)(loApicBase + LOAPIC_THERMAL) |= LOAPIC_MASK;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* loApicIntUnlock - unlock the Local APIC interrupts
|
|
*
|
|
* This routine unlocks the Local APIC interrupts
|
|
* It should be called in the interrupt disable state(IF bit is 0).
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* SEE ALSO: loApicIntLock()
|
|
*
|
|
* ARGSUSED0
|
|
*/
|
|
|
|
LOCAL void loApicIntUnlock (void)
|
|
{
|
|
|
|
if (sysIntMaskLocalApic & LOCKED_TIMER)
|
|
*(int *)(loApicBase + LOAPIC_TIMER) &= ~LOAPIC_MASK;
|
|
if (sysIntMaskLocalApic & LOCKED_LINT0)
|
|
*(int *)(loApicBase + LOAPIC_LINT0) &= ~LOAPIC_MASK;
|
|
if (sysIntMaskLocalApic & LOCKED_LINT1)
|
|
*(int *)(loApicBase + LOAPIC_LINT1) &= ~LOAPIC_MASK;
|
|
if (sysIntMaskLocalApic & LOCKED_ERROR)
|
|
*(int *)(loApicBase + LOAPIC_ERROR) &= ~LOAPIC_MASK;
|
|
if ((loApicMaxLvt >= LOAPIC_LVT_P6) &&
|
|
(sysIntMaskLocalApic & LOCKED_PMC))
|
|
*(int *)(loApicBase + LOAPIC_PMC) &= ~LOAPIC_MASK;
|
|
if ((loApicMaxLvt >= LOAPIC_LVT_PENTIUM4) &&
|
|
(sysIntMaskLocalApic & LOCKED_THERMAL))
|
|
*(int *)(loApicBase + LOAPIC_THERMAL) &= ~LOAPIC_MASK;
|
|
|
|
sysIntMaskLocalApic = 0; /* reset the flags */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* loApicIntEoi - 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
|
|
*/
|
|
|
|
LOCAL void loApicIntEoi
|
|
(
|
|
INT32 irqNo /* INIIN number to send EOI */
|
|
)
|
|
{
|
|
#ifdef SYS_INT_DEBUG
|
|
extern UINT32 sysIntCount [];
|
|
sysIntCount [irqNo]++;
|
|
#endif /* SYS_INT_DEBUG */
|
|
|
|
*(int *)(loApicBase + LOAPIC_EOI) = 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* loApicIpi - generate a Inter Processor Interrupt
|
|
*
|
|
* This routine delivers an Inter-Processor interrupt to the specified CPU
|
|
* in the specified mode, with the specified vectorNo. Level should always
|
|
* be 1 except INIT De-assert mode.
|
|
*
|
|
* RETURNS: OK, or ERROR if the delivery status is not idle
|
|
*/
|
|
|
|
STATUS loApicIpi
|
|
(
|
|
INT32 apicId, /* APIC ID number to send the interrupt */
|
|
INT32 shortHand, /* destination short hand */
|
|
INT32 trigger, /* trigger mode. 0: edge or 1: level */
|
|
INT32 level, /* level. 0: de-assert or 1: assert */
|
|
INT32 destMode, /* destination mode. 0: physical or 1: logical */
|
|
INT32 deliMode, /* delivery mode. 000: fixed ... 110: startup */
|
|
INT32 vectorNo /* vector number */
|
|
)
|
|
{
|
|
UINT32 icrHi = (*(int *)(loApicBase + LOAPIC_ICRHI)) & 0x00ffffff;
|
|
UINT32 icrLo = (*(int *)(loApicBase + LOAPIC_ICRLO)) & 0xfff32000;
|
|
|
|
/* ShortHand, Trigger, Level, DeliveryMode and Vector */
|
|
|
|
icrLo |= ((shortHand << 18) & 0xc0000) |
|
|
((trigger << 15) & 0x8000) |
|
|
((level << 14) & 0x4000) |
|
|
((destMode << 11) & 0x800) |
|
|
((deliMode << 8) & 0x700) |
|
|
(vectorNo & 0xff);
|
|
|
|
/* Destination Apic Id */
|
|
|
|
icrHi |= (apicId << 24) & 0xff000000;
|
|
|
|
/* is the interrupt idle? */
|
|
|
|
if (*(int *)(loApicBase + LOAPIC_ICRLO) & STATUS_PEND)
|
|
{
|
|
loApicBusy++;
|
|
LOAPIC_DBG_MSG ("ipi: LOAPIC busy 0x%x\n",
|
|
*(int *)(loApicBase + LOAPIC_ESR))
|
|
return (ERROR);
|
|
}
|
|
|
|
/* reset the error status register */
|
|
|
|
#ifndef _WRS_CONFIG_SMP
|
|
*(int *)(loApicBase + LOAPIC_ESR) = 0;
|
|
*(int *)(loApicBase + LOAPIC_ESR) = 0;
|
|
#endif /* !_WRS_CONFIG_SMP */
|
|
|
|
if (*(int *)(loApicBase + LOAPIC_ESR) != 0)
|
|
{
|
|
LOAPIC_DBG_MSG ("ipi: 0. ESR=0x%x\n",
|
|
*(int *)(loApicBase + LOAPIC_ESR))
|
|
return (ERROR);
|
|
}
|
|
|
|
/* Send it! */
|
|
|
|
#ifdef _WRS_CONFIG_SMP
|
|
/* Can send shorthand message by performing a single
|
|
* 32-bit memory write to the low half of the ICR.
|
|
*/
|
|
if ((shortHand == 0x00) || (destMode == 0x01))
|
|
*(int *)(loApicBase + LOAPIC_ICRHI) = icrHi;
|
|
#else
|
|
*(int *)(loApicBase + LOAPIC_ICRHI) = icrHi;
|
|
#endif
|
|
*(int *)(loApicBase + LOAPIC_ICRLO) = icrLo;
|
|
|
|
/* Wait till interrupt is sent */
|
|
|
|
while (*(int *)(loApicBase + LOAPIC_ICRLO) & STATUS_PEND)
|
|
if (*(int *)(loApicBase + LOAPIC_ESR) != 0)
|
|
{
|
|
LOAPIC_DBG_MSG ("ipi: 1. ESR=0x%x\n",
|
|
*(int *)(loApicBase + LOAPIC_ESR))
|
|
return (ERROR);
|
|
}
|
|
|
|
return(OK);
|
|
}
|
|
|