- add APIC support for the SCC

- seems to work, only the APIC timer doesn't work!


git-svn-id: http://svn.lfbs.rwth-aachen.de/svn/scc/trunk/MetalSVM@273 315a16e6-25f9-4109-90ae-ca3045a26c18
This commit is contained in:
stefan 2010-11-26 05:33:02 +00:00
parent 1cfc42c5a6
commit bc67c946af
7 changed files with 155 additions and 45 deletions

View file

@ -87,6 +87,8 @@ typedef struct {
typedef struct {
uint32_t pid;
uint32_t tile_frequency; // in MHz
uint32_t router_frequency; // in MHz
mem_region_t private_mem[SCC_PMEM_REGIONS];
} scc_info_t;

View file

@ -34,7 +34,7 @@
#define APIC_TPR 0x0080 // Task Priority Regster
#define APIC_EOI 0x00B0 // EOI Register
#define APIC_SVR 0x00F0 // Spurious Interrupt Vector Register
#define APIC_LVT_T 0x0320 // LVT Thermal Sensor Register (P4/Xeon only)
#define APIC_LVT_T 0x0320 // LVT Timer Register
#define APIC_LVT_PMC 0x0340 // LVT Performance Monitoring Counters Register
#define APIC_LINT0 0x0350 // LVT LINT0 Register
#define APIC_LINT1 0x0360 // LVT LINT1 Register
@ -45,8 +45,14 @@
static apic_mp_t* apic_mp = NULL;
static apic_config_table_t* apic_config = NULL;
static uint32_t lapic = 0;
static uint32_t ncores = 1;
static inline void apic_write(uint32_t addr, uint32_t value)
{
asm volatile ("movl (%%eax), %%edx; movl %%ebx, (%%eax)" :: "a"(addr), "b"(value) : "%edx");
}
#ifndef CONFIG_MULTIBOOT
static unsigned int* search_apic(unsigned int base, unsigned int limit) {
unsigned int *ptr;
@ -65,7 +71,8 @@ static unsigned int* search_apic(unsigned int base, unsigned int limit) {
*/
void apic_eoi(void)
{
*((uint32_t*) (apic_config->lapic+APIC_EOI)) = 0;
if (lapic)
apic_write(lapic+APIC_EOI, 0);
}
/*
@ -74,6 +81,7 @@ void apic_eoi(void)
*/
int apic_calibration(void)
{
#ifndef CONFIG_ROCKCREEK
uint64_t ticks, old;
uint32_t diff;
@ -86,23 +94,55 @@ int apic_calibration(void)
while((ticks = get_clock_tick()) - old == 0)
;
*((uint32_t*) (apic_config->lapic+APIC_DCR)) = 0xB; // set it to 1 clock increments
*((uint32_t*) (apic_config->lapic+APIC_LVT_T)) = 0x20030; // connects the timer to 48 and enables it
*((uint32_t*) (apic_config->lapic+APIC_ICR)) = 0xFFFFFFFF;
apic_write(lapic+APIC_DCR, 0xB); // set it to 1 clock increments
apic_write(lapic+APIC_LVT_T, 0x20030); // connects the timer to 48 and enables it
apic_write(lapic+APIC_ICR), 0xFFFFFFFF);
/* wait 3 time slices to determine a ICR */
while(get_clock_tick() - ticks < 3)
;
diff = 0xFFFFFFFF - *((uint32_t*) (apic_config->lapic+APIC_CCR));
diff = 0xFFFFFFFF - *((uint32_t*) (lapic+APIC_CCR));
*((uint32_t*) (apic_config->lapic+APIC_DCR)) = 0xB; // set it to 1 clock increments
*((uint32_t*) (apic_config->lapic+APIC_LVT_T)) = 0x20030; // connects the timer to 48 and enables it
*((uint32_t*) (apic_config->lapic+APIC_ICR)) = diff / 3;
apic_write(lapic+APIC_DCR, 0xB); // set it to 1 clock increments
apic_write(lapic+APIC_LVT_T, 0x20030); // connects the timer to 48 and enables it
apic_write(lapic+APIC_ICR, diff / 3);
// Now, MetalSVM is able to use the APIC => Therefore, we disable the PIC
outportb(0xA1, 0xFF);
outportb(0x21, 0xFF);
#else
/*
* On the SCC, we already know the processor frequency
* and possess no PIC timer. Therfore, we use the rdtsc to
* to calibrate the APIC timer.
*/
uint64_t start, end, ticks;
uint32_t diff;
if (!has_apic())
return -ENXIO;
apic_write(lapic+APIC_DCR, 0xB); // set it to 1 clock increments
apic_write(lapic+APIC_LVT_T, 0x20030); // connects the timer to 48 and enables it
apic_write(lapic+APIC_ICR, 0xFFFFFFFF);
/* wait 3 time slices to determine a ICR */
start = rdtsc();
do {
flush_pipeline();
end = rdtsc();
ticks = end > start ? end - start : start - end;
} while(ticks < 3*scc_info.tile_frequency*1000000 / TIMER_FREQ);
diff = 0xFFFFFFFF - *((uint32_t*) (lapic+APIC_CCR));
apic_write(lapic+APIC_DCR, 0xB); // set it to 1 clock increments
apic_write(lapic+APIC_LVT_T, 0x20030); // connects the timer to 48 and enables it
apic_write(lapic+APIC_ICR, diff / 3);
#endif
kprintf("APIC calibration detects an ICR of 0x%x\n", diff / 3);
return 0;
}
@ -110,7 +150,7 @@ int apic_calibration(void)
static int apic_probe(void)
{
size_t addr;
uint32_t i, count, dummy;
uint32_t i, count;
// searching MP signature in the reserved memory areas
#ifdef CONFIG_MULTIBOOT
@ -133,28 +173,28 @@ static int apic_probe(void)
mmap++;
}
}
found_mp:
#else
apic_mp = (apic_mp_t*) search_apic(0xF0000, 0x100000);
if (!apic_mp)
apic_mp = (apic_mp_t*) search_apic(0x9F000, 0xA0000);
#endif
found_mp:
if (!apic_mp)
goto out;
goto no_mp;
kprintf("System uses Multiprocessing Specification 1.%d\n", apic_mp->version);
kprintf("MP features 1: %d\n", apic_mp->features[0]);
if (apic_mp->features[0]) {
kputs("Currently, MetalSVM supports only multiprocessing via the MP config tables!\n");
goto out;
goto no_mp;
}
apic_config = (apic_config_table_t*) apic_mp->mp_config;
if (!apic_config || strncmp((void*) &apic_config->signature, "PCMP", 4) !=0) {
kputs("Invalid MP config table\n");
goto out;
goto no_mp;
}
addr = (size_t) apic_config;
@ -172,34 +212,45 @@ found_mp:
if (count > MAX_CORES) {
kputs("Found too many cores! Increase the macro MAX_CORES!\n");
goto out;
goto no_mp;
}
ncores = count;
i = *((uint32_t*) (apic_config->lapic+APIC_VERSION));
kprintf("Found APIC at 0x%x\n", apic_config->lapic);
check_lapic:
if (apic_config) {
lapic = apic_config->lapic;
} else {
uint32_t eax, edx, dummy;
cpuid(0x1, &eax, &dummy, &dummy, &edx);
if (edx & (1 << 9))
lapic = 0xFEE00000;
kprintf("Processor familiy %u, model %u, stepping %u\n", (eax>>8)&0xF, (eax>>4)&0xF, eax&0xF);
}
if (!lapic)
goto out;
i = *((uint32_t*) (lapic+APIC_VERSION));
kprintf("Found APIC at 0x%x\n", lapic);
kprintf("Maximum LVT Entry: 0x%x\n", (i >> 16) & 0xFF);
kprintf("APIC Version: 0x%x\n", i & 0xFF);
cpuid(0x1, &i, &dummy, &dummy, &dummy);
if (!(i & (1 << 5))) {
kputs("Unable to use Machine-Specific Registers (MSR)\n");
goto out;
}
if (!(rdmsr(0x1B) & (1 << 11))) {
kputs("Unable to use APIC Global Enable flag!\n");
goto out;
}
return 0;
out:
apic_mp = NULL;
apic_config = NULL;
lapic = 0;
ncores = 1;
return -ENXIO;
no_mp:
apic_mp = NULL;
apic_config = NULL;
ncores = 1;
goto check_lapic;
}
int apic_init(void)
@ -210,18 +261,18 @@ int apic_init(void)
if (!ret)
return ret;
*((uint32_t*) (apic_config->lapic+APIC_TPR)) = 0x20; // inhibit softint delivery
*((uint32_t*) (apic_config->lapic+APIC_LVT_T)) = 0x10000; // disable timer interrupt
*((uint32_t*) (apic_config->lapic+APIC_LVT_PMC)) = 0x10000; // disable performance counter interrupt
*((uint32_t*) (apic_config->lapic+APIC_LINT0)) = 0x31; // connect LINT0 to idt entry 49
*((uint32_t*) (apic_config->lapic+APIC_LINT1)) = 0x32; // connect LINT1 to idt entry 50
*((uint32_t*) (apic_config->lapic+APIC_LVT_ER)) = 0x33; // connect error to idt entry 51
*((uint32_t*) (apic_config->lapic+APIC_SVR)) = 0x134; // enable the apic and connect to the idt entry 52
apic_write(lapic+APIC_TPR, 0x00); // allow all interrupts
apic_write(lapic+APIC_LVT_T, 0x10000); // disable timer interrupt
apic_write(lapic+APIC_LVT_PMC, 0x10000);// disable performance counter interrupt
apic_write(lapic+APIC_LINT0, 0x31); // connect LINT0 to idt entry 49
apic_write(lapic+APIC_LINT1, 0x32); // connect LINT1 to idt entry 50
apic_write(lapic+APIC_LVT_ER, 0x33); // connect error to idt entry 51
apic_write(lapic+APIC_SVR, 0x17F); // enable the apic and connect to the idt entry 207
return 0;
}
int has_apic(void)
{
return ((apic_mp != NULL) && (apic_config != NULL));
return (lapic != 0);
}

View file

@ -718,7 +718,7 @@ apic_svr:
; Therefore, the interrupt flag (IF) is already cleared.
; cli
push byte 0
push byte 52
push byte 127
jmp irq_common_stub

View file

@ -51,7 +51,7 @@ extern void apic_lint1(void);
extern void apic_error(void);
extern void apic_svr(void);
#define MAX_HANDLERS 32
#define MAX_HANDLERS 256
/*
* This array is actually an array of function pointers. We use
@ -93,6 +93,7 @@ int irq_uninstall_handler(unsigned int irq)
*/
static int irq_remap(void)
{
#ifndef CONFIG_ROCKCREEK
outportb(0x20, 0x11);
outportb(0xA0, 0x11);
outportb(0x21, 0x20);
@ -103,6 +104,7 @@ static int irq_remap(void)
outportb(0xA1, 0x01);
outportb(0x21, 0x0);
outportb(0xA1, 0x0);
#endif
return 0;
}
@ -158,7 +160,7 @@ static int irq_install(void)
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(51, (unsigned)apic_error, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
idt_set_gate(52, (unsigned)apic_svr, KERNEL_CODE_SELECTOR,
idt_set_gate(127, (unsigned)apic_svr, KERNEL_CODE_SELECTOR,
IDT_FLAG_PRESENT|IDT_FLAG_RING0|IDT_FLAG_32BIT|IDT_FLAG_INTTRAP);
}
@ -194,6 +196,11 @@ void irq_handler(struct state *s)
/* This is a blank function pointer */
void (*handler) (struct state * s);
if (s->int_no - 32 > MAX_HANDLERS) {
apic_eoi();
return;
}
/*
* Find out if we have a custom handler to run for this
* IRQ and then finally, run it

View file

@ -22,6 +22,7 @@
#include <metalsvm/tasks.h>
#include <asm/irqflags.h>
#include <asm/isrs.h>
#include <asm/apic.h>
#include <asm/idt.h>
/*
@ -172,9 +173,12 @@ void fault_handler(struct state *s)
kputs(" Exception.\n");
/* Now, we signalize that we have handled the interrupt */
outportb(0x20, 0x20);
irq_enable();
if (has_apic())
apic_eoi();
else
outportb(0x20, 0x20);
irq_enable();
abort();
}
}

View file

@ -18,25 +18,29 @@
*/
#include <metalsvm/stdio.h>
#include <metalsvm/errno.h>
#include <asm/scc.h>
#include <asm/io.h>
#ifdef CONFIG_ROCKCREEK
#define CCR_INTR_ACTIVE 0x02
scc_info_t scc_info;
int scc_init(void)
{
int tmp, x, y, z;
uint32_t x, y, z;
uint32_t tmp;
kprintf("Initialize Rock Creek!\n");
tmp = *((int*) (CRB_OWN+MYTILEID));
tmp = *((uint32_t*) (CRB_OWN+MYTILEID));
x=(tmp>>3) & 0x0f; // bits 06:03
y=(tmp>>7) & 0x0f; // bits 10:07
z=(tmp ) & 0x07; // bits 02:00
scc_info.pid = PID(x, y, z);
kprintf("SCC Processor Id: %d (%d,%d,%d)\n", scc_info.pid, x, y, z);
kprintf("SCC Processor Id: %u (%u,%u,%u)\n", scc_info.pid, x, y, z);
/* default values for 16 GB system */
scc_info.private_mem[0].low = 0x00;
@ -44,6 +48,42 @@ int scc_init(void)
scc_info.private_mem[1].low = 0xFF000000;
scc_info.private_mem[1].high = 0xFFFFFFFF;
tmp = *((uint32_t*) (CRB_OWN+GCBCFG));
tmp = (tmp & 0x3FFFFFF) >> 7;
//kprintf("Own GCBCFG is 0x%x\n", tmp);
if (tmp == 0x70E1) {
scc_info.tile_frequency = 800;
scc_info.router_frequency = 1600;
} else {
scc_info.tile_frequency = 533;
scc_info.router_frequency = 800;
}
kprintf("The default tile frequency is %u MHz\nThe default router frequency is %u MHz\n",
scc_info.tile_frequency, scc_info.router_frequency);
if (z == 0)
tmp = *((uint32_t*) (CRB_OWN+GLCFG0));
else if (z == 1)
tmp = *((uint32_t*) (CRB_OWN+GLCFG1));
else
tmp = 0;
/* set INTR to enable maskable interrupts */
tmp = tmp | CCR_INTR_ACTIVE;
if (z == 0)
*((uint32_t*) (CRB_OWN+GLCFG0)) = tmp;
else if (z == 1)
*((uint32_t*) (CRB_OWN+GLCFG1)) = tmp;
/* reload core configuration */
tmp = 0;
if (z == 0)
tmp = *((uint32_t*) (CRB_OWN+GLCFG0));
else if (z == 1)
tmp = *((uint32_t*) (CRB_OWN+GLCFG1));
kprintf("Core Configuration %u: 0x%x\n", z, tmp);
return 0;
}

View file

@ -87,6 +87,11 @@ int timer_init(void)
irq_install_handler(0, timer_handler);
irq_install_handler(16, timer_handler);
/*
* The Rock Creek processor doesn't posseess an tradional PIC.
* Therefore, we have not to configure the PIC timer.
*/
#ifndef CONFIG_ROCKCREEK
outportb(0x43, 0x34);
/* before we write to 0x40, we wait some time */
start = rdtsc();
@ -98,6 +103,7 @@ int timer_init(void)
while(rdtsc() - start < 1000000)
;
outportb(0x40, LATCH(TIMER_FREQ) >> 8); /* high byte */
#endif
return 0;
}