summaryrefslogtreecommitdiff
path: root/arch/x86/cpu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/cpu')
-rw-r--r--arch/x86/cpu/Makefile2
-rw-r--r--arch/x86/cpu/baytrail/Makefile1
-rw-r--r--arch/x86/cpu/baytrail/cpu.c205
-rw-r--r--arch/x86/cpu/baytrail/valleyview.c1
-rw-r--r--arch/x86/cpu/config.mk2
-rw-r--r--arch/x86/cpu/coreboot/pci.c11
-rw-r--r--arch/x86/cpu/cpu.c71
-rw-r--r--arch/x86/cpu/interrupts.c5
-rw-r--r--arch/x86/cpu/ivybridge/car.S1
-rw-r--r--arch/x86/cpu/ivybridge/cpu.c5
-rw-r--r--arch/x86/cpu/ivybridge/early_me.c13
-rw-r--r--arch/x86/cpu/ivybridge/model_206ax.c4
-rw-r--r--arch/x86/cpu/ivybridge/sdram.c3
-rw-r--r--arch/x86/cpu/mp_init.c496
-rw-r--r--arch/x86/cpu/pci.c21
-rw-r--r--arch/x86/cpu/quark/quark.c14
-rw-r--r--arch/x86/cpu/queensbay/Makefile2
-rw-r--r--arch/x86/cpu/queensbay/irq.c242
-rw-r--r--arch/x86/cpu/queensbay/tnc.c14
-rw-r--r--arch/x86/cpu/sipi_vector.S216
20 files changed, 1292 insertions, 37 deletions
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile
index 6ded0a7f0f..043bea258c 100644
--- a/arch/x86/cpu/Makefile
+++ b/arch/x86/cpu/Makefile
@@ -19,6 +19,8 @@ obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/
obj-$(CONFIG_INTEL_QUARK) += quark/
obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/
obj-y += lapic.o
+obj-$(CONFIG_SMP) += mp_init.o
obj-y += mtrr.o
obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_SMP) += sipi_vector.o
obj-y += turbo.o
diff --git a/arch/x86/cpu/baytrail/Makefile b/arch/x86/cpu/baytrail/Makefile
index 8914e8b6d5..c78b644eb7 100644
--- a/arch/x86/cpu/baytrail/Makefile
+++ b/arch/x86/cpu/baytrail/Makefile
@@ -4,6 +4,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
+obj-y += cpu.o
obj-y += early_uart.o
obj-y += fsp_configs.o
obj-y += pci.o
diff --git a/arch/x86/cpu/baytrail/cpu.c b/arch/x86/cpu/baytrail/cpu.c
new file mode 100644
index 0000000000..1d482064b2
--- /dev/null
+++ b/arch/x86/cpu/baytrail/cpu.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Based on code from coreboot
+ */
+
+#include <common.h>
+#include <cpu.h>
+#include <dm.h>
+#include <asm/cpu.h>
+#include <asm/lapic.h>
+#include <asm/mp.h>
+#include <asm/msr.h>
+#include <asm/turbo.h>
+
+#ifdef CONFIG_SMP
+static int enable_smis(struct udevice *cpu, void *unused)
+{
+ return 0;
+}
+
+static struct mp_flight_record mp_steps[] = {
+ MP_FR_BLOCK_APS(mp_init_cpu, NULL, mp_init_cpu, NULL),
+ /* Wait for APs to finish initialization before proceeding. */
+ MP_FR_BLOCK_APS(NULL, NULL, enable_smis, NULL),
+};
+
+static int detect_num_cpus(void)
+{
+ int ecx = 0;
+
+ /*
+ * Use the algorithm described in Intel 64 and IA-32 Architectures
+ * Software Developer's Manual Volume 3 (3A, 3B & 3C): System
+ * Programming Guide, Jan-2015. Section 8.9.2: Hierarchical Mapping
+ * of CPUID Extended Topology Leaf.
+ */
+ while (1) {
+ struct cpuid_result leaf_b;
+
+ leaf_b = cpuid_ext(0xb, ecx);
+
+ /*
+ * Bay Trail doesn't have hyperthreading so just determine the
+ * number of cores by from level type (ecx[15:8] == * 2)
+ */
+ if ((leaf_b.ecx & 0xff00) == 0x0200)
+ return leaf_b.ebx & 0xffff;
+ ecx++;
+ }
+}
+
+static int baytrail_init_cpus(void)
+{
+ struct mp_params mp_params;
+
+ lapic_setup();
+
+ mp_params.num_cpus = detect_num_cpus();
+ mp_params.parallel_microcode_load = 0,
+ mp_params.flight_plan = &mp_steps[0];
+ mp_params.num_records = ARRAY_SIZE(mp_steps);
+ mp_params.microcode_pointer = 0;
+
+ if (mp_init(&mp_params)) {
+ printf("Warning: MP init failure\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+#endif
+
+int x86_init_cpus(void)
+{
+#ifdef CONFIG_SMP
+ debug("Init additional CPUs\n");
+ baytrail_init_cpus();
+#endif
+
+ return 0;
+}
+
+static void set_max_freq(void)
+{
+ msr_t perf_ctl;
+ msr_t msr;
+
+ /* Enable speed step */
+ msr = msr_read(MSR_IA32_MISC_ENABLES);
+ msr.lo |= (1 << 16);
+ msr_write(MSR_IA32_MISC_ENABLES, msr);
+
+ /*
+ * Set guaranteed ratio [21:16] from IACORE_RATIOS to bits [15:8] of
+ * the PERF_CTL
+ */
+ msr = msr_read(MSR_IACORE_RATIOS);
+ perf_ctl.lo = (msr.lo & 0x3f0000) >> 8;
+
+ /*
+ * Set guaranteed vid [21:16] from IACORE_VIDS to bits [7:0] of
+ * the PERF_CTL
+ */
+ msr = msr_read(MSR_IACORE_VIDS);
+ perf_ctl.lo |= (msr.lo & 0x7f0000) >> 16;
+ perf_ctl.hi = 0;
+
+ msr_write(MSR_IA32_PERF_CTL, perf_ctl);
+}
+
+static int cpu_x86_baytrail_probe(struct udevice *dev)
+{
+ debug("Init BayTrail core\n");
+
+ /*
+ * On BayTrail the turbo disable bit is actually scoped at the
+ * building-block level, not package. For non-BSP cores that are
+ * within a building block, enable turbo. The cores within the BSP's
+ * building block will just see it already enabled and move on.
+ */
+ if (lapicid())
+ turbo_enable();
+
+ /* Dynamic L2 shrink enable and threshold */
+ msr_clrsetbits_64(MSR_PMG_CST_CONFIG_CONTROL, 0x3f000f, 0xe0008),
+
+ /* Disable C1E */
+ msr_clrsetbits_64(MSR_POWER_CTL, 2, 0);
+ msr_setbits_64(MSR_POWER_MISC, 0x44);
+
+ /* Set this core to max frequency ratio */
+ set_max_freq();
+
+ return 0;
+}
+
+static unsigned bus_freq(void)
+{
+ msr_t clk_info = msr_read(MSR_BSEL_CR_OVERCLOCK_CONTROL);
+ switch (clk_info.lo & 0x3) {
+ case 0:
+ return 83333333;
+ case 1:
+ return 100000000;
+ case 2:
+ return 133333333;
+ case 3:
+ return 116666666;
+ default:
+ return 0;
+ }
+}
+
+static unsigned long tsc_freq(void)
+{
+ msr_t platform_info;
+ ulong bclk = bus_freq();
+
+ if (!bclk)
+ return 0;
+
+ platform_info = msr_read(MSR_PLATFORM_INFO);
+
+ return bclk * ((platform_info.lo >> 8) & 0xff);
+}
+
+static int baytrail_get_info(struct udevice *dev, struct cpu_info *info)
+{
+ info->cpu_freq = tsc_freq();
+ info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU;
+
+ return 0;
+}
+
+static int cpu_x86_baytrail_bind(struct udevice *dev)
+{
+ struct cpu_platdata *plat = dev_get_parent_platdata(dev);
+
+ plat->cpu_id = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+ "intel,apic-id", -1);
+
+ return 0;
+}
+
+static const struct cpu_ops cpu_x86_baytrail_ops = {
+ .get_desc = x86_cpu_get_desc,
+ .get_info = baytrail_get_info,
+};
+
+static const struct udevice_id cpu_x86_baytrail_ids[] = {
+ { .compatible = "intel,baytrail-cpu" },
+ { }
+};
+
+U_BOOT_DRIVER(cpu_x86_baytrail_drv) = {
+ .name = "cpu_x86_baytrail",
+ .id = UCLASS_CPU,
+ .of_match = cpu_x86_baytrail_ids,
+ .bind = cpu_x86_baytrail_bind,
+ .probe = cpu_x86_baytrail_probe,
+ .ops = &cpu_x86_baytrail_ops,
+};
diff --git a/arch/x86/cpu/baytrail/valleyview.c b/arch/x86/cpu/baytrail/valleyview.c
index a3e837d43e..9915da5bd7 100644
--- a/arch/x86/cpu/baytrail/valleyview.c
+++ b/arch/x86/cpu/baytrail/valleyview.c
@@ -16,7 +16,6 @@ static struct pci_device_id mmc_supported[] = {
int cpu_mmc_init(bd_t *bis)
{
- printf("mmc init\n");
return pci_mmc_init("ValleyView SDHCI", mmc_supported,
ARRAY_SIZE(mmc_supported));
}
diff --git a/arch/x86/cpu/config.mk b/arch/x86/cpu/config.mk
index 84aeaf3edc..4c4d0c7cd2 100644
--- a/arch/x86/cpu/config.mk
+++ b/arch/x86/cpu/config.mk
@@ -7,7 +7,7 @@
CROSS_COMPILE ?= i386-linux-
-PLATFORM_CPPFLAGS += -D__I386__ -Werror
+PLATFORM_CPPFLAGS += -D__I386__
# DO NOT MODIFY THE FOLLOWING UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!
LDPPFLAGS += -DRESET_SEG_START=0xffff0000
diff --git a/arch/x86/cpu/coreboot/pci.c b/arch/x86/cpu/coreboot/pci.c
index fa415dd42b..67eb14ce99 100644
--- a/arch/x86/cpu/coreboot/pci.c
+++ b/arch/x86/cpu/coreboot/pci.c
@@ -34,3 +34,14 @@ U_BOOT_DRIVER(pci_x86_drv) = {
.of_match = pci_x86_ids,
.ops = &pci_x86_ops,
};
+
+static const struct udevice_id generic_pch_ids[] = {
+ { .compatible = "intel,pch" },
+ { }
+};
+
+U_BOOT_DRIVER(generic_pch_drv) = {
+ .name = "pch",
+ .id = UCLASS_PCH,
+ .of_match = generic_pch_ids,
+};
diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
index a9ca50b1e4..bb4a110c00 100644
--- a/arch/x86/cpu/cpu.c
+++ b/arch/x86/cpu/cpu.c
@@ -21,6 +21,8 @@
#include <common.h>
#include <command.h>
+#include <cpu.h>
+#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <asm/control_regs.h>
@@ -29,6 +31,7 @@
#include <asm/processor.h>
#include <asm/processor-flags.h>
#include <asm/interrupt.h>
+#include <asm/tables.h>
#include <linux/compiler.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -132,6 +135,7 @@ static void load_gdt(const u64 *boot_gdt, u16 num_entries)
void setup_gdt(gd_t *id, u64 *gdt_addr)
{
+ id->arch.gdt = gdt_addr;
/* CS: code, read/execute, 4 GB, base 0 */
gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff);
@@ -379,21 +383,17 @@ void flush_cache(unsigned long dummy1, unsigned long dummy2)
asm("wbinvd\n");
}
-void __attribute__ ((regparm(0))) generate_gpf(void);
-
-/* segment 0x70 is an arbitrary segment which does not exist */
-asm(".globl generate_gpf\n"
- ".hidden generate_gpf\n"
- ".type generate_gpf, @function\n"
- "generate_gpf:\n"
- "ljmp $0x70, $0x47114711\n");
-
__weak void reset_cpu(ulong addr)
{
- printf("Resetting using x86 Triple Fault\n");
- set_vector(13, generate_gpf); /* general protection fault handler */
- set_vector(8, generate_gpf); /* double fault handler */
- generate_gpf(); /* start the show */
+ /* Do a hard reset through the chipset's reset control register */
+ outb(SYS_RST | RST_CPU, PORT_RESET);
+ for (;;)
+ cpu_hlt();
+}
+
+void x86_full_reset(void)
+{
+ outb(FULL_RST | SYS_RST | RST_CPU, PORT_RESET);
}
int dcache_status(void)
@@ -520,6 +520,16 @@ char *cpu_get_name(char *name)
return ptr;
}
+int x86_cpu_get_desc(struct udevice *dev, char *buf, int size)
+{
+ if (size < CPU_MAX_NAME_LEN)
+ return -ENOSPC;
+
+ cpu_get_name(buf);
+
+ return 0;
+}
+
int default_print_cpuinfo(void)
{
printf("CPU: %s, vendor %s, device %xh\n",
@@ -593,3 +603,38 @@ void show_boot_progress(int val)
#endif
outb(val, POST_PORT);
}
+
+#ifndef CONFIG_SYS_COREBOOT
+int last_stage_init(void)
+{
+ write_tables();
+
+ return 0;
+}
+#endif
+
+__weak int x86_init_cpus(void)
+{
+ return 0;
+}
+
+int cpu_init_r(void)
+{
+ return x86_init_cpus();
+}
+
+static const struct cpu_ops cpu_x86_ops = {
+ .get_desc = x86_cpu_get_desc,
+};
+
+static const struct udevice_id cpu_x86_ids[] = {
+ { .compatible = "cpu-x86" },
+ { }
+};
+
+U_BOOT_DRIVER(cpu_x86_drv) = {
+ .name = "cpu_x86",
+ .id = UCLASS_CPU,
+ .of_match = cpu_x86_ids,
+ .ops = &cpu_x86_ops,
+};
diff --git a/arch/x86/cpu/interrupts.c b/arch/x86/cpu/interrupts.c
index a21d2a6c50..c777d3646f 100644
--- a/arch/x86/cpu/interrupts.c
+++ b/arch/x86/cpu/interrupts.c
@@ -147,6 +147,11 @@ int cpu_init_interrupts(void)
return 0;
}
+void *x86_get_idt(void)
+{
+ return &idt_ptr;
+}
+
void __do_irq(int irq)
{
printf("Unhandled IRQ : %d\n", irq);
diff --git a/arch/x86/cpu/ivybridge/car.S b/arch/x86/cpu/ivybridge/car.S
index 9441666f5a..407e451adc 100644
--- a/arch/x86/cpu/ivybridge/car.S
+++ b/arch/x86/cpu/ivybridge/car.S
@@ -15,6 +15,7 @@
#include <asm/msr-index.h>
#include <asm/mtrr.h>
#include <asm/post.h>
+#include <asm/processor.h>
#include <asm/processor-flags.h>
#include <asm/arch/microcode.h>
diff --git a/arch/x86/cpu/ivybridge/cpu.c b/arch/x86/cpu/ivybridge/cpu.c
index 37f373148c..cce5923f0b 100644
--- a/arch/x86/cpu/ivybridge/cpu.c
+++ b/arch/x86/cpu/ivybridge/cpu.c
@@ -92,7 +92,7 @@ static int set_flex_ratio_to_tdp_nominal(void)
/* Issue warm reset, will be "CPU only" due to soft reset data */
outb(0x0, PORT_RESET);
- outb(0x6, PORT_RESET);
+ outb(SYS_RST | RST_CPU, PORT_RESET);
cpu_hlt();
/* Not reached */
@@ -286,8 +286,7 @@ int print_cpuinfo(void)
/* System is not happy after keyboard reset... */
debug("Issuing CF9 warm reset\n");
- outb(0x6, 0xcf9);
- cpu_hlt();
+ reset_cpu(0);
}
/* Early chipset init required before RAM init can work */
diff --git a/arch/x86/cpu/ivybridge/early_me.c b/arch/x86/cpu/ivybridge/early_me.c
index 356bbb4a38..711470f364 100644
--- a/arch/x86/cpu/ivybridge/early_me.c
+++ b/arch/x86/cpu/ivybridge/early_me.c
@@ -117,7 +117,6 @@ static inline void set_global_reset(int enable)
int intel_early_me_init_done(u8 status)
{
- u8 reset;
int count;
u32 mebase_l, mebase_h;
struct me_hfs hfs;
@@ -156,7 +155,6 @@ int intel_early_me_init_done(u8 status)
/* Check status after acknowledgement */
intel_early_me_status();
- reset = 0;
switch (hfs.ack_data) {
case ME_HFS_ACK_CONTINUE:
/* Continue to boot */
@@ -164,17 +162,17 @@ int intel_early_me_init_done(u8 status)
case ME_HFS_ACK_RESET:
/* Non-power cycle reset */
set_global_reset(0);
- reset = 0x06;
+ reset_cpu(0);
break;
case ME_HFS_ACK_PWR_CYCLE:
/* Power cycle reset */
set_global_reset(0);
- reset = 0x0e;
+ x86_full_reset();
break;
case ME_HFS_ACK_GBL_RESET:
/* Global reset */
set_global_reset(1);
- reset = 0x0e;
+ x86_full_reset();
break;
case ME_HFS_ACK_S3:
case ME_HFS_ACK_S4:
@@ -182,10 +180,5 @@ int intel_early_me_init_done(u8 status)
break;
}
- /* Perform the requested reset */
- if (reset) {
- outb(reset, 0xcf9);
- cpu_hlt();
- }
return -1;
}
diff --git a/arch/x86/cpu/ivybridge/model_206ax.c b/arch/x86/cpu/ivybridge/model_206ax.c
index 11dc625da9..8b08c40bcb 100644
--- a/arch/x86/cpu/ivybridge/model_206ax.c
+++ b/arch/x86/cpu/ivybridge/model_206ax.c
@@ -435,8 +435,8 @@ static int intel_cores_init(struct x86_cpu_priv *cpu)
debug("CPU: %u has core %u\n", cpu->apic_id, new_cpu->apic_id);
-#if CONFIG_SMP && CONFIG_MAX_CPUS > 1
- /* Start the new cpu */
+#if 0 && CONFIG_SMP && CONFIG_MAX_CPUS > 1
+ /* TODO(sjg@chromium.org): Start the new cpu */
if (!start_cpu(new_cpu)) {
/* Record the error in cpu? */
printk(BIOS_ERR, "CPU %u would not start!\n",
diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c
index 9a6da37d09..af907c5b9b 100644
--- a/arch/x86/cpu/ivybridge/sdram.c
+++ b/arch/x86/cpu/ivybridge/sdram.c
@@ -393,8 +393,7 @@ int sdram_initialise(struct pei_data *pei_data)
/* If MRC data is not found we cannot continue S3 resume. */
if (pei_data->boot_mode == PEI_BOOT_RESUME && !pei_data->mrc_input) {
debug("Giving up in sdram_initialize: No MRC data\n");
- outb(0x6, PORT_RESET);
- cpu_hlt();
+ reset_cpu(0);
}
/* Pass console handler in pei_data */
diff --git a/arch/x86/cpu/mp_init.c b/arch/x86/cpu/mp_init.c
new file mode 100644
index 0000000000..ac5753a1fd
--- /dev/null
+++ b/arch/x86/cpu/mp_init.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * Based on code from the coreboot file of the same name
+ */
+
+#include <common.h>
+#include <cpu.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <asm/atomic.h>
+#include <asm/cpu.h>
+#include <asm/interrupt.h>
+#include <asm/lapic.h>
+#include <asm/mp.h>
+#include <asm/mtrr.h>
+#include <asm/sipi.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <linux/linkage.h>
+
+/* This also needs to match the sipi.S assembly code for saved MSR encoding */
+struct saved_msr {
+ uint32_t index;
+ uint32_t lo;
+ uint32_t hi;
+} __packed;
+
+
+struct mp_flight_plan {
+ int num_records;
+ struct mp_flight_record *records;
+};
+
+static struct mp_flight_plan mp_info;
+
+struct cpu_map {
+ struct udevice *dev;
+ int apic_id;
+ int err_code;
+};
+
+static inline void barrier_wait(atomic_t *b)
+{
+ while (atomic_read(b) == 0)
+ asm("pause");
+ mfence();
+}
+
+static inline void release_barrier(atomic_t *b)
+{
+ mfence();
+ atomic_set(b, 1);
+}
+
+/* Returns 1 if timeout waiting for APs. 0 if target APs found */
+static int wait_for_aps(atomic_t *val, int target, int total_delay,
+ int delay_step)
+{
+ int timeout = 0;
+ int delayed = 0;
+
+ while (atomic_read(val) != target) {
+ udelay(delay_step);
+ delayed += delay_step;
+ if (delayed >= total_delay) {
+ timeout = 1;
+ break;
+ }
+ }
+
+ return timeout;
+}
+
+static void ap_do_flight_plan(struct udevice *cpu)
+{
+ int i;
+
+ for (i = 0; i < mp_info.num_records; i++) {
+ struct mp_flight_record *rec = &mp_info.records[i];
+
+ atomic_inc(&rec->cpus_entered);
+ barrier_wait(&rec->barrier);
+
+ if (rec->ap_call != NULL)
+ rec->ap_call(cpu, rec->ap_arg);
+ }
+}
+
+static int find_cpu_by_apid_id(int apic_id, struct udevice **devp)
+{
+ struct udevice *dev;
+
+ *devp = NULL;
+ for (uclass_find_first_device(UCLASS_CPU, &dev);
+ dev;
+ uclass_find_next_device(&dev)) {
+ struct cpu_platdata *plat = dev_get_parent_platdata(dev);
+
+ if (plat->cpu_id == apic_id) {
+ *devp = dev;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * By the time APs call ap_init() caching has been setup, and microcode has
+ * been loaded
+ */
+static void ap_init(unsigned int cpu_index)
+{
+ struct udevice *dev;
+ int apic_id;
+ int ret;
+
+ /* Ensure the local apic is enabled */
+ enable_lapic();
+
+ apic_id = lapicid();
+ ret = find_cpu_by_apid_id(apic_id, &dev);
+ if (ret) {
+ debug("Unknown CPU apic_id %x\n", apic_id);
+ goto done;
+ }
+
+ debug("AP: slot %d apic_id %x, dev %s\n", cpu_index, apic_id,
+ dev ? dev->name : "(apic_id not found)");
+
+ /* Walk the flight plan */
+ ap_do_flight_plan(dev);
+
+ /* Park the AP */
+ debug("parking\n");
+done:
+ stop_this_cpu();
+}
+
+static const unsigned int fixed_mtrrs[NUM_FIXED_MTRRS] = {
+ MTRR_FIX_64K_00000_MSR, MTRR_FIX_16K_80000_MSR, MTRR_FIX_16K_A0000_MSR,
+ MTRR_FIX_4K_C0000_MSR, MTRR_FIX_4K_C8000_MSR, MTRR_FIX_4K_D0000_MSR,
+ MTRR_FIX_4K_D8000_MSR, MTRR_FIX_4K_E0000_MSR, MTRR_FIX_4K_E8000_MSR,
+ MTRR_FIX_4K_F0000_MSR, MTRR_FIX_4K_F8000_MSR,
+};
+
+static inline struct saved_msr *save_msr(int index, struct saved_msr *entry)
+{
+ msr_t msr;
+
+ msr = msr_read(index);
+ entry->index = index;
+ entry->lo = msr.lo;
+ entry->hi = msr.hi;
+
+ /* Return the next entry */
+ entry++;
+ return entry;
+}
+
+static int save_bsp_msrs(char *start, int size)
+{
+ int msr_count;
+ int num_var_mtrrs;
+ struct saved_msr *msr_entry;
+ int i;
+ msr_t msr;
+
+ /* Determine number of MTRRs need to be saved */
+ msr = msr_read(MTRR_CAP_MSR);
+ num_var_mtrrs = msr.lo & 0xff;
+
+ /* 2 * num_var_mtrrs for base and mask. +1 for IA32_MTRR_DEF_TYPE */
+ msr_count = 2 * num_var_mtrrs + NUM_FIXED_MTRRS + 1;
+
+ if ((msr_count * sizeof(struct saved_msr)) > size) {
+ printf("Cannot mirror all %d msrs.\n", msr_count);
+ return -ENOSPC;
+ }
+
+ msr_entry = (void *)start;
+ for (i = 0; i < NUM_FIXED_MTRRS; i++)
+ msr_entry = save_msr(fixed_mtrrs[i], msr_entry);
+
+ for (i = 0; i < num_var_mtrrs; i++) {
+ msr_entry = save_msr(MTRR_PHYS_BASE_MSR(i), msr_entry);
+ msr_entry = save_msr(MTRR_PHYS_MASK_MSR(i), msr_entry);
+ }
+
+ msr_entry = save_msr(MTRR_DEF_TYPE_MSR, msr_entry);
+
+ return msr_count;
+}
+
+static int load_sipi_vector(atomic_t **ap_countp)
+{
+ struct sipi_params_16bit *params16;
+ struct sipi_params *params;
+ static char msr_save[512];
+ char *stack;
+ ulong addr;
+ int code_len;
+ int size;
+ int ret;
+
+ /* Copy in the code */
+ code_len = ap_start16_code_end - ap_start16;
+ debug("Copying SIPI code to %x: %d bytes\n", AP_DEFAULT_BASE,
+ code_len);
+ memcpy((void *)AP_DEFAULT_BASE, ap_start16, code_len);
+
+ addr = AP_DEFAULT_BASE + (ulong)sipi_params_16bit - (ulong)ap_start16;
+ params16 = (struct sipi_params_16bit *)addr;
+ params16->ap_start = (uint32_t)ap_start;
+ params16->gdt = (uint32_t)gd->arch.gdt;
+ params16->gdt_limit = X86_GDT_SIZE - 1;
+ debug("gdt = %x, gdt_limit = %x\n", params16->gdt, params16->gdt_limit);
+
+ params = (struct sipi_params *)sipi_params;
+ debug("SIPI 32-bit params at %p\n", params);
+ params->idt_ptr = (uint32_t)x86_get_idt();
+
+ params->stack_size = CONFIG_AP_STACK_SIZE;
+ size = params->stack_size * CONFIG_MAX_CPUS;
+ stack = memalign(size, 4096);
+ if (!stack)
+ return -ENOMEM;
+ params->stack_top = (u32)(stack + size);
+
+ params->microcode_ptr = 0;
+ params->msr_table_ptr = (u32)msr_save;
+ ret = save_bsp_msrs(msr_save, sizeof(msr_save));
+ if (ret < 0)
+ return ret;
+ params->msr_count = ret;
+
+ params->c_handler = (uint32_t)&ap_init;
+
+ *ap_countp = &params->ap_count;
+ atomic_set(*ap_countp, 0);
+ debug("SIPI vector is ready\n");
+
+ return 0;
+}
+
+static int check_cpu_devices(int expected_cpus)
+{
+ int i;
+
+ for (i = 0; i < expected_cpus; i++) {
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_find_device(UCLASS_CPU, i, &dev);
+ if (ret) {
+ debug("Cannot find CPU %d in device tree\n", i);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Returns 1 for timeout. 0 on success */
+static int apic_wait_timeout(int total_delay, int delay_step)
+{
+ int total = 0;
+ int timeout = 0;
+
+ while (lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY) {
+ udelay(delay_step);
+ total += delay_step;
+ if (total >= total_delay) {
+ timeout = 1;
+ break;
+ }
+ }
+
+ return timeout;
+}
+
+static int start_aps(int ap_count, atomic_t *num_aps)
+{
+ int sipi_vector;
+ /* Max location is 4KiB below 1MiB */
+ const int max_vector_loc = ((1 << 20) - (1 << 12)) >> 12;
+
+ if (ap_count == 0)
+ return 0;
+
+ /* The vector is sent as a 4k aligned address in one byte */
+ sipi_vector = AP_DEFAULT_BASE >> 12;
+
+ if (sipi_vector > max_vector_loc) {
+ printf("SIPI vector too large! 0x%08x\n",
+ sipi_vector);
+ return -1;
+ }
+
+ debug("Attempting to start %d APs\n", ap_count);
+
+ if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) {
+ debug("Waiting for ICR not to be busy...");
+ if (apic_wait_timeout(1000, 50)) {
+ debug("timed out. Aborting.\n");
+ return -1;
+ } else {
+ debug("done.\n");
+ }
+ }
+
+ /* Send INIT IPI to all but self */
+ lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
+ lapic_write_around(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT |
+ LAPIC_DM_INIT);
+ debug("Waiting for 10ms after sending INIT.\n");
+ mdelay(10);
+
+ /* Send 1st SIPI */
+ if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) {
+ debug("Waiting for ICR not to be busy...");
+ if (apic_wait_timeout(1000, 50)) {
+ debug("timed out. Aborting.\n");
+ return -1;
+ } else {
+ debug("done.\n");
+ }
+ }
+
+ lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
+ lapic_write_around(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT |
+ LAPIC_DM_STARTUP | sipi_vector);
+ debug("Waiting for 1st SIPI to complete...");
+ if (apic_wait_timeout(10000, 50)) {
+ debug("timed out.\n");
+ return -1;
+ } else {
+ debug("done.\n");
+ }
+
+ /* Wait for CPUs to check in up to 200 us */
+ wait_for_aps(num_aps, ap_count, 200, 15);
+
+ /* Send 2nd SIPI */
+ if ((lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY)) {
+ debug("Waiting for ICR not to be busy...");
+ if (apic_wait_timeout(1000, 50)) {
+ debug("timed out. Aborting.\n");
+ return -1;
+ } else {
+ debug("done.\n");
+ }
+ }
+
+ lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
+ lapic_write_around(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT |
+ LAPIC_DM_STARTUP | sipi_vector);
+ debug("Waiting for 2nd SIPI to complete...");
+ if (apic_wait_timeout(10000, 50)) {
+ debug("timed out.\n");
+ return -1;
+ } else {
+ debug("done.\n");
+ }
+
+ /* Wait for CPUs to check in */
+ if (wait_for_aps(num_aps, ap_count, 10000, 50)) {
+ debug("Not all APs checked in: %d/%d.\n",
+ atomic_read(num_aps), ap_count);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bsp_do_flight_plan(struct udevice *cpu, struct mp_params *mp_params)
+{
+ int i;
+ int ret = 0;
+ const int timeout_us = 100000;
+ const int step_us = 100;
+ int num_aps = mp_params->num_cpus - 1;
+
+ for (i = 0; i < mp_params->num_records; i++) {
+ struct mp_flight_record *rec = &mp_params->flight_plan[i];
+
+ /* Wait for APs if the record is not released */
+ if (atomic_read(&rec->barrier) == 0) {
+ /* Wait for the APs to check in */
+ if (wait_for_aps(&rec->cpus_entered, num_aps,
+ timeout_us, step_us)) {
+ debug("MP record %d timeout.\n", i);
+ ret = -1;
+ }
+ }
+
+ if (rec->bsp_call != NULL)
+ rec->bsp_call(cpu, rec->bsp_arg);
+
+ release_barrier(&rec->barrier);
+ }
+ return ret;
+}
+
+static int init_bsp(struct udevice **devp)
+{
+ char processor_name[CPU_MAX_NAME_LEN];
+ int apic_id;
+ int ret;
+
+ cpu_get_name(processor_name);
+ debug("CPU: %s.\n", processor_name);
+
+ enable_lapic();
+
+ apic_id = lapicid();
+ ret = find_cpu_by_apid_id(apic_id, devp);
+ if (ret) {
+ printf("Cannot find boot CPU, APIC ID %d\n", apic_id);
+ return ret;
+ }
+
+ return 0;
+}
+
+int mp_init(struct mp_params *p)
+{
+ int num_aps;
+ atomic_t *ap_count;
+ struct udevice *cpu;
+ int ret;
+
+ /* This will cause the CPUs devices to be bound */
+ struct uclass *uc;
+ ret = uclass_get(UCLASS_CPU, &uc);
+ if (ret)
+ return ret;
+
+ ret = init_bsp(&cpu);
+ if (ret) {
+ debug("Cannot init boot CPU: err=%d\n", ret);
+ return ret;
+ }
+
+ if (p == NULL || p->flight_plan == NULL || p->num_records < 1) {
+ printf("Invalid MP parameters\n");
+ return -1;
+ }
+
+ ret = check_cpu_devices(p->num_cpus);
+ if (ret)
+ debug("Warning: Device tree does not describe all CPUs. Extra ones will not be started correctly\n");
+
+ /* Copy needed parameters so that APs have a reference to the plan */
+ mp_info.num_records = p->num_records;
+ mp_info.records = p->flight_plan;
+
+ /* Load the SIPI vector */
+ ret = load_sipi_vector(&ap_count);
+ if (ap_count == NULL)
+ return -1;
+
+ /*
+ * Make sure SIPI data hits RAM so the APs that come up will see
+ * the startup code even if the caches are disabled
+ */
+ wbinvd();
+
+ /* Start the APs providing number of APs and the cpus_entered field */
+ num_aps = p->num_cpus - 1;
+ ret = start_aps(num_aps, ap_count);
+ if (ret) {
+ mdelay(1000);
+ debug("%d/%d eventually checked in?\n", atomic_read(ap_count),
+ num_aps);
+ return ret;
+ }
+
+ /* Walk the flight plan for the BSP */
+ ret = bsp_do_flight_plan(cpu, p);
+ if (ret) {
+ debug("CPU init failed: err=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int mp_init_cpu(struct udevice *cpu, void *unused)
+{
+ return device_probe(cpu);
+}
diff --git a/arch/x86/cpu/pci.c b/arch/x86/cpu/pci.c
index e23b233961..c209f15ec4 100644
--- a/arch/x86/cpu/pci.c
+++ b/arch/x86/cpu/pci.c
@@ -151,3 +151,24 @@ int pci_x86_write_config(struct udevice *bus, pci_dev_t bdf, uint offset,
return 0;
}
+
+void pci_assign_irqs(int bus, int device, int func, u8 irq[4])
+{
+ pci_dev_t bdf;
+ u8 pin, line;
+
+ bdf = PCI_BDF(bus, device, func);
+
+ pin = x86_pci_read_config8(bdf, PCI_INTERRUPT_PIN);
+
+ /* PCI spec says all values except 1..4 are reserved */
+ if ((pin < 1) || (pin > 4))
+ return;
+
+ line = irq[pin - 1];
+
+ debug("Assigning IRQ %d to PCI device %d.%x.%d (INT%c)\n",
+ line, bus, device, func, 'A' + pin - 1);
+
+ x86_pci_write_config8(bdf, PCI_INTERRUPT_LINE, line);
+}
diff --git a/arch/x86/cpu/quark/quark.c b/arch/x86/cpu/quark/quark.c
index e4b19c2759..e78a271c50 100644
--- a/arch/x86/cpu/quark/quark.c
+++ b/arch/x86/cpu/quark/quark.c
@@ -72,6 +72,15 @@ static void quark_setup_bars(void)
CONFIG_PCIE_ECAM_BASE | MEM_BAR_EN);
}
+static void quark_enable_legacy_seg(void)
+{
+ u32 hmisc2;
+
+ hmisc2 = msg_port_read(MSG_PORT_HOST_BRIDGE, HMISC2);
+ hmisc2 |= (HMISC2_SEGE | HMISC2_SEGF | HMISC2_SEGAB);
+ msg_port_write(MSG_PORT_HOST_BRIDGE, HMISC2, hmisc2);
+}
+
int arch_cpu_init(void)
{
struct pci_controller *hose;
@@ -96,6 +105,9 @@ int arch_cpu_init(void)
*/
quark_setup_bars();
+ /* Turn on legacy segments (A/B/E/F) decode to system RAM */
+ quark_enable_legacy_seg();
+
unprotect_spi_flash();
return 0;
@@ -110,7 +122,7 @@ int print_cpuinfo(void)
void reset_cpu(ulong addr)
{
/* cold reset */
- outb(0x08, PORT_RESET);
+ x86_full_reset();
}
int cpu_mmc_init(bd_t *bis)
diff --git a/arch/x86/cpu/queensbay/Makefile b/arch/x86/cpu/queensbay/Makefile
index d8761fdfbd..4599a4896d 100644
--- a/arch/x86/cpu/queensbay/Makefile
+++ b/arch/x86/cpu/queensbay/Makefile
@@ -5,5 +5,5 @@
#
obj-y += fsp_configs.o
-obj-y += tnc.o topcliff.o
+obj-y += irq.o tnc.o topcliff.o
obj-$(CONFIG_PCI) += tnc_pci.o
diff --git a/arch/x86/cpu/queensbay/irq.c b/arch/x86/cpu/queensbay/irq.c
new file mode 100644
index 0000000000..faf951544f
--- /dev/null
+++ b/arch/x86/cpu/queensbay/irq.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <asm/pci.h>
+#include <asm/post.h>
+#include <asm/processor.h>
+#include <asm/pirq_routing.h>
+#include <asm/arch/device.h>
+#include <asm/arch/tnc.h>
+#include <asm/arch/irq.h>
+
+static struct irq_routing_table *pirq_routing_table;
+
+bool pirq_check_irq_routed(int link, u8 irq)
+{
+ u8 pirq;
+
+ pirq = x86_pci_read_config8(TNC_LPC, LINK_N2V(link));
+ pirq &= 0xf;
+
+ /* IRQ# 0/1/2/8/13 are reserved */
+ if (pirq < 3 || pirq == 8 || pirq == 13)
+ return false;
+
+ return pirq == irq ? true : false;
+}
+
+int pirq_translate_link(int link)
+{
+ return LINK_V2N(link);
+}
+
+void pirq_assign_irq(int link, u8 irq)
+{
+ /* IRQ# 0/1/2/8/13 are reserved */
+ if (irq < 3 || irq == 8 || irq == 13)
+ return;
+
+ x86_pci_write_config8(TNC_LPC, LINK_N2V(link), irq);
+}
+
+static inline void fill_irq_info(struct irq_info **slotp, int *entries, u8 bus,
+ u8 device, u8 func, u8 pin, u8 pirq)
+{
+ struct irq_info *slot = *slotp;
+
+ slot->bus = bus;
+ slot->devfn = (device << 3) | func;
+ slot->irq[pin - 1].link = LINK_N2V(pirq);
+ slot->irq[pin - 1].bitmap = PIRQ_BITMAP;
+ (*entries)++;
+ (*slotp)++;
+}
+
+/* PCIe port downstream INTx swizzle */
+static inline u8 pin_swizzle(u8 pin, int port)
+{
+ return (pin + port) % 4;
+}
+
+__weak int board_fill_irq_info(struct irq_info *slot)
+{
+ return 0;
+}
+
+static int create_pirq_routing_table(void)
+{
+ struct irq_routing_table *rt;
+ struct irq_info *slot;
+ int irq_entries = 0;
+ pci_dev_t tcf_bdf;
+ u8 tcf_bus, bus;
+ int i;
+
+ rt = malloc(sizeof(struct irq_routing_table));
+ if (!rt)
+ return -ENOMEM;
+ memset((char *)rt, 0, sizeof(struct irq_routing_table));
+
+ /* Populate the PIRQ table fields */
+ rt->signature = PIRQ_SIGNATURE;
+ rt->version = PIRQ_VERSION;
+ rt->rtr_bus = 0;
+ rt->rtr_devfn = (TNC_LPC_DEV << 3) | TNC_LPC_FUNC;
+ rt->rtr_vendor = PCI_VENDOR_ID_INTEL;
+ rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31;
+
+ slot = rt->slots;
+
+ /*
+ * Now fill in the irq_info entries in the PIRQ table
+ *
+ * We start from internal TunnelCreek PCI devices first, then
+ * followed by all the 4 PCIe ports downstream devices, including
+ * the Queensbay platform Topcliff chipset devices.
+ */
+ fill_irq_info(&slot, &irq_entries, 0, TNC_IGD_DEV,
+ TNC_IGD_FUNC, INTA, PIRQE);
+ fill_irq_info(&slot, &irq_entries, 0, TNC_SDVO_DEV,
+ TNC_SDVO_FUNC, INTA, PIRQF);
+ fill_irq_info(&slot, &irq_entries, 0, TNC_HDA_DEV,
+ TNC_HDA_FUNC, INTA, PIRQG);
+ fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE0_DEV,
+ TNC_PCIE0_FUNC, INTA, PIRQE);
+ fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE1_DEV,
+ TNC_PCIE1_FUNC, INTA, PIRQF);
+ fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE2_DEV,
+ TNC_PCIE2_FUNC, INTA, PIRQG);
+ fill_irq_info(&slot, &irq_entries, 0, TNC_PCIE3_DEV,
+ TNC_PCIE3_FUNC, INTA, PIRQH);
+
+ /* Check which PCIe port the Topcliff chipset is connected to */
+ tcf_bdf = pci_find_device(PCI_VENDOR_ID_INTEL, 0x8800, 0);
+ tcf_bus = PCI_BUS(tcf_bdf);
+ for (i = 0; i < 4; i++) {
+ bus = x86_pci_read_config8(PCI_BDF(0, TNC_PCIE0_DEV + i, 0),
+ PCI_SECONDARY_BUS);
+ if (bus == tcf_bus)
+ break;
+ }
+
+ /* Fill in the Topcliff chipset devices' irq info */
+ if (i < 4) {
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_PCIE_PORT_DEV,
+ TCF_PCIE_PORT_FUNC, INTA, pin_swizzle(PIRQA, i));
+
+ tcf_bus++;
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_0,
+ TCF_GBE_FUNC, INTA, pin_swizzle(PIRQA, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_0,
+ TCF_GPIO_FUNC, INTA, pin_swizzle(PIRQA, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
+ TCF_USB1_OHCI0_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
+ TCF_USB1_OHCI1_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
+ TCF_USB1_OHCI2_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
+ TCF_USB1_EHCI_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_2,
+ TCF_USB_DEVICE_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_4,
+ TCF_SDIO0_FUNC, INTC, pin_swizzle(PIRQC, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_4,
+ TCF_SDIO1_FUNC, INTC, pin_swizzle(PIRQC, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_6,
+ TCF_SATA_FUNC, INTD, pin_swizzle(PIRQD, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8,
+ TCF_USB2_OHCI0_FUNC, INTA, pin_swizzle(PIRQA, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8,
+ TCF_USB2_OHCI1_FUNC, INTA, pin_swizzle(PIRQA, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8,
+ TCF_USB2_OHCI2_FUNC, INTA, pin_swizzle(PIRQA, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_8,
+ TCF_USB2_EHCI_FUNC, INTA, pin_swizzle(PIRQA, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
+ TCF_DMA1_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
+ TCF_UART0_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
+ TCF_UART1_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
+ TCF_UART2_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_10,
+ TCF_UART3_FUNC, INTB, pin_swizzle(PIRQB, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
+ TCF_DMA2_FUNC, INTC, pin_swizzle(PIRQC, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
+ TCF_SPI_FUNC, INTC, pin_swizzle(PIRQC, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
+ TCF_I2C_FUNC, INTC, pin_swizzle(PIRQC, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
+ TCF_CAN_FUNC, INTC, pin_swizzle(PIRQC, i));
+ fill_irq_info(&slot, &irq_entries, tcf_bus, TCF_DEV_12,
+ TCF_1588_FUNC, INTC, pin_swizzle(PIRQC, i));
+ }
+
+ /* Call board-specific routine to fill in add-in card's irq info */
+ irq_entries += board_fill_irq_info(slot);
+
+ rt->size = irq_entries * sizeof(struct irq_info) + 32;
+
+ pirq_routing_table = rt;
+
+ return 0;
+}
+
+void pirq_init(void)
+{
+ struct tnc_rcba *rcba;
+ u32 base;
+
+ base = x86_pci_read_config32(TNC_LPC, LPC_RCBA);
+ base &= ~MEM_BAR_EN;
+ rcba = (struct tnc_rcba *)base;
+
+ /* Make sure all internal PCI devices are using INTA */
+ writel(INTA, &rcba->d02ip);
+ writel(INTA, &rcba->d03ip);
+ writel(INTA, &rcba->d27ip);
+ writel(INTA, &rcba->d31ip);
+ writel(INTA, &rcba->d23ip);
+ writel(INTA, &rcba->d24ip);
+ writel(INTA, &rcba->d25ip);
+ writel(INTA, &rcba->d26ip);
+
+ /*
+ * Route TunnelCreek PCI device interrupt pin to PIRQ
+ *
+ * Since PCIe downstream ports received INTx are routed to PIRQ
+ * A/B/C/D directly and not configurable, we route internal PCI
+ * device's INTx to PIRQ E/F/G/H.
+ */
+ writew(PIRQE, &rcba->d02ir);
+ writew(PIRQF, &rcba->d03ir);
+ writew(PIRQG, &rcba->d27ir);
+ writew(PIRQH, &rcba->d31ir);
+ writew(PIRQE, &rcba->d23ir);
+ writew(PIRQF, &rcba->d24ir);
+ writew(PIRQG, &rcba->d25ir);
+ writew(PIRQH, &rcba->d26ir);
+
+ if (create_pirq_routing_table()) {
+ debug("Failed to create pirq routing table\n");
+ } else {
+ /* Route PIRQ */
+ pirq_route_irqs(pirq_routing_table->slots,
+ get_irq_slot_count(pirq_routing_table));
+ }
+}
+
+u32 write_pirq_routing_table(u32 addr)
+{
+ return copy_pirq_routing_table(addr, pirq_routing_table);
+}
diff --git a/arch/x86/cpu/queensbay/tnc.c b/arch/x86/cpu/queensbay/tnc.c
index b7236e7b60..b46a7e996f 100644
--- a/arch/x86/cpu/queensbay/tnc.c
+++ b/arch/x86/cpu/queensbay/tnc.c
@@ -8,7 +8,8 @@
#include <asm/io.h>
#include <asm/pci.h>
#include <asm/post.h>
-#include <asm/arch/tnc.h>
+#include <asm/arch/device.h>
+#include <asm/arch/irq.h>
#include <asm/fsp/fsp_support.h>
#include <asm/processor.h>
@@ -16,9 +17,9 @@ static void unprotect_spi_flash(void)
{
u32 bc;
- bc = x86_pci_read_config32(PCH_LPC_DEV, 0xd8);
+ bc = x86_pci_read_config32(TNC_LPC, 0xd8);
bc |= 0x1; /* unprotect the flash */
- x86_pci_write_config32(PCH_LPC_DEV, 0xd8, bc);
+ x86_pci_write_config32(TNC_LPC, 0xd8, bc);
}
int arch_cpu_init(void)
@@ -43,3 +44,10 @@ int arch_cpu_init(void)
return 0;
}
+
+int arch_misc_init(void)
+{
+ pirq_init();
+
+ return 0;
+}
diff --git a/arch/x86/cpu/sipi_vector.S b/arch/x86/cpu/sipi_vector.S
new file mode 100644
index 0000000000..bcef12c6f1
--- /dev/null
+++ b/arch/x86/cpu/sipi_vector.S
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * Taken from coreboot file of the same name
+ */
+
+/*
+ * The SIPI vector is responsible for initializing the APs in the sytem. It
+ * loads microcode, sets up MSRs, and enables caching before calling into
+ * C code
+ */
+
+#include <asm/global_data.h>
+#include <asm/msr-index.h>
+#include <asm/processor.h>
+#include <asm/processor-flags.h>
+#include <asm/sipi.h>
+
+#define CODE_SEG (X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE)
+#define DATA_SEG (X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE)
+
+/*
+ * First we have the 16-bit section. Every AP process starts here.
+ * The simple task is to load U-Boot's Global Descriptor Table (GDT) to allow
+ * U-Boot's 32-bit code to become visible, then jump to ap_start.
+ *
+ * Note that this code is copied to RAM below 1MB in mp_init.c, and runs from
+ * there, but the 32-bit code (ap_start and onwards) is part of U-Boot and
+ * is therefore relocated to the top of RAM with other U-Boot code. This
+ * means that for the 16-bit code we must write relocatable code, but for the
+ * rest, we can do what we like.
+ */
+.text
+.code16
+.globl ap_start16
+ap_start16:
+ cli
+ xorl %eax, %eax
+ movl %eax, %cr3 /* Invalidate TLB */
+
+ /* setup the data segment */
+ movw %cs, %ax
+ movw %ax, %ds
+
+ /* Use an address relative to the data segment for the GDT */
+ movl $gdtaddr, %ebx
+ subl $ap_start16, %ebx
+
+ data32 lgdt (%ebx)
+
+ movl %cr0, %eax
+ andl $(~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE | \
+ X86_CR0_TS | X86_CR0_EM | X86_CR0_MP)), %eax
+ orl $(X86_CR0_NW | X86_CR0_CD | X86_CR0_PE), %eax
+ movl %eax, %cr0
+
+ movl $ap_start_jmp, %eax
+ subl $ap_start16, %eax
+ movw %ax, %bp
+
+ /* Jump to ap_start within U-Boot */
+data32 cs ljmp *(%bp)
+
+ .align 4
+.globl sipi_params_16bit
+sipi_params_16bit:
+ /* 48-bit far pointer */
+ap_start_jmp:
+ .long 0 /* offset set to ap_start by U-Boot */
+ .word CODE_SEG /* segment */
+
+ .word 0 /* padding */
+gdtaddr:
+ .word 0 /* limit */
+ .long 0 /* table */
+ .word 0 /* unused */
+
+.globl ap_start16_code_end
+ap_start16_code_end:
+
+/*
+ * Set up the special 'fs' segment for global_data. Then jump to ap_continue
+ * to set up the AP.
+ */
+.globl ap_start
+ap_start:
+ .code32
+ movw $DATA_SEG, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %ss
+ movw %ax, %gs
+
+ movw $(X86_GDT_ENTRY_32BIT_FS * X86_GDT_ENTRY_SIZE), %ax
+ movw %ax, %fs
+
+ /* Load the Interrupt descriptor table */
+ mov idt_ptr, %ebx
+ lidt (%ebx)
+
+ /* Obtain cpu number */
+ movl ap_count, %eax
+1:
+ movl %eax, %ecx
+ inc %ecx
+ lock cmpxchg %ecx, ap_count
+ jnz 1b
+
+ /* Setup stacks for each CPU */
+ movl stack_size, %eax
+ mul %ecx
+ movl stack_top, %edx
+ subl %eax, %edx
+ mov %edx, %esp
+ /* Save cpu number */
+ mov %ecx, %esi
+
+ /* Determine if one should check microcode versions */
+ mov microcode_ptr, %edi
+ test %edi, %edi
+ jz microcode_done /* Bypass if no microde exists */
+
+ /* Get the Microcode version */
+ mov $1, %eax
+ cpuid
+ mov $MSR_IA32_UCODE_REV, %ecx
+ rdmsr
+ /* If something already loaded skip loading again */
+ test %edx, %edx
+ jnz microcode_done
+
+ /* Determine if parallel microcode loading is allowed */
+ cmp $0xffffffff, microcode_lock
+ je load_microcode
+
+ /* Protect microcode loading */
+lock_microcode:
+ lock bts $0, microcode_lock
+ jc lock_microcode
+
+load_microcode:
+ /* Load new microcode */
+ mov $MSR_IA32_UCODE_WRITE, %ecx
+ xor %edx, %edx
+ mov %edi, %eax
+ /*
+ * The microcode pointer is passed in pointing to the header. Adjust
+ * pointer to reflect the payload (header size is 48 bytes)
+ */
+ add $UCODE_HEADER_LEN, %eax
+ pusha
+ wrmsr
+ popa
+
+ /* Unconditionally unlock microcode loading */
+ cmp $0xffffffff, microcode_lock
+ je microcode_done
+
+ xor %eax, %eax
+ mov %eax, microcode_lock
+
+microcode_done:
+ /*
+ * Load MSRs. Each entry in the table consists of:
+ * 0: index,
+ * 4: value[31:0]
+ * 8: value[63:32]
+ * See struct saved_msr in mp_init.c.
+ */
+ mov msr_table_ptr, %edi
+ mov msr_count, %ebx
+ test %ebx, %ebx
+ jz 1f
+load_msr:
+ mov (%edi), %ecx
+ mov 4(%edi), %eax
+ mov 8(%edi), %edx
+ wrmsr
+ add $12, %edi
+ dec %ebx
+ jnz load_msr
+
+1:
+ /* Enable caching */
+ mov %cr0, %eax
+ andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax
+ mov %eax, %cr0
+
+ /* c_handler(cpu_num) */
+ movl %esi, %eax /* cpu_num */
+ mov c_handler, %eax
+ call *%eax
+
+ .align 4
+.globl sipi_params
+sipi_params:
+idt_ptr:
+ .long 0
+stack_top:
+ .long 0
+stack_size:
+ .long 0
+microcode_lock:
+ .long 0
+microcode_ptr:
+ .long 0
+msr_table_ptr:
+ .long 0
+msr_count:
+ .long 0
+c_handler:
+ .long 0
+ap_count:
+ .long 0