2006-07-12 19:19:24 +02:00
|
|
|
#include "types.h"
|
|
|
|
#include "mp.h"
|
|
|
|
#include "defs.h"
|
|
|
|
#include "param.h"
|
|
|
|
#include "x86.h"
|
|
|
|
#include "traps.h"
|
|
|
|
#include "mmu.h"
|
|
|
|
#include "proc.h"
|
|
|
|
|
2006-09-06 19:53:15 +02:00
|
|
|
enum { // Local APIC registers
|
|
|
|
LAPIC_ID = 0x0020, // ID
|
|
|
|
LAPIC_VER = 0x0030, // Version
|
|
|
|
LAPIC_TPR = 0x0080, // Task Priority
|
|
|
|
LAPIC_APR = 0x0090, // Arbitration Priority
|
|
|
|
LAPIC_PPR = 0x00A0, // Processor Priority
|
|
|
|
LAPIC_EOI = 0x00B0, // EOI
|
|
|
|
LAPIC_LDR = 0x00D0, // Logical Destination
|
|
|
|
LAPIC_DFR = 0x00E0, // Destination Format
|
|
|
|
LAPIC_SVR = 0x00F0, // Spurious Interrupt Vector
|
|
|
|
LAPIC_ISR = 0x0100, // Interrupt Status (8 registers)
|
|
|
|
LAPIC_TMR = 0x0180, // Trigger Mode (8 registers)
|
|
|
|
LAPIC_IRR = 0x0200, // Interrupt Request (8 registers)
|
|
|
|
LAPIC_ESR = 0x0280, // Error Status
|
|
|
|
LAPIC_ICRLO = 0x0300, // Interrupt Command
|
|
|
|
LAPIC_ICRHI = 0x0310, // Interrupt Command [63:32]
|
|
|
|
LAPIC_TIMER = 0x0320, // Local Vector Table 0 (TIMER)
|
|
|
|
LAPIC_PCINT = 0x0340, // Performance Counter LVT
|
|
|
|
LAPIC_LINT0 = 0x0350, // Local Vector Table 1 (LINT0)
|
|
|
|
LAPIC_LINT1 = 0x0360, // Local Vector Table 2 (LINT1)
|
|
|
|
LAPIC_ERROR = 0x0370, // Local Vector Table 3 (ERROR)
|
|
|
|
LAPIC_TICR = 0x0380, // Timer Initial Count
|
|
|
|
LAPIC_TCCR = 0x0390, // Timer Current Count
|
|
|
|
LAPIC_TDCR = 0x03E0, // Timer Divide Configuration
|
2006-07-12 19:19:24 +02:00
|
|
|
};
|
|
|
|
|
2006-09-06 19:53:15 +02:00
|
|
|
enum { // LAPIC_SVR
|
|
|
|
LAPIC_ENABLE = 0x00000100, // Unit Enable
|
|
|
|
LAPIC_FOCUS = 0x00000200, // Focus Processor Checking Disable
|
2006-07-12 19:19:24 +02:00
|
|
|
};
|
|
|
|
|
2006-09-06 19:53:15 +02:00
|
|
|
enum { // LAPIC_ICRLO
|
|
|
|
// [14] IPI Trigger Mode Level (RW)
|
|
|
|
LAPIC_DEASSERT = 0x00000000, // Deassert level-sensitive interrupt
|
|
|
|
LAPIC_ASSERT = 0x00004000, // Assert level-sensitive interrupt
|
2006-09-06 19:50:20 +02:00
|
|
|
|
|
|
|
// [17:16] Remote Read Status
|
2006-09-06 19:53:15 +02:00
|
|
|
LAPIC_INVALID = 0x00000000, // Invalid
|
|
|
|
LAPIC_WAIT = 0x00010000, // In-Progress
|
|
|
|
LAPIC_VALID = 0x00020000, // Valid
|
2006-09-06 19:50:20 +02:00
|
|
|
|
|
|
|
// [19:18] Destination Shorthand
|
2006-09-06 19:53:15 +02:00
|
|
|
LAPIC_FIELD = 0x00000000, // No shorthand
|
|
|
|
LAPIC_SELF = 0x00040000, // Self is single destination
|
|
|
|
LAPIC_ALLINC = 0x00080000, // All including self
|
|
|
|
LAPIC_ALLEXC = 0x000C0000, // All Excluding self
|
2006-07-12 19:19:24 +02:00
|
|
|
};
|
|
|
|
|
2006-09-06 19:53:15 +02:00
|
|
|
enum { // LAPIC_ESR
|
|
|
|
LAPIC_SENDCS = 0x00000001, // Send CS Error
|
|
|
|
LAPIC_RCVCS = 0x00000002, // Receive CS Error
|
|
|
|
LAPIC_SENDACCEPT = 0x00000004, // Send Accept Error
|
|
|
|
LAPIC_RCVACCEPT = 0x00000008, // Receive Accept Error
|
|
|
|
LAPIC_SENDVECTOR = 0x00000020, // Send Illegal Vector
|
|
|
|
LAPIC_RCVVECTOR = 0x00000040, // Receive Illegal Vector
|
|
|
|
LAPIC_REGISTER = 0x00000080, // Illegal Register Address
|
2006-07-12 19:19:24 +02:00
|
|
|
};
|
|
|
|
|
2006-09-06 19:53:15 +02:00
|
|
|
enum { // LAPIC_TIMER
|
|
|
|
// [17] Timer Mode (RW)
|
|
|
|
LAPIC_ONESHOT = 0x00000000, // One-shot
|
|
|
|
LAPIC_PERIODIC = 0x00020000, // Periodic
|
2006-07-12 19:19:24 +02:00
|
|
|
|
2006-09-06 19:50:20 +02:00
|
|
|
// [19:18] Timer Base (RW)
|
2006-09-06 19:53:15 +02:00
|
|
|
LAPIC_CLKIN = 0x00000000, // use CLKIN as input
|
|
|
|
LAPIC_TMBASE = 0x00040000, // use TMBASE
|
|
|
|
LAPIC_DIVIDER = 0x00080000, // use output of the divider
|
2006-07-12 19:19:24 +02:00
|
|
|
};
|
|
|
|
|
2006-09-06 19:53:15 +02:00
|
|
|
enum { // LAPIC_TDCR
|
|
|
|
LAPIC_X2 = 0x00000000, // divide by 2
|
|
|
|
LAPIC_X4 = 0x00000001, // divide by 4
|
|
|
|
LAPIC_X8 = 0x00000002, // divide by 8
|
|
|
|
LAPIC_X16 = 0x00000003, // divide by 16
|
|
|
|
LAPIC_X32 = 0x00000008, // divide by 32
|
|
|
|
LAPIC_X64 = 0x00000009, // divide by 64
|
|
|
|
LAPIC_X128 = 0x0000000A, // divide by 128
|
|
|
|
LAPIC_X1 = 0x0000000B, // divide by 1
|
2006-07-12 19:19:24 +02:00
|
|
|
};
|
|
|
|
|
2006-07-20 11:07:53 +02:00
|
|
|
uint *lapicaddr;
|
2006-07-12 19:19:24 +02:00
|
|
|
|
|
|
|
static int
|
|
|
|
lapic_read(int r)
|
|
|
|
{
|
|
|
|
return *(lapicaddr+(r/sizeof(*lapicaddr)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
lapic_write(int r, int data)
|
|
|
|
{
|
|
|
|
*(lapicaddr+(r/sizeof(*lapicaddr))) = data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2006-07-17 03:25:22 +02:00
|
|
|
lapic_timerinit(void)
|
2006-07-12 19:19:24 +02:00
|
|
|
{
|
2006-09-08 17:14:43 +02:00
|
|
|
if(!lapicaddr)
|
2006-09-08 17:07:45 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
lapic_write(LAPIC_TDCR, LAPIC_X1);
|
|
|
|
lapic_write(LAPIC_TIMER, LAPIC_CLKIN | LAPIC_PERIODIC |
|
|
|
|
(IRQ_OFFSET + IRQ_TIMER));
|
|
|
|
lapic_write(LAPIC_TCCR, 10000000);
|
|
|
|
lapic_write(LAPIC_TICR, 10000000);
|
2006-07-12 19:19:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-07-17 03:25:22 +02:00
|
|
|
lapic_timerintr(void)
|
2006-07-12 19:19:24 +02:00
|
|
|
{
|
2006-09-08 17:14:43 +02:00
|
|
|
if(lapicaddr)
|
2006-09-07 03:37:58 +02:00
|
|
|
lapic_write(LAPIC_EOI, 0);
|
2006-07-12 19:19:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
lapic_init(int c)
|
|
|
|
{
|
2006-07-20 11:07:53 +02:00
|
|
|
uint r, lvt;
|
2006-07-12 19:19:24 +02:00
|
|
|
|
2006-09-08 17:14:43 +02:00
|
|
|
if(!lapicaddr)
|
2006-09-07 03:37:58 +02:00
|
|
|
return;
|
|
|
|
|
2006-09-06 21:08:14 +02:00
|
|
|
lapic_write(LAPIC_DFR, 0xFFFFFFFF); // Set dst format register
|
|
|
|
r = (lapic_read(LAPIC_ID)>>24) & 0xFF; // Read APIC ID
|
|
|
|
lapic_write(LAPIC_LDR, (1<<r)<<24); // Set logical dst register to r
|
|
|
|
lapic_write(LAPIC_TPR, 0xFF); // No interrupts for now
|
|
|
|
|
|
|
|
// Enable APIC
|
|
|
|
lapic_write(LAPIC_SVR, LAPIC_ENABLE|(IRQ_OFFSET+IRQ_SPURIOUS));
|
2006-07-12 19:19:24 +02:00
|
|
|
|
2006-09-06 19:50:20 +02:00
|
|
|
// In virtual wire mode, set up the LINT0 and LINT1 as follows:
|
2006-07-12 19:19:24 +02:00
|
|
|
lapic_write(LAPIC_LINT0, APIC_IMASK | APIC_EXTINT);
|
|
|
|
lapic_write(LAPIC_LINT1, APIC_IMASK | APIC_NMI);
|
|
|
|
|
2006-09-06 21:08:14 +02:00
|
|
|
lapic_write(LAPIC_EOI, 0); // Ack any outstanding interrupts.
|
2006-07-12 19:19:24 +02:00
|
|
|
|
|
|
|
lvt = (lapic_read(LAPIC_VER)>>16) & 0xFF;
|
|
|
|
if(lvt >= 4)
|
|
|
|
lapic_write(LAPIC_PCINT, APIC_IMASK);
|
|
|
|
lapic_write(LAPIC_ERROR, IRQ_OFFSET+IRQ_ERROR);
|
|
|
|
lapic_write(LAPIC_ESR, 0);
|
|
|
|
lapic_read(LAPIC_ESR);
|
|
|
|
|
2006-09-06 19:50:20 +02:00
|
|
|
// Issue an INIT Level De-Assert to synchronise arbitration ID's.
|
2006-07-12 19:19:24 +02:00
|
|
|
lapic_write(LAPIC_ICRHI, 0);
|
2006-09-06 21:08:14 +02:00
|
|
|
lapic_write(LAPIC_ICRLO, LAPIC_ALLINC|APIC_LEVEL|
|
|
|
|
LAPIC_DEASSERT|APIC_INIT);
|
2006-07-12 19:19:24 +02:00
|
|
|
while(lapic_read(LAPIC_ICRLO) & APIC_DELIVS)
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-09-06 19:27:19 +02:00
|
|
|
lapic_enableintr(void)
|
2006-07-12 19:19:24 +02:00
|
|
|
{
|
2006-09-08 17:14:43 +02:00
|
|
|
if(lapicaddr)
|
2006-09-07 03:37:58 +02:00
|
|
|
lapic_write(LAPIC_TPR, 0);
|
2006-07-12 19:19:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-09-06 19:27:19 +02:00
|
|
|
lapic_disableintr(void)
|
2006-07-12 19:19:24 +02:00
|
|
|
{
|
2006-09-08 17:14:43 +02:00
|
|
|
if(lapicaddr)
|
2006-09-07 03:37:58 +02:00
|
|
|
lapic_write(LAPIC_TPR, 0xFF);
|
2006-07-12 19:19:24 +02:00
|
|
|
}
|
|
|
|
|
2006-08-04 20:12:31 +02:00
|
|
|
void
|
|
|
|
lapic_eoi(void)
|
|
|
|
{
|
2006-09-08 17:14:43 +02:00
|
|
|
if(lapicaddr)
|
2006-09-07 03:37:58 +02:00
|
|
|
lapic_write(LAPIC_EOI, 0);
|
2006-08-04 20:12:31 +02:00
|
|
|
}
|
|
|
|
|
2006-07-12 19:19:24 +02:00
|
|
|
int
|
|
|
|
cpu(void)
|
|
|
|
{
|
2006-09-07 03:37:58 +02:00
|
|
|
int x;
|
2006-09-08 17:14:43 +02:00
|
|
|
if(lapicaddr)
|
2006-09-07 03:37:58 +02:00
|
|
|
x = (lapic_read(LAPIC_ID)>>24) & 0xFF;
|
|
|
|
else
|
|
|
|
x = 0;
|
2006-08-08 21:58:06 +02:00
|
|
|
return x;
|
2006-07-12 19:19:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-07-20 11:07:53 +02:00
|
|
|
lapic_startap(uchar apicid, int v)
|
2006-07-12 19:19:24 +02:00
|
|
|
{
|
|
|
|
int crhi, i;
|
|
|
|
volatile int j = 0;
|
|
|
|
|
|
|
|
crhi = apicid<<24;
|
|
|
|
lapic_write(LAPIC_ICRHI, crhi);
|
2006-09-06 21:08:14 +02:00
|
|
|
lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_LEVEL|
|
|
|
|
LAPIC_ASSERT|APIC_INIT);
|
2006-07-12 19:19:24 +02:00
|
|
|
|
2006-09-06 19:27:19 +02:00
|
|
|
while(j++ < 10000) {;}
|
2006-09-06 21:08:14 +02:00
|
|
|
lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_LEVEL|
|
|
|
|
LAPIC_DEASSERT|APIC_INIT);
|
2006-07-12 19:19:24 +02:00
|
|
|
|
2006-09-06 19:27:19 +02:00
|
|
|
while(j++ < 1000000) {;}
|
2006-07-12 19:19:24 +02:00
|
|
|
|
|
|
|
// in p9 code, this was i < 2, which is what the spec says on page B-3
|
|
|
|
for(i = 0; i < 1; i++){
|
|
|
|
lapic_write(LAPIC_ICRHI, crhi);
|
2006-08-29 21:06:37 +02:00
|
|
|
lapic_write(LAPIC_ICRLO, LAPIC_FIELD|APIC_EDGE|APIC_STARTUP|(v/4096));
|
2006-09-06 19:27:19 +02:00
|
|
|
while(j++ < 100000) {;}
|
2006-07-12 19:19:24 +02:00
|
|
|
}
|
|
|
|
}
|