summaryrefslogtreecommitdiff
path: root/sys/src/9/pc/apic.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/pc/apic.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/pc/apic.c')
-rwxr-xr-xsys/src/9/pc/apic.c421
1 files changed, 421 insertions, 0 deletions
diff --git a/sys/src/9/pc/apic.c b/sys/src/9/pc/apic.c
new file mode 100755
index 000000000..ccaa8b5c7
--- /dev/null
+++ b/sys/src/9/pc/apic.c
@@ -0,0 +1,421 @@
+#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)
+{
+ return *(lapicbase+(r/sizeof(*lapicbase)));
+}
+
+static void
+lapicw(int r, ulong data)
+{
+ *(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;
+ }
+ lapictimer.div = hz/lapictimer.hz;
+ }
+}
+
+void
+lapicinit(Apic* apic)
+{
+ ulong dfr, ldr, lvt;
+
+ if(lapicbase == 0)
+ lapicbase = apic->addr;
+
+ /*
+ * 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);
+ 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 duing 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;
+
+ crhi = apic->apicno<<24;
+ lapicw(LapicICRHI, crhi);
+ lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
+ microdelay(200);
+ lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
+ delay(10);
+
+ for(i = 0; i < 2; i++){
+ lapicw(LapicICRHI, crhi);
+ 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);
+ 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)
+{
+ lapicw(LapicPCINT, ApicNMI);
+}
+
+void
+lapicnmidisable(void)
+{
+ lapicw(LapicPCINT, ApicIMASK);
+}