diff options
Diffstat (limited to 'src/arch/i386')
-rw-r--r-- | src/arch/i386/lib/cpu.c | 6 | ||||
-rw-r--r-- | src/arch/i386/lib/pci_ops.c | 6 | ||||
-rw-r--r-- | src/arch/i386/smp/ioapic.c | 86 | ||||
-rw-r--r-- | src/arch/i386/smp/mpspec.c | 3 | ||||
-rw-r--r-- | src/arch/i386/smp/start_stop.c | 249 |
5 files changed, 345 insertions, 5 deletions
diff --git a/src/arch/i386/lib/cpu.c b/src/arch/i386/lib/cpu.c index 3e27e7acdc..8ace3fbf53 100644 --- a/src/arch/i386/lib/cpu.c +++ b/src/arch/i386/lib/cpu.c @@ -18,6 +18,7 @@ #define APIC 1 #endif + static void cache_on(struct mem_range *mem) { post_code(0x60); @@ -90,6 +91,11 @@ static void interrupts_on() | (APIC_LVT_REMOTE_IRR |APIC_SEND_PENDING | APIC_DELIVERY_MODE_NMI) ); +#if 1 + printk_debug(" apic_id: %d ", + apic_read(APIC_ID)); +#endif + #else /* APIC */ #ifdef i686 /* Only Pentium Pro and later have those MSR stuff */ diff --git a/src/arch/i386/lib/pci_ops.c b/src/arch/i386/lib/pci_ops.c index 80921bf9da..7ea40ba738 100644 --- a/src/arch/i386/lib/pci_ops.c +++ b/src/arch/i386/lib/pci_ops.c @@ -1,9 +1,9 @@ #include <console/console.h> #include <arch/io.h> #include <arch/pciconf.h> -#include <pci.h> -#include <pci_ids.h> -#include <pci_ops.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> static const struct pci_ops *conf; struct pci_ops { 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; + } + } +} |