summaryrefslogtreecommitdiff
path: root/src/arch/i386/smp
diff options
context:
space:
mode:
authorEric Biederman <ebiederm@xmission.com>2003-04-24 06:25:08 +0000
committerEric Biederman <ebiederm@xmission.com>2003-04-24 06:25:08 +0000
commit5899fd82aa2f5c3855eb6630f702f3239b6b7015 (patch)
treee699faf63cd467933b0134f591291e702cf55c3b /src/arch/i386/smp
parent8ca8d7665d671e10d72b8fcb4d69121d75f7906e (diff)
downloadcoreboot-5899fd82aa2f5c3855eb6630f702f3239b6b7015.tar.xz
- Small step forward Linux boots and almost works...
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@795 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/arch/i386/smp')
-rw-r--r--src/arch/i386/smp/ioapic.c86
-rw-r--r--src/arch/i386/smp/mpspec.c3
-rw-r--r--src/arch/i386/smp/start_stop.c249
3 files changed, 336 insertions, 2 deletions
diff --git a/src/arch/i386/smp/ioapic.c b/src/arch/i386/smp/ioapic.c
new file mode 100644
index 0000000000..344b30d0dc
--- /dev/null
+++ b/src/arch/i386/smp/ioapic.c
@@ -0,0 +1,86 @@
+#include <console/console.h>
+#include <arch/ioapic.h>
+
+/* TODO: this must move to chip/intel */
+/* we have to do more than we thought. I assumed Linux would do all the
+ * interesting parts, and I was wrong.
+ */
+struct ioapicreg {
+ unsigned int reg;
+ unsigned int value_low, value_high;
+};
+struct ioapicreg ioapicregvalues[] = {
+#define ALL (0xff << 24)
+#define NONE (0)
+#define DISABLED (1 << 16)
+#define ENABLED (0 << 16)
+#define TRIGGER_EDGE (0 << 15)
+#define TRIGGER_LEVEL (1 << 15)
+#define POLARITY_HIGH (0 << 13)
+#define POLARITY_LOW (1 << 13)
+#define PHYSICAL_DEST (0 << 11)
+#define LOGICAL_DEST (1 << 11)
+#define ExtINT (7 << 8)
+#define NMI (4 << 8)
+#define SMI (2 << 8)
+#define INT (1 << 8)
+ /* mask, trigger, polarity, destination, delivery, vector */
+ {0x00, DISABLED, NONE},
+ {0x01, DISABLED, NONE},
+ {0x02, DISABLED, NONE},
+ {0x03, DISABLED, NONE},
+ {0x04, DISABLED, NONE},
+ {0x05, DISABLED, NONE},
+ {0x06, DISABLED, NONE},
+ {0x07, DISABLED, NONE},
+ {0x08, DISABLED, NONE},
+ {0x09, DISABLED, NONE},
+ {0x0a, DISABLED, NONE},
+ {0x0b, DISABLED, NONE},
+ {0x0c, DISABLED, NONE},
+ {0x0d, DISABLED, NONE},
+ {0x0e, DISABLED, NONE},
+ {0x0f, DISABLED, NONE},
+ {0x10, DISABLED, NONE},
+ {0x11, DISABLED, NONE},
+ {0x12, DISABLED, NONE},
+ {0x13, DISABLED, NONE},
+ {0x14, DISABLED, NONE},
+ {0x14, DISABLED, NONE},
+ {0x15, DISABLED, NONE},
+ {0x16, DISABLED, NONE},
+ {0x17, DISABLED, NONE},
+};
+
+void setup_ioapic(void)
+{
+ int i;
+ unsigned long value_low, value_high;
+ unsigned long nvram = 0xfec00000;
+ volatile unsigned long *l;
+ struct ioapicreg *a = ioapicregvalues;
+
+ l = (unsigned long *) nvram;
+#if defined(i786)
+ /* For the pentium 4 and above apic deliver their interrupts
+ * on the front side bus, enable that.
+ */
+ l[0] = 0x03;
+ l[4] = 1;
+#endif /* i786 */
+ for (i = 0; i < sizeof(ioapicregvalues) / sizeof(ioapicregvalues[0]);
+ i++, a++) {
+ l[0] = (a->reg * 2) + 0x10;
+ l[4] = a->value_low;
+ value_low = l[4];
+ l[0] = (a->reg *2) + 0x11;
+ l[4] = a->value_high;
+ value_high = l[4];
+ if ((i==0) && (value_low == 0xffffffff)) {
+ printk_warning("IO APIC not responding.\n");
+ return;
+ }
+ printk_spew("for IRQ, reg 0x%08x value 0x%08x 0x%08x\n",
+ a->reg, a->value_low, a->value_high);
+ }
+}
diff --git a/src/arch/i386/smp/mpspec.c b/src/arch/i386/smp/mpspec.c
index 3bb5fa1985..ff935bb996 100644
--- a/src/arch/i386/smp/mpspec.c
+++ b/src/arch/i386/smp/mpspec.c
@@ -113,14 +113,13 @@ void smp_write_processors(struct mp_config_table *mc,
unsigned long cpu_flag;
if(initial_apicid[i]==-1)
continue;
- cpu_flag = MPC_CPU_ENABLED
+ cpu_flag = MPC_CPU_ENABLED;
if (processor_map[i] & CPU_BOOTPROCESSOR) {
cpu_flag |= MPC_CPU_BOOTPROCESSOR;
}
smp_write_processor(mc, cpu_apicid, apic_version,
cpu_flag, cpu_features, cpu_feature_flags
);
-
}
}
diff --git a/src/arch/i386/smp/start_stop.c b/src/arch/i386/smp/start_stop.c
new file mode 100644
index 0000000000..018ec30705
--- /dev/null
+++ b/src/arch/i386/smp/start_stop.c
@@ -0,0 +1,249 @@
+#include <smp/start_stop.h>
+#include <arch/smp/mpspec.h>
+#include <cpu/p6/apic.h>
+#include <delay.h>
+#include <string.h>
+
+#ifndef START_CPU_SEG
+#define START_CPU_SEG 0x90000
+#endif
+#if (START_CPU_SEG&0xffff) != 0
+#error START_CPU_SEG must be 64k aligned
+#endif
+
+static inline void hlt(void)
+{
+ asm("hlt");
+ return;
+}
+
+unsigned long this_processors_id(void)
+{
+ return apic_read(APIC_ID) >> 24;
+}
+
+int processor_index(unsigned long apicid)
+{
+ int i;
+ for(i = 0; i < MAX_CPUS; i++) {
+ if (initial_apicid[i] == apicid) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void stop_cpu(unsigned long apicid)
+{
+ int timeout;
+ unsigned long send_status;
+
+ /* send an APIC INIT to myself */
+ apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
+ apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT);
+
+ /* wait for the ipi send to finish */
+ printk_spew("Waiting for send to finish...\n");
+ timeout = 0;
+ do {
+ printk_spew("+");
+ udelay(100);
+ send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
+ } while (send_status && (timeout++ < 1000));
+ if (timeout >= 1000) {
+ printk_err("timed out\n");
+ }
+ mdelay(10);
+
+ printk_spew("Deasserting INIT.\n");
+ /* Deassert the APIC INIT */
+ apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
+ apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
+
+ printk_spew("Waiting for send to finish...\n");
+ timeout = 0;
+ do {
+ printk_spew("+");
+ udelay(100);
+ send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
+ } while (send_status && (timeout++ < 1000));
+ if (timeout >= 1000) {
+ printk_err("timed out\n");
+ }
+
+ while(1) {
+ hlt();
+ }
+}
+
+/* This is a lot more paranoid now, since Linux can NOT handle
+ * being told there is a CPU when none exists. So any errors
+ * will return 0, meaning no CPU.
+ *
+ * We actually handling that case by noting which cpus startup
+ * and not telling anyone about the ones that dont.
+ */
+int start_cpu(unsigned long apicid)
+{
+ int timeout;
+ unsigned long send_status, accept_status, start_eip;
+ int j, num_starts, maxlvt;
+ extern char _secondary_start[];
+
+ /*
+ * Starting actual IPI sequence...
+ */
+
+ printk_spew("Asserting INIT.\n");
+
+ /*
+ * Turn INIT on target chip
+ */
+ apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
+
+ /*
+ * Send IPI
+ */
+
+ apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT
+ | APIC_DM_INIT);
+
+ printk_spew("Waiting for send to finish...\n");
+ timeout = 0;
+ do {
+ printk_spew("+");
+ udelay(100);
+ send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
+ } while (send_status && (timeout++ < 1000));
+ if (timeout >= 1000) {
+ printk_err("CPU %d: First apic write timed out. Disabling\n",
+ apicid);
+ // too bad.
+ printk_err("ESR is 0x%x\n", apic_read(APIC_ESR));
+ if (apic_read(APIC_ESR)) {
+ printk_err("Try to reset ESR\n");
+ apic_write_around(APIC_ESR, 0);
+ printk_err("ESR is 0x%x\n", apic_read(APIC_ESR));
+ }
+ return 0;
+ }
+ mdelay(10);
+
+ printk_spew("Deasserting INIT.\n");
+
+ /* Target chip */
+ apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
+
+ /* Send IPI */
+ apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
+
+ printk_spew("Waiting for send to finish...\n");
+ timeout = 0;
+ do {
+ printk_spew("+");
+ udelay(100);
+ send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
+ } while (send_status && (timeout++ < 1000));
+ if (timeout >= 1000) {
+ printk_err("CPU %d: Second apic write timed out. Disabling\n",
+ apicid);
+ // too bad.
+ return 0;
+ }
+
+ start_eip = (unsigned long)_secondary_start;
+ printk_spew("start_eip=0x%08lx\n", start_eip);
+
+ num_starts = 2;
+
+ /*
+ * Run STARTUP IPI loop.
+ */
+ printk_spew("#startup loops: %d.\n", num_starts);
+
+ maxlvt = 4;
+
+ for (j = 1; j <= num_starts; j++) {
+ printk_spew("Sending STARTUP #%d to %u.\n", j, apicid);
+ apic_read_around(APIC_SPIV);
+ apic_write(APIC_ESR, 0);
+ apic_read(APIC_ESR);
+ printk_spew("After apic_write.\n");
+
+ /*
+ * STARTUP IPI
+ */
+
+ /* Target chip */
+ apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
+
+ /* Boot on the stack */
+ /* Kick the second */
+ apic_write_around(APIC_ICR, APIC_DM_STARTUP
+ | (start_eip >> 12));
+
+ /*
+ * Give the other CPU some time to accept the IPI.
+ */
+ udelay(300);
+
+ printk_spew("Startup point 1.\n");
+
+ printk_spew("Waiting for send to finish...\n");
+ timeout = 0;
+ do {
+ printk_spew("+");
+ udelay(100);
+ send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
+ } while (send_status && (timeout++ < 1000));
+
+ /*
+ * Give the other CPU some time to accept the IPI.
+ */
+ udelay(200);
+ /*
+ * Due to the Pentium erratum 3AP.
+ */
+ if (maxlvt > 3) {
+ apic_read_around(APIC_SPIV);
+ apic_write(APIC_ESR, 0);
+ }
+ accept_status = (apic_read(APIC_ESR) & 0xEF);
+ if (send_status || accept_status)
+ break;
+ }
+ printk_spew("After Startup.\n");
+ if (send_status)
+ printk_warning("APIC never delivered???\n");
+ if (accept_status)
+ printk_warning("APIC delivery error (%lx).\n", accept_status);
+ if (send_status || accept_status)
+ return 0;
+ return 1;
+}
+
+
+void startup_other_cpus(unsigned long *processor_map)
+{
+ unsigned long apicid = this_processors_id();
+ int i;
+
+ /* Assume the cpus are densly packed by apicid */
+ for(i = 0; i < MAX_CPUS; i++) {
+ unsigned long cpu_apicid = initial_apicid[i];
+ if (cpu_apicid == -1) {
+ printk_err("CPU %d not found\n",i);
+ processor_map[i] = 0;
+ continue;
+ }
+ if (cpu_apicid == apicid ) {
+ continue;
+ }
+ if (!start_cpu(cpu_apicid)) {
+ /* Put an error in processor_map[i]? */
+ printk_err("CPU %d/%u would not start!\n",
+ i, cpu_apicid);
+ processor_map[i] = 0;
+ }
+ }
+}