/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h" #include "mp.h" enum { /* Local APIC registers */ LapicID = 0x0020, /* ID */ LapicVER = 0x0030, /* Version */ LapicTPR = 0x0080, /* Task Priority */ LapicAPR = 0x0090, /* Arbitration Priority */ LapicPPR = 0x00A0, /* Processor Priority */ LapicEOI = 0x00B0, /* EOI */ LapicLDR = 0x00D0, /* Logical Destination */ LapicDFR = 0x00E0, /* Destination Format */ LapicSVR = 0x00F0, /* Spurious Interrupt Vector */ LapicISR = 0x0100, /* Interrupt Status (8 registers) */ LapicTMR = 0x0180, /* Trigger Mode (8 registers) */ LapicIRR = 0x0200, /* Interrupt Request (8 registers) */ LapicESR = 0x0280, /* Error Status */ LapicICRLO = 0x0300, /* Interrupt Command */ LapicICRHI = 0x0310, /* Interrupt Command [63:32] */ LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */ LapicPCINT = 0x0340, /* Performance Counter LVT */ LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */ LapicLINT1 = 0x0360, /* Local Vector Table 2 (LINT1) */ LapicERROR = 0x0370, /* Local Vector Table 3 (ERROR) */ LapicTICR = 0x0380, /* Timer Initial Count */ LapicTCCR = 0x0390, /* Timer Current Count */ LapicTDCR = 0x03E0, /* Timer Divide Configuration */}; enum { /* LapicSVR */ LapicENABLE = 0x00000100, /* Unit Enable */ LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */}; enum { /* LapicICRLO */ /* [14] IPI Trigger Mode Level (RW) */ LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */ LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */ /* [17:16] Remote Read Status */ LapicINVALID = 0x00000000, /* Invalid */ LapicWAIT = 0x00010000, /* In-Progress */ LapicVALID = 0x00020000, /* Valid */ /* [19:18] Destination Shorthand */ LapicFIELD = 0x00000000, /* No shorthand */ LapicSELF = 0x00040000, /* Self is single destination */ LapicALLINC = 0x00080000, /* All including self */ LapicALLEXC = 0x000C0000, /* All Excluding self */}; enum { /* LapicESR */ LapicSENDCS = 0x00000001, /* Send CS Error */ LapicRCVCS = 0x00000002, /* Receive CS Error */ LapicSENDACCEPT = 0x00000004, /* Send Accept Error */ LapicRCVACCEPT = 0x00000008, /* Receive Accept Error */ LapicSENDVECTOR = 0x00000020, /* Send Illegal Vector */ LapicRCVVECTOR = 0x00000040, /* Receive Illegal Vector */ LapicREGISTER = 0x00000080, /* Illegal Register Address */}; enum { /* LapicTIMER */ /* [17] Timer Mode (RW) */ LapicONESHOT = 0x00000000, /* One-shot */ LapicPERIODIC = 0x00020000, /* Periodic */ /* [19:18] Timer Base (RW) */ LapicCLKIN = 0x00000000, /* use CLKIN as input */ LapicTMBASE = 0x00040000, /* use TMBASE */ LapicDIVIDER = 0x00080000, /* use output of the divider */}; enum { /* LapicTDCR */ LapicX2 = 0x00000000, /* divide by 2 */ LapicX4 = 0x00000001, /* divide by 4 */ LapicX8 = 0x00000002, /* divide by 8 */ LapicX16 = 0x00000003, /* divide by 16 */ LapicX32 = 0x00000008, /* divide by 32 */ LapicX64 = 0x00000009, /* divide by 64 */ LapicX128 = 0x0000000A, /* divide by 128 */ LapicX1 = 0x0000000B, /* divide by 1 */}; static ulong* lapicbase; struct{ uvlong hz; ulong max; ulong min; ulong div;} lapictimer; static ulonglapicr(int r){ if(lapicbase == 0) panic("lapicr: no lapic"); return *(lapicbase+(r/sizeof(*lapicbase)));} static voidlapicw(int r, ulong data){ if(lapicbase == 0) panic("lapicw: no lapic"); *(lapicbase+(r/sizeof(*lapicbase))) = data; data = *(lapicbase+(LapicID/sizeof(*lapicbase))); USED(data);} voidlapiconline(void){ /* * Reload the timer to de-synchronise the processors, * then lower the task priority to allow interrupts to be * accepted by the APIC. */ microdelay((TK2MS(1)*1000/conf.nmach) * m->machno); lapicw(LapicTICR, lapictimer.max); lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER)); lapicw(LapicTPR, 0);} /* * use the i8253 clock to figure out our lapic timer rate. */static voidlapictimerinit(void){ uvlong x, v, hz; v = m->cpuhz/1000; lapicw(LapicTDCR, LapicX1); lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER)); if(lapictimer.hz == 0ULL){ x = fastticks(&hz); x += hz/10; lapicw(LapicTICR, 0xffffffff); do{ v = fastticks(nil); }while(v < x); lapictimer.hz = (0xffffffffUL-lapicr(LapicTCCR))*10; lapictimer.max = lapictimer.hz/HZ; lapictimer.min = lapictimer.hz/(100*HZ); if(lapictimer.hz > hz-(hz/10)){ if(lapictimer.hz > hz+(hz/10)) panic("lapic clock %lld > cpu clock > %lld\n", lapictimer.hz, hz); lapictimer.hz = hz; } assert(lapictimer.hz != 0); lapictimer.div = hz/lapictimer.hz; }} voidlapicinit(Apic* apic){ ulong dfr, ldr, lvt; if(lapicbase == 0) lapicbase = apic->addr; if(lapicbase == 0) { print("lapicinit: no lapic\n"); return; } /* * These don't really matter in Physical mode; * set the defaults anyway. */ if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0) dfr = 0xf0000000; else dfr = 0xffffffff; ldr = 0x00000000; lapicw(LapicDFR, dfr); lapicw(LapicLDR, ldr); lapicw(LapicTPR, 0xff); lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS)); lapictimerinit(); /* * Some Pentium revisions have a bug whereby spurious * interrupts are generated in the through-local mode. */ switch(m->cpuidax & 0xFFF){ case 0x526: /* stepping cB1 */ case 0x52B: /* stepping E0 */ case 0x52C: /* stepping cC0 */ wrmsr(0x0E, 1<<14); /* TR12 */ break; } /* * Set the local interrupts. It's likely these should just be * masked off for SMP mode as some Pentium Pros have problems if * LINT[01] are set to ExtINT. * Acknowledge any outstanding interrupts. lapicw(LapicLINT0, apic->lintr[0]); lapicw(LapicLINT1, apic->lintr[1]); */ lapiceoi(0); lvt = (lapicr(LapicVER)>>16) & 0xFF; if(lvt >= 4) lapicw(LapicPCINT, ApicIMASK|(VectorPIC+IrqPCINT)); lapicw(LapicERROR, VectorPIC+IrqERROR); lapicw(LapicESR, 0); lapicr(LapicESR); /* * Issue an INIT Level De-Assert to synchronise arbitration ID's. */ lapicw(LapicICRHI, 0); lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT); while(lapicr(LapicICRLO) & ApicDELIVS) ; /* * Do not allow acceptance of interrupts until all initialisation * for this processor is done. For the bootstrap processor this can be * early during initialisation. For the application processors this should * be after the bootstrap processor has lowered priority and is accepting * interrupts. lapicw(LapicTPR, 0); */} voidlapicstartap(Apic* apic, int v){ int i; ulong crhi; /* make apic's processor do a warm reset */ crhi = apic->apicno<<24; lapicw(LapicICRHI, crhi); lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT); microdelay(200); lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT); delay(10); /* assumes apic is not an 82489dx */ for(i = 0; i < 2; i++){ lapicw(LapicICRHI, crhi); /* make apic's processor start at v in real mode */ lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG)); microdelay(200); }} voidlapicerror(Ureg*, void*){ ulong esr; lapicw(LapicESR, 0); esr = lapicr(LapicESR); switch(m->cpuidax & 0xFFF){ case 0x526: /* stepping cB1 */ case 0x52B: /* stepping E0 */ case 0x52C: /* stepping cC0 */ return; } print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);} voidlapicspurious(Ureg*, void*){ print("cpu%d: lapicspurious\n", m->machno);} intlapicisr(int v){ ulong isr; isr = lapicr(LapicISR + (v/32)); return isr & (1<<(v%32));} intlapiceoi(int v){ lapicw(LapicEOI, 0); return v;} voidlapicicrw(ulong hi, ulong lo){ lapicw(LapicICRHI, hi); lapicw(LapicICRLO, lo);} voidioapicrdtr(Apic* apic, int sel, int* hi, int* lo){ ulong *iowin; iowin = apic->addr+(0x10/sizeof(ulong)); sel = IoapicRDT + 2*sel; lock(apic); *apic->addr = sel+1; if(hi) *hi = *iowin; *apic->addr = sel; if(lo) *lo = *iowin; unlock(apic);} voidioapicrdtw(Apic* apic, int sel, int hi, int lo){ ulong *iowin; iowin = apic->addr+(0x10/sizeof(ulong)); sel = IoapicRDT + 2*sel; lock(apic); *apic->addr = sel+1; *iowin = hi; *apic->addr = sel; *iowin = lo; unlock(apic);} voidioapicinit(Apic* apic, int apicno){ int hi, lo, v; ulong *iowin; /* * Initialise the I/O APIC. * The MultiProcessor Specification says it is the responsibility * of the O/S to set the APIC id. * Make sure interrupts are all masked off for now. */ iowin = apic->addr+(0x10/sizeof(ulong)); lock(apic); *apic->addr = IoapicVER; apic->mre = (*iowin>>16) & 0xFF; *apic->addr = IoapicID; *iowin = apicno<<24; unlock(apic); hi = 0; lo = ApicIMASK; for(v = 0; v <= apic->mre; v++) ioapicrdtw(apic, v, hi, lo);} voidlapictimerset(uvlong next){ vlong period; int x; x = splhi(); lock(&m->apictimerlock); period = lapictimer.max; if(next != 0){ period = next - fastticks(nil); if (lapictimer.div == 0) panic("lapictimerset: zero lapictimer.div"); period /= lapictimer.div; if(period < lapictimer.min) period = lapictimer.min; else if(period > lapictimer.max - lapictimer.min) period = lapictimer.max; } lapicw(LapicTICR, period); unlock(&m->apictimerlock); splx(x);} voidlapicclock(Ureg *u, void*){ /* * since the MTRR updates need to be synchronized across processors, * we want to do this within the clock tick. */ mtrrclock(); timerintr(u, 0);} voidlapicintron(void){ lapicw(LapicTPR, 0);} voidlapicintroff(void){ lapicw(LapicTPR, 0xFF);} voidlapicnmienable(void){ /* * On the one hand the manual says the vector information * is ignored if the delivery mode is NMI, and on the other * a "Receive Illegal Vector" should be generated for a * vector in the range 0 through 15. * Some implementations generate the error interrupt if the * NMI vector is invalid, so always give a valid value. */ if (lapicbase) lapicw(LapicPCINT, ApicNMI|(VectorPIC+IrqPCINT)); else print("lapicnmienable: no lapic\n");} voidlapicnmidisable(void){ if (lapicbase) lapicw(LapicPCINT, ApicIMASK|(VectorPIC+IrqPCINT)); else print("lapicnmidisable: no lapic\n");}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "mp.h"
enum { /* Local APIC registers */
LapicID = 0x0020, /* ID */
LapicVER = 0x0030, /* Version */
LapicTPR = 0x0080, /* Task Priority */
LapicAPR = 0x0090, /* Arbitration Priority */
LapicPPR = 0x00A0, /* Processor Priority */
LapicEOI = 0x00B0, /* EOI */
LapicLDR = 0x00D0, /* Logical Destination */
LapicDFR = 0x00E0, /* Destination Format */
LapicSVR = 0x00F0, /* Spurious Interrupt Vector */
LapicISR = 0x0100, /* Interrupt Status (8 registers) */
LapicTMR = 0x0180, /* Trigger Mode (8 registers) */
LapicIRR = 0x0200, /* Interrupt Request (8 registers) */
LapicESR = 0x0280, /* Error Status */
LapicICRLO = 0x0300, /* Interrupt Command */
LapicICRHI = 0x0310, /* Interrupt Command [63:32] */
LapicTIMER = 0x0320, /* Local Vector Table 0 (TIMER) */
LapicPCINT = 0x0340, /* Performance Counter LVT */
LapicLINT0 = 0x0350, /* Local Vector Table 1 (LINT0) */
LapicLINT1 = 0x0360, /* Local Vector Table 2 (LINT1) */
LapicERROR = 0x0370, /* Local Vector Table 3 (ERROR) */
LapicTICR = 0x0380, /* Timer Initial Count */
LapicTCCR = 0x0390, /* Timer Current Count */
LapicTDCR = 0x03E0, /* Timer Divide Configuration */
};
enum { /* LapicSVR */
LapicENABLE = 0x00000100, /* Unit Enable */
LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */
};
enum { /* LapicICRLO */
/* [14] IPI Trigger Mode Level (RW) */
LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */
LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */
/* [17:16] Remote Read Status */
LapicINVALID = 0x00000000, /* Invalid */
LapicWAIT = 0x00010000, /* In-Progress */
LapicVALID = 0x00020000, /* Valid */
/* [19:18] Destination Shorthand */
LapicFIELD = 0x00000000, /* No shorthand */
LapicSELF = 0x00040000, /* Self is single destination */
LapicALLINC = 0x00080000, /* All including self */
LapicALLEXC = 0x000C0000, /* All Excluding self */
};
enum { /* LapicESR */
LapicSENDCS = 0x00000001, /* Send CS Error */
LapicRCVCS = 0x00000002, /* Receive CS Error */
LapicSENDACCEPT = 0x00000004, /* Send Accept Error */
LapicRCVACCEPT = 0x00000008, /* Receive Accept Error */
LapicSENDVECTOR = 0x00000020, /* Send Illegal Vector */
LapicRCVVECTOR = 0x00000040, /* Receive Illegal Vector */
LapicREGISTER = 0x00000080, /* Illegal Register Address */
};
enum { /* LapicTIMER */
/* [17] Timer Mode (RW) */
LapicONESHOT = 0x00000000, /* One-shot */
LapicPERIODIC = 0x00020000, /* Periodic */
/* [19:18] Timer Base (RW) */
LapicCLKIN = 0x00000000, /* use CLKIN as input */
LapicTMBASE = 0x00040000, /* use TMBASE */
LapicDIVIDER = 0x00080000, /* use output of the divider */
};
enum { /* LapicTDCR */
LapicX2 = 0x00000000, /* divide by 2 */
LapicX4 = 0x00000001, /* divide by 4 */
LapicX8 = 0x00000002, /* divide by 8 */
LapicX16 = 0x00000003, /* divide by 16 */
LapicX32 = 0x00000008, /* divide by 32 */
LapicX64 = 0x00000009, /* divide by 64 */
LapicX128 = 0x0000000A, /* divide by 128 */
LapicX1 = 0x0000000B, /* divide by 1 */
};
static ulong* lapicbase;
struct
{
uvlong hz;
ulong max;
ulong min;
ulong div;
} lapictimer;
static ulong
lapicr(int r)
{
if(lapicbase == 0)
panic("lapicr: no lapic");
return *(lapicbase+(r/sizeof(*lapicbase)));
}
static void
lapicw(int r, ulong data)
{
if(lapicbase == 0)
panic("lapicw: no lapic");
*(lapicbase+(r/sizeof(*lapicbase))) = data;
data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
USED(data);
}
void
lapiconline(void)
{
/*
* Reload the timer to de-synchronise the processors,
* then lower the task priority to allow interrupts to be
* accepted by the APIC.
*/
microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
lapicw(LapicTICR, lapictimer.max);
lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
lapicw(LapicTPR, 0);
}
/*
* use the i8253 clock to figure out our lapic timer rate.
*/
static void
lapictimerinit(void)
{
uvlong x, v, hz;
v = m->cpuhz/1000;
lapicw(LapicTDCR, LapicX1);
lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
if(lapictimer.hz == 0ULL){
x = fastticks(&hz);
x += hz/10;
lapicw(LapicTICR, 0xffffffff);
do{
v = fastticks(nil);
}while(v < x);
lapictimer.hz = (0xffffffffUL-lapicr(LapicTCCR))*10;
lapictimer.max = lapictimer.hz/HZ;
lapictimer.min = lapictimer.hz/(100*HZ);
if(lapictimer.hz > hz-(hz/10)){
if(lapictimer.hz > hz+(hz/10))
panic("lapic clock %lld > cpu clock > %lld\n",
lapictimer.hz, hz);
lapictimer.hz = hz;
}
assert(lapictimer.hz != 0);
lapictimer.div = hz/lapictimer.hz;
}
}
void
lapicinit(Apic* apic)
{
ulong dfr, ldr, lvt;
if(lapicbase == 0)
lapicbase = apic->addr;
if(lapicbase == 0) {
print("lapicinit: no lapic\n");
return;
}
/*
* These don't really matter in Physical mode;
* set the defaults anyway.
*/
if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
dfr = 0xf0000000;
else
dfr = 0xffffffff;
ldr = 0x00000000;
lapicw(LapicDFR, dfr);
lapicw(LapicLDR, ldr);
lapicw(LapicTPR, 0xff);
lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
lapictimerinit();
/*
* Some Pentium revisions have a bug whereby spurious
* interrupts are generated in the through-local mode.
*/
switch(m->cpuidax & 0xFFF){
case 0x526: /* stepping cB1 */
case 0x52B: /* stepping E0 */
case 0x52C: /* stepping cC0 */
wrmsr(0x0E, 1<<14); /* TR12 */
break;
}
/*
* Set the local interrupts. It's likely these should just be
* masked off for SMP mode as some Pentium Pros have problems if
* LINT[01] are set to ExtINT.
* Acknowledge any outstanding interrupts.
lapicw(LapicLINT0, apic->lintr[0]);
lapicw(LapicLINT1, apic->lintr[1]);
*/
lapiceoi(0);
lvt = (lapicr(LapicVER)>>16) & 0xFF;
if(lvt >= 4)
lapicw(LapicPCINT, ApicIMASK|(VectorPIC+IrqPCINT));
lapicw(LapicERROR, VectorPIC+IrqERROR);
lapicw(LapicESR, 0);
lapicr(LapicESR);
/*
* Issue an INIT Level De-Assert to synchronise arbitration ID's.
*/
lapicw(LapicICRHI, 0);
lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
while(lapicr(LapicICRLO) & ApicDELIVS)
;
/*
* Do not allow acceptance of interrupts until all initialisation
* for this processor is done. For the bootstrap processor this can be
* early during initialisation. For the application processors this should
* be after the bootstrap processor has lowered priority and is accepting
* interrupts.
lapicw(LapicTPR, 0);
*/
}
void
lapicstartap(Apic* apic, int v)
{
int i;
ulong crhi;
/* make apic's processor do a warm reset */
crhi = apic->apicno<<24;
lapicw(LapicICRHI, crhi);
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
microdelay(200);
lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
delay(10);
/* assumes apic is not an 82489dx */
for(i = 0; i < 2; i++){
lapicw(LapicICRHI, crhi);
/* make apic's processor start at v in real mode */
lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG));
microdelay(200);
}
}
void
lapicerror(Ureg*, void*)
{
ulong esr;
lapicw(LapicESR, 0);
esr = lapicr(LapicESR);
switch(m->cpuidax & 0xFFF){
case 0x526: /* stepping cB1 */
case 0x52B: /* stepping E0 */
case 0x52C: /* stepping cC0 */
return;
}
print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
}
void
lapicspurious(Ureg*, void*)
{
print("cpu%d: lapicspurious\n", m->machno);
}
int
lapicisr(int v)
{
ulong isr;
isr = lapicr(LapicISR + (v/32));
return isr & (1<<(v%32));
}
int
lapiceoi(int v)
{
lapicw(LapicEOI, 0);
return v;
}
void
lapicicrw(ulong hi, ulong lo)
{
lapicw(LapicICRHI, hi);
lapicw(LapicICRLO, lo);
}
void
ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
{
ulong *iowin;
iowin = apic->addr+(0x10/sizeof(ulong));
sel = IoapicRDT + 2*sel;
lock(apic);
*apic->addr = sel+1;
if(hi)
*hi = *iowin;
*apic->addr = sel;
if(lo)
*lo = *iowin;
unlock(apic);
}
void
ioapicrdtw(Apic* apic, int sel, int hi, int lo)
{
ulong *iowin;
iowin = apic->addr+(0x10/sizeof(ulong));
sel = IoapicRDT + 2*sel;
lock(apic);
*apic->addr = sel+1;
*iowin = hi;
*apic->addr = sel;
*iowin = lo;
unlock(apic);
}
void
ioapicinit(Apic* apic, int apicno)
{
int hi, lo, v;
ulong *iowin;
/*
* Initialise the I/O APIC.
* The MultiProcessor Specification says it is the responsibility
* of the O/S to set the APIC id.
* Make sure interrupts are all masked off for now.
*/
iowin = apic->addr+(0x10/sizeof(ulong));
lock(apic);
*apic->addr = IoapicVER;
apic->mre = (*iowin>>16) & 0xFF;
*apic->addr = IoapicID;
*iowin = apicno<<24;
unlock(apic);
hi = 0;
lo = ApicIMASK;
for(v = 0; v <= apic->mre; v++)
ioapicrdtw(apic, v, hi, lo);
}
void
lapictimerset(uvlong next)
{
vlong period;
int x;
x = splhi();
lock(&m->apictimerlock);
period = lapictimer.max;
if(next != 0){
period = next - fastticks(nil);
if (lapictimer.div == 0)
panic("lapictimerset: zero lapictimer.div");
period /= lapictimer.div;
if(period < lapictimer.min)
period = lapictimer.min;
else if(period > lapictimer.max - lapictimer.min)
period = lapictimer.max;
}
lapicw(LapicTICR, period);
unlock(&m->apictimerlock);
splx(x);
}
void
lapicclock(Ureg *u, void*)
{
/*
* since the MTRR updates need to be synchronized across processors,
* we want to do this within the clock tick.
*/
mtrrclock();
timerintr(u, 0);
}
void
lapicintron(void)
{
lapicw(LapicTPR, 0);
}
void
lapicintroff(void)
{
lapicw(LapicTPR, 0xFF);
}
void
lapicnmienable(void)
{
/*
* On the one hand the manual says the vector information
* is ignored if the delivery mode is NMI, and on the other
* a "Receive Illegal Vector" should be generated for a
* vector in the range 0 through 15.
* Some implementations generate the error interrupt if the
* NMI vector is invalid, so always give a valid value.
*/
if (lapicbase)
lapicw(LapicPCINT, ApicNMI|(VectorPIC+IrqPCINT));
else
print("lapicnmienable: no lapic\n");
}
void
lapicnmidisable(void)
{
if (lapicbase)
lapicw(LapicPCINT, ApicIMASK|(VectorPIC+IrqPCINT));
else
print("lapicnmidisable: no lapic\n");
}