avionic design with actual uboot and tooling

submodule of avionic design uboot bootloader and with included tools to
get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
2026-03-03 21:46:32 +02:00
parent fe3ba02c96
commit 68d74d3181
11967 changed files with 2221897 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2002
# Daniel Engström, Omicron Ceti AB, daniel@omicron.se.
#
# SPDX-License-Identifier: GPL-2.0+
#
extra-y = start.o
obj-$(CONFIG_X86_RESET_VECTOR) += resetvec.o start16.o
obj-y += interrupts.o cpu.o cpu_x86.o call64.o
AFLAGS_REMOVE_call32.o := -mregparm=3 \
$(if $(CONFIG_EFI_STUB_64BIT),-march=i386 -m32)
AFLAGS_call32.o := -fpic -fshort-wchar
extra-y += call32.o
obj-y += intel_common/
obj-$(CONFIG_INTEL_BAYTRAIL) += baytrail/
obj-$(CONFIG_INTEL_BROADWELL) += broadwell/
obj-$(CONFIG_SYS_COREBOOT) += coreboot/
obj-$(CONFIG_EFI_APP) += efi/
obj-$(CONFIG_QEMU) += qemu/
obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/
obj-$(CONFIG_INTEL_QUARK) += quark/
obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/
obj-y += irq.o lapic.o ioapic.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

View File

@@ -0,0 +1,9 @@
#
# Copyright (C) 2015 Google, Inc
#
# SPDX-License-Identifier: GPL-2.0+
#
config INTEL_BAYTRAIL
bool
select HAVE_FSP if !EFI

View File

@@ -0,0 +1,11 @@
#
# Copyright (C) 2015 Google, Inc
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += cpu.o
obj-y += early_uart.o
obj-y += fsp_configs.o
obj-y += valleyview.o
obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/acpi_table.h>
#include <asm/ioapic.h>
#include <asm/mpspec.h>
#include <asm/tables.h>
#include <asm/arch/iomap.h>
void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs,
void *dsdt)
{
struct acpi_table_header *header = &(fadt->header);
u16 pmbase = ACPI_BASE_ADDRESS;
memset((void *)fadt, 0, sizeof(struct acpi_fadt));
acpi_fill_header(header, "FACP");
header->length = sizeof(struct acpi_fadt);
header->revision = 4;
fadt->firmware_ctrl = (u32)facs;
fadt->dsdt = (u32)dsdt;
fadt->preferred_pm_profile = ACPI_PM_MOBILE;
fadt->sci_int = 9;
fadt->smi_cmd = 0;
fadt->acpi_enable = 0;
fadt->acpi_disable = 0;
fadt->s4bios_req = 0;
fadt->pstate_cnt = 0;
fadt->pm1a_evt_blk = pmbase;
fadt->pm1b_evt_blk = 0x0;
fadt->pm1a_cnt_blk = pmbase + 0x4;
fadt->pm1b_cnt_blk = 0x0;
fadt->pm2_cnt_blk = pmbase + 0x50;
fadt->pm_tmr_blk = pmbase + 0x8;
fadt->gpe0_blk = pmbase + 0x20;
fadt->gpe1_blk = 0;
fadt->pm1_evt_len = 4;
fadt->pm1_cnt_len = 2;
fadt->pm2_cnt_len = 1;
fadt->pm_tmr_len = 4;
fadt->gpe0_blk_len = 8;
fadt->gpe1_blk_len = 0;
fadt->gpe1_base = 0;
fadt->cst_cnt = 0;
fadt->p_lvl2_lat = ACPI_FADT_C2_NOT_SUPPORTED;
fadt->p_lvl3_lat = ACPI_FADT_C3_NOT_SUPPORTED;
fadt->flush_size = 0;
fadt->flush_stride = 0;
fadt->duty_offset = 1;
fadt->duty_width = 0;
fadt->day_alrm = 0x0d;
fadt->mon_alrm = 0x00;
fadt->century = 0x00;
fadt->iapc_boot_arch = ACPI_FADT_LEGACY_DEVICES | ACPI_FADT_8042;
fadt->flags = ACPI_FADT_WBINVD | ACPI_FADT_C1_SUPPORTED |
ACPI_FADT_C2_MP_SUPPORTED | ACPI_FADT_SLEEP_BUTTON |
ACPI_FADT_S4_RTC_WAKE | ACPI_FADT_RESET_REGISTER |
ACPI_FADT_PLATFORM_CLOCK;
fadt->reset_reg.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->reset_reg.bit_width = 8;
fadt->reset_reg.bit_offset = 0;
fadt->reset_reg.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
fadt->reset_reg.addrl = IO_PORT_RESET;
fadt->reset_reg.addrh = 0;
fadt->reset_value = SYS_RST | RST_CPU;
fadt->x_firmware_ctl_l = (u32)facs;
fadt->x_firmware_ctl_h = 0;
fadt->x_dsdt_l = (u32)dsdt;
fadt->x_dsdt_h = 0;
fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8;
fadt->x_pm1a_evt_blk.bit_offset = 0;
fadt->x_pm1a_evt_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
fadt->x_pm1a_evt_blk.addrl = fadt->pm1a_evt_blk;
fadt->x_pm1a_evt_blk.addrh = 0x0;
fadt->x_pm1b_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm1b_evt_blk.bit_width = 0;
fadt->x_pm1b_evt_blk.bit_offset = 0;
fadt->x_pm1b_evt_blk.access_size = 0;
fadt->x_pm1b_evt_blk.addrl = 0x0;
fadt->x_pm1b_evt_blk.addrh = 0x0;
fadt->x_pm1a_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm1a_cnt_blk.bit_width = fadt->pm1_cnt_len * 8;
fadt->x_pm1a_cnt_blk.bit_offset = 0;
fadt->x_pm1a_cnt_blk.access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
fadt->x_pm1a_cnt_blk.addrl = fadt->pm1a_cnt_blk;
fadt->x_pm1a_cnt_blk.addrh = 0x0;
fadt->x_pm1b_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm1b_cnt_blk.bit_width = 0;
fadt->x_pm1b_cnt_blk.bit_offset = 0;
fadt->x_pm1b_cnt_blk.access_size = 0;
fadt->x_pm1b_cnt_blk.addrl = 0x0;
fadt->x_pm1b_cnt_blk.addrh = 0x0;
fadt->x_pm2_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm2_cnt_blk.bit_width = fadt->pm2_cnt_len * 8;
fadt->x_pm2_cnt_blk.bit_offset = 0;
fadt->x_pm2_cnt_blk.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
fadt->x_pm2_cnt_blk.addrl = fadt->pm2_cnt_blk;
fadt->x_pm2_cnt_blk.addrh = 0x0;
fadt->x_pm_tmr_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm_tmr_blk.bit_width = fadt->pm_tmr_len * 8;
fadt->x_pm_tmr_blk.bit_offset = 0;
fadt->x_pm_tmr_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
fadt->x_pm_tmr_blk.addrl = fadt->pm_tmr_blk;
fadt->x_pm_tmr_blk.addrh = 0x0;
fadt->x_gpe0_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_gpe0_blk.bit_width = fadt->gpe0_blk_len * 8;
fadt->x_gpe0_blk.bit_offset = 0;
fadt->x_gpe0_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
fadt->x_gpe0_blk.addrl = fadt->gpe0_blk;
fadt->x_gpe0_blk.addrh = 0x0;
fadt->x_gpe1_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_gpe1_blk.bit_width = 0;
fadt->x_gpe1_blk.bit_offset = 0;
fadt->x_gpe1_blk.access_size = 0;
fadt->x_gpe1_blk.addrl = 0x0;
fadt->x_gpe1_blk.addrh = 0x0;
header->checksum = table_compute_checksum(fadt, header->length);
}
static int acpi_create_madt_irq_overrides(u32 current)
{
struct acpi_madt_irqoverride *irqovr;
u16 sci_flags = MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_HIGH;
int length = 0;
irqovr = (void *)current;
length += acpi_create_madt_irqoverride(irqovr, 0, 0, 2, 0);
irqovr = (void *)(current + length);
length += acpi_create_madt_irqoverride(irqovr, 0, 9, 9, sci_flags);
return length;
}
u32 acpi_fill_madt(u32 current)
{
current += acpi_create_madt_lapics(current);
current += acpi_create_madt_ioapic((struct acpi_madt_ioapic *)current,
io_apic_read(IO_APIC_ID) >> 24, IO_APIC_ADDR, 0);
current += acpi_create_madt_irq_overrides(current);
return current;
}

View File

@@ -0,0 +1,158 @@
/*
* 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/cpu_x86.h>
#include <asm/lapic.h>
#include <asm/msr.h>
#include <asm/turbo.h>
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)
{
if (!ll_boot_init())
return 0;
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 baytrail_get_count(struct udevice *dev)
{
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++;
}
return 0;
}
static const struct cpu_ops cpu_x86_baytrail_ops = {
.get_desc = cpu_x86_get_desc,
.get_info = baytrail_get_info,
.get_count = baytrail_get_count,
};
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_bind,
.probe = cpu_x86_baytrail_probe,
.ops = &cpu_x86_baytrail_ops,
};

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2015 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#define PCI_DEV_CONFIG(segbus, dev, fn) ( \
(((segbus) & 0xfff) << 20) | \
(((dev) & 0x1f) << 15) | \
(((fn) & 0x07) << 12))
/* Platform Controller Unit */
#define LPC_DEV 0x1f
#define LPC_FUNC 0
/* Enable UART */
#define UART_CONT 0x80
/* SCORE Pad definitions */
#define UART_RXD_PAD 82
#define UART_TXD_PAD 83
/* Pad base: PAD_CONF0[n]= PAD_BASE + 16 * n */
#define GPSCORE_PAD_BASE (IO_BASE_ADDRESS + IO_BASE_OFFSET_GPSCORE)
/* IO Memory */
#define IO_BASE_ADDRESS 0xfed0c000
#define IO_BASE_OFFSET_GPSCORE 0x0000
#define IO_BASE_OFFSET_GPNCORE 0x1000
#define IO_BASE_OFFSET_GPSSUS 0x2000
#define IO_BASE_SIZE 0x4000
static inline unsigned int score_pconf0(int pad_num)
{
return GPSCORE_PAD_BASE + pad_num * 16;
}
static void score_select_func(int pad, int func)
{
uint32_t reg;
uint32_t pconf0_addr = score_pconf0(pad);
reg = readl(pconf0_addr);
reg &= ~0x7;
reg |= func & 0x7;
writel(reg, pconf0_addr);
}
static void x86_pci_write_config32(int dev, unsigned int where, u32 value)
{
unsigned long addr;
addr = CONFIG_PCIE_ECAM_BASE | dev | (where & ~3);
writel(value, addr);
}
/* This can be called after memory-mapped PCI is working */
int setup_internal_uart(int enable)
{
/* Enable or disable the legacy UART hardware */
x86_pci_write_config32(PCI_DEV_CONFIG(0, LPC_DEV, LPC_FUNC), UART_CONT,
enable);
/* All done for the disable part, so just return */
if (!enable)
return 0;
/*
* Set up the pads to the UART function. This allows the signals to
* leave the chip
*/
score_select_func(UART_RXD_PAD, 1);
score_select_func(UART_TXD_PAD, 1);
/* TODO(sjg@chromium.org): Call debug_uart_init() */
return 0;
}

View File

@@ -0,0 +1,265 @@
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
* Copyright (C) 2015, Kodak Alaris, Inc
*
* SPDX-License-Identifier: Intel
*/
#include <common.h>
#include <fdtdec.h>
#include <asm/arch/fsp/azalia.h>
#include <asm/fsp/fsp_support.h>
DECLARE_GLOBAL_DATA_PTR;
/* ALC262 Verb Table - 10EC0262 */
static const uint32_t verb_table_data13[] = {
/* Pin Complex (NID 0x11) */
0x01171cf0,
0x01171d11,
0x01171e11,
0x01171f41,
/* Pin Complex (NID 0x12) */
0x01271cf0,
0x01271d11,
0x01271e11,
0x01271f41,
/* Pin Complex (NID 0x14) */
0x01471c10,
0x01471d40,
0x01471e01,
0x01471f01,
/* Pin Complex (NID 0x15) */
0x01571cf0,
0x01571d11,
0x01571e11,
0x01571f41,
/* Pin Complex (NID 0x16) */
0x01671cf0,
0x01671d11,
0x01671e11,
0x01671f41,
/* Pin Complex (NID 0x18) */
0x01871c20,
0x01871d98,
0x01871ea1,
0x01871f01,
/* Pin Complex (NID 0x19) */
0x01971c21,
0x01971d98,
0x01971ea1,
0x01971f02,
/* Pin Complex (NID 0x1A) */
0x01a71c2f,
0x01a71d30,
0x01a71e81,
0x01a71f01,
/* Pin Complex */
0x01b71c1f,
0x01b71d40,
0x01b71e21,
0x01b71f02,
/* Pin Complex */
0x01c71cf0,
0x01c71d11,
0x01c71e11,
0x01c71f41,
/* Pin Complex */
0x01d71c01,
0x01d71dc6,
0x01d71e14,
0x01d71f40,
/* Pin Complex */
0x01e71cf0,
0x01e71d11,
0x01e71e11,
0x01e71f41,
/* Pin Complex */
0x01f71cf0,
0x01f71d11,
0x01f71e11,
0x01f71f41,
};
/*
* This needs to be in ROM since if we put it in CAR, FSP init loses it when
* it drops CAR.
*
* TODO(sjg@chromium.org): Move to device tree when FSP allows it
*
* VerbTable: (RealTek ALC262)
* Revision ID = 0xFF, support all steps
* Codec Verb Table For AZALIA
* Codec Address: CAd value (0/1/2)
* Codec Vendor: 0x10EC0262
*/
static const struct pch_azalia_verb_table azalia_verb_table[] = {
{
{
0x10ec0262,
0x0000,
0xff,
0x01,
0x000b,
0x0002,
},
verb_table_data13
}
};
const struct pch_azalia_config azalia_config = {
.pme_enable = 1,
.docking_supported = 1,
.docking_attached = 0,
.hdmi_codec_enable = 1,
.azalia_v_ci_enable = 1,
.rsvdbits = 0,
.azalia_verb_table_num = 1,
.azalia_verb_table = azalia_verb_table,
.reset_wait_timer_us = 300
};
/**
* Override the FSP's configuration data.
* If the device tree does not specify an integer setting, use the default
* provided in Intel's Baytrail_FSP_Gold4.tgz release FSP/BayleyBayFsp.bsf file.
*/
void update_fsp_configs(struct fsp_config_data *config,
struct fspinit_rtbuf *rt_buf)
{
struct upd_region *fsp_upd = &config->fsp_upd;
struct memory_down_data *mem;
const void *blob = gd->fdt_blob;
int node;
/* Initialize runtime buffer for fsp_init() */
rt_buf->common.stack_top = config->common.stack_top - 32;
rt_buf->common.boot_mode = config->common.boot_mode;
rt_buf->common.upd_data = &config->fsp_upd;
fsp_upd->azalia_config_ptr = (uint32_t)&azalia_config;
node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_BAYTRAIL_FSP);
if (node < 0) {
debug("%s: Cannot find FSP node\n", __func__);
return;
}
fsp_upd->mrc_init_tseg_size = fdtdec_get_int(blob, node,
"fsp,mrc-init-tseg-size",
0);
fsp_upd->mrc_init_mmio_size = fdtdec_get_int(blob, node,
"fsp,mrc-init-mmio-size",
0x800);
fsp_upd->mrc_init_spd_addr1 = fdtdec_get_int(blob, node,
"fsp,mrc-init-spd-addr1",
0xa0);
fsp_upd->mrc_init_spd_addr2 = fdtdec_get_int(blob, node,
"fsp,mrc-init-spd-addr2",
0xa2);
fsp_upd->emmc_boot_mode = fdtdec_get_int(blob, node,
"fsp,emmc-boot-mode", 2);
fsp_upd->enable_sdio = fdtdec_get_bool(blob, node, "fsp,enable-sdio");
fsp_upd->enable_sdcard = fdtdec_get_bool(blob, node,
"fsp,enable-sdcard");
fsp_upd->enable_hsuart0 = fdtdec_get_bool(blob, node,
"fsp,enable-hsuart0");
fsp_upd->enable_hsuart1 = fdtdec_get_bool(blob, node,
"fsp,enable-hsuart1");
fsp_upd->enable_spi = fdtdec_get_bool(blob, node, "fsp,enable-spi");
fsp_upd->enable_sata = fdtdec_get_bool(blob, node, "fsp,enable-sata");
fsp_upd->sata_mode = fdtdec_get_int(blob, node, "fsp,sata-mode", 1);
fsp_upd->enable_azalia = fdtdec_get_bool(blob, node,
"fsp,enable-azalia");
fsp_upd->enable_xhci = fdtdec_get_bool(blob, node, "fsp,enable-xhci");
fsp_upd->enable_lpe = fdtdec_get_bool(blob, node, "fsp,enable-lpe");
fsp_upd->lpss_sio_enable_pci_mode = fdtdec_get_bool(blob, node,
"fsp,lpss-sio-enable-pci-mode");
fsp_upd->enable_dma0 = fdtdec_get_bool(blob, node, "fsp,enable-dma0");
fsp_upd->enable_dma1 = fdtdec_get_bool(blob, node, "fsp,enable-dma1");
fsp_upd->enable_i2_c0 = fdtdec_get_bool(blob, node, "fsp,enable-i2c0");
fsp_upd->enable_i2_c1 = fdtdec_get_bool(blob, node, "fsp,enable-i2c1");
fsp_upd->enable_i2_c2 = fdtdec_get_bool(blob, node, "fsp,enable-i2c2");
fsp_upd->enable_i2_c3 = fdtdec_get_bool(blob, node, "fsp,enable-i2c3");
fsp_upd->enable_i2_c4 = fdtdec_get_bool(blob, node, "fsp,enable-i2c4");
fsp_upd->enable_i2_c5 = fdtdec_get_bool(blob, node, "fsp,enable-i2c5");
fsp_upd->enable_i2_c6 = fdtdec_get_bool(blob, node, "fsp,enable-i2c6");
fsp_upd->enable_pwm0 = fdtdec_get_bool(blob, node, "fsp,enable-pwm0");
fsp_upd->enable_pwm1 = fdtdec_get_bool(blob, node, "fsp,enable-pwm1");
fsp_upd->enable_hsi = fdtdec_get_bool(blob, node, "fsp,enable-hsi");
fsp_upd->igd_dvmt50_pre_alloc = fdtdec_get_int(blob, node,
"fsp,igd-dvmt50-pre-alloc", 2);
fsp_upd->aperture_size = fdtdec_get_int(blob, node, "fsp,aperture-size",
2);
fsp_upd->gtt_size = fdtdec_get_int(blob, node, "fsp,gtt-size", 2);
fsp_upd->serial_debug_port_address = fdtdec_get_int(blob, node,
"fsp,serial-debug-port-address", 0x3f8);
fsp_upd->serial_debug_port_type = fdtdec_get_int(blob, node,
"fsp,serial-debug-port-type", 1);
fsp_upd->mrc_debug_msg = fdtdec_get_bool(blob, node,
"fsp,mrc-debug-msg");
fsp_upd->isp_enable = fdtdec_get_bool(blob, node, "fsp,isp-enable");
fsp_upd->scc_enable_pci_mode = fdtdec_get_bool(blob, node,
"fsp,scc-enable-pci-mode");
fsp_upd->igd_render_standby = fdtdec_get_bool(blob, node,
"fsp,igd-render-standby");
fsp_upd->txe_uma_enable = fdtdec_get_bool(blob, node,
"fsp,txe-uma-enable");
fsp_upd->os_selection = fdtdec_get_int(blob, node, "fsp,os-selection",
4);
fsp_upd->emmc45_ddr50_enabled = fdtdec_get_bool(blob, node,
"fsp,emmc45-ddr50-enabled");
fsp_upd->emmc45_hs200_enabled = fdtdec_get_bool(blob, node,
"fsp,emmc45-hs200-enabled");
fsp_upd->emmc45_retune_timer_value = fdtdec_get_int(blob, node,
"fsp,emmc45-retune-timer-value", 8);
fsp_upd->enable_igd = fdtdec_get_bool(blob, node, "fsp,enable-igd");
mem = &fsp_upd->memory_params;
mem->enable_memory_down = fdtdec_get_bool(blob, node,
"fsp,enable-memory-down");
if (mem->enable_memory_down) {
node = fdtdec_next_compatible(blob, node,
COMPAT_INTEL_BAYTRAIL_FSP_MDP);
if (node < 0) {
debug("%s: Cannot find FSP memory-down-params node\n",
__func__);
} else {
mem->dram_speed = fdtdec_get_int(blob, node,
"fsp,dram-speed",
0x02);
mem->dram_type = fdtdec_get_int(blob, node,
"fsp,dram-type", 0x01);
mem->dimm_0_enable = fdtdec_get_bool(blob, node,
"fsp,dimm-0-enable");
mem->dimm_1_enable = fdtdec_get_bool(blob, node,
"fsp,dimm-1-enable");
mem->dimm_width = fdtdec_get_int(blob, node,
"fsp,dimm-width",
0x00);
mem->dimm_density = fdtdec_get_int(blob, node,
"fsp,dimm-density",
0x01);
mem->dimm_bus_width = fdtdec_get_int(blob, node,
"fsp,dimm-bus-width", 0x03);
mem->dimm_sides = fdtdec_get_int(blob, node,
"fsp,dimm-sides",
0x00);
mem->dimm_tcl = fdtdec_get_int(blob, node,
"fsp,dimm-tcl", 0x09);
mem->dimm_trpt_rcd = fdtdec_get_int(blob, node,
"fsp,dimm-trpt-rcd", 0x09);
mem->dimm_twr = fdtdec_get_int(blob, node,
"fsp,dimm-twr", 0x0A);
mem->dimm_twtr = fdtdec_get_int(blob, node,
"fsp,dimm-twtr", 0x05);
mem->dimm_trrd = fdtdec_get_int(blob, node,
"fsp,dimm-trrd", 0x04);
mem->dimm_trtp = fdtdec_get_int(blob, node,
"fsp,dimm-trtp", 0x05);
mem->dimm_tfaw = fdtdec_get_int(blob, node,
"fsp,dimm-tfaw", 0x14);
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <mmc.h>
#include <pci_ids.h>
#include <asm/irq.h>
#include <asm/mrccache.h>
#include <asm/post.h>
static struct pci_device_id mmc_supported[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VALLEYVIEW_SDIO },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VALLEYVIEW_SDCARD },
{},
};
int cpu_mmc_init(bd_t *bis)
{
return pci_mmc_init("ValleyView SDHCI", mmc_supported);
}
#ifndef CONFIG_EFI_APP
int arch_cpu_init(void)
{
int ret;
post_code(POST_CPU_INIT);
ret = x86_cpu_init_f();
if (ret)
return ret;
return 0;
}
int arch_misc_init(void)
{
if (!ll_boot_init())
return 0;
#ifdef CONFIG_ENABLE_MRC_CACHE
/*
* We intend not to check any return value here, as even MRC cache
* is not saved successfully, it is not a severe error that will
* prevent system from continuing to boot.
*/
mrccache_save();
#endif
return 0;
}
#endif
void reset_cpu(ulong addr)
{
/* cold reset */
x86_full_reset();
}

View File

@@ -0,0 +1,30 @@
#
# Copyright (C) 2016 Google Inc.
#
# SPDX-License-Identifier: GPL-2.0
config INTEL_BROADWELL
bool
select CACHE_MRC_BIN
if INTEL_BROADWELL
config DCACHE_RAM_BASE
default 0xff7c0000
config DCACHE_RAM_SIZE
default 0x40000
config DCACHE_RAM_MRC_VAR_SIZE
default 0x30000
config CPU_SPECIFIC_OPTIONS
def_bool y
select SMM_TSEG
select X86_RAMTEST
config SMM_TSEG_SIZE
hex
default 0x800000
endif

View File

@@ -0,0 +1,17 @@
#
# Copyright (c) 2016 Google, Inc
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += cpu.o
obj-y += iobp.o
obj-y += lpc.o
obj-y += me.o
obj-y += northbridge.o
obj-y += pch.o
obj-y += pinctrl_broadwell.o
obj-y += power_state.o
obj-y += refcode.o
obj-y += sata.o
obj-y += sdram.o

View File

@@ -0,0 +1,761 @@
/*
* Copyright (c) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*
* Based on code from coreboot src/soc/intel/broadwell/cpu.c
*/
#include <common.h>
#include <dm.h>
#include <cpu.h>
#include <asm/cpu.h>
#include <asm/cpu_x86.h>
#include <asm/cpu_common.h>
#include <asm/intel_regs.h>
#include <asm/msr.h>
#include <asm/post.h>
#include <asm/turbo.h>
#include <asm/arch/cpu.h>
#include <asm/arch/pch.h>
#include <asm/arch/rcb.h>
struct cpu_broadwell_priv {
bool ht_disabled;
};
/* Convert time in seconds to POWER_LIMIT_1_TIME MSR value */
static const u8 power_limit_time_sec_to_msr[] = {
[0] = 0x00,
[1] = 0x0a,
[2] = 0x0b,
[3] = 0x4b,
[4] = 0x0c,
[5] = 0x2c,
[6] = 0x4c,
[7] = 0x6c,
[8] = 0x0d,
[10] = 0x2d,
[12] = 0x4d,
[14] = 0x6d,
[16] = 0x0e,
[20] = 0x2e,
[24] = 0x4e,
[28] = 0x6e,
[32] = 0x0f,
[40] = 0x2f,
[48] = 0x4f,
[56] = 0x6f,
[64] = 0x10,
[80] = 0x30,
[96] = 0x50,
[112] = 0x70,
[128] = 0x11,
};
/* Convert POWER_LIMIT_1_TIME MSR value to seconds */
static const u8 power_limit_time_msr_to_sec[] = {
[0x00] = 0,
[0x0a] = 1,
[0x0b] = 2,
[0x4b] = 3,
[0x0c] = 4,
[0x2c] = 5,
[0x4c] = 6,
[0x6c] = 7,
[0x0d] = 8,
[0x2d] = 10,
[0x4d] = 12,
[0x6d] = 14,
[0x0e] = 16,
[0x2e] = 20,
[0x4e] = 24,
[0x6e] = 28,
[0x0f] = 32,
[0x2f] = 40,
[0x4f] = 48,
[0x6f] = 56,
[0x10] = 64,
[0x30] = 80,
[0x50] = 96,
[0x70] = 112,
[0x11] = 128,
};
int arch_cpu_init_dm(void)
{
struct udevice *dev;
int ret;
/* Start up the LPC so we have serial */
ret = uclass_first_device(UCLASS_LPC, &dev);
if (ret)
return ret;
if (!dev)
return -ENODEV;
ret = cpu_set_flex_ratio_to_tdp_nominal();
if (ret)
return ret;
return 0;
}
void set_max_freq(void)
{
msr_t msr, perf_ctl, platform_info;
/* Check for configurable TDP option */
platform_info = msr_read(MSR_PLATFORM_INFO);
if ((platform_info.hi >> 1) & 3) {
/* Set to nominal TDP ratio */
msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
perf_ctl.lo = (msr.lo & 0xff) << 8;
} else {
/* Platform Info bits 15:8 give max ratio */
msr = msr_read(MSR_PLATFORM_INFO);
perf_ctl.lo = msr.lo & 0xff00;
}
perf_ctl.hi = 0;
msr_write(IA32_PERF_CTL, perf_ctl);
debug("CPU: frequency set to %d MHz\n",
((perf_ctl.lo >> 8) & 0xff) * CPU_BCLK);
}
int arch_cpu_init(void)
{
post_code(POST_CPU_INIT);
return x86_cpu_init_f();
}
int print_cpuinfo(void)
{
char processor_name[CPU_MAX_NAME_LEN];
const char *name;
int ret;
set_max_freq();
ret = cpu_common_init();
if (ret)
return ret;
gd->arch.pei_boot_mode = PEI_BOOT_NONE;
/* Print processor name */
name = cpu_get_name(processor_name);
printf("CPU: %s\n", name);
return 0;
}
/*
* The core 100MHz BLCK is disabled in deeper c-states. One needs to calibrate
* the 100MHz BCLCK against the 24MHz BLCK to restore the clocks properly
* when a core is woken up
*/
static int pcode_ready(void)
{
int wait_count;
const int delay_step = 10;
wait_count = 0;
do {
if (!(readl(MCHBAR_REG(BIOS_MAILBOX_INTERFACE)) &
MAILBOX_RUN_BUSY))
return 0;
wait_count += delay_step;
udelay(delay_step);
} while (wait_count < 1000);
return -ETIMEDOUT;
}
static u32 pcode_mailbox_read(u32 command)
{
int ret;
ret = pcode_ready();
if (ret) {
debug("PCODE: mailbox timeout on wait ready\n");
return ret;
}
/* Send command and start transaction */
writel(command | MAILBOX_RUN_BUSY, MCHBAR_REG(BIOS_MAILBOX_INTERFACE));
ret = pcode_ready();
if (ret) {
debug("PCODE: mailbox timeout on completion\n");
return ret;
}
/* Read mailbox */
return readl(MCHBAR_REG(BIOS_MAILBOX_DATA));
}
static int pcode_mailbox_write(u32 command, u32 data)
{
int ret;
ret = pcode_ready();
if (ret) {
debug("PCODE: mailbox timeout on wait ready\n");
return ret;
}
writel(data, MCHBAR_REG(BIOS_MAILBOX_DATA));
/* Send command and start transaction */
writel(command | MAILBOX_RUN_BUSY, MCHBAR_REG(BIOS_MAILBOX_INTERFACE));
ret = pcode_ready();
if (ret) {
debug("PCODE: mailbox timeout on completion\n");
return ret;
}
return 0;
}
/* @dev is the CPU device */
static void initialize_vr_config(struct udevice *dev)
{
int ramp, min_vid;
msr_t msr;
debug("Initializing VR config\n");
/* Configure VR_CURRENT_CONFIG */
msr = msr_read(MSR_VR_CURRENT_CONFIG);
/*
* Preserve bits 63 and 62. Bit 62 is PSI4 enable, but it is only valid
* on ULT systems
*/
msr.hi &= 0xc0000000;
msr.hi |= (0x01 << (52 - 32)); /* PSI3 threshold - 1A */
msr.hi |= (0x05 << (42 - 32)); /* PSI2 threshold - 5A */
msr.hi |= (0x14 << (32 - 32)); /* PSI1 threshold - 20A */
msr.hi |= (1 << (62 - 32)); /* Enable PSI4 */
/* Leave the max instantaneous current limit (12:0) to default */
msr_write(MSR_VR_CURRENT_CONFIG, msr);
/* Configure VR_MISC_CONFIG MSR */
msr = msr_read(MSR_VR_MISC_CONFIG);
/* Set the IOUT_SLOPE scalar applied to dIout in U10.1.9 format */
msr.hi &= ~(0x3ff << (40 - 32));
msr.hi |= (0x200 << (40 - 32)); /* 1.0 */
/* Set IOUT_OFFSET to 0 */
msr.hi &= ~0xff;
/* Set entry ramp rate to slow */
msr.hi &= ~(1 << (51 - 32));
/* Enable decay mode on C-state entry */
msr.hi |= (1 << (52 - 32));
/* Set the slow ramp rate */
msr.hi &= ~(0x3 << (53 - 32));
/* Configure the C-state exit ramp rate */
ramp = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "intel,slow-ramp",
-1);
if (ramp != -1) {
/* Configured slow ramp rate */
msr.hi |= ((ramp & 0x3) << (53 - 32));
/* Set exit ramp rate to slow */
msr.hi &= ~(1 << (50 - 32));
} else {
/* Fast ramp rate / 4 */
msr.hi |= (0x01 << (53 - 32));
/* Set exit ramp rate to fast */
msr.hi |= (1 << (50 - 32));
}
/* Set MIN_VID (31:24) to allow CPU to have full control */
msr.lo &= ~0xff000000;
min_vid = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "intel,min-vid",
0);
msr.lo |= (min_vid & 0xff) << 24;
msr_write(MSR_VR_MISC_CONFIG, msr);
/* Configure VR_MISC_CONFIG2 MSR */
msr = msr_read(MSR_VR_MISC_CONFIG2);
msr.lo &= ~0xffff;
/*
* Allow CPU to control minimum voltage completely (15:8) and
* set the fast ramp voltage in 10mV steps
*/
if (cpu_get_family_model() == BROADWELL_FAMILY_ULT)
msr.lo |= 0x006a; /* 1.56V */
else
msr.lo |= 0x006f; /* 1.60V */
msr_write(MSR_VR_MISC_CONFIG2, msr);
/* Set C9/C10 VCC Min */
pcode_mailbox_write(MAILBOX_BIOS_CMD_WRITE_C9C10_VOLTAGE, 0x1f1f);
}
static int calibrate_24mhz_bclk(void)
{
int err_code;
int ret;
ret = pcode_ready();
if (ret)
return ret;
/* A non-zero value initiates the PCODE calibration */
writel(~0, MCHBAR_REG(BIOS_MAILBOX_DATA));
writel(MAILBOX_RUN_BUSY | MAILBOX_BIOS_CMD_FSM_MEASURE_INTVL,
MCHBAR_REG(BIOS_MAILBOX_INTERFACE));
ret = pcode_ready();
if (ret)
return ret;
err_code = readl(MCHBAR_REG(BIOS_MAILBOX_INTERFACE)) & 0xff;
debug("PCODE: 24MHz BLCK calibration response: %d\n", err_code);
/* Read the calibrated value */
writel(MAILBOX_RUN_BUSY | MAILBOX_BIOS_CMD_READ_CALIBRATION,
MCHBAR_REG(BIOS_MAILBOX_INTERFACE));
ret = pcode_ready();
if (ret)
return ret;
debug("PCODE: 24MHz BLCK calibration value: 0x%08x\n",
readl(MCHBAR_REG(BIOS_MAILBOX_DATA)));
return 0;
}
static void configure_pch_power_sharing(void)
{
u32 pch_power, pch_power_ext, pmsync, pmsync2;
int i;
/* Read PCH Power levels from PCODE */
pch_power = pcode_mailbox_read(MAILBOX_BIOS_CMD_READ_PCH_POWER);
pch_power_ext = pcode_mailbox_read(MAILBOX_BIOS_CMD_READ_PCH_POWER_EXT);
debug("PCH Power: PCODE Levels 0x%08x 0x%08x\n", pch_power,
pch_power_ext);
pmsync = readl(RCB_REG(PMSYNC_CONFIG));
pmsync2 = readl(RCB_REG(PMSYNC_CONFIG2));
/*
* Program PMSYNC_TPR_CONFIG PCH power limit values
* pmsync[0:4] = mailbox[0:5]
* pmsync[8:12] = mailbox[6:11]
* pmsync[16:20] = mailbox[12:17]
*/
for (i = 0; i < 3; i++) {
u32 level = pch_power & 0x3f;
pch_power >>= 6;
pmsync &= ~(0x1f << (i * 8));
pmsync |= (level & 0x1f) << (i * 8);
}
writel(pmsync, RCB_REG(PMSYNC_CONFIG));
/*
* Program PMSYNC_TPR_CONFIG2 Extended PCH power limit values
* pmsync2[0:4] = mailbox[23:18]
* pmsync2[8:12] = mailbox_ext[6:11]
* pmsync2[16:20] = mailbox_ext[12:17]
* pmsync2[24:28] = mailbox_ext[18:22]
*/
pmsync2 &= ~0x1f;
pmsync2 |= pch_power & 0x1f;
for (i = 1; i < 4; i++) {
u32 level = pch_power_ext & 0x3f;
pch_power_ext >>= 6;
pmsync2 &= ~(0x1f << (i * 8));
pmsync2 |= (level & 0x1f) << (i * 8);
}
writel(pmsync2, RCB_REG(PMSYNC_CONFIG2));
}
static int bsp_init_before_ap_bringup(struct udevice *dev)
{
int ret;
initialize_vr_config(dev);
ret = calibrate_24mhz_bclk();
if (ret)
return ret;
configure_pch_power_sharing();
return 0;
}
int cpu_config_tdp_levels(void)
{
msr_t platform_info;
/* Bits 34:33 indicate how many levels supported */
platform_info = msr_read(MSR_PLATFORM_INFO);
return (platform_info.hi >> 1) & 3;
}
static void set_max_ratio(void)
{
msr_t msr, perf_ctl;
perf_ctl.hi = 0;
/* Check for configurable TDP option */
if (turbo_get_state() == TURBO_ENABLED) {
msr = msr_read(MSR_NHM_TURBO_RATIO_LIMIT);
perf_ctl.lo = (msr.lo & 0xff) << 8;
} else if (cpu_config_tdp_levels()) {
/* Set to nominal TDP ratio */
msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
perf_ctl.lo = (msr.lo & 0xff) << 8;
} else {
/* Platform Info bits 15:8 give max ratio */
msr = msr_read(MSR_PLATFORM_INFO);
perf_ctl.lo = msr.lo & 0xff00;
}
msr_write(IA32_PERF_CTL, perf_ctl);
debug("cpu: frequency set to %d\n",
((perf_ctl.lo >> 8) & 0xff) * CPU_BCLK);
}
int broadwell_init(struct udevice *dev)
{
struct cpu_broadwell_priv *priv = dev_get_priv(dev);
int num_threads;
int num_cores;
msr_t msr;
int ret;
msr = msr_read(CORE_THREAD_COUNT_MSR);
num_threads = (msr.lo >> 0) & 0xffff;
num_cores = (msr.lo >> 16) & 0xffff;
debug("CPU has %u cores, %u threads enabled\n", num_cores,
num_threads);
priv->ht_disabled = num_threads == num_cores;
ret = bsp_init_before_ap_bringup(dev);
if (ret)
return ret;
set_max_ratio();
return ret;
}
static void configure_mca(void)
{
msr_t msr;
const unsigned int mcg_cap_msr = 0x179;
int i;
int num_banks;
msr = msr_read(mcg_cap_msr);
num_banks = msr.lo & 0xff;
msr.lo = 0;
msr.hi = 0;
/*
* TODO(adurbin): This should only be done on a cold boot. Also, some
* of these banks are core vs package scope. For now every CPU clears
* every bank
*/
for (i = 0; i < num_banks; i++)
msr_write(MSR_IA32_MC0_STATUS + (i * 4), msr);
}
static void enable_lapic_tpr(void)
{
msr_t msr;
msr = msr_read(MSR_PIC_MSG_CONTROL);
msr.lo &= ~(1 << 10); /* Enable APIC TPR updates */
msr_write(MSR_PIC_MSG_CONTROL, msr);
}
static void configure_c_states(void)
{
msr_t msr;
msr = msr_read(MSR_PMG_CST_CONFIG_CONTROL);
msr.lo |= (1 << 31); /* Timed MWAIT Enable */
msr.lo |= (1 << 30); /* Package c-state Undemotion Enable */
msr.lo |= (1 << 29); /* Package c-state Demotion Enable */
msr.lo |= (1 << 28); /* C1 Auto Undemotion Enable */
msr.lo |= (1 << 27); /* C3 Auto Undemotion Enable */
msr.lo |= (1 << 26); /* C1 Auto Demotion Enable */
msr.lo |= (1 << 25); /* C3 Auto Demotion Enable */
msr.lo &= ~(1 << 10); /* Disable IO MWAIT redirection */
/* The deepest package c-state defaults to factory-configured value */
msr_write(MSR_PMG_CST_CONFIG_CONTROL, msr);
msr = msr_read(MSR_MISC_PWR_MGMT);
msr.lo &= ~(1 << 0); /* Enable P-state HW_ALL coordination */
msr_write(MSR_MISC_PWR_MGMT, msr);
msr = msr_read(MSR_POWER_CTL);
msr.lo |= (1 << 18); /* Enable Energy Perf Bias MSR 0x1b0 */
msr.lo |= (1 << 1); /* C1E Enable */
msr.lo |= (1 << 0); /* Bi-directional PROCHOT# */
msr_write(MSR_POWER_CTL, msr);
/* C-state Interrupt Response Latency Control 0 - package C3 latency */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_0_LIMIT;
msr_write(MSR_C_STATE_LATENCY_CONTROL_0, msr);
/* C-state Interrupt Response Latency Control 1 */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_1_LIMIT;
msr_write(MSR_C_STATE_LATENCY_CONTROL_1, msr);
/* C-state Interrupt Response Latency Control 2 - package C6/C7 short */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_2_LIMIT;
msr_write(MSR_C_STATE_LATENCY_CONTROL_2, msr);
/* C-state Interrupt Response Latency Control 3 - package C8 */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_3_LIMIT;
msr_write(MSR_C_STATE_LATENCY_CONTROL_3, msr);
/* C-state Interrupt Response Latency Control 4 - package C9 */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_4_LIMIT;
msr_write(MSR_C_STATE_LATENCY_CONTROL_4, msr);
/* C-state Interrupt Response Latency Control 5 - package C10 */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | C_STATE_LATENCY_CONTROL_5_LIMIT;
msr_write(MSR_C_STATE_LATENCY_CONTROL_5, msr);
}
static void configure_misc(void)
{
msr_t msr;
msr = msr_read(MSR_IA32_MISC_ENABLE);
msr.lo |= (1 << 0); /* Fast String enable */
msr.lo |= (1 << 3); /* TM1/TM2/EMTTM enable */
msr.lo |= (1 << 16); /* Enhanced SpeedStep Enable */
msr_write(MSR_IA32_MISC_ENABLE, msr);
/* Disable thermal interrupts */
msr.lo = 0;
msr.hi = 0;
msr_write(MSR_IA32_THERM_INTERRUPT, msr);
/* Enable package critical interrupt only */
msr.lo = 1 << 4;
msr.hi = 0;
msr_write(MSR_IA32_PACKAGE_THERM_INTERRUPT, msr);
}
static void configure_thermal_target(struct udevice *dev)
{
int tcc_offset;
msr_t msr;
tcc_offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"intel,tcc-offset", 0);
/* Set TCC activaiton offset if supported */
msr = msr_read(MSR_PLATFORM_INFO);
if ((msr.lo & (1 << 30)) && tcc_offset) {
msr = msr_read(MSR_TEMPERATURE_TARGET);
msr.lo &= ~(0xf << 24); /* Bits 27:24 */
msr.lo |= (tcc_offset & 0xf) << 24;
msr_write(MSR_TEMPERATURE_TARGET, msr);
}
}
static void configure_dca_cap(void)
{
struct cpuid_result cpuid_regs;
msr_t msr;
/* Check feature flag in CPUID.(EAX=1):ECX[18]==1 */
cpuid_regs = cpuid(1);
if (cpuid_regs.ecx & (1 << 18)) {
msr = msr_read(MSR_IA32_PLATFORM_DCA_CAP);
msr.lo |= 1;
msr_write(MSR_IA32_PLATFORM_DCA_CAP, msr);
}
}
static void set_energy_perf_bias(u8 policy)
{
msr_t msr;
int ecx;
/* Determine if energy efficient policy is supported */
ecx = cpuid_ecx(0x6);
if (!(ecx & (1 << 3)))
return;
/* Energy Policy is bits 3:0 */
msr = msr_read(MSR_IA32_ENERGY_PERFORMANCE_BIAS);
msr.lo &= ~0xf;
msr.lo |= policy & 0xf;
msr_write(MSR_IA32_ENERGY_PERFORMANCE_BIAS, msr);
debug("cpu: energy policy set to %u\n", policy);
}
/* All CPUs including BSP will run the following function */
static void cpu_core_init(struct udevice *dev)
{
/* Clear out pending MCEs */
configure_mca();
/* Enable the local cpu apics */
enable_lapic_tpr();
/* Configure C States */
configure_c_states();
/* Configure Enhanced SpeedStep and Thermal Sensors */
configure_misc();
/* Thermal throttle activation offset */
configure_thermal_target(dev);
/* Enable Direct Cache Access */
configure_dca_cap();
/* Set energy policy */
set_energy_perf_bias(ENERGY_POLICY_NORMAL);
/* Enable Turbo */
turbo_enable();
}
/*
* Configure processor power limits if possible
* This must be done AFTER set of BIOS_RESET_CPL
*/
void cpu_set_power_limits(int power_limit_1_time)
{
msr_t msr;
msr_t limit;
unsigned power_unit;
unsigned tdp, min_power, max_power, max_time;
u8 power_limit_1_val;
msr = msr_read(MSR_PLATFORM_INFO);
if (power_limit_1_time > ARRAY_SIZE(power_limit_time_sec_to_msr))
power_limit_1_time = 28;
if (!(msr.lo & PLATFORM_INFO_SET_TDP))
return;
/* Get units */
msr = msr_read(MSR_PKG_POWER_SKU_UNIT);
power_unit = 2 << ((msr.lo & 0xf) - 1);
/* Get power defaults for this SKU */
msr = msr_read(MSR_PKG_POWER_SKU);
tdp = msr.lo & 0x7fff;
min_power = (msr.lo >> 16) & 0x7fff;
max_power = msr.hi & 0x7fff;
max_time = (msr.hi >> 16) & 0x7f;
debug("CPU TDP: %u Watts\n", tdp / power_unit);
if (power_limit_time_msr_to_sec[max_time] > power_limit_1_time)
power_limit_1_time = power_limit_time_msr_to_sec[max_time];
if (min_power > 0 && tdp < min_power)
tdp = min_power;
if (max_power > 0 && tdp > max_power)
tdp = max_power;
power_limit_1_val = power_limit_time_sec_to_msr[power_limit_1_time];
/* Set long term power limit to TDP */
limit.lo = 0;
limit.lo |= tdp & PKG_POWER_LIMIT_MASK;
limit.lo |= PKG_POWER_LIMIT_EN;
limit.lo |= (power_limit_1_val & PKG_POWER_LIMIT_TIME_MASK) <<
PKG_POWER_LIMIT_TIME_SHIFT;
/* Set short term power limit to 1.25 * TDP */
limit.hi = 0;
limit.hi |= ((tdp * 125) / 100) & PKG_POWER_LIMIT_MASK;
limit.hi |= PKG_POWER_LIMIT_EN;
/* Power limit 2 time is only programmable on server SKU */
msr_write(MSR_PKG_POWER_LIMIT, limit);
/* Set power limit values in MCHBAR as well */
writel(limit.lo, MCHBAR_REG(MCH_PKG_POWER_LIMIT_LO));
writel(limit.hi, MCHBAR_REG(MCH_PKG_POWER_LIMIT_HI));
/* Set DDR RAPL power limit by copying from MMIO to MSR */
msr.lo = readl(MCHBAR_REG(MCH_DDR_POWER_LIMIT_LO));
msr.hi = readl(MCHBAR_REG(MCH_DDR_POWER_LIMIT_HI));
msr_write(MSR_DDR_RAPL_LIMIT, msr);
/* Use nominal TDP values for CPUs with configurable TDP */
if (cpu_config_tdp_levels()) {
msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
limit.hi = 0;
limit.lo = msr.lo & 0xff;
msr_write(MSR_TURBO_ACTIVATION_RATIO, limit);
}
}
static int broadwell_get_info(struct udevice *dev, struct cpu_info *info)
{
msr_t msr;
msr = msr_read(IA32_PERF_CTL);
info->cpu_freq = ((msr.lo >> 8) & 0xff) * BROADWELL_BCLK * 1000000;
info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU |
1 << CPU_FEAT_UCODE | 1 << CPU_FEAT_DEVICE_ID;
return 0;
}
static int broadwell_get_count(struct udevice *dev)
{
return 4;
}
static int cpu_x86_broadwell_probe(struct udevice *dev)
{
if (dev->seq == 0) {
cpu_core_init(dev);
return broadwell_init(dev);
}
return 0;
}
static const struct cpu_ops cpu_x86_broadwell_ops = {
.get_desc = cpu_x86_get_desc,
.get_info = broadwell_get_info,
.get_count = broadwell_get_count,
};
static const struct udevice_id cpu_x86_broadwell_ids[] = {
{ .compatible = "intel,core-i3-gen5" },
{ }
};
U_BOOT_DRIVER(cpu_x86_broadwell_drv) = {
.name = "cpu_x86_broadwell",
.id = UCLASS_CPU,
.of_match = cpu_x86_broadwell_ids,
.bind = cpu_x86_bind,
.probe = cpu_x86_broadwell_probe,
.ops = &cpu_x86_broadwell_ops,
.priv_auto_alloc_size = sizeof(struct cpu_broadwell_priv),
};

View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2016 Google, Inc
*
* Modified from coreboot
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <errno.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/arch/pch.h>
#define IOBP_RETRY 1000
/* IO Buffer Programming */
#define IOBPIRI 0x2330
#define IOBPD 0x2334
#define IOBPS 0x2338
#define IOBPS_READY 0x0001
#define IOBPS_TX_MASK 0x0006
#define IOBPS_MASK 0xff00
#define IOBPS_READ 0x0600
#define IOBPS_WRITE 0x0700
#define IOBPU 0x233a
#define IOBPU_MAGIC 0xf000
#define IOBP_PCICFG_READ 0x0400
#define IOBP_PCICFG_WRITE 0x0500
static inline int iobp_poll(void)
{
unsigned try;
for (try = IOBP_RETRY; try > 0; try--) {
u16 status = readw(RCB_REG(IOBPS));
if ((status & IOBPS_READY) == 0)
return 1;
udelay(10);
}
printf("IOBP: timeout waiting for transaction to complete\n");
return 0;
}
int pch_iobp_trans_start(u32 address, int op)
{
if (!iobp_poll())
return 0;
/* Set the address */
writel(address, RCB_REG(IOBPIRI));
/* READ OPCODE */
clrsetbits_le16(RCB_REG(IOBPS), IOBPS_MASK, op);
return 1;
}
int pch_iobp_trans_finish(void)
{
u16 status;
/* Undocumented magic */
writew(IOBPU_MAGIC, RCB_REG(IOBPU));
/* Set ready bit */
setbits_le16(RCB_REG(IOBPS), IOBPS_READY);
if (!iobp_poll())
return 1;
/* Check for successful transaction */
status = readw(RCB_REG(IOBPS));
if (status & IOBPS_TX_MASK)
return 1;
return 0;
}
u32 pch_iobp_read(u32 address)
{
if (!pch_iobp_trans_start(address, IOBPS_READ))
return 0;
if (pch_iobp_trans_finish()) {
printf("IOBP: read 0x%08x failed\n", address);
return 0;
}
/* Read IOBP data */
return readl(RCB_REG(IOBPD));
}
int pch_iobp_write(u32 address, u32 data)
{
if (!pch_iobp_trans_start(address, IOBPS_WRITE))
return -EIO;
writel(data, RCB_REG(IOBPD));
if (pch_iobp_trans_finish()) {
printf("IOBP: write 0x%08x failed\n", address);
return -EIO;
}
return 0;
}
int pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
{
u32 data = pch_iobp_read(address);
/* Update the data */
data &= andvalue;
data |= orvalue;
return pch_iobp_write(address, data);
}
int pch_iobp_exec(u32 addr, u16 op_code, u8 route_id, u32 *data, u8 *resp)
{
if (!data || !resp)
return 0;
*resp = -1;
if (!iobp_poll())
return -EIO;
writel(addr, RCB_REG(IOBPIRI));
clrsetbits_le16(RCB_REG(IOBPS), 0xff00, op_code);
writew(IOBPU_MAGIC | route_id, RCB_REG(IOBPU));
writel(*data, RCB_REG(IOBPD));
/* Set IOBPS[0] to trigger IOBP transaction*/
setbits_le16(RCB_REG(IOBPS), 1);
if (!iobp_poll())
return -EIO;
*resp = (readw(RCB_REG(IOBPS)) & IOBPS_TX_MASK) >> 1;
*data = readl(RCB_REG(IOBPD));
return 0;
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2016 Google, Inc
*
* From coreboot broadwell support
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <pch.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/lpc_common.h>
#include <asm/arch/pch.h>
#include <asm/arch/spi.h>
static void set_spi_speed(void)
{
u32 fdod;
u8 ssfc;
/* Observe SPI Descriptor Component Section 0 */
writel(0x1000, SPI_REG(SPIBAR_FDOC));
/* Extract the Write/Erase SPI Frequency from descriptor */
fdod = readl(SPI_REG(SPIBAR_FDOD));
fdod >>= 24;
fdod &= 7;
/* Set Software Sequence frequency to match */
ssfc = readb(SPI_REG(SPIBAR_SSFC + 2));
ssfc &= ~7;
ssfc |= fdod;
writeb(ssfc, SPI_REG(SPIBAR_SSFC + 2));
}
static int broadwell_lpc_early_init(struct udevice *dev)
{
set_spi_speed();
return 0;
}
static int lpc_init_extra(struct udevice *dev)
{
return 0;
}
static int broadwell_lpc_probe(struct udevice *dev)
{
int ret;
if (!(gd->flags & GD_FLG_RELOC)) {
ret = lpc_common_early_init(dev);
if (ret) {
debug("%s: lpc_early_init() failed\n", __func__);
return ret;
}
return broadwell_lpc_early_init(dev);
}
return lpc_init_extra(dev);
}
static const struct udevice_id broadwell_lpc_ids[] = {
{ .compatible = "intel,broadwell-lpc" },
{ }
};
U_BOOT_DRIVER(broadwell_lpc_drv) = {
.name = "lpc",
.id = UCLASS_LPC,
.of_match = broadwell_lpc_ids,
.probe = broadwell_lpc_probe,
};

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*
* Based on code from coreboot src/soc/intel/broadwell/me_status.c
*/
#include <common.h>
#include <errno.h>
#include <asm/arch/me.h>
static inline void me_read_dword_ptr(struct udevice *dev, void *ptr, int offset)
{
u32 dword;
dm_pci_read_config32(dev, offset, &dword);
memcpy(ptr, &dword, sizeof(dword));
}
int intel_me_hsio_version(struct udevice *dev, uint16_t *versionp,
uint16_t *checksump)
{
int count;
u32 hsiover;
struct me_hfs hfs;
/* Query for HSIO version, overloads H_GS and HFS */
dm_pci_write_config32(dev, PCI_ME_H_GS,
ME_HSIO_MESSAGE | ME_HSIO_CMD_GETHSIOVER);
/* Must wait for ME acknowledgement */
for (count = ME_RETRY; count > 0; --count) {
me_read_dword_ptr(dev, &hfs, PCI_ME_HFS);
if (hfs.bios_msg_ack)
break;
udelay(ME_DELAY);
}
if (!count) {
debug("ERROR: ME failed to respond\n");
return -ETIMEDOUT;
}
/* HSIO version should be in HFS_5 */
dm_pci_read_config32(dev, PCI_ME_HFS5, &hsiover);
*versionp = hsiover >> 16;
*checksump = hsiover & 0xffff;
debug("ME: HSIO Version : %d (CRC 0x%04x)\n",
*versionp, *checksump);
/* Reset registers to normal behavior */
dm_pci_write_config32(dev, PCI_ME_H_GS,
ME_HSIO_MESSAGE | ME_HSIO_CMD_GETHSIOVER);
return 0;
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2011 The Chromium Authors
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <asm/arch/iomap.h>
#include <asm/arch/pch.h>
static int broadwell_northbridge_early_init(struct udevice *dev)
{
/* Move earlier? */
dm_pci_write_config32(dev, PCIEXBAR + 4, 0);
/* 64MiB - 0-63 buses */
dm_pci_write_config32(dev, PCIEXBAR, MCFG_BASE_ADDRESS | 4 | 1);
dm_pci_write_config32(dev, MCHBAR, MCH_BASE_ADDRESS | 1);
dm_pci_write_config32(dev, DMIBAR, DMI_BASE_ADDRESS | 1);
dm_pci_write_config32(dev, EPBAR, EP_BASE_ADDRESS | 1);
writel(EDRAM_BASE_ADDRESS | 1, MCH_BASE_ADDRESS + EDRAMBAR);
writel(GDXC_BASE_ADDRESS | 1, MCH_BASE_ADDRESS + GDXCBAR);
/* Set C0000-FFFFF to access RAM on both reads and writes */
dm_pci_write_config8(dev, PAM0, 0x30);
dm_pci_write_config8(dev, PAM1, 0x33);
dm_pci_write_config8(dev, PAM2, 0x33);
dm_pci_write_config8(dev, PAM3, 0x33);
dm_pci_write_config8(dev, PAM4, 0x33);
dm_pci_write_config8(dev, PAM5, 0x33);
dm_pci_write_config8(dev, PAM6, 0x33);
/* Device enable: IGD and Mini-HD */
dm_pci_write_config32(dev, DEVEN, DEVEN_D0EN | DEVEN_D2EN | DEVEN_D3EN);
return 0;
}
static int broadwell_northbridge_probe(struct udevice *dev)
{
if (!(gd->flags & GD_FLG_RELOC))
return broadwell_northbridge_early_init(dev);
return 0;
}
static const struct udevice_id broadwell_northbridge_ids[] = {
{ .compatible = "intel,broadwell-northbridge" },
{ }
};
U_BOOT_DRIVER(broadwell_northbridge_drv) = {
.name = "broadwell_northbridge",
.id = UCLASS_NORTHBRIDGE,
.of_match = broadwell_northbridge_ids,
.probe = broadwell_northbridge_probe,
};

View File

@@ -0,0 +1,541 @@
/*
* Copyright (c) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <pch.h>
#include <asm/cpu.h>
#include <asm/gpio.h>
#include <asm/i8259.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/ioapic.h>
#include <asm/lpc_common.h>
#include <asm/pch_common.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/iomap.h>
#include <asm/arch/pch.h>
#include <asm/arch/pm.h>
#include <asm/arch/rcb.h>
#include <asm/arch/spi.h>
#define BIOS_CTRL 0xdc
bool cpu_is_ult(void)
{
u32 fm = cpu_get_family_model();
return fm == BROADWELL_FAMILY_ULT || fm == HASWELL_FAMILY_ULT;
}
static int broadwell_pch_early_init(struct udevice *dev)
{
struct gpio_desc desc;
struct udevice *bus;
pci_dev_t bdf;
int ret;
dm_pci_write_config32(dev, PCH_RCBA, RCB_BASE_ADDRESS | 1);
dm_pci_write_config32(dev, PMBASE, ACPI_BASE_ADDRESS | 1);
dm_pci_write_config8(dev, ACPI_CNTL, ACPI_EN);
dm_pci_write_config32(dev, GPIO_BASE, GPIO_BASE_ADDRESS | 1);
dm_pci_write_config8(dev, GPIO_CNTL, GPIO_EN);
/* Enable IOAPIC */
writew(0x1000, RCB_REG(OIC));
/* Read back for posted write */
readw(RCB_REG(OIC));
/* Set HPET address and enable it */
clrsetbits_le32(RCB_REG(HPTC), 3, 1 << 7);
/* Read back for posted write */
readl(RCB_REG(HPTC));
/* Enable HPET to start counter */
setbits_le32(HPET_BASE_ADDRESS + 0x10, 1 << 0);
setbits_le32(RCB_REG(GCS), 1 << 5);
/*
* Enable PP3300_AUTOBAHN_EN after initial GPIO setup
* to prevent possible brownout. This will cause the GPIOs to be set
* up if it has not been done already.
*/
ret = gpio_request_by_name(dev, "power-enable-gpio", 0, &desc,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
if (ret)
return ret;
/* 8.14 Additional PCI Express Programming Steps, step #1 */
bdf = PCI_BDF(0, 0x1c, 0);
bus = pci_get_controller(dev);
pci_bus_clrset_config32(bus, bdf, 0xf4, 0x60, 0);
pci_bus_clrset_config32(bus, bdf, 0xf4, 0x80, 0x80);
pci_bus_clrset_config32(bus, bdf, 0xe2, 0x30, 0x30);
return 0;
}
static void pch_misc_init(struct udevice *dev)
{
/* Setup SLP signal assertion, SLP_S4=4s, SLP_S3=50ms */
dm_pci_clrset_config8(dev, GEN_PMCON_3, 3 << 4 | 1 << 10,
1 << 3 | 1 << 11 | 1 << 12);
/* Prepare sleep mode */
clrsetio_32(ACPI_BASE_ADDRESS + PM1_CNT, SLP_TYP, SCI_EN);
/* Setup NMI on errors, disable SERR */
clrsetio_8(0x61, 0xf0, 1 << 2);
/* Disable NMI sources */
setio_8(0x70, 1 << 7);
/* Indicate DRAM init done for MRC */
dm_pci_clrset_config8(dev, GEN_PMCON_2, 0, 1 << 7);
/* Clear status bits to prevent unexpected wake */
setbits_le32(RCB_REG(0x3310), 0x0000002f);
clrsetbits_le32(RCB_REG(0x3f02), 0x0000000f, 0);
/* Enable PCIe Relaxed Order */
setbits_le32(RCB_REG(0x2314), 1 << 31 | 1 << 7);
setbits_le32(RCB_REG(0x1114), 1 << 15 | 1 << 14);
/* Setup SERIRQ, enable continuous mode */
dm_pci_clrset_config8(dev, SERIRQ_CNTL, 0, 1 << 7 | 1 << 6);
};
static void pch_enable_ioapic(void)
{
u32 reg32;
/* Make sure this is a unique ID within system */
io_apic_set_id(0x04);
/* affirm full set of redirection table entries ("write once") */
reg32 = io_apic_read(0x01);
/* PCH-LP has 39 redirection entries */
reg32 &= ~0x00ff0000;
reg32 |= 0x00270000;
io_apic_write(0x01, reg32);
/*
* Select Boot Configuration register (0x03) and
* use Processor System Bus (0x01) to deliver interrupts.
*/
io_apic_write(0x03, 0x01);
}
/* Enable all requested GPE */
void enable_all_gpe(u32 set1, u32 set2, u32 set3, u32 set4)
{
outl(set1, ACPI_BASE_ADDRESS + GPE0_EN(GPE_31_0));
outl(set2, ACPI_BASE_ADDRESS + GPE0_EN(GPE_63_32));
outl(set3, ACPI_BASE_ADDRESS + GPE0_EN(GPE_94_64));
outl(set4, ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
}
/*
* Enable GPIO SMI events - it would be good to put this in the GPIO driver
* but it would need a new driver operation.
*/
int enable_alt_smi(struct udevice *pch, u32 mask)
{
struct pch_lp_gpio_regs *regs;
u32 gpiobase;
int ret;
ret = pch_get_gpio_base(pch, &gpiobase);
if (ret) {
debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
gpiobase);
return -EINVAL;
}
regs = (struct pch_lp_gpio_regs *)gpiobase;
setio_32(regs->alt_gpi_smi_en, mask);
return 0;
}
static int pch_power_options(struct udevice *dev)
{
int pwr_on_after_power_fail = MAINBOARD_POWER_OFF;
const char *state;
u32 enable[4];
u16 reg16;
int ret;
dm_pci_read_config16(dev, GEN_PMCON_3, &reg16);
reg16 &= 0xfffe;
switch (pwr_on_after_power_fail) {
case MAINBOARD_POWER_OFF:
reg16 |= 1;
state = "off";
break;
case MAINBOARD_POWER_ON:
reg16 &= ~1;
state = "on";
break;
case MAINBOARD_POWER_KEEP:
reg16 &= ~1;
state = "state keep";
break;
default:
state = "undefined";
}
dm_pci_write_config16(dev, GEN_PMCON_3, reg16);
debug("Set power %s after power failure.\n", state);
/* GPE setup based on device tree configuration */
ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
"intel,gpe0-en", enable, ARRAY_SIZE(enable));
if (ret)
return -EINVAL;
enable_all_gpe(enable[0], enable[1], enable[2], enable[3]);
/* SMI setup based on device tree configuration */
enable_alt_smi(dev, fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"intel,alt-gp-smi-enable", 0));
return 0;
}
/* Magic register settings for power management */
static void pch_pm_init_magic(struct udevice *dev)
{
dm_pci_write_config8(dev, 0xa9, 0x46);
clrbits_le32(RCB_REG(0x232c), 1),
setbits_le32(RCB_REG(0x1100), 0x0000c13f);
clrsetbits_le32(RCB_REG(0x2320), 0x60, 0x10);
writel(0x00012fff, RCB_REG(0x3314));
clrsetbits_le32(RCB_REG(0x3318), 0x000f0330, 0x0dcf0400);
writel(0x04000000, RCB_REG(0x3324));
writel(0x00041400, RCB_REG(0x3368));
writel(0x3f8ddbff, RCB_REG(0x3388));
writel(0x00007001, RCB_REG(0x33ac));
writel(0x00181900, RCB_REG(0x33b0));
writel(0x00060A00, RCB_REG(0x33c0));
writel(0x06200840, RCB_REG(0x33d0));
writel(0x01010101, RCB_REG(0x3a28));
writel(0x040c0404, RCB_REG(0x3a2c));
writel(0x9000000a, RCB_REG(0x3a9c));
writel(0x03808033, RCB_REG(0x2b1c));
writel(0x80000009, RCB_REG(0x2b34));
writel(0x022ddfff, RCB_REG(0x3348));
writel(0x00000001, RCB_REG(0x334c));
writel(0x0001c000, RCB_REG(0x3358));
writel(0x3f8ddbff, RCB_REG(0x3380));
writel(0x0001c7e1, RCB_REG(0x3384));
writel(0x0001c7e1, RCB_REG(0x338c));
writel(0x0001c000, RCB_REG(0x3398));
writel(0x00181900, RCB_REG(0x33a8));
writel(0x00080000, RCB_REG(0x33dc));
writel(0x00000001, RCB_REG(0x33e0));
writel(0x0000040c, RCB_REG(0x3a20));
writel(0x01010101, RCB_REG(0x3a24));
writel(0x01010101, RCB_REG(0x3a30));
dm_pci_clrset_config32(dev, 0xac, 0x00200000, 0);
setbits_le32(RCB_REG(0x0410), 0x00000003);
setbits_le32(RCB_REG(0x2618), 0x08000000);
setbits_le32(RCB_REG(0x2300), 0x00000002);
setbits_le32(RCB_REG(0x2600), 0x00000008);
writel(0x00007001, RCB_REG(0x33b4));
writel(0x022ddfff, RCB_REG(0x3350));
writel(0x00000001, RCB_REG(0x3354));
/* Power Optimizer */
setbits_le32(RCB_REG(0x33d4), 0x08000000);
/*
* This stops the LCD from turning on:
* setbits_le32(RCB_REG(0x33c8), 0x08000080);
*/
writel(0x0000883c, RCB_REG(0x2b10));
writel(0x1e0a4616, RCB_REG(0x2b14));
writel(0x40000005, RCB_REG(0x2b24));
writel(0x0005db01, RCB_REG(0x2b20));
writel(0x05145005, RCB_REG(0x3a80));
writel(0x00001005, RCB_REG(0x3a84));
setbits_le32(RCB_REG(0x33d4), 0x2fff2fb1);
setbits_le32(RCB_REG(0x33c8), 0x00008000);
};
static int pch_type(struct udevice *dev)
{
u16 type;
dm_pci_read_config16(dev, PCI_DEVICE_ID, &type);
return type;
}
/* Return 1 if PCH type is WildcatPoint */
static int pch_is_wpt(struct udevice *dev)
{
return ((pch_type(dev) & 0xfff0) == 0x9cc0) ? 1 : 0;
}
/* Return 1 if PCH type is WildcatPoint ULX */
static int pch_is_wpt_ulx(struct udevice *dev)
{
u16 lpcid = pch_type(dev);
switch (lpcid) {
case PCH_WPT_BDW_Y_SAMPLE:
case PCH_WPT_BDW_Y_PREMIUM:
case PCH_WPT_BDW_Y_BASE:
return 1;
}
return 0;
}
static u32 pch_read_soft_strap(int id)
{
clrbits_le32(SPI_REG(SPIBAR_FDOC), 0x00007ffc);
setbits_le32(SPI_REG(SPIBAR_FDOC), 0x00004000 | id * 4);
return readl(SPI_REG(SPIBAR_FDOD));
}
static void pch_enable_mphy(struct udevice *dev)
{
u32 data_and = 0xffffffff;
u32 data_or = (1 << 14) | (1 << 13) | (1 << 12);
data_or |= (1 << 0);
if (pch_is_wpt(dev)) {
data_and &= ~((1 << 7) | (1 << 6) | (1 << 3));
data_or |= (1 << 5) | (1 << 4);
if (pch_is_wpt_ulx(dev)) {
/* Check if SATA and USB3 MPHY are enabled */
u32 strap19 = pch_read_soft_strap(19);
strap19 &= ((1 << 31) | (1 << 30));
strap19 >>= 30;
if (strap19 == 3) {
data_or |= (1 << 3);
debug("Enable ULX MPHY PG control in single domain\n");
} else if (strap19 == 0) {
debug("Enable ULX MPHY PG control in split domains\n");
} else {
debug("Invalid PCH Soft Strap 19 configuration\n");
}
} else {
data_or |= (1 << 3);
}
}
pch_iobp_update(0xCF000000, data_and, data_or);
}
static void pch_init_deep_sx(bool deep_sx_enable_ac, bool deep_sx_enable_dc)
{
if (deep_sx_enable_ac) {
setbits_le32(RCB_REG(DEEP_S3_POL), DEEP_S3_EN_AC);
setbits_le32(RCB_REG(DEEP_S5_POL), DEEP_S5_EN_AC);
}
if (deep_sx_enable_dc) {
setbits_le32(RCB_REG(DEEP_S3_POL), DEEP_S3_EN_DC);
setbits_le32(RCB_REG(DEEP_S5_POL), DEEP_S5_EN_DC);
}
if (deep_sx_enable_ac || deep_sx_enable_dc) {
setbits_le32(RCB_REG(DEEP_SX_CONFIG),
DEEP_SX_WAKE_PIN_EN | DEEP_SX_GP27_PIN_EN);
}
}
/* Power Management init */
static void pch_pm_init(struct udevice *dev)
{
debug("PCH PM init\n");
pch_init_deep_sx(false, false);
pch_enable_mphy(dev);
pch_pm_init_magic(dev);
if (pch_is_wpt(dev)) {
setbits_le32(RCB_REG(0x33e0), 1 << 4 | 1 << 1);
setbits_le32(RCB_REG(0x2b1c), 1 << 22 | 1 << 14 | 1 << 13);
writel(0x16bf0002, RCB_REG(0x33e4));
setbits_le32(RCB_REG(0x33e4), 0x1);
}
pch_iobp_update(0xCA000000, ~0UL, 0x00000009);
/* Set RCBA 0x2b1c[29]=1 if DSP disabled */
if (readl(RCB_REG(FD)) & PCH_DISABLE_ADSPD)
setbits_le32(RCB_REG(0x2b1c), 1 << 29);
}
static void pch_cg_init(struct udevice *dev)
{
struct udevice *bus = pci_get_controller(dev);
u32 reg32;
u16 reg16;
ulong val;
/* DMI */
setbits_le32(RCB_REG(0x2234), 0xf);
dm_pci_read_config16(dev, GEN_PMCON_1, &reg16);
reg16 &= ~(1 << 10); /* Disable BIOS_PCI_EXP_EN for native PME */
if (pch_is_wpt(dev))
reg16 &= ~(1 << 11);
else
reg16 |= 1 << 11;
reg16 |= 1 << 5 | 1 << 6 | 1 << 7 | 1 << 12;
reg16 |= 1 << 2; /* PCI CLKRUN# Enable */
dm_pci_write_config16(dev, GEN_PMCON_1, reg16);
/*
* RCBA + 0x2614[27:25,14:13,10,8] = 101,11,1,1
* RCBA + 0x2614[23:16] = 0x20
* RCBA + 0x2614[30:28] = 0x0
* RCBA + 0x2614[26] = 1 (IF 0:2.0@0x08 >= 0x0b)
*/
clrsetbits_le32(RCB_REG(0x2614), 0x64ff0000, 0x0a206500);
/* Check for 0:2.0@0x08 >= 0x0b */
pci_bus_read_config(bus, PCI_BDF(0, 0x2, 0), 0x8, &val, PCI_SIZE_8);
if (pch_is_wpt(dev) || val >= 0x0b)
setbits_le32(RCB_REG(0x2614), 1 << 26);
setbits_le32(RCB_REG(0x900), 0x0000031f);
reg32 = readl(RCB_REG(CG));
if (readl(RCB_REG(0x3454)) & (1 << 4))
reg32 &= ~(1 << 29); /* LPC Dynamic */
else
reg32 |= (1 << 29); /* LPC Dynamic */
reg32 |= 1 << 31; /* LP LPC */
reg32 |= 1 << 30; /* LP BLA */
if (readl(RCB_REG(0x3454)) & (1 << 4))
reg32 &= ~(1 << 29);
else
reg32 |= 1 << 29;
reg32 |= 1 << 28; /* GPIO Dynamic */
reg32 |= 1 << 27; /* HPET Dynamic */
reg32 |= 1 << 26; /* Generic Platform Event Clock */
if (readl(RCB_REG(BUC)) & PCH_DISABLE_GBE)
reg32 |= 1 << 23; /* GbE Static */
if (readl(RCB_REG(FD)) & PCH_DISABLE_HD_AUDIO)
reg32 |= 1 << 21; /* HDA Static */
reg32 |= 1 << 22; /* HDA Dynamic */
writel(reg32, RCB_REG(CG));
/* PCH-LP LPC */
if (pch_is_wpt(dev))
clrsetbits_le32(RCB_REG(0x3434), 0x1f, 0x17);
else
setbits_le32(RCB_REG(0x3434), 0x7);
/* SPI */
setbits_le32(RCB_REG(0x38c0), 0x3c07);
pch_iobp_update(0xCE00C000, ~1UL, 0x00000000);
}
static void systemagent_init(void)
{
/* Enable Power Aware Interrupt Routing */
clrsetbits_8(MCHBAR_REG(MCH_PAIR), 0x7, 0x4); /* Fixed Priority */
/*
* Set bits 0+1 of BIOS_RESET_CPL to indicate to the CPU
* that BIOS has initialized memory and power management
*/
setbits_8(MCHBAR_REG(BIOS_RESET_CPL), 3);
debug("Set BIOS_RESET_CPL\n");
/* Configure turbo power limits 1ms after reset complete bit */
mdelay(1);
cpu_set_power_limits(28);
}
static int broadwell_pch_init(struct udevice *dev)
{
int ret;
/* Enable upper 128 bytes of CMOS */
setbits_le32(RCB_REG(RC), 1 << 2);
/*
* TODO: TCO timer halt - this hangs
* setio_16(ACPI_BASE_ADDRESS + TCO1_CNT, TCO_TMR_HLT);
*/
/* Disable unused device (always) */
setbits_le32(RCB_REG(FD), PCH_DISABLE_ALWAYS);
pch_misc_init(dev);
/* Interrupt configuration */
pch_enable_ioapic();
/* Initialize power management */
ret = pch_power_options(dev);
if (ret)
return ret;
pch_pm_init(dev);
pch_cg_init(dev);
systemagent_init();
return 0;
}
static int broadwell_pch_probe(struct udevice *dev)
{
if (!(gd->flags & GD_FLG_RELOC))
return broadwell_pch_early_init(dev);
else
return broadwell_pch_init(dev);
}
static int broadwell_pch_get_spi_base(struct udevice *dev, ulong *sbasep)
{
u32 rcba;
dm_pci_read_config32(dev, PCH_RCBA, &rcba);
/* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable */
rcba = rcba & 0xffffc000;
*sbasep = rcba + 0x3800;
return 0;
}
static int broadwell_set_spi_protect(struct udevice *dev, bool protect)
{
return lpc_set_spi_protect(dev, BIOS_CTRL, protect);
}
static int broadwell_get_gpio_base(struct udevice *dev, u32 *gbasep)
{
dm_pci_read_config32(dev, GPIO_BASE, gbasep);
*gbasep &= PCI_BASE_ADDRESS_IO_MASK;
return 0;
}
static const struct pch_ops broadwell_pch_ops = {
.get_spi_base = broadwell_pch_get_spi_base,
.set_spi_protect = broadwell_set_spi_protect,
.get_gpio_base = broadwell_get_gpio_base,
};
static const struct udevice_id broadwell_pch_ids[] = {
{ .compatible = "intel,broadwell-pch" },
{ }
};
U_BOOT_DRIVER(broadwell_pch) = {
.name = "broadwell_pch",
.id = UCLASS_PCH,
.of_match = broadwell_pch_ids,
.probe = broadwell_pch_probe,
.ops = &broadwell_pch_ops,
};

View File

@@ -0,0 +1,278 @@
/*
* Copyright (C) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <pch.h>
#include <pci.h>
#include <asm/cpu.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/pci.h>
#include <asm/arch/gpio.h>
#include <dt-bindings/gpio/x86-gpio.h>
#include <dm/pinctrl.h>
DECLARE_GLOBAL_DATA_PTR;
enum {
MAX_GPIOS = 95,
};
#define PIRQ_SHIFT 16
#define CONF_MASK 0xffff
struct pin_info {
int node;
int phandle;
bool mode_gpio;
bool dir_input;
bool invert;
bool trigger_level;
bool output_high;
bool sense_disable;
bool owner_gpio;
bool route_smi;
bool irq_enable;
bool reset_rsmrst;
bool pirq_apic_route;
};
static int broadwell_pinctrl_read_configs(struct udevice *dev,
struct pin_info *conf, int max_pins)
{
const void *blob = gd->fdt_blob;
int count = 0;
int node;
debug("%s: starting\n", __func__);
for (node = fdt_first_subnode(blob, dev->of_offset);
node > 0;
node = fdt_next_subnode(blob, node)) {
int phandle = fdt_get_phandle(blob, node);
if (!phandle)
continue;
if (count == max_pins)
return -ENOSPC;
/* We've found a new configuration */
memset(conf, '\0', sizeof(*conf));
conf->node = node;
conf->phandle = phandle;
conf->mode_gpio = fdtdec_get_bool(blob, node, "mode-gpio");
if (fdtdec_get_int(blob, node, "direction", -1) == PIN_INPUT)
conf->dir_input = true;
conf->invert = fdtdec_get_bool(blob, node, "invert");
if (fdtdec_get_int(blob, node, "trigger", -1) == TRIGGER_LEVEL)
conf->trigger_level = true;
if (fdtdec_get_int(blob, node, "output-value", -1) == 1)
conf->output_high = true;
conf->sense_disable = fdtdec_get_bool(blob, node,
"sense-disable");
if (fdtdec_get_int(blob, node, "owner", -1) == OWNER_GPIO)
conf->owner_gpio = true;
if (fdtdec_get_int(blob, node, "route", -1) == ROUTE_SMI)
conf->route_smi = true;
conf->irq_enable = fdtdec_get_bool(blob, node, "irq-enable");
conf->reset_rsmrst = fdtdec_get_bool(blob, node,
"reset-rsmrst");
if (fdtdec_get_int(blob, node, "pirq-apic", -1) ==
PIRQ_APIC_ROUTE)
conf->pirq_apic_route = true;
debug("config: phandle=%d\n", phandle);
count++;
conf++;
}
debug("%s: Found %d configurations\n", __func__, count);
return count;
}
static int broadwell_pinctrl_lookup_phandle(struct pin_info *conf,
int conf_count, int phandle)
{
int i;
for (i = 0; i < conf_count; i++) {
if (conf[i].phandle == phandle)
return i;
}
return -ENOENT;
}
static int broadwell_pinctrl_read_pins(struct udevice *dev,
struct pin_info *conf, int conf_count, int gpio_conf[],
int num_gpios)
{
const void *blob = gd->fdt_blob;
int count = 0;
int node;
for (node = fdt_first_subnode(blob, dev->of_offset);
node > 0;
node = fdt_next_subnode(blob, node)) {
int len, i;
const u32 *prop = fdt_getprop(blob, node, "config", &len);
if (!prop)
continue;
/* There are three cells per pin */
count = len / (sizeof(u32) * 3);
debug("Found %d GPIOs to configure\n", count);
for (i = 0; i < count; i++) {
uint gpio = fdt32_to_cpu(prop[i * 3]);
uint phandle = fdt32_to_cpu(prop[i * 3 + 1]);
int val;
if (gpio >= num_gpios) {
debug("%s: GPIO %d out of range\n", __func__,
gpio);
return -EDOM;
}
val = broadwell_pinctrl_lookup_phandle(conf, conf_count,
phandle);
if (val < 0) {
debug("%s: Cannot find phandle %d\n", __func__,
phandle);
return -EINVAL;
}
gpio_conf[gpio] = val |
fdt32_to_cpu(prop[i * 3 + 2]) << PIRQ_SHIFT;
}
}
return 0;
}
static void broadwell_pinctrl_commit(struct pch_lp_gpio_regs *regs,
struct pin_info *pin_info,
int gpio_conf[], int count)
{
u32 owner_gpio[GPIO_BANKS] = {0};
u32 route_smi[GPIO_BANKS] = {0};
u32 irq_enable[GPIO_BANKS] = {0};
u32 reset_rsmrst[GPIO_BANKS] = {0};
u32 pirq2apic = 0;
int set, bit, gpio = 0;
for (gpio = 0; gpio < MAX_GPIOS; gpio++) {
int confnum = gpio_conf[gpio] & CONF_MASK;
struct pin_info *pin = &pin_info[confnum];
u32 val;
val = pin->mode_gpio << CONFA_MODE_SHIFT |
pin->dir_input << CONFA_DIR_SHIFT |
pin->invert << CONFA_INVERT_SHIFT |
pin->trigger_level << CONFA_TRIGGER_SHIFT |
pin->output_high << CONFA_OUTPUT_SHIFT;
outl(val, &regs->config[gpio].conf_a);
outl(pin->sense_disable << CONFB_SENSE_SHIFT,
&regs->config[gpio].conf_b);
/* Determine set and bit based on GPIO number */
set = gpio / GPIO_PER_BANK;
bit = gpio % GPIO_PER_BANK;
/* Apply settings to set specific bits */
owner_gpio[set] |= pin->owner_gpio << bit;
route_smi[set] |= pin->route_smi << bit;
irq_enable[set] |= pin->irq_enable << bit;
reset_rsmrst[set] |= pin->reset_rsmrst << bit;
/* PIRQ to IO-APIC map */
if (pin->pirq_apic_route)
pirq2apic |= gpio_conf[gpio] >> PIRQ_SHIFT;
debug("gpio %d: conf %d, mode_gpio %d, dir_input %d, output_high %d\n",
gpio, confnum, pin->mode_gpio, pin->dir_input,
pin->output_high);
}
for (set = 0; set < GPIO_BANKS; set++) {
outl(owner_gpio[set], &regs->own[set]);
outl(route_smi[set], &regs->gpi_route[set]);
outl(irq_enable[set], &regs->gpi_ie[set]);
outl(reset_rsmrst[set], &regs->rst_sel[set]);
}
outl(pirq2apic, &regs->pirq_to_ioxapic);
}
static int broadwell_pinctrl_probe(struct udevice *dev)
{
struct pch_lp_gpio_regs *regs;
struct pin_info conf[12];
int gpio_conf[MAX_GPIOS];
struct udevice *pch;
int conf_count;
u32 gpiobase;
int ret;
ret = uclass_first_device(UCLASS_PCH, &pch);
if (ret)
return ret;
if (!pch)
return -ENODEV;
debug("%s: start\n", __func__);
/* Only init once, before relocation */
if (gd->flags & GD_FLG_RELOC)
return 0;
/*
* Get the memory/io base address to configure every pins.
* IOBASE is used to configure the mode/pads
* GPIOBASE is used to configure the direction and default value
*/
ret = pch_get_gpio_base(pch, &gpiobase);
if (ret) {
debug("%s: invalid GPIOBASE address (%08x)\n", __func__,
gpiobase);
return -EINVAL;
}
conf_count = broadwell_pinctrl_read_configs(dev, conf,
ARRAY_SIZE(conf));
if (conf_count < 0) {
debug("%s: Cannot read configs: err=%d\n", __func__, ret);
return conf_count;
}
/*
* Assume that pin settings are provided for every pin. Pins not
* mentioned will get the first config mentioned in the list.
*/
ret = broadwell_pinctrl_read_pins(dev, conf, conf_count, gpio_conf,
MAX_GPIOS);
if (ret) {
debug("%s: Cannot read pin settings: err=%d\n", __func__, ret);
return ret;
}
regs = (struct pch_lp_gpio_regs *)gpiobase;
broadwell_pinctrl_commit(regs, conf, gpio_conf, ARRAY_SIZE(conf));
debug("%s: done\n", __func__);
return 0;
}
static const struct udevice_id broadwell_pinctrl_match[] = {
{ .compatible = "intel,x86-broadwell-pinctrl",
.data = X86_SYSCON_PINCONF },
{ /* sentinel */ }
};
U_BOOT_DRIVER(broadwell_pinctrl) = {
.name = "broadwell_pinctrl",
.id = UCLASS_SYSCON,
.of_match = broadwell_pinctrl_match,
.probe = broadwell_pinctrl_probe,
};

View File

@@ -0,0 +1,90 @@
/*
* From coreboot src/soc/intel/broadwell/romstage/power_state.c
*
* Copyright (C) 2016 Google, Inc.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <pci.h>
#include <asm/io.h>
#include <asm/intel_regs.h>
#include <asm/arch/iomap.h>
#include <asm/arch/lpc.h>
#include <asm/arch/pch.h>
#include <asm/arch/pm.h>
/* Return 0, 3, or 5 to indicate the previous sleep state. */
static int prev_sleep_state(struct chipset_power_state *ps)
{
/* Default to S0. */
int prev_sleep_state = SLEEP_STATE_S0;
if (ps->pm1_sts & WAK_STS) {
switch ((ps->pm1_cnt & SLP_TYP) >> SLP_TYP_SHIFT) {
#if CONFIG_HAVE_ACPI_RESUME
case SLP_TYP_S3:
prev_sleep_state = SLEEP_STATE_S3;
break;
#endif
case SLP_TYP_S5:
prev_sleep_state = SLEEP_STATE_S5;
break;
}
/* Clear SLP_TYP. */
outl(ps->pm1_cnt & ~(SLP_TYP), ACPI_BASE_ADDRESS + PM1_CNT);
}
if (ps->gen_pmcon3 & (PWR_FLR | SUS_PWR_FLR))
prev_sleep_state = SLEEP_STATE_S5;
return prev_sleep_state;
}
static void dump_power_state(struct chipset_power_state *ps)
{
debug("PM1_STS: %04x\n", ps->pm1_sts);
debug("PM1_EN: %04x\n", ps->pm1_en);
debug("PM1_CNT: %08x\n", ps->pm1_cnt);
debug("TCO_STS: %04x %04x\n", ps->tco1_sts, ps->tco2_sts);
debug("GPE0_STS: %08x %08x %08x %08x\n",
ps->gpe0_sts[0], ps->gpe0_sts[1],
ps->gpe0_sts[2], ps->gpe0_sts[3]);
debug("GPE0_EN: %08x %08x %08x %08x\n",
ps->gpe0_en[0], ps->gpe0_en[1],
ps->gpe0_en[2], ps->gpe0_en[3]);
debug("GEN_PMCON: %04x %04x %04x\n",
ps->gen_pmcon1, ps->gen_pmcon2, ps->gen_pmcon3);
debug("Previous Sleep State: S%d\n",
ps->prev_sleep_state);
}
/* Fill power state structure from ACPI PM registers */
void power_state_get(struct udevice *pch_dev, struct chipset_power_state *ps)
{
ps->pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS);
ps->pm1_en = inw(ACPI_BASE_ADDRESS + PM1_EN);
ps->pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
ps->tco1_sts = inw(ACPI_BASE_ADDRESS + TCO1_STS);
ps->tco2_sts = inw(ACPI_BASE_ADDRESS + TCO2_STS);
ps->gpe0_sts[0] = inl(ACPI_BASE_ADDRESS + GPE0_STS(0));
ps->gpe0_sts[1] = inl(ACPI_BASE_ADDRESS + GPE0_STS(1));
ps->gpe0_sts[2] = inl(ACPI_BASE_ADDRESS + GPE0_STS(2));
ps->gpe0_sts[3] = inl(ACPI_BASE_ADDRESS + GPE0_STS(3));
ps->gpe0_en[0] = inl(ACPI_BASE_ADDRESS + GPE0_EN(0));
ps->gpe0_en[1] = inl(ACPI_BASE_ADDRESS + GPE0_EN(1));
ps->gpe0_en[2] = inl(ACPI_BASE_ADDRESS + GPE0_EN(2));
ps->gpe0_en[3] = inl(ACPI_BASE_ADDRESS + GPE0_EN(3));
dm_pci_read_config16(pch_dev, GEN_PMCON_1, &ps->gen_pmcon1);
dm_pci_read_config16(pch_dev, GEN_PMCON_2, &ps->gen_pmcon2);
dm_pci_read_config16(pch_dev, GEN_PMCON_3, &ps->gen_pmcon3);
ps->prev_sleep_state = prev_sleep_state(ps);
dump_power_state(ps);
}

View File

@@ -0,0 +1,113 @@
/*
* Read a coreboot rmodule and execute it.
* The rmodule_header struct is from coreboot.
*
* Copyright (c) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <errno.h>
#include <asm/arch/pei_data.h>
#define RMODULE_MAGIC 0xf8fe
#define RMODULE_VERSION_1 1
/*
* All fields with '_offset' in the name are byte offsets into the flat blob.
* The linker and the linker script takes are of assigning the values.
*/
struct rmodule_header {
uint16_t magic;
uint8_t version;
uint8_t type;
/* The payload represents the program's loadable code and data */
uint32_t payload_begin_offset;
uint32_t payload_end_offset;
/* Begin and of relocation information about the program module */
uint32_t relocations_begin_offset;
uint32_t relocations_end_offset;
/*
* The starting address of the linked program. This address is vital
* for determining relocation offsets as the relocation info and other
* symbols (bss, entry point) need this value as a basis to calculate
* the offsets.
*/
uint32_t module_link_start_address;
/*
* The module_program_size is the size of memory used while running
* the program. The program is assumed to consume a contiguous amount
* of memory
*/
uint32_t module_program_size;
/* This is program's execution entry point */
uint32_t module_entry_point;
/*
* Optional parameter structure that can be used to pass data into
* the module
*/
uint32_t parameters_begin;
uint32_t parameters_end;
/* BSS section information so the loader can clear the bss */
uint32_t bss_begin;
uint32_t bss_end;
/* Add some room for growth */
uint32_t padding[4];
} __packed;
int cpu_run_reference_code(void)
{
struct pei_data _pei_data __aligned(8);
struct pei_data *pei_data = &_pei_data;
asmlinkage int (*func)(void *);
struct rmodule_header *hdr;
char *src, *dest;
int ret, dummy;
int size;
hdr = (struct rmodule_header *)CONFIG_X86_REFCODE_ADDR;
debug("Extracting code from rmodule at %p\n", hdr);
if (hdr->magic != RMODULE_MAGIC) {
debug("Invalid rmodule magic\n");
return -EINVAL;
}
if (hdr->module_link_start_address != 0) {
debug("Link start address must be 0\n");
return -EPERM;
}
if (hdr->module_entry_point != 0) {
debug("Entry point must be 0\n");
return -EPERM;
}
memset(pei_data, '\0', sizeof(struct pei_data));
broadwell_fill_pei_data(pei_data);
mainboard_fill_pei_data(pei_data);
pei_data->saved_data = (void *)&dummy;
src = (char *)hdr + hdr->payload_begin_offset;
dest = (char *)CONFIG_X86_REFCODE_RUN_ADDR;
size = hdr->payload_end_offset - hdr->payload_begin_offset;
debug("Copying refcode from %p to %p, size %x\n", src, dest, size);
memcpy(dest, src, size);
size = hdr->bss_end - hdr->bss_begin;
debug("Zeroing BSS at %p, size %x\n", dest + hdr->bss_begin, size);
memset(dest + hdr->bss_begin, '\0', size);
func = (asmlinkage int (*)(void *))dest;
debug("Running reference code at %p\n", func);
#ifdef DEBUG
print_buffer(CONFIG_X86_REFCODE_RUN_ADDR, (void *)func, 1, 0x40, 0);
#endif
ret = func(pei_data);
if (ret != 0) {
debug("Reference code returned %d\n", ret);
return -EL2HLT;
}
debug("Refereence code completed\n");
return 0;
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright (c) 2016 Google, Inc
*
* From coreboot src/soc/intel/broadwell/sata.c
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/intel_regs.h>
#include <asm/lpc_common.h>
#include <asm/pch_common.h>
#include <asm/pch_common.h>
#include <asm/arch/pch.h>
struct sata_platdata {
int port_map;
uint port0_gen3_tx;
uint port1_gen3_tx;
uint port0_gen3_dtle;
uint port1_gen3_dtle;
/*
* SATA DEVSLP Mux
* 0 = port 0 DEVSLP on DEVSLP0/GPIO33
* 1 = port 3 DEVSLP on DEVSLP0/GPIO33
*/
int devslp_mux;
/*
* DEVSLP Disable
* 0: DEVSLP is enabled
* 1: DEVSLP is disabled
*/
int devslp_disable;
};
static void broadwell_sata_init(struct udevice *dev)
{
struct sata_platdata *plat = dev_get_platdata(dev);
u32 reg32;
u8 *abar;
u16 reg16;
int port;
debug("SATA: Initializing controller in AHCI mode.\n");
/* Set timings */
dm_pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE);
dm_pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE);
/* for AHCI, Port Enable is managed in memory mapped space */
dm_pci_read_config16(dev, 0x92, &reg16);
reg16 &= ~0xf;
reg16 |= 0x8000 | plat->port_map;
dm_pci_write_config16(dev, 0x92, reg16);
udelay(2);
/* Setup register 98h */
dm_pci_read_config32(dev, 0x98, &reg32);
reg32 &= ~((1 << 31) | (1 << 30));
reg32 |= 1 << 23;
reg32 |= 1 << 24; /* Enable MPHY Dynamic Power Gating */
dm_pci_write_config32(dev, 0x98, reg32);
/* Setup register 9Ch */
reg16 = 0; /* Disable alternate ID */
reg16 = 1 << 5; /* BWG step 12 */
dm_pci_write_config16(dev, 0x9c, reg16);
/* SATA Initialization register */
reg32 = 0x183;
reg32 |= (plat->port_map ^ 0xf) << 24;
reg32 |= (plat->devslp_mux & 1) << 15;
dm_pci_write_config32(dev, 0x94, reg32);
/* Initialize AHCI memory-mapped space */
dm_pci_read_config32(dev, PCI_BASE_ADDRESS_5, &reg32);
abar = (u8 *)reg32;
debug("ABAR: %p\n", abar);
/* CAP (HBA Capabilities) : enable power management */
clrsetbits_le32(abar + 0x00, 0x00020060 /* SXS+EMS+PMS */,
0x0c006000 /* PSC+SSC+SALP+SSS */ |
1 << 18); /* SAM: SATA AHCI MODE ONLY */
/* PI (Ports implemented) */
writel(plat->port_map, abar + 0x0c);
(void) readl(abar + 0x0c); /* Read back 1 */
(void) readl(abar + 0x0c); /* Read back 2 */
/* CAP2 (HBA Capabilities Extended)*/
if (plat->devslp_disable) {
clrbits_le32(abar + 0x24, 1 << 3);
} else {
/* Enable DEVSLP */
setbits_le32(abar + 0x24, 1 << 5 | 1 << 4 | 1 << 3 | 1 << 2);
for (port = 0; port < 4; port++) {
if (!(plat->port_map & (1 << port)))
continue;
/* DEVSLP DSP */
setbits_le32(abar + 0x144 + (0x80 * port), 1 << 1);
}
}
/* Static Power Gating for unused ports */
reg32 = readl(RCB_REG(0x3a84));
/* Port 3 and 2 disabled */
if ((plat->port_map & ((1 << 3)|(1 << 2))) == 0)
reg32 |= (1 << 24) | (1 << 26);
/* Port 1 and 0 disabled */
if ((plat->port_map & ((1 << 1)|(1 << 0))) == 0)
reg32 |= (1 << 20) | (1 << 18);
writel(reg32, RCB_REG(0x3a84));
/* Set Gen3 Transmitter settings if needed */
if (plat->port0_gen3_tx)
pch_iobp_update(SATA_IOBP_SP0_SECRT88,
~(SATA_SECRT88_VADJ_MASK <<
SATA_SECRT88_VADJ_SHIFT),
(plat->port0_gen3_tx &
SATA_SECRT88_VADJ_MASK)
<< SATA_SECRT88_VADJ_SHIFT);
if (plat->port1_gen3_tx)
pch_iobp_update(SATA_IOBP_SP1_SECRT88,
~(SATA_SECRT88_VADJ_MASK <<
SATA_SECRT88_VADJ_SHIFT),
(plat->port1_gen3_tx &
SATA_SECRT88_VADJ_MASK)
<< SATA_SECRT88_VADJ_SHIFT);
/* Set Gen3 DTLE DATA / EDGE registers if needed */
if (plat->port0_gen3_dtle) {
pch_iobp_update(SATA_IOBP_SP0DTLE_DATA,
~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT),
(plat->port0_gen3_dtle & SATA_DTLE_MASK)
<< SATA_DTLE_DATA_SHIFT);
pch_iobp_update(SATA_IOBP_SP0DTLE_EDGE,
~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT),
(plat->port0_gen3_dtle & SATA_DTLE_MASK)
<< SATA_DTLE_EDGE_SHIFT);
}
if (plat->port1_gen3_dtle) {
pch_iobp_update(SATA_IOBP_SP1DTLE_DATA,
~(SATA_DTLE_MASK << SATA_DTLE_DATA_SHIFT),
(plat->port1_gen3_dtle & SATA_DTLE_MASK)
<< SATA_DTLE_DATA_SHIFT);
pch_iobp_update(SATA_IOBP_SP1DTLE_EDGE,
~(SATA_DTLE_MASK << SATA_DTLE_EDGE_SHIFT),
(plat->port1_gen3_dtle & SATA_DTLE_MASK)
<< SATA_DTLE_EDGE_SHIFT);
}
/*
* Additional Programming Requirements for Power Optimizer
*/
/* Step 1 */
pch_common_sir_write(dev, 0x64, 0x883c9003);
/* Step 2: SIR 68h[15:0] = 880Ah */
reg32 = pch_common_sir_read(dev, 0x68);
reg32 &= 0xffff0000;
reg32 |= 0x880a;
pch_common_sir_write(dev, 0x68, reg32);
/* Step 3: SIR 60h[3] = 1 */
reg32 = pch_common_sir_read(dev, 0x60);
reg32 |= (1 << 3);
pch_common_sir_write(dev, 0x60, reg32);
/* Step 4: SIR 60h[0] = 1 */
reg32 = pch_common_sir_read(dev, 0x60);
reg32 |= (1 << 0);
pch_common_sir_write(dev, 0x60, reg32);
/* Step 5: SIR 60h[1] = 1 */
reg32 = pch_common_sir_read(dev, 0x60);
reg32 |= (1 << 1);
pch_common_sir_write(dev, 0x60, reg32);
/* Clock Gating */
pch_common_sir_write(dev, 0x70, 0x3f00bf1f);
pch_common_sir_write(dev, 0x54, 0xcf000f0f);
pch_common_sir_write(dev, 0x58, 0x00190000);
clrsetbits_le32(RCB_REG(0x333c), 0x00300000, 0x00c00000);
dm_pci_read_config32(dev, 0x300, &reg32);
reg32 |= 1 << 17 | 1 << 16 | 1 << 19;
reg32 |= 1 << 31 | 1 << 30 | 1 << 29;
dm_pci_write_config32(dev, 0x300, reg32);
dm_pci_read_config32(dev, 0x98, &reg32);
reg32 |= 1 << 29;
dm_pci_write_config32(dev, 0x98, reg32);
/* Register Lock */
dm_pci_read_config32(dev, 0x9c, &reg32);
reg32 |= 1 << 31;
dm_pci_write_config32(dev, 0x9c, reg32);
}
static int broadwell_sata_enable(struct udevice *dev)
{
struct sata_platdata *plat = dev_get_platdata(dev);
struct gpio_desc desc;
u16 map;
int ret;
/*
* Set SATA controller mode early so the resource allocator can
* properly assign IO/Memory resources for the controller.
*/
map = 0x0060;
map |= (plat->port_map ^ 0x3f) << 8;
dm_pci_write_config16(dev, 0x90, map);
ret = gpio_request_by_name(dev, "reset-gpio", 0, &desc, GPIOD_IS_OUT);
if (ret)
return ret;
return 0;
}
static int broadwell_sata_ofdata_to_platdata(struct udevice *dev)
{
struct sata_platdata *plat = dev_get_platdata(dev);
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
plat->port_map = fdtdec_get_int(blob, node, "intel,sata-port-map", 0);
plat->port0_gen3_tx = fdtdec_get_int(blob, node,
"intel,sata-port0-gen3-tx", 0);
return 0;
}
static int broadwell_sata_probe(struct udevice *dev)
{
if (!(gd->flags & GD_FLG_RELOC))
return broadwell_sata_enable(dev);
else
broadwell_sata_init(dev);
return 0;
}
static const struct udevice_id broadwell_ahci_ids[] = {
{ .compatible = "intel,wildcatpoint-ahci" },
{ }
};
U_BOOT_DRIVER(ahci_broadwell_drv) = {
.name = "ahci_broadwell",
.id = UCLASS_AHCI,
.of_match = broadwell_ahci_ids,
.ofdata_to_platdata = broadwell_sata_ofdata_to_platdata,
.probe = broadwell_sata_probe,
.platdata_auto_alloc_size = sizeof(struct sata_platdata),
};

View File

@@ -0,0 +1,302 @@
/*
* Copyright (c) 2016 Google, Inc
*
* From coreboot src/soc/intel/broadwell/romstage/raminit.c
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <pci.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/io.h>
#include <asm/lpc_common.h>
#include <asm/mrccache.h>
#include <asm/mrc_common.h>
#include <asm/mtrr.h>
#include <asm/pci.h>
#include <asm/arch/iomap.h>
#include <asm/arch/me.h>
#include <asm/arch/pch.h>
#include <asm/arch/pei_data.h>
#include <asm/arch/pm.h>
ulong board_get_usable_ram_top(ulong total_size)
{
return mrc_common_board_get_usable_ram_top(total_size);
}
void dram_init_banksize(void)
{
mrc_common_dram_init_banksize();
}
void broadwell_fill_pei_data(struct pei_data *pei_data)
{
pei_data->pei_version = PEI_VERSION;
pei_data->board_type = BOARD_TYPE_ULT;
pei_data->pciexbar = MCFG_BASE_ADDRESS;
pei_data->smbusbar = SMBUS_BASE_ADDRESS;
pei_data->ehcibar = EARLY_EHCI_BAR;
pei_data->xhcibar = EARLY_XHCI_BAR;
pei_data->gttbar = EARLY_GTT_BAR;
pei_data->pmbase = ACPI_BASE_ADDRESS;
pei_data->gpiobase = GPIO_BASE_ADDRESS;
pei_data->tseg_size = CONFIG_SMM_TSEG_SIZE;
pei_data->temp_mmio_base = EARLY_TEMP_MMIO;
pei_data->tx_byte = sdram_console_tx_byte;
pei_data->ddr_refresh_2x = 1;
}
static inline void pei_data_usb2_port(struct pei_data *pei_data, int port,
uint16_t length, uint8_t enable,
uint8_t oc_pin, uint8_t location)
{
pei_data->usb2_ports[port].length = length;
pei_data->usb2_ports[port].enable = enable;
pei_data->usb2_ports[port].oc_pin = oc_pin;
pei_data->usb2_ports[port].location = location;
}
static inline void pei_data_usb3_port(struct pei_data *pei_data, int port,
uint8_t enable, uint8_t oc_pin,
uint8_t fixed_eq)
{
pei_data->usb3_ports[port].enable = enable;
pei_data->usb3_ports[port].oc_pin = oc_pin;
pei_data->usb3_ports[port].fixed_eq = fixed_eq;
}
void mainboard_fill_pei_data(struct pei_data *pei_data)
{
/* DQ byte map for Samus board */
const u8 dq_map[2][6][2] = {
{ { 0x0F, 0xF0 }, { 0x00, 0xF0 }, { 0x0F, 0xF0 },
{ 0x0F, 0x00 }, { 0xFF, 0x00 }, { 0xFF, 0x00 } },
{ { 0x0F, 0xF0 }, { 0x00, 0xF0 }, { 0x0F, 0xF0 },
{ 0x0F, 0x00 }, { 0xFF, 0x00 }, { 0xFF, 0x00 } } };
/* DQS CPU<>DRAM map for Samus board */
const u8 dqs_map[2][8] = {
{ 2, 0, 1, 3, 6, 4, 7, 5 },
{ 2, 1, 0, 3, 6, 5, 4, 7 } };
pei_data->ec_present = 1;
/* One installed DIMM per channel */
pei_data->dimm_channel0_disabled = 2;
pei_data->dimm_channel1_disabled = 2;
memcpy(pei_data->dq_map, dq_map, sizeof(dq_map));
memcpy(pei_data->dqs_map, dqs_map, sizeof(dqs_map));
/* P0: HOST PORT */
pei_data_usb2_port(pei_data, 0, 0x0080, 1, 0,
USB_PORT_BACK_PANEL);
/* P1: HOST PORT */
pei_data_usb2_port(pei_data, 1, 0x0080, 1, 1,
USB_PORT_BACK_PANEL);
/* P2: RAIDEN */
pei_data_usb2_port(pei_data, 2, 0x0080, 1, USB_OC_PIN_SKIP,
USB_PORT_BACK_PANEL);
/* P3: SD CARD */
pei_data_usb2_port(pei_data, 3, 0x0040, 1, USB_OC_PIN_SKIP,
USB_PORT_INTERNAL);
/* P4: RAIDEN */
pei_data_usb2_port(pei_data, 4, 0x0080, 1, USB_OC_PIN_SKIP,
USB_PORT_BACK_PANEL);
/* P5: WWAN (Disabled) */
pei_data_usb2_port(pei_data, 5, 0x0000, 0, USB_OC_PIN_SKIP,
USB_PORT_SKIP);
/* P6: CAMERA */
pei_data_usb2_port(pei_data, 6, 0x0040, 1, USB_OC_PIN_SKIP,
USB_PORT_INTERNAL);
/* P7: BT */
pei_data_usb2_port(pei_data, 7, 0x0040, 1, USB_OC_PIN_SKIP,
USB_PORT_INTERNAL);
/* P1: HOST PORT */
pei_data_usb3_port(pei_data, 0, 1, 0, 0);
/* P2: HOST PORT */
pei_data_usb3_port(pei_data, 1, 1, 1, 0);
/* P3: RAIDEN */
pei_data_usb3_port(pei_data, 2, 1, USB_OC_PIN_SKIP, 0);
/* P4: RAIDEN */
pei_data_usb3_port(pei_data, 3, 1, USB_OC_PIN_SKIP, 0);
}
static unsigned long get_top_of_ram(struct udevice *dev)
{
/*
* Base of DPR is top of usable DRAM below 4GiB. The register has
* 1 MiB alignment and reports the TOP of the range, the base
* must be calculated from the size in MiB in bits 11:4.
*/
u32 dpr, tom;
dm_pci_read_config32(dev, DPR, &dpr);
tom = dpr & ~((1 << 20) - 1);
debug("dpt %08x tom %08x\n", dpr, tom);
/* Subtract DMA Protected Range size if enabled */
if (dpr & DPR_EPM)
tom -= (dpr & DPR_SIZE_MASK) << 16;
return (unsigned long)tom;
}
/**
* sdram_find() - Find available memory
*
* This is a bit complicated since on x86 there are system memory holes all
* over the place. We create a list of available memory blocks
*
* @dev: Northbridge device
*/
static int sdram_find(struct udevice *dev)
{
struct memory_info *info = &gd->arch.meminfo;
ulong top_of_ram;
top_of_ram = get_top_of_ram(dev);
mrc_add_memory_area(info, 0, top_of_ram);
/* Add MTRRs for memory */
mtrr_add_request(MTRR_TYPE_WRBACK, 0, 2ULL << 30);
return 0;
}
static int prepare_mrc_cache(struct pei_data *pei_data)
{
struct mrc_data_container *mrc_cache;
struct mrc_region entry;
int ret;
ret = mrccache_get_region(NULL, &entry);
if (ret)
return ret;
mrc_cache = mrccache_find_current(&entry);
if (!mrc_cache)
return -ENOENT;
pei_data->saved_data = mrc_cache->data;
pei_data->saved_data_size = mrc_cache->data_size;
debug("%s: at %p, size %x checksum %04x\n", __func__,
pei_data->saved_data, pei_data->saved_data_size,
mrc_cache->checksum);
return 0;
}
int dram_init(void)
{
struct pei_data _pei_data __aligned(8);
struct pei_data *pei_data = &_pei_data;
struct udevice *dev, *me_dev, *pch_dev;
struct chipset_power_state ps;
const void *spd_data;
int ret, size;
memset(pei_data, '\0', sizeof(struct pei_data));
/* Print ME state before MRC */
ret = syscon_get_by_driver_data(X86_SYSCON_ME, &me_dev);
if (ret)
return ret;
intel_me_status(me_dev);
/* Save ME HSIO version */
ret = uclass_first_device(UCLASS_PCH, &pch_dev);
if (ret)
return ret;
if (!pch_dev)
return -ENODEV;
power_state_get(pch_dev, &ps);
intel_me_hsio_version(me_dev, &ps.hsio_version, &ps.hsio_checksum);
broadwell_fill_pei_data(pei_data);
mainboard_fill_pei_data(pei_data);
ret = uclass_first_device(UCLASS_NORTHBRIDGE, &dev);
if (ret)
return ret;
if (!dev)
return -ENODEV;
size = 256;
ret = mrc_locate_spd(dev, size, &spd_data);
if (ret)
return ret;
memcpy(pei_data->spd_data[0][0], spd_data, size);
memcpy(pei_data->spd_data[1][0], spd_data, size);
ret = prepare_mrc_cache(pei_data);
if (ret)
debug("prepare_mrc_cache failed: %d\n", ret);
debug("PEI version %#x\n", pei_data->pei_version);
ret = mrc_common_init(dev, pei_data, true);
if (ret)
return ret;
debug("Memory init done\n");
ret = sdram_find(dev);
if (ret)
return ret;
gd->ram_size = gd->arch.meminfo.total_32bit_memory;
debug("RAM size %llx\n", (unsigned long long)gd->ram_size);
debug("MRC output data length %#x at %p\n", pei_data->data_to_save_size,
pei_data->data_to_save);
/* S3 resume: don't save scrambler seed or MRC data */
if (pei_data->boot_mode != SLEEP_STATE_S3) {
/*
* This will be copied to SDRAM in reserve_arch(), then written
* to SPI flash in mrccache_save()
*/
gd->arch.mrc_output = (char *)pei_data->data_to_save;
gd->arch.mrc_output_len = pei_data->data_to_save_size;
}
gd->arch.pei_meminfo = pei_data->meminfo;
return 0;
}
/* Use this hook to save our SDRAM parameters */
int misc_init_r(void)
{
int ret;
ret = mrccache_save();
if (ret)
printf("Unable to save MRC data: %d\n", ret);
else
debug("Saved MRC cache data\n");
return 0;
}
void board_debug_uart_init(void)
{
struct udevice *bus = NULL;
/* com1 / com2 decode range */
pci_x86_write_config(bus, PCH_DEV_LPC, LPC_IO_DEC, 1 << 4, PCI_SIZE_16);
pci_x86_write_config(bus, PCH_DEV_LPC, LPC_EN, COMA_LPC_EN,
PCI_SIZE_16);
}
static const struct udevice_id broadwell_syscon_ids[] = {
{ .compatible = "intel,me", .data = X86_SYSCON_ME },
{ .compatible = "intel,gma", .data = X86_SYSCON_GMA },
{ }
};
U_BOOT_DRIVER(syscon_intel_me) = {
.name = "intel_me_syscon",
.id = UCLASS_SYSCON,
.of_match = broadwell_syscon_ids,
};

View File

@@ -0,0 +1,64 @@
/*
* (C) Copyright 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/global_data.h>
#include <asm/msr-index.h>
#include <asm/processor-flags.h>
/*
* rdi - 32-bit code segment selector
* rsi - target address
* rdx - table address (0 if none)
*/
.code64
.globl cpu_call32
cpu_call32:
cli
/* Save table pointer */
mov %edx, %ebx
/*
* Debugging option, this outputs characters to the console UART
* mov $0x3f8,%edx
* mov $'a',%al
* out %al,(%dx)
*/
pushf
push %rdi /* 32-bit code segment */
lea compat(%rip), %rax
push %rax
.byte 0x48 /* REX prefix to force 64-bit far return */
retf
.code32
compat:
/*
* We are now in compatibility mode with a default operand size of
* 32 bits. First disable paging.
*/
movl %cr0, %eax
andl $~X86_CR0_PG, %eax
movl %eax, %cr0
/* Invalidate TLB */
xorl %eax, %eax
movl %eax, %cr3
/* Disable Long mode in EFER (Extended Feature Enable Register) */
movl $MSR_EFER, %ecx
rdmsr
btr $_EFER_LME, %eax
wrmsr
/* Set up table pointer for _x86boot_start */
mov %ebx, %ecx
/* Jump to the required target */
pushl %edi /* 32-bit code segment */
pushl %esi /* 32-bit target address */
retf

View File

@@ -0,0 +1,93 @@
/*
* (C) Copyright 2014 Google, Inc
* Copyright (C) 1991, 1992, 1993 Linus Torvalds
*
* Parts of this copied from Linux arch/x86/boot/compressed/head_64.S
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/global_data.h>
#include <asm/msr-index.h>
#include <asm/processor-flags.h>
.code32
.globl cpu_call64
cpu_call64:
/*
* cpu_call64(ulong pgtable, ulong setup_base, ulong target)
*
* eax - pgtable
* edx - setup_base
* ecx - target
*/
cli
push %ecx /* arg2 = target */
push %edx /* arg1 = setup_base */
mov %eax, %ebx
/* Load new GDT with the 64bit segments using 32bit descriptor */
leal gdt, %eax
movl %eax, gdt+2
lgdt gdt
/* Enable PAE mode */
movl $(X86_CR4_PAE), %eax
movl %eax, %cr4
/* Enable the boot page tables */
leal (%ebx), %eax
movl %eax, %cr3
/* Enable Long mode in EFER (Extended Feature Enable Register) */
movl $MSR_EFER, %ecx
rdmsr
btsl $_EFER_LME, %eax
wrmsr
/* After gdt is loaded */
xorl %eax, %eax
lldt %ax
movl $0x20, %eax
ltr %ax
/*
* Setup for the jump to 64bit mode
*
* When the jump is performed we will be in long mode but
* in 32bit compatibility mode with EFER.LME = 1, CS.L = 0, CS.D = 1
* (and in turn EFER.LMA = 1). To jump into 64bit mode we use
* the new gdt/idt that has __KERNEL_CS with CS.L = 1.
* We place all of the values on our mini stack so lret can
* used to perform that far jump. See the gdt below.
*/
pop %esi /* setup_base */
pushl $0x10
leal lret_target, %eax
pushl %eax
/* Enter paged protected Mode, activating Long Mode */
movl $(X86_CR0_PG | X86_CR0_PE), %eax
movl %eax, %cr0
/* Jump from 32bit compatibility mode into 64bit mode. */
lret
code64:
lret_target:
pop %eax /* target */
mov %eax, %eax /* Clear bits 63:32 */
jmp *%eax /* Jump to the 64-bit target */
.data
gdt:
.word gdt_end - gdt - 1
.long gdt /* Fixed up by code above */
.word 0
.quad 0x0000000000000000 /* NULL descriptor */
.quad 0x00af9a000000ffff /* __KERNEL_CS */
.quad 0x00cf92000000ffff /* __KERNEL_DS */
.quad 0x0080890000000000 /* TS descriptor */
.quad 0x0000000000000000 /* TS continued */
gdt_end:

View File

@@ -0,0 +1,17 @@
#
# (C) Copyright 2002
# Daniel Engström, Omicron Ceti AB, daniel@omicron.se.
#
# SPDX-License-Identifier: GPL-2.0+
#
CROSS_COMPILE ?= i386-linux-
PLATFORM_CPPFLAGS += -D__I386__
# DO NOT MODIFY THE FOLLOWING UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!
LDPPFLAGS += -DRESET_SEG_START=$(CONFIG_RESET_SEG_START)
LDPPFLAGS += -DRESET_SEG_SIZE=$(CONFIG_RESET_SEG_SIZE)
LDPPFLAGS += -DRESET_VEC_LOC=$(CONFIG_RESET_VEC_LOC)
LDPPFLAGS += -DSTART_16=$(CONFIG_SYS_X86_START16)
LDPPFLAGS += -DRESET_BASE="CONFIG_SYS_TEXT_BASE + (CONFIG_SYS_MONITOR_LEN - RESET_SEG_SIZE)"

View File

@@ -0,0 +1,15 @@
if TARGET_COREBOOT
config SYS_COREBOOT
bool
default y
config CBMEM_CONSOLE
bool
default y
config VIDEO_COREBOOT
bool
default y
endif

View File

@@ -0,0 +1,20 @@
#
# Copyright (c) 2011 The Chromium OS Authors.
#
# (C) Copyright 2008
# Graeme Russ, graeme.russ@gmail.com.
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2002
# Daniel Engström, Omicron Ceti AB, daniel@omicron.se.
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += car.o
obj-y += coreboot.o
obj-y += tables.o
obj-y += sdram.o
obj-y += timestamp.o

View File

@@ -0,0 +1,13 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2010-2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
.section .text
.globl car_init
car_init:
jmp car_init_ret

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2008
* Graeme Russ, graeme.russ@gmail.com.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <fdtdec.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
#include <asm/arch/sysinfo.h>
#include <asm/arch/timestamp.h>
DECLARE_GLOBAL_DATA_PTR;
int arch_cpu_init(void)
{
int ret = get_coreboot_info(&lib_sysinfo);
if (ret != 0) {
printf("Failed to parse coreboot tables.\n");
return ret;
}
timestamp_init();
return x86_cpu_init_f();
}
int board_early_init_f(void)
{
return 0;
}
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}
static void board_final_cleanup(void)
{
/*
* Un-cache the ROM so the kernel has one
* more MTRR available.
*
* Coreboot should have assigned this to the
* top available variable MTRR.
*/
u8 top_mtrr = (native_read_msr(MTRR_CAP_MSR) & 0xff) - 1;
u8 top_type = native_read_msr(MTRR_PHYS_BASE_MSR(top_mtrr)) & 0xff;
/* Make sure this MTRR is the correct Write-Protected type */
if (top_type == MTRR_TYPE_WRPROT) {
struct mtrr_state state;
mtrr_open(&state);
wrmsrl(MTRR_PHYS_BASE_MSR(top_mtrr), 0);
wrmsrl(MTRR_PHYS_MASK_MSR(top_mtrr), 0);
mtrr_close(&state);
}
if (!fdtdec_get_config_bool(gd->fdt_blob, "u-boot,no-apm-finalize")) {
/*
* Issue SMI to coreboot to lock down ME and registers
* when allowed via device tree
*/
printf("Finalizing coreboot\n");
outb(0xcb, 0xb2);
}
}
int last_stage_init(void)
{
if (gd->flags & GD_FLG_COLD_BOOT)
timestamp_add_to_bootstage();
board_final_cleanup();
return 0;
}
int misc_init_r(void)
{
return 0;
}
int arch_misc_init(void)
{
return 0;
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2010,2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/e820.h>
#include <asm/arch/sysinfo.h>
DECLARE_GLOBAL_DATA_PTR;
unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
{
unsigned num_entries;
int i;
num_entries = min((unsigned)lib_sysinfo.n_memranges, max_entries);
if (num_entries < lib_sysinfo.n_memranges) {
printf("Warning: Limiting e820 map to %d entries.\n",
num_entries);
}
for (i = 0; i < num_entries; i++) {
struct memrange *memrange = &lib_sysinfo.memrange[i];
entries[i].addr = memrange->base;
entries[i].size = memrange->size;
/*
* coreboot has some extensions (type 6 & 16) to the E820 types.
* When we detect this, mark it as E820_RESERVED.
*/
if (memrange->type == CB_MEM_VENDOR_RSVD ||
memrange->type == CB_MEM_TABLE)
entries[i].type = E820_RESERVED;
else
entries[i].type = memrange->type;
}
return num_entries;
}
/*
* This function looks for the highest region of memory lower than 4GB which
* has enough space for U-Boot where U-Boot is aligned on a page boundary. It
* overrides the default implementation found elsewhere which simply picks the
* end of ram, wherever that may be. The location of the stack, the relocation
* address, and how far U-Boot is moved by relocation are set in the global
* data structure.
*/
ulong board_get_usable_ram_top(ulong total_size)
{
uintptr_t dest_addr = 0;
int i;
for (i = 0; i < lib_sysinfo.n_memranges; i++) {
struct memrange *memrange = &lib_sysinfo.memrange[i];
/* Force U-Boot to relocate to a page aligned address. */
uint64_t start = roundup(memrange->base, 1 << 12);
uint64_t end = memrange->base + memrange->size;
/* Ignore non-memory regions. */
if (memrange->type != CB_MEM_RAM)
continue;
/* Filter memory over 4GB. */
if (end > 0xffffffffULL)
end = 0x100000000ULL;
/* Skip this region if it's too small. */
if (end - start < total_size)
continue;
/* Use this address if it's the largest so far. */
if (end > dest_addr)
dest_addr = end;
}
/* If no suitable area was found, return an error. */
if (!dest_addr)
panic("No available memory found for relocation");
return (ulong)dest_addr;
}
int dram_init(void)
{
int i;
phys_size_t ram_size = 0;
for (i = 0; i < lib_sysinfo.n_memranges; i++) {
struct memrange *memrange = &lib_sysinfo.memrange[i];
unsigned long long end = memrange->base + memrange->size;
if (memrange->type == CB_MEM_RAM && end > ram_size)
ram_size += memrange->size;
}
gd->ram_size = ram_size;
if (ram_size == 0)
return -1;
return 0;
}
void dram_init_banksize(void)
{
int i, j;
if (CONFIG_NR_DRAM_BANKS) {
for (i = 0, j = 0; i < lib_sysinfo.n_memranges; i++) {
struct memrange *memrange = &lib_sysinfo.memrange[i];
if (memrange->type == CB_MEM_RAM) {
gd->bd->bi_dram[j].start = memrange->base;
gd->bd->bi_dram[j].size = memrange->size;
j++;
if (j >= CONFIG_NR_DRAM_BANKS)
break;
}
}
}
}

View File

@@ -0,0 +1,234 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 Advanced Micro Devices, Inc.
* Copyright (C) 2009 coresystems GmbH
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common.h>
#include <net.h>
#include <asm/arch/sysinfo.h>
/*
* This needs to be in the .data section so that it's copied over during
* relocation. By default it's put in the .bss section which is simply filled
* with zeroes when transitioning from "ROM", which is really RAM, to other
* RAM.
*/
struct sysinfo_t lib_sysinfo __attribute__((section(".data")));
/*
* Some of this is x86 specific, and the rest of it is generic. Right now,
* since we only support x86, we'll avoid trying to make lots of infrastructure
* we don't need. If in the future, we want to use coreboot on some other
* architecture, then take out the generic parsing code and move it elsewhere.
*/
/* === Parsing code === */
/* This is the generic parsing code. */
static void cb_parse_memory(unsigned char *ptr, struct sysinfo_t *info)
{
struct cb_memory *mem = (struct cb_memory *)ptr;
int count = MEM_RANGE_COUNT(mem);
int i;
if (count > SYSINFO_MAX_MEM_RANGES)
count = SYSINFO_MAX_MEM_RANGES;
info->n_memranges = 0;
for (i = 0; i < count; i++) {
struct cb_memory_range *range =
(struct cb_memory_range *)MEM_RANGE_PTR(mem, i);
info->memrange[info->n_memranges].base =
UNPACK_CB64(range->start);
info->memrange[info->n_memranges].size =
UNPACK_CB64(range->size);
info->memrange[info->n_memranges].type = range->type;
info->n_memranges++;
}
}
static void cb_parse_serial(unsigned char *ptr, struct sysinfo_t *info)
{
struct cb_serial *ser = (struct cb_serial *)ptr;
info->serial = ser;
}
static void cb_parse_vbnv(unsigned char *ptr, struct sysinfo_t *info)
{
struct cb_vbnv *vbnv = (struct cb_vbnv *)ptr;
info->vbnv_start = vbnv->vbnv_start;
info->vbnv_size = vbnv->vbnv_size;
}
static void cb_parse_gpios(unsigned char *ptr, struct sysinfo_t *info)
{
int i;
struct cb_gpios *gpios = (struct cb_gpios *)ptr;
info->num_gpios = (gpios->count < SYSINFO_MAX_GPIOS) ?
(gpios->count) : SYSINFO_MAX_GPIOS;
for (i = 0; i < info->num_gpios; i++)
info->gpios[i] = gpios->gpios[i];
}
static void cb_parse_vdat(unsigned char *ptr, struct sysinfo_t *info)
{
struct cb_vdat *vdat = (struct cb_vdat *) ptr;
info->vdat_addr = vdat->vdat_addr;
info->vdat_size = vdat->vdat_size;
}
static void cb_parse_tstamp(unsigned char *ptr, struct sysinfo_t *info)
{
info->tstamp_table = ((struct cb_cbmem_tab *)ptr)->cbmem_tab;
}
static void cb_parse_cbmem_cons(unsigned char *ptr, struct sysinfo_t *info)
{
info->cbmem_cons = ((struct cb_cbmem_tab *)ptr)->cbmem_tab;
}
static void cb_parse_framebuffer(unsigned char *ptr, struct sysinfo_t *info)
{
info->framebuffer = (struct cb_framebuffer *)ptr;
}
static void cb_parse_string(unsigned char *ptr, char **info)
{
*info = (char *)((struct cb_string *)ptr)->string;
}
static int cb_parse_header(void *addr, int len, struct sysinfo_t *info)
{
struct cb_header *header;
unsigned char *ptr = (unsigned char *)addr;
int i;
for (i = 0; i < len; i += 16, ptr += 16) {
header = (struct cb_header *)ptr;
if (!strncmp((const char *)header->signature, "LBIO", 4))
break;
}
/* We walked the entire space and didn't find anything. */
if (i >= len)
return -1;
if (!header->table_bytes)
return 0;
/* Make sure the checksums match. */
if (!ip_checksum_ok(header, sizeof(*header)))
return -1;
if (compute_ip_checksum(ptr + sizeof(*header), header->table_bytes) !=
header->table_checksum)
return -1;
/* Now, walk the tables. */
ptr += header->header_bytes;
/* Inintialize some fields to sentinel values. */
info->vbnv_start = info->vbnv_size = (uint32_t)(-1);
for (i = 0; i < header->table_entries; i++) {
struct cb_record *rec = (struct cb_record *)ptr;
/* We only care about a few tags here (maybe more later). */
switch (rec->tag) {
case CB_TAG_FORWARD:
return cb_parse_header(
(void *)(unsigned long)
((struct cb_forward *)rec)->forward,
len, info);
continue;
case CB_TAG_MEMORY:
cb_parse_memory(ptr, info);
break;
case CB_TAG_SERIAL:
cb_parse_serial(ptr, info);
break;
case CB_TAG_VERSION:
cb_parse_string(ptr, &info->version);
break;
case CB_TAG_EXTRA_VERSION:
cb_parse_string(ptr, &info->extra_version);
break;
case CB_TAG_BUILD:
cb_parse_string(ptr, &info->build);
break;
case CB_TAG_COMPILE_TIME:
cb_parse_string(ptr, &info->compile_time);
break;
case CB_TAG_COMPILE_BY:
cb_parse_string(ptr, &info->compile_by);
break;
case CB_TAG_COMPILE_HOST:
cb_parse_string(ptr, &info->compile_host);
break;
case CB_TAG_COMPILE_DOMAIN:
cb_parse_string(ptr, &info->compile_domain);
break;
case CB_TAG_COMPILER:
cb_parse_string(ptr, &info->compiler);
break;
case CB_TAG_LINKER:
cb_parse_string(ptr, &info->linker);
break;
case CB_TAG_ASSEMBLER:
cb_parse_string(ptr, &info->assembler);
break;
/*
* FIXME we should warn on serial if coreboot set up a
* framebuffer buf the payload does not know about it.
*/
case CB_TAG_FRAMEBUFFER:
cb_parse_framebuffer(ptr, info);
break;
case CB_TAG_GPIO:
cb_parse_gpios(ptr, info);
break;
case CB_TAG_VDAT:
cb_parse_vdat(ptr, info);
break;
case CB_TAG_TIMESTAMPS:
cb_parse_tstamp(ptr, info);
break;
case CB_TAG_CBMEM_CONSOLE:
cb_parse_cbmem_cons(ptr, info);
break;
case CB_TAG_VBNV:
cb_parse_vbnv(ptr, info);
break;
}
ptr += rec->size;
}
return 1;
}
/* == Architecture specific == */
/* This is the x86 specific stuff. */
int get_coreboot_info(struct sysinfo_t *info)
{
int ret = cb_parse_header((void *)0x00000000, 0x1000, info);
if (ret != 1)
ret = cb_parse_header((void *)0x000f0000, 0x1000, info);
return (ret == 1) ? 0 : -1;
}

View File

@@ -0,0 +1,86 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/arch/timestamp.h>
#include <asm/arch/sysinfo.h>
#include <linux/compiler.h>
struct timestamp_entry {
uint32_t entry_id;
uint64_t entry_stamp;
} __packed;
struct timestamp_table {
uint64_t base_time;
uint32_t max_entries;
uint32_t num_entries;
struct timestamp_entry entries[0]; /* Variable number of entries */
} __packed;
static struct timestamp_table *ts_table __attribute__((section(".data")));
void timestamp_init(void)
{
timestamp_add_now(TS_U_BOOT_INITTED);
}
void timestamp_add(enum timestamp_id id, uint64_t ts_time)
{
struct timestamp_entry *tse;
if (!ts_table || (ts_table->num_entries == ts_table->max_entries))
return;
tse = &ts_table->entries[ts_table->num_entries++];
tse->entry_id = id;
tse->entry_stamp = ts_time - ts_table->base_time;
}
void timestamp_add_now(enum timestamp_id id)
{
timestamp_add(id, rdtsc());
}
int timestamp_add_to_bootstage(void)
{
uint i;
if (!ts_table)
return -1;
for (i = 0; i < ts_table->num_entries; i++) {
struct timestamp_entry *tse = &ts_table->entries[i];
const char *name = NULL;
switch (tse->entry_id) {
case TS_START_ROMSTAGE:
name = "start-romstage";
break;
case TS_BEFORE_INITRAM:
name = "before-initram";
break;
case TS_DEVICE_INITIALIZE:
name = "device-initialize";
break;
case TS_DEVICE_DONE:
name = "device-done";
break;
case TS_SELFBOOT_JUMP:
name = "selfboot-jump";
break;
}
if (name) {
bootstage_add_record(0, name, BOOTSTAGEF_ALLOC,
tse->entry_stamp /
get_tbclk_mhz());
}
}
return 0;
}

775
u-boot/arch/x86/cpu/cpu.c Normal file
View File

@@ -0,0 +1,775 @@
/*
* (C) Copyright 2008-2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Alex Zuepke <azu@sysgo.de>
*
* Part of this file is adapted from coreboot
* src/arch/x86/lib/cpu.c
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <syscon.h>
#include <asm/control_regs.h>
#include <asm/coreboot_tables.h>
#include <asm/cpu.h>
#include <asm/lapic.h>
#include <asm/microcode.h>
#include <asm/mp.h>
#include <asm/mrccache.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
#include <asm/post.h>
#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;
/*
* Constructor for a conventional segment GDT (or LDT) entry
* This is a macro so it can be used in initialisers
*/
#define GDT_ENTRY(flags, base, limit) \
((((base) & 0xff000000ULL) << (56-24)) | \
(((flags) & 0x0000f0ffULL) << 40) | \
(((limit) & 0x000f0000ULL) << (48-16)) | \
(((base) & 0x00ffffffULL) << 16) | \
(((limit) & 0x0000ffffULL)))
struct gdt_ptr {
u16 len;
u32 ptr;
} __packed;
struct cpu_device_id {
unsigned vendor;
unsigned device;
};
struct cpuinfo_x86 {
uint8_t x86; /* CPU family */
uint8_t x86_vendor; /* CPU vendor */
uint8_t x86_model;
uint8_t x86_mask;
};
/*
* List of cpu vendor strings along with their normalized
* id values.
*/
static const struct {
int vendor;
const char *name;
} x86_vendors[] = {
{ X86_VENDOR_INTEL, "GenuineIntel", },
{ X86_VENDOR_CYRIX, "CyrixInstead", },
{ X86_VENDOR_AMD, "AuthenticAMD", },
{ X86_VENDOR_UMC, "UMC UMC UMC ", },
{ X86_VENDOR_NEXGEN, "NexGenDriven", },
{ X86_VENDOR_CENTAUR, "CentaurHauls", },
{ X86_VENDOR_RISE, "RiseRiseRise", },
{ X86_VENDOR_TRANSMETA, "GenuineTMx86", },
{ X86_VENDOR_TRANSMETA, "TransmetaCPU", },
{ X86_VENDOR_NSC, "Geode by NSC", },
{ X86_VENDOR_SIS, "SiS SiS SiS ", },
};
static const char *const x86_vendor_name[] = {
[X86_VENDOR_INTEL] = "Intel",
[X86_VENDOR_CYRIX] = "Cyrix",
[X86_VENDOR_AMD] = "AMD",
[X86_VENDOR_UMC] = "UMC",
[X86_VENDOR_NEXGEN] = "NexGen",
[X86_VENDOR_CENTAUR] = "Centaur",
[X86_VENDOR_RISE] = "Rise",
[X86_VENDOR_TRANSMETA] = "Transmeta",
[X86_VENDOR_NSC] = "NSC",
[X86_VENDOR_SIS] = "SiS",
};
static void load_ds(u32 segment)
{
asm volatile("movl %0, %%ds" : : "r" (segment * X86_GDT_ENTRY_SIZE));
}
static void load_es(u32 segment)
{
asm volatile("movl %0, %%es" : : "r" (segment * X86_GDT_ENTRY_SIZE));
}
static void load_fs(u32 segment)
{
asm volatile("movl %0, %%fs" : : "r" (segment * X86_GDT_ENTRY_SIZE));
}
static void load_gs(u32 segment)
{
asm volatile("movl %0, %%gs" : : "r" (segment * X86_GDT_ENTRY_SIZE));
}
static void load_ss(u32 segment)
{
asm volatile("movl %0, %%ss" : : "r" (segment * X86_GDT_ENTRY_SIZE));
}
static void load_gdt(const u64 *boot_gdt, u16 num_entries)
{
struct gdt_ptr gdt;
gdt.len = (num_entries * X86_GDT_ENTRY_SIZE) - 1;
gdt.ptr = (u32)boot_gdt;
asm volatile("lgdtl %0\n" : : "m" (gdt));
}
void arch_setup_gd(gd_t *new_gd)
{
u64 *gdt_addr;
gdt_addr = new_gd->arch.gdt;
/*
* CS: code, read/execute, 4 GB, base 0
*
* Some OS (like VxWorks) requires GDT entry 1 to be the 32-bit CS
*/
gdt_addr[X86_GDT_ENTRY_UNUSED] = GDT_ENTRY(0xc09b, 0, 0xfffff);
gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff);
/* DS: data, read/write, 4 GB, base 0 */
gdt_addr[X86_GDT_ENTRY_32BIT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff);
/* FS: data, read/write, 4 GB, base (Global Data Pointer) */
new_gd->arch.gd_addr = new_gd;
gdt_addr[X86_GDT_ENTRY_32BIT_FS] = GDT_ENTRY(0xc093,
(ulong)&new_gd->arch.gd_addr, 0xfffff);
/* 16-bit CS: code, read/execute, 64 kB, base 0 */
gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x009b, 0, 0x0ffff);
/* 16-bit DS: data, read/write, 64 kB, base 0 */
gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x0093, 0, 0x0ffff);
gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_CS] = GDT_ENTRY(0x809b, 0, 0xfffff);
gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_DS] = GDT_ENTRY(0x8093, 0, 0xfffff);
load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES);
load_ds(X86_GDT_ENTRY_32BIT_DS);
load_es(X86_GDT_ENTRY_32BIT_DS);
load_gs(X86_GDT_ENTRY_32BIT_DS);
load_ss(X86_GDT_ENTRY_32BIT_DS);
load_fs(X86_GDT_ENTRY_32BIT_FS);
}
#ifdef CONFIG_HAVE_FSP
/*
* Setup FSP execution environment GDT
*
* Per Intel FSP external architecture specification, before calling any FSP
* APIs, we need make sure the system is in flat 32-bit mode and both the code
* and data selectors should have full 4GB access range. Here we reuse the one
* we used in arch/x86/cpu/start16.S, and reload the segement registers.
*/
void setup_fsp_gdt(void)
{
load_gdt((const u64 *)(gdt_rom + CONFIG_RESET_SEG_START), 4);
load_ds(X86_GDT_ENTRY_32BIT_DS);
load_ss(X86_GDT_ENTRY_32BIT_DS);
load_es(X86_GDT_ENTRY_32BIT_DS);
load_fs(X86_GDT_ENTRY_32BIT_DS);
load_gs(X86_GDT_ENTRY_32BIT_DS);
}
#endif
int __weak x86_cleanup_before_linux(void)
{
#ifdef CONFIG_BOOTSTAGE_STASH
bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR,
CONFIG_BOOTSTAGE_STASH_SIZE);
#endif
return 0;
}
/*
* Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
* by the fact that they preserve the flags across the division of 5/2.
* PII and PPro exhibit this behavior too, but they have cpuid available.
*/
/*
* Perform the Cyrix 5/2 test. A Cyrix won't change
* the flags, while other 486 chips will.
*/
static inline int test_cyrix_52div(void)
{
unsigned int test;
__asm__ __volatile__(
"sahf\n\t" /* clear flags (%eax = 0x0005) */
"div %b2\n\t" /* divide 5 by 2 */
"lahf" /* store flags into %ah */
: "=a" (test)
: "0" (5), "q" (2)
: "cc");
/* AH is 0x02 on Cyrix after the divide.. */
return (unsigned char) (test >> 8) == 0x02;
}
/*
* Detect a NexGen CPU running without BIOS hypercode new enough
* to have CPUID. (Thanks to Herbert Oppmann)
*/
static int deep_magic_nexgen_probe(void)
{
int ret;
__asm__ __volatile__ (
" movw $0x5555, %%ax\n"
" xorw %%dx,%%dx\n"
" movw $2, %%cx\n"
" divw %%cx\n"
" movl $0, %%eax\n"
" jnz 1f\n"
" movl $1, %%eax\n"
"1:\n"
: "=a" (ret) : : "cx", "dx");
return ret;
}
static bool has_cpuid(void)
{
return flag_is_changeable_p(X86_EFLAGS_ID);
}
static bool has_mtrr(void)
{
return cpuid_edx(0x00000001) & (1 << 12) ? true : false;
}
static int build_vendor_name(char *vendor_name)
{
struct cpuid_result result;
result = cpuid(0x00000000);
unsigned int *name_as_ints = (unsigned int *)vendor_name;
name_as_ints[0] = result.ebx;
name_as_ints[1] = result.edx;
name_as_ints[2] = result.ecx;
return result.eax;
}
static void identify_cpu(struct cpu_device_id *cpu)
{
char vendor_name[16];
int i;
vendor_name[0] = '\0'; /* Unset */
cpu->device = 0; /* fix gcc 4.4.4 warning */
/* Find the id and vendor_name */
if (!has_cpuid()) {
/* Its a 486 if we can modify the AC flag */
if (flag_is_changeable_p(X86_EFLAGS_AC))
cpu->device = 0x00000400; /* 486 */
else
cpu->device = 0x00000300; /* 386 */
if ((cpu->device == 0x00000400) && test_cyrix_52div()) {
memcpy(vendor_name, "CyrixInstead", 13);
/* If we ever care we can enable cpuid here */
}
/* Detect NexGen with old hypercode */
else if (deep_magic_nexgen_probe())
memcpy(vendor_name, "NexGenDriven", 13);
}
if (has_cpuid()) {
int cpuid_level;
cpuid_level = build_vendor_name(vendor_name);
vendor_name[12] = '\0';
/* Intel-defined flags: level 0x00000001 */
if (cpuid_level >= 0x00000001) {
cpu->device = cpuid_eax(0x00000001);
} else {
/* Have CPUID level 0 only unheard of */
cpu->device = 0x00000400;
}
}
cpu->vendor = X86_VENDOR_UNKNOWN;
for (i = 0; i < ARRAY_SIZE(x86_vendors); i++) {
if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) {
cpu->vendor = x86_vendors[i].vendor;
break;
}
}
}
static inline void get_fms(struct cpuinfo_x86 *c, uint32_t tfms)
{
c->x86 = (tfms >> 8) & 0xf;
c->x86_model = (tfms >> 4) & 0xf;
c->x86_mask = tfms & 0xf;
if (c->x86 == 0xf)
c->x86 += (tfms >> 20) & 0xff;
if (c->x86 >= 0x6)
c->x86_model += ((tfms >> 16) & 0xF) << 4;
}
u32 cpu_get_family_model(void)
{
return gd->arch.x86_device & 0x0fff0ff0;
}
u32 cpu_get_stepping(void)
{
return gd->arch.x86_mask;
}
int x86_cpu_init_f(void)
{
const u32 em_rst = ~X86_CR0_EM;
const u32 mp_ne_set = X86_CR0_MP | X86_CR0_NE;
if (ll_boot_init()) {
/* initialize FPU, reset EM, set MP and NE */
asm ("fninit\n" \
"movl %%cr0, %%eax\n" \
"andl %0, %%eax\n" \
"orl %1, %%eax\n" \
"movl %%eax, %%cr0\n" \
: : "i" (em_rst), "i" (mp_ne_set) : "eax");
}
/* identify CPU via cpuid and store the decoded info into gd->arch */
if (has_cpuid()) {
struct cpu_device_id cpu;
struct cpuinfo_x86 c;
identify_cpu(&cpu);
get_fms(&c, cpu.device);
gd->arch.x86 = c.x86;
gd->arch.x86_vendor = cpu.vendor;
gd->arch.x86_model = c.x86_model;
gd->arch.x86_mask = c.x86_mask;
gd->arch.x86_device = cpu.device;
gd->arch.has_mtrr = has_mtrr();
}
/* Don't allow PCI region 3 to use memory in the 2-4GB memory hole */
gd->pci_ram_top = 0x80000000U;
/* Configure fixed range MTRRs for some legacy regions */
if (gd->arch.has_mtrr) {
u64 mtrr_cap;
mtrr_cap = native_read_msr(MTRR_CAP_MSR);
if (mtrr_cap & MTRR_CAP_FIX) {
/* Mark the VGA RAM area as uncacheable */
native_write_msr(MTRR_FIX_16K_A0000_MSR,
MTRR_FIX_TYPE(MTRR_TYPE_UNCACHEABLE),
MTRR_FIX_TYPE(MTRR_TYPE_UNCACHEABLE));
/*
* Mark the PCI ROM area as cacheable to improve ROM
* execution performance.
*/
native_write_msr(MTRR_FIX_4K_C0000_MSR,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
native_write_msr(MTRR_FIX_4K_C8000_MSR,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
native_write_msr(MTRR_FIX_4K_D0000_MSR,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
native_write_msr(MTRR_FIX_4K_D8000_MSR,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
/* Enable the fixed range MTRRs */
msr_setbits_64(MTRR_DEF_TYPE_MSR, MTRR_DEF_TYPE_FIX_EN);
}
}
#ifdef CONFIG_I8254_TIMER
/* Set up the i8254 timer if required */
i8254_init();
#endif
return 0;
}
void x86_enable_caches(void)
{
unsigned long cr0;
cr0 = read_cr0();
cr0 &= ~(X86_CR0_NW | X86_CR0_CD);
write_cr0(cr0);
wbinvd();
}
void enable_caches(void) __attribute__((weak, alias("x86_enable_caches")));
void x86_disable_caches(void)
{
unsigned long cr0;
cr0 = read_cr0();
cr0 |= X86_CR0_NW | X86_CR0_CD;
wbinvd();
write_cr0(cr0);
wbinvd();
}
void disable_caches(void) __attribute__((weak, alias("x86_disable_caches")));
int x86_init_cache(void)
{
enable_caches();
return 0;
}
int init_cache(void) __attribute__((weak, alias("x86_init_cache")));
int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
printf("resetting ...\n");
/* wait 50 ms */
udelay(50000);
disable_interrupts();
reset_cpu(0);
/*NOTREACHED*/
return 0;
}
void flush_cache(unsigned long dummy1, unsigned long dummy2)
{
asm("wbinvd\n");
}
__weak void reset_cpu(ulong addr)
{
/* Do a hard reset through the chipset's reset control register */
outb(SYS_RST | RST_CPU, IO_PORT_RESET);
for (;;)
cpu_hlt();
}
void x86_full_reset(void)
{
outb(FULL_RST | SYS_RST | RST_CPU, IO_PORT_RESET);
}
int dcache_status(void)
{
return !(read_cr0() & X86_CR0_CD);
}
/* Define these functions to allow ehch-hcd to function */
void flush_dcache_range(unsigned long start, unsigned long stop)
{
}
void invalidate_dcache_range(unsigned long start, unsigned long stop)
{
}
void dcache_enable(void)
{
enable_caches();
}
void dcache_disable(void)
{
disable_caches();
}
void icache_enable(void)
{
}
void icache_disable(void)
{
}
int icache_status(void)
{
return 1;
}
void cpu_enable_paging_pae(ulong cr3)
{
__asm__ __volatile__(
/* Load the page table address */
"movl %0, %%cr3\n"
/* Enable pae */
"movl %%cr4, %%eax\n"
"orl $0x00000020, %%eax\n"
"movl %%eax, %%cr4\n"
/* Enable paging */
"movl %%cr0, %%eax\n"
"orl $0x80000000, %%eax\n"
"movl %%eax, %%cr0\n"
:
: "r" (cr3)
: "eax");
}
void cpu_disable_paging_pae(void)
{
/* Turn off paging */
__asm__ __volatile__ (
/* Disable paging */
"movl %%cr0, %%eax\n"
"andl $0x7fffffff, %%eax\n"
"movl %%eax, %%cr0\n"
/* Disable pae */
"movl %%cr4, %%eax\n"
"andl $0xffffffdf, %%eax\n"
"movl %%eax, %%cr4\n"
:
:
: "eax");
}
static bool can_detect_long_mode(void)
{
return cpuid_eax(0x80000000) > 0x80000000UL;
}
static bool has_long_mode(void)
{
return cpuid_edx(0x80000001) & (1 << 29) ? true : false;
}
int cpu_has_64bit(void)
{
return has_cpuid() && can_detect_long_mode() &&
has_long_mode();
}
const char *cpu_vendor_name(int vendor)
{
const char *name;
name = "<invalid cpu vendor>";
if ((vendor < (ARRAY_SIZE(x86_vendor_name))) &&
(x86_vendor_name[vendor] != 0))
name = x86_vendor_name[vendor];
return name;
}
char *cpu_get_name(char *name)
{
unsigned int *name_as_ints = (unsigned int *)name;
struct cpuid_result regs;
char *ptr;
int i;
/* This bit adds up to 48 bytes */
for (i = 0; i < 3; i++) {
regs = cpuid(0x80000002 + i);
name_as_ints[i * 4 + 0] = regs.eax;
name_as_ints[i * 4 + 1] = regs.ebx;
name_as_ints[i * 4 + 2] = regs.ecx;
name_as_ints[i * 4 + 3] = regs.edx;
}
name[CPU_MAX_NAME_LEN - 1] = '\0';
/* Skip leading spaces. */
ptr = name;
while (*ptr == ' ')
ptr++;
return ptr;
}
int default_print_cpuinfo(void)
{
printf("CPU: %s, vendor %s, device %xh\n",
cpu_has_64bit() ? "x86_64" : "x86",
cpu_vendor_name(gd->arch.x86_vendor), gd->arch.x86_device);
return 0;
}
#define PAGETABLE_SIZE (6 * 4096)
/**
* build_pagetable() - build a flat 4GiB page table structure for 64-bti mode
*
* @pgtable: Pointer to a 24iKB block of memory
*/
static void build_pagetable(uint32_t *pgtable)
{
uint i;
memset(pgtable, '\0', PAGETABLE_SIZE);
/* Level 4 needs a single entry */
pgtable[0] = (uint32_t)&pgtable[1024] + 7;
/* Level 3 has one 64-bit entry for each GiB of memory */
for (i = 0; i < 4; i++) {
pgtable[1024 + i * 2] = (uint32_t)&pgtable[2048] +
0x1000 * i + 7;
}
/* Level 2 has 2048 64-bit entries, each repesenting 2MiB */
for (i = 0; i < 2048; i++)
pgtable[2048 + i * 2] = 0x183 + (i << 21UL);
}
int cpu_jump_to_64bit(ulong setup_base, ulong target)
{
uint32_t *pgtable;
pgtable = memalign(4096, PAGETABLE_SIZE);
if (!pgtable)
return -ENOMEM;
build_pagetable(pgtable);
cpu_call64((ulong)pgtable, setup_base, target);
free(pgtable);
return -EFAULT;
}
void show_boot_progress(int val)
{
outb(val, POST_PORT);
}
#ifndef CONFIG_SYS_COREBOOT
/*
* Implement a weak default function for boards that optionally
* need to clean up the system before jumping to the kernel.
*/
__weak void board_final_cleanup(void)
{
}
int last_stage_init(void)
{
write_tables();
board_final_cleanup();
return 0;
}
#endif
#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 x86_mp_init(void)
{
struct mp_params mp_params;
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
static int x86_init_cpus(void)
{
#ifdef CONFIG_SMP
debug("Init additional CPUs\n");
x86_mp_init();
#else
struct udevice *dev;
/*
* This causes the cpu-x86 driver to be probed.
* We don't check return value here as we want to allow boards
* which have not been converted to use cpu uclass driver to boot.
*/
uclass_first_device(UCLASS_CPU, &dev);
#endif
return 0;
}
int cpu_init_r(void)
{
struct udevice *dev;
int ret;
if (!ll_boot_init())
return 0;
ret = x86_init_cpus();
if (ret)
return ret;
/*
* Set up the northbridge, PCH and LPC if available. Note that these
* may have had some limited pre-relocation init if they were probed
* before relocation, but this is post relocation.
*/
uclass_first_device(UCLASS_NORTHBRIDGE, &dev);
uclass_first_device(UCLASS_PCH, &dev);
uclass_first_device(UCLASS_LPC, &dev);
/* Set up pin control if available */
ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev);
debug("%s, pinctrl=%p, ret=%d\n", __func__, dev, ret);
return 0;
}
#ifndef CONFIG_EFI_STUB
int reserve_arch(void)
{
#ifdef CONFIG_ENABLE_MRC_CACHE
mrccache_reserve();
#endif
#ifdef CONFIG_SEABIOS
high_table_reserve();
#endif
return 0;
}
#endif

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <errno.h>
#include <asm/cpu.h>
DECLARE_GLOBAL_DATA_PTR;
int cpu_x86_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;
}
int cpu_x86_get_desc(struct udevice *dev, char *buf, int size)
{
if (size < CPU_MAX_NAME_LEN)
return -ENOSPC;
cpu_get_name(buf);
return 0;
}
static int cpu_x86_get_count(struct udevice *dev)
{
int node, cpu;
int num = 0;
node = fdt_path_offset(gd->fdt_blob, "/cpus");
if (node < 0)
return -ENOENT;
for (cpu = fdt_first_subnode(gd->fdt_blob, node);
cpu >= 0;
cpu = fdt_next_subnode(gd->fdt_blob, cpu)) {
const char *device_type;
device_type = fdt_getprop(gd->fdt_blob, cpu,
"device_type", NULL);
if (!device_type)
continue;
if (strcmp(device_type, "cpu") == 0)
num++;
}
return num;
}
static const struct cpu_ops cpu_x86_ops = {
.get_desc = cpu_x86_get_desc,
.get_count = cpu_x86_get_count,
};
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,
.bind = cpu_x86_bind,
.ops = &cpu_x86_ops,
};

View File

@@ -0,0 +1,8 @@
#
# Copyright (c) 2015 Google, Inc
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += efi.o
obj-y += sdram.o

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2015 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <fdtdec.h>
#include <netdev.h>
int arch_cpu_init(void)
{
return 0;
}
int board_early_init_f(void)
{
return 0;
}
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}
void board_final_cleanup(void)
{
}
int misc_init_r(void)
{
return 0;
}
int arch_misc_init(void)
{
return 0;
}

View File

@@ -0,0 +1,94 @@
/*
* U-Boot EFI linker script
*
* SPDX-License-Identifier: BSD-2-Clause
*
* Modified from usr/lib32/elf_ia32_efi.lds in gnu-efi
*/
#include <config.h>
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
image_base = .;
.hash : { *(.hash) } /* this MUST come first, EFI expects it */
. = ALIGN(4096);
.text :
{
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
}
. = ALIGN(4096);
.sdata :
{
*(.got.plt)
*(.got)
*(.srodata)
*(.sdata)
*(.sbss)
*(.scommon)
}
. = ALIGN(4096);
.data :
{
*(.rodata*)
*(.data)
*(.data1)
*(.data.*)
*(.sdata)
*(.got.plt)
*(.got)
/*
* the EFI loader doesn't seem to like a .bss section, so we
* stick it all into .data:
*/
*(.sbss)
*(.scommon)
*(.dynbss)
*(.bss)
*(COMMON)
/* U-Boot lists and device tree */
. = ALIGN(8);
*(SORT(.u_boot_list*));
. = ALIGN(8);
*(.dtb*);
}
. = ALIGN(4096);
.dynamic : { *(.dynamic) }
. = ALIGN(4096);
.rel :
{
*(.rel.data)
*(.rel.data.*)
*(.rel.got)
*(.rel.stab)
*(.data.rel.ro.local)
*(.data.rel.local)
*(.data.rel.ro)
*(.data.rel*)
*(.rel.u_boot_list*)
}
. = ALIGN(4096);
.reloc : /* This is the PECOFF .reloc section! */
{
*(.reloc)
}
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
. = ALIGN(4096);
.dynstr : { *(.dynstr) }
. = ALIGN(4096);
/DISCARD/ :
{
*(.rel.reloc)
*(.eh_frame)
*(.note.GNU-stack)
}
.comment 0 : { *(.comment) }
}

View File

@@ -0,0 +1,83 @@
/*
* U-Boot EFI linker script
*
* SPDX-License-Identifier: BSD-2-Clause
*
* Modified from usr/lib32/elf_x86_64_efi.lds in gnu-efi
*/
#include <config.h>
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SECTIONS
{
image_base = .;
.hash : { *(.hash) } /* this MUST come first, EFI expects it */
. = ALIGN(4096);
.eh_frame : {
*(.eh_frame)
}
. = ALIGN(4096);
.text : {
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
}
. = ALIGN(4096);
.reloc : {
*(.reloc)
}
. = ALIGN(4096);
.data : {
*(.rodata*)
*(.got.plt)
*(.got)
*(.data*)
*(.sdata)
/* the EFI loader doesn't seem to like a .bss section, so we stick
* it all into .data: */
*(.sbss)
*(.scommon)
*(.dynbss)
*(.bss)
*(COMMON)
*(.rel.local)
/* U-Boot lists and device tree */
. = ALIGN(8);
*(SORT(.u_boot_list*));
. = ALIGN(8);
*(.dtb*);
}
. = ALIGN(4096);
.dynamic : { *(.dynamic) }
. = ALIGN(4096);
.rela : {
*(.rela.data*)
*(.rela.got)
*(.rela.stab)
}
. = ALIGN(4096);
.dynsym : { *(.dynsym) }
. = ALIGN(4096);
.dynstr : { *(.dynstr) }
. = ALIGN(4096);
.ignored.reloc : {
*(.rela.reloc)
*(.eh_frame)
*(.note.GNU-stack)
}
.comment 0 : { *(.comment) }
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2015 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <efi.h>
#include <asm/u-boot-x86.h>
DECLARE_GLOBAL_DATA_PTR;
ulong board_get_usable_ram_top(ulong total_size)
{
return (ulong)efi_get_ram_base() + gd->ram_size;
}
int dram_init(void)
{
/* gd->ram_size is set as part of EFI init */
return 0;
}
void dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = efi_get_ram_base();
gd->bd->bi_dram[0].size = CONFIG_EFI_RAM_SIZE;
}

View File

@@ -0,0 +1,16 @@
#
# Copyright (c) 2016 Google, Inc
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-$(CONFIG_HAVE_MRC) += car.o
obj-y += cpu.o
obj-y += lpc.o
obj-$(CONFIG_HAVE_MRC) += me_status.o
ifndef CONFIG_TARGET_EFI
obj-y += microcode.o
endif
obj-y += pch.o
obj-$(CONFIG_HAVE_MRC) += report_platform.o
obj-$(CONFIG_HAVE_MRC) += mrc.o

View File

@@ -0,0 +1,243 @@
/*
* Copyright (c) 2014 Google, Inc
*
* From Coreboot file cpu/intel/model_206ax/cache_as_ram.inc
*
* Copyright (C) 2000,2007 Ronald G. Minnich <rminnich@gmail.com>
* Copyright (C) 2005 Tyan (written by Yinghai Lu for Tyan)
* Copyright (C) 2007-2008 coresystems GmbH
* Copyright (C) 2012 Kyösti Mälkki <kyosti.malkki@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <asm/microcode.h>
#include <asm/msr-index.h>
#include <asm/mtrr.h>
#include <asm/post.h>
#include <asm/processor.h>
#include <asm/processor-flags.h>
#define MTRR_PHYS_BASE_MSR(reg) (0x200 + 2 * (reg))
#define MTRR_PHYS_MASK_MSR(reg) (0x200 + 2 * (reg) + 1)
#define CACHE_AS_RAM_SIZE CONFIG_DCACHE_RAM_SIZE
#define CACHE_AS_RAM_BASE CONFIG_DCACHE_RAM_BASE
/* Cache 4GB - MRC_SIZE_KB for MRC */
#define CACHE_MRC_BYTES ((CONFIG_CACHE_MRC_SIZE_KB << 10) - 1)
#define CACHE_MRC_BASE (0xFFFFFFFF - CACHE_MRC_BYTES)
#define CACHE_MRC_MASK (~CACHE_MRC_BYTES)
#define CPU_PHYSMASK_HI (1 << (CONFIG_CPU_ADDR_BITS - 32) - 1)
#define NOEVICTMOD_MSR 0x2e0
/*
* Note: ebp must not be touched in this code as it holds the BIST
* value (built-in self test). We preserve this value until it can
* be written to global_data when CAR is ready for use.
*/
.globl car_init
car_init:
post_code(POST_CAR_START)
/* Send INIT IPI to all excluding ourself */
movl $0x000C4500, %eax
movl $0xFEE00300, %esi
movl %eax, (%esi)
/* TODO: Load microcode later - the 'no eviction' mode breaks this */
movl $MSR_IA32_UCODE_WRITE, %ecx
xorl %edx, %edx
movl $_dt_ucode_base_size, %eax
movl (%eax), %eax
addl $UCODE_HEADER_LEN, %eax
wrmsr
post_code(POST_CAR_SIPI)
/* Zero out all fixed range and variable range MTRRs */
movl $mtrr_table, %esi
movl $((mtrr_table_end - mtrr_table) / 2), %edi
xorl %eax, %eax
xorl %edx, %edx
clear_mtrrs:
movw (%esi), %bx
movzx %bx, %ecx
wrmsr
add $2, %esi
dec %edi
jnz clear_mtrrs
post_code(POST_CAR_MTRR)
/* Configure the default memory type to uncacheable */
movl $MTRR_DEF_TYPE_MSR, %ecx
rdmsr
andl $(~0x00000cff), %eax
wrmsr
post_code(POST_CAR_UNCACHEABLE)
/* Set Cache-as-RAM base address */
movl $(MTRR_PHYS_BASE_MSR(0)), %ecx
movl $(CACHE_AS_RAM_BASE | MTRR_TYPE_WRBACK), %eax
xorl %edx, %edx
wrmsr
post_code(POST_CAR_BASE_ADDRESS)
/* Set Cache-as-RAM mask */
movl $(MTRR_PHYS_MASK_MSR(0)), %ecx
movl $(~(CACHE_AS_RAM_SIZE - 1) | MTRR_PHYS_MASK_VALID), %eax
movl $CPU_PHYSMASK_HI, %edx
wrmsr
post_code(POST_CAR_MASK)
/* Enable MTRR */
movl $MTRR_DEF_TYPE_MSR, %ecx
rdmsr
orl $MTRR_DEF_TYPE_EN, %eax
wrmsr
/* Enable cache (CR0.CD = 0, CR0.NW = 0) */
movl %cr0, %eax
andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax
invd
movl %eax, %cr0
/* enable the 'no eviction' mode */
movl $NOEVICTMOD_MSR, %ecx
rdmsr
orl $1, %eax
andl $~2, %eax
wrmsr
/* Clear the cache memory region. This will also fill up the cache */
movl $CACHE_AS_RAM_BASE, %esi
movl %esi, %edi
movl $(CACHE_AS_RAM_SIZE / 4), %ecx
xorl %eax, %eax
rep stosl
/* enable the 'no eviction run' state */
movl $NOEVICTMOD_MSR, %ecx
rdmsr
orl $3, %eax
wrmsr
post_code(POST_CAR_FILL)
/* Enable Cache-as-RAM mode by disabling cache */
movl %cr0, %eax
orl $X86_CR0_CD, %eax
movl %eax, %cr0
/* Enable cache for our code in Flash because we do XIP here */
movl $MTRR_PHYS_BASE_MSR(1), %ecx
xorl %edx, %edx
movl $car_init_ret, %eax
andl $(~(CONFIG_XIP_ROM_SIZE - 1)), %eax
orl $MTRR_TYPE_WRPROT, %eax
wrmsr
movl $MTRR_PHYS_MASK_MSR(1), %ecx
movl $CPU_PHYSMASK_HI, %edx
movl $(~(CONFIG_XIP_ROM_SIZE - 1) | MTRR_PHYS_MASK_VALID), %eax
wrmsr
post_code(POST_CAR_ROM_CACHE)
#ifdef CONFIG_CACHE_MRC_BIN
/* Enable caching for ram init code to run faster */
movl $MTRR_PHYS_BASE_MSR(2), %ecx
movl $(CACHE_MRC_BASE | MTRR_TYPE_WRPROT), %eax
xorl %edx, %edx
wrmsr
movl $MTRR_PHYS_MASK_MSR(2), %ecx
movl $(CACHE_MRC_MASK | MTRR_PHYS_MASK_VALID), %eax
movl $CPU_PHYSMASK_HI, %edx
wrmsr
#endif
post_code(POST_CAR_MRC_CACHE)
/* Enable cache */
movl %cr0, %eax
andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax
movl %eax, %cr0
post_code(POST_CAR_CPU_CACHE)
/* All CPUs need to be in Wait for SIPI state */
wait_for_sipi:
movl (%esi), %eax
bt $12, %eax
jc wait_for_sipi
/* return */
jmp car_init_ret
.globl car_uninit
car_uninit:
/* Disable cache */
movl %cr0, %eax
orl $X86_CR0_CD, %eax
movl %eax, %cr0
/* Disable MTRRs */
movl $MTRR_DEF_TYPE_MSR, %ecx
rdmsr
andl $(~MTRR_DEF_TYPE_EN), %eax
wrmsr
/* Disable the no-eviction run state */
movl $NOEVICTMOD_MSR, %ecx
rdmsr
andl $~2, %eax
wrmsr
invd
/* Disable the no-eviction mode */
rdmsr
andl $~1, %eax
wrmsr
#ifdef CONFIG_CACHE_MRC_BIN
/* Clear the MTRR that was used to cache MRC */
xorl %eax, %eax
xorl %edx, %edx
movl $MTRR_PHYS_BASE_MSR(2), %ecx
wrmsr
movl $MTRR_PHYS_MASK_MSR(2), %ecx
wrmsr
#endif
/* Enable MTRRs */
movl $MTRR_DEF_TYPE_MSR, %ecx
rdmsr
orl $MTRR_DEF_TYPE_EN, %eax
wrmsr
invd
ret
mtrr_table:
/* Fixed MTRRs */
.word 0x250, 0x258, 0x259
.word 0x268, 0x269, 0x26A
.word 0x26B, 0x26C, 0x26D
.word 0x26E, 0x26F
/* Variable MTRRs */
.word 0x200, 0x201, 0x202, 0x203
.word 0x204, 0x205, 0x206, 0x207
.word 0x208, 0x209, 0x20A, 0x20B
.word 0x20C, 0x20D, 0x20E, 0x20F
.word 0x210, 0x211, 0x212, 0x213
mtrr_table_end:
.align 4
_dt_ucode_base_size:
/* These next two fields are filled in by ifdtool */
.globl ucode_base
ucode_base: /* Declared in microcode.h */
.long 0 /* microcode base */
.long 0 /* microcode size */

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <asm/cpu_common.h>
#include <asm/intel_regs.h>
#include <asm/lapic.h>
#include <asm/lpc_common.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
#include <asm/post.h>
#include <asm/microcode.h>
DECLARE_GLOBAL_DATA_PTR;
static int report_bist_failure(void)
{
if (gd->arch.bist != 0) {
post_code(POST_BIST_FAILURE);
printf("BIST failed: %08x\n", gd->arch.bist);
return -EFAULT;
}
return 0;
}
int cpu_common_init(void)
{
struct udevice *dev, *lpc;
int ret;
/* Halt if there was a built in self test failure */
ret = report_bist_failure();
if (ret)
return ret;
enable_lapic();
ret = microcode_update_intel();
if (ret && ret != -EEXIST)
return ret;
/* Enable upper 128bytes of CMOS */
writel(1 << 2, RCB_REG(RC));
/* Early chipset init required before RAM init can work */
uclass_first_device(UCLASS_NORTHBRIDGE, &dev);
ret = uclass_first_device(UCLASS_LPC, &lpc);
if (ret)
return ret;
if (!lpc)
return -ENODEV;
/* Cause the SATA device to do its early init */
uclass_first_device(UCLASS_AHCI, &dev);
return 0;
}
int cpu_set_flex_ratio_to_tdp_nominal(void)
{
msr_t flex_ratio, msr;
u8 nominal_ratio;
/* Check for Flex Ratio support */
flex_ratio = msr_read(MSR_FLEX_RATIO);
if (!(flex_ratio.lo & FLEX_RATIO_EN))
return -EINVAL;
/* Check for >0 configurable TDPs */
msr = msr_read(MSR_PLATFORM_INFO);
if (((msr.hi >> 1) & 3) == 0)
return -EINVAL;
/* Use nominal TDP ratio for flex ratio */
msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
nominal_ratio = msr.lo & 0xff;
/* See if flex ratio is already set to nominal TDP ratio */
if (((flex_ratio.lo >> 8) & 0xff) == nominal_ratio)
return 0;
/* Set flex ratio to nominal TDP ratio */
flex_ratio.lo &= ~0xff00;
flex_ratio.lo |= nominal_ratio << 8;
flex_ratio.lo |= FLEX_RATIO_LOCK;
msr_write(MSR_FLEX_RATIO, flex_ratio);
/* Set flex ratio in soft reset data register bits 11:6 */
clrsetbits_le32(RCB_REG(SOFT_RESET_DATA), 0x3f << 6,
(nominal_ratio & 0x3f) << 6);
debug("CPU: Soft reset to set up flex ratio\n");
/* Set soft reset control to use register value */
setbits_le32(RCB_REG(SOFT_RESET_CTRL), 1);
/* Issue warm reset, will be "CPU only" due to soft reset data */
outb(0x0, IO_PORT_RESET);
outb(SYS_RST | RST_CPU, IO_PORT_RESET);
cpu_hlt();
/* Not reached */
return -EINVAL;
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <pch.h>
#include <pci.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/lpc_common.h>
DECLARE_GLOBAL_DATA_PTR;
/* Enable Prefetching and Caching */
static void enable_spi_prefetch(struct udevice *pch)
{
u8 reg8;
dm_pci_read_config8(pch, 0xdc, &reg8);
reg8 &= ~(3 << 2);
reg8 |= (2 << 2); /* Prefetching and Caching Enabled */
dm_pci_write_config8(pch, 0xdc, reg8);
}
static void enable_port80_on_lpc(struct udevice *pch)
{
/* Enable port 80 POST on LPC */
dm_pci_write_config32(pch, PCH_RCBA_BASE, RCB_BASE_ADDRESS | 1);
clrbits_le32(RCB_REG(GCS), 4);
}
/**
* lpc_early_init() - set up LPC serial ports and other early things
*
* @dev: LPC device
* @return 0 if OK, -ve on error
*/
int lpc_common_early_init(struct udevice *dev)
{
struct udevice *pch = dev->parent;
struct reg_info {
u32 base;
u32 size;
} values[4], *ptr;
int count;
int i;
count = fdtdec_get_int_array_count(gd->fdt_blob, dev->of_offset,
"intel,gen-dec", (u32 *)values,
sizeof(values) / sizeof(u32));
if (count < 0)
return -EINVAL;
/* Set COM1/COM2 decode range */
dm_pci_write_config16(pch, LPC_IO_DEC, 0x0010);
/* Enable PS/2 Keyboard/Mouse, EC areas and COM1 */
dm_pci_write_config16(pch, LPC_EN, KBC_LPC_EN | MC_LPC_EN |
GAMEL_LPC_EN | COMA_LPC_EN);
/* Write all registers but use 0 if we run out of data */
count = count * sizeof(u32) / sizeof(values[0]);
for (i = 0, ptr = values; i < ARRAY_SIZE(values); i++, ptr++) {
u32 reg = 0;
if (i < count)
reg = ptr->base | PCI_COMMAND_IO | (ptr->size << 16);
dm_pci_write_config32(pch, LPC_GENX_DEC(i), reg);
}
enable_spi_prefetch(pch);
/* This is already done in start.S, but let's do it in C */
enable_port80_on_lpc(pch);
return 0;
}
int lpc_set_spi_protect(struct udevice *dev, int bios_ctrl, bool protect)
{
uint8_t bios_cntl;
/* Adjust the BIOS write protect and SMM BIOS Write Protect Disable */
dm_pci_read_config8(dev, bios_ctrl, &bios_cntl);
if (protect) {
bios_cntl &= ~BIOS_CTRL_BIOSWE;
bios_cntl |= BIT(5);
} else {
bios_cntl |= BIOS_CTRL_BIOSWE;
bios_cntl &= ~BIT(5);
}
dm_pci_write_config8(dev, bios_ctrl, bios_cntl);
return 0;
}

View File

@@ -0,0 +1,213 @@
/*
* From Coreboot src/southbridge/intel/bd82x6x/me_status.c
*
* Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <asm/arch/me.h>
/* HFS1[3:0] Current Working State Values */
static const char *const me_cws_values[] = {
[ME_HFS_CWS_RESET] = "Reset",
[ME_HFS_CWS_INIT] = "Initializing",
[ME_HFS_CWS_REC] = "Recovery",
[ME_HFS_CWS_NORMAL] = "Normal",
[ME_HFS_CWS_WAIT] = "Platform Disable Wait",
[ME_HFS_CWS_TRANS] = "OP State Transition",
[ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In"
};
/* HFS1[8:6] Current Operation State Values */
static const char *const me_opstate_values[] = {
[ME_HFS_STATE_PREBOOT] = "Preboot",
[ME_HFS_STATE_M0_UMA] = "M0 with UMA",
[ME_HFS_STATE_M3] = "M3 without UMA",
[ME_HFS_STATE_M0] = "M0 without UMA",
[ME_HFS_STATE_BRINGUP] = "Bring up",
[ME_HFS_STATE_ERROR] = "M0 without UMA but with error"
};
/* HFS[19:16] Current Operation Mode Values */
static const char *const me_opmode_values[] = {
[ME_HFS_MODE_NORMAL] = "Normal",
[ME_HFS_MODE_DEBUG] = "Debug",
[ME_HFS_MODE_DIS] = "Soft Temporary Disable",
[ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper",
[ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message"
};
/* HFS[15:12] Error Code Values */
static const char *const me_error_values[] = {
[ME_HFS_ERROR_NONE] = "No Error",
[ME_HFS_ERROR_UNCAT] = "Uncategorized Failure",
[ME_HFS_ERROR_IMAGE] = "Image Failure",
[ME_HFS_ERROR_DEBUG] = "Debug Failure"
};
/* GMES[31:28] ME Progress Code */
static const char *const me_progress_values[] = {
[ME_GMES_PHASE_ROM] = "ROM Phase",
[ME_GMES_PHASE_BUP] = "BUP Phase",
[ME_GMES_PHASE_UKERNEL] = "uKernel Phase",
[ME_GMES_PHASE_POLICY] = "Policy Module",
[ME_GMES_PHASE_MODULE] = "Module Loading",
[ME_GMES_PHASE_UNKNOWN] = "Unknown",
[ME_GMES_PHASE_HOST] = "Host Communication"
};
/* GMES[27:24] Power Management Event */
static const char *const me_pmevent_values[] = {
[0x00] = "Clean Moff->Mx wake",
[0x01] = "Moff->Mx wake after an error",
[0x02] = "Clean global reset",
[0x03] = "Global reset after an error",
[0x04] = "Clean Intel ME reset",
[0x05] = "Intel ME reset due to exception",
[0x06] = "Pseudo-global reset",
[0x07] = "S0/M0->Sx/M3",
[0x08] = "Sx/M3->S0/M0",
[0x09] = "Non-power cycle reset",
[0x0a] = "Power cycle reset through M3",
[0x0b] = "Power cycle reset through Moff",
[0x0c] = "Sx/Mx->Sx/Moff"
};
/* Progress Code 0 states */
static const char *const me_progress_rom_values[] = {
[0x00] = "BEGIN",
[0x06] = "DISABLE"
};
/* Progress Code 1 states */
static const char *const me_progress_bup_values[] = {
[0x00] = "Initialization starts",
[0x01] = "Disable the host wake event",
[0x04] = "Flow determination start process",
[0x08] = "Error reading/matching the VSCC table in the descriptor",
[0x0a] = "Check to see if straps say ME DISABLED",
[0x0b] = "Timeout waiting for PWROK",
[0x0d] = "Possibly handle BUP manufacturing override strap",
[0x11] = "Bringup in M3",
[0x12] = "Bringup in M0",
[0x13] = "Flow detection error",
[0x15] = "M3 clock switching error",
[0x18] = "M3 kernel load",
[0x1c] = "T34 missing - cannot program ICC",
[0x1f] = "Waiting for DID BIOS message",
[0x20] = "Waiting for DID BIOS message failure",
[0x21] = "DID reported an error",
[0x22] = "Enabling UMA",
[0x23] = "Enabling UMA error",
[0x24] = "Sending DID Ack to BIOS",
[0x25] = "Sending DID Ack to BIOS error",
[0x26] = "Switching clocks in M0",
[0x27] = "Switching clocks in M0 error",
[0x28] = "ME in temp disable",
[0x32] = "M0 kernel load",
};
/* Progress Code 3 states */
static const char *const me_progress_policy_values[] = {
[0x00] = "Entery into Policy Module",
[0x03] = "Received S3 entry",
[0x04] = "Received S4 entry",
[0x05] = "Received S5 entry",
[0x06] = "Received UPD entry",
[0x07] = "Received PCR entry",
[0x08] = "Received NPCR entry",
[0x09] = "Received host wake",
[0x0a] = "Received AC<>DC switch",
[0x0b] = "Received DRAM Init Done",
[0x0c] = "VSCC Data not found for flash device",
[0x0d] = "VSCC Table is not valid",
[0x0e] = "Flash Partition Boundary is outside address space",
[0x0f] = "ME cannot access the chipset descriptor region",
[0x10] = "Required VSCC values for flash parts do not match",
};
/**
* _intel_me_status() - Check Intel Management Engine status
*
* struct hfs: Firmware status
* struct gmes: Management engine status
*/
static void _intel_me_status(struct me_hfs *hfs, struct me_gmes *gmes)
{
/* Check Current States */
debug("ME: FW Partition Table : %s\n",
hfs->fpt_bad ? "BAD" : "OK");
debug("ME: Bringup Loader Failure : %s\n",
hfs->ft_bup_ld_flr ? "YES" : "NO");
debug("ME: Firmware Init Complete : %s\n",
hfs->fw_init_complete ? "YES" : "NO");
debug("ME: Manufacturing Mode : %s\n",
hfs->mfg_mode ? "YES" : "NO");
debug("ME: Boot Options Present : %s\n",
hfs->boot_options_present ? "YES" : "NO");
debug("ME: Update In Progress : %s\n",
hfs->update_in_progress ? "YES" : "NO");
debug("ME: Current Working State : %s\n",
me_cws_values[hfs->working_state]);
debug("ME: Current Operation State : %s\n",
me_opstate_values[hfs->operation_state]);
debug("ME: Current Operation Mode : %s\n",
me_opmode_values[hfs->operation_mode]);
debug("ME: Error Code : %s\n",
me_error_values[hfs->error_code]);
debug("ME: Progress Phase : %s\n",
me_progress_values[gmes->progress_code]);
debug("ME: Power Management Event : %s\n",
me_pmevent_values[gmes->current_pmevent]);
debug("ME: Progress Phase State : ");
switch (gmes->progress_code) {
case ME_GMES_PHASE_ROM: /* ROM Phase */
debug("%s", me_progress_rom_values[gmes->current_state]);
break;
case ME_GMES_PHASE_BUP: /* Bringup Phase */
if (gmes->current_state < ARRAY_SIZE(me_progress_bup_values) &&
me_progress_bup_values[gmes->current_state])
debug("%s",
me_progress_bup_values[gmes->current_state]);
else
debug("0x%02x", gmes->current_state);
break;
case ME_GMES_PHASE_POLICY: /* Policy Module Phase */
if (gmes->current_state <
ARRAY_SIZE(me_progress_policy_values) &&
me_progress_policy_values[gmes->current_state])
debug("%s",
me_progress_policy_values[gmes->current_state]);
else
debug("0x%02x", gmes->current_state);
break;
case ME_GMES_PHASE_HOST: /* Host Communication Phase */
if (!gmes->current_state)
debug("Host communication established");
else
debug("0x%02x", gmes->current_state);
break;
default:
debug("Unknown 0x%02x", gmes->current_state);
}
debug("\n");
}
void intel_me_status(struct udevice *me_dev)
{
struct me_hfs hfs;
struct me_gmes gmes;
pci_read_dword_ptr(me_dev, &hfs, PCI_ME_HFS);
pci_read_dword_ptr(me_dev, &gmes, PCI_ME_GMES);
_intel_me_status(&hfs, &gmes);
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (c) 2014 Google, Inc
* Copyright (C) 2000 Ronald G. Minnich
*
* Microcode update for Intel PIII and later CPUs
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <errno.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <asm/cpu.h>
#include <asm/microcode.h>
#include <asm/msr.h>
#include <asm/msr-index.h>
#include <asm/processor.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* struct microcode_update - standard microcode header from Intel
*
* We read this information out of the device tree and use it to determine
* whether the update is applicable or not. We also use the same structure
* to read information from the CPU.
*/
struct microcode_update {
uint header_version;
uint update_revision;
uint date_code;
uint processor_signature;
uint checksum;
uint loader_revision;
uint processor_flags;
const void *data;
int size;
};
static int microcode_decode_node(const void *blob, int node,
struct microcode_update *update)
{
update->data = fdt_getprop(blob, node, "data", &update->size);
if (!update->data)
return -EINVAL;
update->data += UCODE_HEADER_LEN;
update->size -= UCODE_HEADER_LEN;
update->header_version = fdtdec_get_int(blob, node,
"intel,header-version", 0);
update->update_revision = fdtdec_get_int(blob, node,
"intel,update-revision", 0);
update->date_code = fdtdec_get_int(blob, node,
"intel,date-code", 0);
update->processor_signature = fdtdec_get_int(blob, node,
"intel,processor-signature", 0);
update->checksum = fdtdec_get_int(blob, node, "intel,checksum", 0);
update->loader_revision = fdtdec_get_int(blob, node,
"intel,loader-revision", 0);
update->processor_flags = fdtdec_get_int(blob, node,
"intel,processor-flags", 0);
return 0;
}
int microcode_read_rev(void)
{
/* Quark does not have microcode MSRs */
#ifdef CONFIG_INTEL_QUARK
return 0;
#else
/*
* Some Intel CPUs can be very finicky about the CPUID sequence used.
* So this is implemented in assembly so that it works reliably.
*/
uint32_t low, high;
asm volatile (
"xorl %%eax, %%eax\n"
"xorl %%edx, %%edx\n"
"movl %2, %%ecx\n"
"wrmsr\n"
"movl $0x01, %%eax\n"
"cpuid\n"
"movl %2, %%ecx\n"
"rdmsr\n"
: /* outputs */
"=a" (low), "=d" (high)
: /* inputs */
"i" (MSR_IA32_UCODE_REV)
: /* clobbers */
"ebx", "ecx"
);
return high;
#endif
}
static void microcode_read_cpu(struct microcode_update *cpu)
{
/* CPUID sets MSR 0x8B iff a microcode update has been loaded. */
unsigned int x86_model, x86_family;
struct cpuid_result result;
uint32_t low, high;
wrmsr(MSR_IA32_UCODE_REV, 0, 0);
result = cpuid(1);
rdmsr(MSR_IA32_UCODE_REV, low, cpu->update_revision);
x86_model = (result.eax >> 4) & 0x0f;
x86_family = (result.eax >> 8) & 0x0f;
cpu->processor_signature = result.eax;
cpu->processor_flags = 0;
if ((x86_model >= 5) || (x86_family > 6)) {
rdmsr(0x17, low, high);
cpu->processor_flags = 1 << ((high >> 18) & 7);
}
debug("microcode: sig=%#x pf=%#x revision=%#x\n",
cpu->processor_signature, cpu->processor_flags,
cpu->update_revision);
}
/* Get a microcode update from the device tree and apply it */
int microcode_update_intel(void)
{
struct microcode_update cpu, update;
const void *blob = gd->fdt_blob;
int skipped;
int count;
int node;
int ret;
int rev;
microcode_read_cpu(&cpu);
node = 0;
count = 0;
skipped = 0;
do {
node = fdtdec_next_compatible(blob, node,
COMPAT_INTEL_MICROCODE);
if (node < 0) {
debug("%s: Found %d updates\n", __func__, count);
return count ? 0 : skipped ? -EEXIST : -ENOENT;
}
ret = microcode_decode_node(blob, node, &update);
if (ret) {
debug("%s: Unable to decode update: %d\n", __func__,
ret);
return ret;
}
if (!(update.processor_signature == cpu.processor_signature &&
(update.processor_flags & cpu.processor_flags))) {
debug("%s: Skipping non-matching update, sig=%x, pf=%x\n",
__func__, update.processor_signature,
update.processor_flags);
skipped++;
continue;
}
wrmsr(MSR_IA32_UCODE_WRITE, (ulong)update.data, 0);
rev = microcode_read_rev();
debug("microcode: updated to revision 0x%x date=%04x-%02x-%02x\n",
rev, update.date_code & 0xffff,
(update.date_code >> 24) & 0xff,
(update.date_code >> 16) & 0xff);
if (update.update_revision != rev) {
printf("Microcode update failed\n");
return -EFAULT;
}
count++;
} while (1);
}

View File

@@ -0,0 +1,271 @@
/*
* Copyright (c) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/gpio.h>
#include <asm/intel_regs.h>
#include <asm/mrc_common.h>
#include <asm/pch_common.h>
#include <asm/post.h>
#include <asm/arch/me.h>
#include <asm/report_platform.h>
static const char *const ecc_decoder[] = {
"inactive",
"active on IO",
"disabled on IO",
"active"
};
ulong mrc_common_board_get_usable_ram_top(ulong total_size)
{
struct memory_info *info = &gd->arch.meminfo;
uintptr_t dest_addr = 0;
struct memory_area *largest = NULL;
int i;
/* Find largest area of memory below 4GB */
for (i = 0; i < info->num_areas; i++) {
struct memory_area *area = &info->area[i];
if (area->start >= 1ULL << 32)
continue;
if (!largest || area->size > largest->size)
largest = area;
}
/* If no suitable area was found, return an error. */
assert(largest);
if (!largest || largest->size < (2 << 20))
panic("No available memory found for relocation");
dest_addr = largest->start + largest->size;
return (ulong)dest_addr;
}
void mrc_common_dram_init_banksize(void)
{
struct memory_info *info = &gd->arch.meminfo;
int num_banks;
int i;
for (i = 0, num_banks = 0; i < info->num_areas; i++) {
struct memory_area *area = &info->area[i];
if (area->start >= 1ULL << 32)
continue;
gd->bd->bi_dram[num_banks].start = area->start;
gd->bd->bi_dram[num_banks].size = area->size;
num_banks++;
}
}
int mrc_add_memory_area(struct memory_info *info, uint64_t start,
uint64_t end)
{
struct memory_area *ptr;
if (info->num_areas == CONFIG_NR_DRAM_BANKS)
return -ENOSPC;
ptr = &info->area[info->num_areas];
ptr->start = start;
ptr->size = end - start;
info->total_memory += ptr->size;
if (ptr->start < (1ULL << 32))
info->total_32bit_memory += ptr->size;
debug("%d: memory %llx size %llx, total now %llx / %llx\n",
info->num_areas, ptr->start, ptr->size,
info->total_32bit_memory, info->total_memory);
info->num_areas++;
return 0;
}
/*
* Dump in the log memory controller configuration as read from the memory
* controller registers.
*/
void report_memory_config(void)
{
u32 addr_decoder_common, addr_decode_ch[2];
int i;
addr_decoder_common = readl(MCHBAR_REG(0x5000));
addr_decode_ch[0] = readl(MCHBAR_REG(0x5004));
addr_decode_ch[1] = readl(MCHBAR_REG(0x5008));
debug("memcfg DDR3 clock %d MHz\n",
(readl(MCHBAR_REG(0x5e04)) * 13333 * 2 + 50) / 100);
debug("memcfg channel assignment: A: %d, B % d, C % d\n",
addr_decoder_common & 3,
(addr_decoder_common >> 2) & 3,
(addr_decoder_common >> 4) & 3);
for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) {
u32 ch_conf = addr_decode_ch[i];
debug("memcfg channel[%d] config (%8.8x):\n", i, ch_conf);
debug(" ECC %s\n", ecc_decoder[(ch_conf >> 24) & 3]);
debug(" enhanced interleave mode %s\n",
((ch_conf >> 22) & 1) ? "on" : "off");
debug(" rank interleave %s\n",
((ch_conf >> 21) & 1) ? "on" : "off");
debug(" DIMMA %d MB width x%d %s rank%s\n",
((ch_conf >> 0) & 0xff) * 256,
((ch_conf >> 19) & 1) ? 16 : 8,
((ch_conf >> 17) & 1) ? "dual" : "single",
((ch_conf >> 16) & 1) ? "" : ", selected");
debug(" DIMMB %d MB width x%d %s rank%s\n",
((ch_conf >> 8) & 0xff) * 256,
((ch_conf >> 20) & 1) ? 16 : 8,
((ch_conf >> 18) & 1) ? "dual" : "single",
((ch_conf >> 16) & 1) ? ", selected" : "");
}
}
int mrc_locate_spd(struct udevice *dev, int size, const void **spd_datap)
{
const void *blob = gd->fdt_blob;
int spd_index;
struct gpio_desc desc[4];
int spd_node;
int node;
int ret;
ret = gpio_request_list_by_name(dev, "board-id-gpios", desc,
ARRAY_SIZE(desc), GPIOD_IS_IN);
if (ret < 0) {
debug("%s: gpio ret=%d\n", __func__, ret);
return ret;
}
spd_index = dm_gpio_get_values_as_int(desc, ret);
debug("spd index %d\n", spd_index);
node = fdt_first_subnode(blob, dev->of_offset);
if (node < 0)
return -EINVAL;
for (spd_node = fdt_first_subnode(blob, node);
spd_node > 0;
spd_node = fdt_next_subnode(blob, spd_node)) {
int len;
if (fdtdec_get_int(blob, spd_node, "reg", -1) != spd_index)
continue;
*spd_datap = fdt_getprop(blob, spd_node, "data", &len);
if (len < size) {
printf("Missing SPD data\n");
return -EINVAL;
}
debug("Using SDRAM SPD data for '%s'\n",
fdt_get_name(blob, spd_node, NULL));
return 0;
}
printf("No SPD data found for index %d\n", spd_index);
return -ENOENT;
}
asmlinkage void sdram_console_tx_byte(unsigned char byte)
{
#ifdef DEBUG
putc(byte);
#endif
}
/**
* Find the PEI executable in the ROM and execute it.
*
* @me_dev: Management Engine device
* @pei_data: configuration data for UEFI PEI reference code
*/
static int sdram_initialise(struct udevice *dev, struct udevice *me_dev,
void *pei_data, bool use_asm_linkage)
{
unsigned version;
const char *data;
report_platform_info(dev);
debug("Starting UEFI PEI System Agent\n");
debug("PEI data at %p:\n", pei_data);
data = (char *)CONFIG_X86_MRC_ADDR;
if (data) {
int rv;
ulong start;
debug("Calling MRC at %p\n", data);
post_code(POST_PRE_MRC);
start = get_timer(0);
if (use_asm_linkage) {
asmlinkage int (*func)(void *);
func = (asmlinkage int (*)(void *))data;
rv = func(pei_data);
} else {
int (*func)(void *);
func = (int (*)(void *))data;
rv = func(pei_data);
}
post_code(POST_MRC);
if (rv) {
switch (rv) {
case -1:
printf("PEI version mismatch.\n");
break;
case -2:
printf("Invalid memory frequency.\n");
break;
default:
printf("MRC returned %x.\n", rv);
}
printf("Nonzero MRC return value.\n");
return -EFAULT;
}
debug("MRC execution time %lu ms\n", get_timer(start));
} else {
printf("UEFI PEI System Agent not found.\n");
return -ENOSYS;
}
version = readl(MCHBAR_REG(MCHBAR_PEI_VERSION));
debug("System Agent Version %d.%d.%d Build %d\n",
version >> 24 , (version >> 16) & 0xff,
(version >> 8) & 0xff, version & 0xff);
#if CONFIG_USBDEBUG
/* mrc.bin reconfigures USB, so reinit it to have debug */
early_usbdebug_init();
#endif
return 0;
}
int mrc_common_init(struct udevice *dev, void *pei_data, bool use_asm_linkage)
{
struct udevice *me_dev;
int ret;
ret = syscon_get_by_driver_data(X86_SYSCON_ME, &me_dev);
if (ret)
return ret;
ret = sdram_initialise(dev, me_dev, pei_data, use_asm_linkage);
if (ret)
return ret;
quick_ram_check();
post_code(POST_DRAM);
report_memory_config();
return 0;
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2016 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <asm/pch_common.h>
u32 pch_common_sir_read(struct udevice *dev, int idx)
{
u32 data;
dm_pci_write_config32(dev, SATA_SIRI, idx);
dm_pci_read_config32(dev, SATA_SIRD, &data);
return data;
}
void pch_common_sir_write(struct udevice *dev, int idx, u32 value)
{
dm_pci_write_config32(dev, SATA_SIRI, idx);
dm_pci_write_config32(dev, SATA_SIRD, value);
}

View File

@@ -0,0 +1,90 @@
/*
* From Coreboot src/northbridge/intel/sandybridge/report_platform.c
*
* Copyright (C) 2012 Google Inc.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <asm/cpu.h>
#include <asm/pci.h>
#include <asm/report_platform.h>
#include <asm/arch/pch.h>
static void report_cpu_info(void)
{
char cpu_string[CPU_MAX_NAME_LEN], *cpu_name;
const char *mode[] = {"NOT ", ""};
struct cpuid_result cpuidr;
int vt, txt, aes;
u32 index;
index = 0x80000000;
cpuidr = cpuid(index);
if (cpuidr.eax < 0x80000004) {
strcpy(cpu_string, "Platform info not available");
cpu_name = cpu_string;
} else {
cpu_name = cpu_get_name(cpu_string);
}
cpuidr = cpuid(1);
debug("CPU id(%x): %s\n", cpuidr.eax, cpu_name);
aes = (cpuidr.ecx & (1 << 25)) ? 1 : 0;
txt = (cpuidr.ecx & (1 << 6)) ? 1 : 0;
vt = (cpuidr.ecx & (1 << 5)) ? 1 : 0;
debug("AES %ssupported, TXT %ssupported, VT %ssupported\n",
mode[aes], mode[txt], mode[vt]);
}
/* The PCI id name match comes from Intel document 472178 */
static struct {
u16 dev_id;
const char *dev_name;
} pch_table[] = {
{0x1E41, "Desktop Sample"},
{0x1E42, "Mobile Sample"},
{0x1E43, "SFF Sample"},
{0x1E44, "Z77"},
{0x1E45, "H71"},
{0x1E46, "Z75"},
{0x1E47, "Q77"},
{0x1E48, "Q75"},
{0x1E49, "B75"},
{0x1E4A, "H77"},
{0x1E53, "C216"},
{0x1E55, "QM77"},
{0x1E56, "QS77"},
{0x1E58, "UM77"},
{0x1E57, "HM77"},
{0x1E59, "HM76"},
{0x1E5D, "HM75"},
{0x1E5E, "HM70"},
{0x1E5F, "NM70"},
};
static void report_pch_info(struct udevice *dev)
{
const char *pch_type = "Unknown";
int i;
u16 dev_id;
uint8_t rev_id;
dm_pci_read_config16(dev, 2, &dev_id);
for (i = 0; i < ARRAY_SIZE(pch_table); i++) {
if (pch_table[i].dev_id == dev_id) {
pch_type = pch_table[i].dev_name;
break;
}
}
dm_pci_read_config8(dev, 8, &rev_id);
debug("PCH type: %s, device id: %x, rev id %x\n", pch_type, dev_id,
rev_id);
}
void report_platform_info(struct udevice *dev)
{
report_cpu_info();
report_pch_info(dev);
}

View File

@@ -0,0 +1,616 @@
/*
* (C) Copyright 2008-2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* Portions of this file are derived from the Linux kernel source
* Copyright (C) 1991, 1992 Linus Torvalds
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <asm/cache.h>
#include <asm/control_regs.h>
#include <asm/i8259.h>
#include <asm/interrupt.h>
#include <asm/io.h>
#include <asm/lapic.h>
#include <asm/msr.h>
#include <asm/processor-flags.h>
#include <asm/processor.h>
#include <asm/u-boot-x86.h>
DECLARE_GLOBAL_DATA_PTR;
#define DECLARE_INTERRUPT(x) \
".globl irq_"#x"\n" \
".hidden irq_"#x"\n" \
".type irq_"#x", @function\n" \
"irq_"#x":\n" \
"pushl $"#x"\n" \
"jmp irq_common_entry\n"
static char *exceptions[] = {
"Divide Error",
"Debug",
"NMI Interrupt",
"Breakpoint",
"Overflow",
"BOUND Range Exceeded",
"Invalid Opcode (Undefined Opcode)",
"Device Not Avaiable (No Math Coprocessor)",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack Segment Fault",
"General Protection",
"Page Fault",
"Reserved",
"x87 FPU Floating-Point Error",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved"
};
static void dump_regs(struct irq_regs *regs)
{
unsigned long cs, eip, eflags;
unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L;
unsigned long d0, d1, d2, d3, d6, d7;
unsigned long sp;
/*
* Some exceptions cause an error code to be saved on the current stack
* after the EIP value. We should extract CS/EIP/EFLAGS from different
* position on the stack based on the exception number.
*/
switch (regs->irq_id) {
case EXC_DF:
case EXC_TS:
case EXC_NP:
case EXC_SS:
case EXC_GP:
case EXC_PF:
case EXC_AC:
cs = regs->context.ctx2.xcs;
eip = regs->context.ctx2.eip;
eflags = regs->context.ctx2.eflags;
/* We should fix up the ESP due to error code */
regs->esp += 4;
break;
default:
cs = regs->context.ctx1.xcs;
eip = regs->context.ctx1.eip;
eflags = regs->context.ctx1.eflags;
break;
}
printf("EIP: %04x:[<%08lx>] EFLAGS: %08lx\n",
(u16)cs, eip, eflags);
if (gd->flags & GD_FLG_RELOC)
printf("Original EIP :[<%08lx>]\n", eip - gd->reloc_off);
printf("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
regs->eax, regs->ebx, regs->ecx, regs->edx);
printf("ESI: %08lx EDI: %08lx EBP: %08lx ESP: %08lx\n",
regs->esi, regs->edi, regs->ebp, regs->esp);
printf(" DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x\n",
(u16)regs->xds, (u16)regs->xes, (u16)regs->xfs,
(u16)regs->xgs, (u16)regs->xss);
cr0 = read_cr0();
cr2 = read_cr2();
cr3 = read_cr3();
cr4 = read_cr4();
printf("CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n",
cr0, cr2, cr3, cr4);
d0 = get_debugreg(0);
d1 = get_debugreg(1);
d2 = get_debugreg(2);
d3 = get_debugreg(3);
printf("DR0: %08lx DR1: %08lx DR2: %08lx DR3: %08lx\n",
d0, d1, d2, d3);
d6 = get_debugreg(6);
d7 = get_debugreg(7);
printf("DR6: %08lx DR7: %08lx\n",
d6, d7);
printf("Stack:\n");
sp = regs->esp;
sp += 64;
while (sp > (regs->esp - 16)) {
if (sp == regs->esp)
printf("--->");
else
printf(" ");
printf("0x%8.8lx : 0x%8.8lx\n", sp, (ulong)readl(sp));
sp -= 4;
}
}
static void do_exception(struct irq_regs *regs)
{
printf("%s\n", exceptions[regs->irq_id]);
dump_regs(regs);
hang();
}
struct idt_entry {
u16 base_low;
u16 selector;
u8 res;
u8 access;
u16 base_high;
} __packed;
struct desc_ptr {
unsigned short size;
unsigned long address;
} __packed;
struct idt_entry idt[256] __aligned(16);
struct desc_ptr idt_ptr;
static inline void load_idt(const struct desc_ptr *dtr)
{
asm volatile("cs lidt %0" : : "m" (*dtr));
}
void set_vector(u8 intnum, void *routine)
{
idt[intnum].base_high = (u16)((u32)(routine) >> 16);
idt[intnum].base_low = (u16)((u32)(routine) & 0xffff);
}
/*
* Ideally these would be defined static to avoid a checkpatch warning, but
* the compiler cannot see them in the inline asm and complains that they
* aren't defined
*/
void irq_0(void);
void irq_1(void);
int cpu_init_interrupts(void)
{
int i;
int irq_entry_size = irq_1 - irq_0;
void *irq_entry = (void *)irq_0;
/* Setup the IDT */
for (i = 0; i < 256; i++) {
idt[i].access = 0x8e;
idt[i].res = 0;
idt[i].selector = X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE;
set_vector(i, irq_entry);
irq_entry += irq_entry_size;
}
idt_ptr.size = 256 * 8 - 1;
idt_ptr.address = (unsigned long) idt;
load_idt(&idt_ptr);
return 0;
}
void *x86_get_idt(void)
{
return &idt_ptr;
}
void __do_irq(int irq)
{
printf("Unhandled IRQ : %d\n", irq);
}
void do_irq(int irq) __attribute__((weak, alias("__do_irq")));
void enable_interrupts(void)
{
asm("sti\n");
}
int disable_interrupts(void)
{
long flags;
asm volatile ("pushfl ; popl %0 ; cli\n" : "=g" (flags) : );
return flags & X86_EFLAGS_IF;
}
int interrupt_init(void)
{
struct udevice *dev;
int ret;
/* Try to set up the interrupt router, but don't require one */
ret = uclass_first_device_err(UCLASS_IRQ, &dev);
if (ret && ret != -ENODEV)
return ret;
/*
* When running as an EFI application we are not in control of
* interrupts and should leave them alone.
*/
#ifndef CONFIG_EFI_APP
/* Just in case... */
disable_interrupts();
#ifdef CONFIG_I8259_PIC
/* Initialize the master/slave i8259 pic */
i8259_init();
#endif
lapic_setup();
/* Initialize core interrupt and exception functionality of CPU */
cpu_init_interrupts();
/*
* It is now safe to enable interrupts.
*
* TODO(sjg@chromium.org): But we don't handle these correctly when
* booted from EFI.
*/
if (ll_boot_init())
enable_interrupts();
#endif
return 0;
}
/* IRQ Low-Level Service Routine */
void irq_llsr(struct irq_regs *regs)
{
/*
* For detailed description of each exception, refer to:
* Intel® 64 and IA-32 Architectures Software Developer's Manual
* Volume 1: Basic Architecture
* Order Number: 253665-029US, November 2008
* Table 6-1. Exceptions and Interrupts
*/
if (regs->irq_id < 32) {
/* Architecture defined exception */
do_exception(regs);
} else {
/* Hardware or User IRQ */
do_irq(regs->irq_id);
}
}
/*
* OK - This looks really horrible, but it serves a purpose - It helps create
* fully relocatable code.
* - The call to irq_llsr will be a relative jump
* - The IRQ entries will be guaranteed to be in order
* Interrupt entries are now very small (a push and a jump) but they are
* now slower (all registers pushed on stack which provides complete
* crash dumps in the low level handlers
*
* Interrupt Entry Point:
* - Interrupt has caused eflags, CS and EIP to be pushed
* - Interrupt Vector Handler has pushed orig_eax
* - pt_regs.esp needs to be adjusted by 40 bytes:
* 12 bytes pushed by CPU (EFLAGSF, CS, EIP)
* 4 bytes pushed by vector handler (irq_id)
* 24 bytes pushed before SP (SS, GS, FS, ES, DS, EAX)
* NOTE: Only longs are pushed on/popped off the stack!
*/
asm(".globl irq_common_entry\n" \
".hidden irq_common_entry\n" \
".type irq_common_entry, @function\n" \
"irq_common_entry:\n" \
"cld\n" \
"pushl %ss\n" \
"pushl %gs\n" \
"pushl %fs\n" \
"pushl %es\n" \
"pushl %ds\n" \
"pushl %eax\n" \
"movl %esp, %eax\n" \
"addl $40, %eax\n" \
"pushl %eax\n" \
"pushl %ebp\n" \
"pushl %edi\n" \
"pushl %esi\n" \
"pushl %edx\n" \
"pushl %ecx\n" \
"pushl %ebx\n" \
"mov %esp, %eax\n" \
"call irq_llsr\n" \
"popl %ebx\n" \
"popl %ecx\n" \
"popl %edx\n" \
"popl %esi\n" \
"popl %edi\n" \
"popl %ebp\n" \
"popl %eax\n" \
"popl %eax\n" \
"popl %ds\n" \
"popl %es\n" \
"popl %fs\n" \
"popl %gs\n" \
"popl %ss\n" \
"add $4, %esp\n" \
"iret\n" \
DECLARE_INTERRUPT(0) \
DECLARE_INTERRUPT(1) \
DECLARE_INTERRUPT(2) \
DECLARE_INTERRUPT(3) \
DECLARE_INTERRUPT(4) \
DECLARE_INTERRUPT(5) \
DECLARE_INTERRUPT(6) \
DECLARE_INTERRUPT(7) \
DECLARE_INTERRUPT(8) \
DECLARE_INTERRUPT(9) \
DECLARE_INTERRUPT(10) \
DECLARE_INTERRUPT(11) \
DECLARE_INTERRUPT(12) \
DECLARE_INTERRUPT(13) \
DECLARE_INTERRUPT(14) \
DECLARE_INTERRUPT(15) \
DECLARE_INTERRUPT(16) \
DECLARE_INTERRUPT(17) \
DECLARE_INTERRUPT(18) \
DECLARE_INTERRUPT(19) \
DECLARE_INTERRUPT(20) \
DECLARE_INTERRUPT(21) \
DECLARE_INTERRUPT(22) \
DECLARE_INTERRUPT(23) \
DECLARE_INTERRUPT(24) \
DECLARE_INTERRUPT(25) \
DECLARE_INTERRUPT(26) \
DECLARE_INTERRUPT(27) \
DECLARE_INTERRUPT(28) \
DECLARE_INTERRUPT(29) \
DECLARE_INTERRUPT(30) \
DECLARE_INTERRUPT(31) \
DECLARE_INTERRUPT(32) \
DECLARE_INTERRUPT(33) \
DECLARE_INTERRUPT(34) \
DECLARE_INTERRUPT(35) \
DECLARE_INTERRUPT(36) \
DECLARE_INTERRUPT(37) \
DECLARE_INTERRUPT(38) \
DECLARE_INTERRUPT(39) \
DECLARE_INTERRUPT(40) \
DECLARE_INTERRUPT(41) \
DECLARE_INTERRUPT(42) \
DECLARE_INTERRUPT(43) \
DECLARE_INTERRUPT(44) \
DECLARE_INTERRUPT(45) \
DECLARE_INTERRUPT(46) \
DECLARE_INTERRUPT(47) \
DECLARE_INTERRUPT(48) \
DECLARE_INTERRUPT(49) \
DECLARE_INTERRUPT(50) \
DECLARE_INTERRUPT(51) \
DECLARE_INTERRUPT(52) \
DECLARE_INTERRUPT(53) \
DECLARE_INTERRUPT(54) \
DECLARE_INTERRUPT(55) \
DECLARE_INTERRUPT(56) \
DECLARE_INTERRUPT(57) \
DECLARE_INTERRUPT(58) \
DECLARE_INTERRUPT(59) \
DECLARE_INTERRUPT(60) \
DECLARE_INTERRUPT(61) \
DECLARE_INTERRUPT(62) \
DECLARE_INTERRUPT(63) \
DECLARE_INTERRUPT(64) \
DECLARE_INTERRUPT(65) \
DECLARE_INTERRUPT(66) \
DECLARE_INTERRUPT(67) \
DECLARE_INTERRUPT(68) \
DECLARE_INTERRUPT(69) \
DECLARE_INTERRUPT(70) \
DECLARE_INTERRUPT(71) \
DECLARE_INTERRUPT(72) \
DECLARE_INTERRUPT(73) \
DECLARE_INTERRUPT(74) \
DECLARE_INTERRUPT(75) \
DECLARE_INTERRUPT(76) \
DECLARE_INTERRUPT(77) \
DECLARE_INTERRUPT(78) \
DECLARE_INTERRUPT(79) \
DECLARE_INTERRUPT(80) \
DECLARE_INTERRUPT(81) \
DECLARE_INTERRUPT(82) \
DECLARE_INTERRUPT(83) \
DECLARE_INTERRUPT(84) \
DECLARE_INTERRUPT(85) \
DECLARE_INTERRUPT(86) \
DECLARE_INTERRUPT(87) \
DECLARE_INTERRUPT(88) \
DECLARE_INTERRUPT(89) \
DECLARE_INTERRUPT(90) \
DECLARE_INTERRUPT(91) \
DECLARE_INTERRUPT(92) \
DECLARE_INTERRUPT(93) \
DECLARE_INTERRUPT(94) \
DECLARE_INTERRUPT(95) \
DECLARE_INTERRUPT(97) \
DECLARE_INTERRUPT(96) \
DECLARE_INTERRUPT(98) \
DECLARE_INTERRUPT(99) \
DECLARE_INTERRUPT(100) \
DECLARE_INTERRUPT(101) \
DECLARE_INTERRUPT(102) \
DECLARE_INTERRUPT(103) \
DECLARE_INTERRUPT(104) \
DECLARE_INTERRUPT(105) \
DECLARE_INTERRUPT(106) \
DECLARE_INTERRUPT(107) \
DECLARE_INTERRUPT(108) \
DECLARE_INTERRUPT(109) \
DECLARE_INTERRUPT(110) \
DECLARE_INTERRUPT(111) \
DECLARE_INTERRUPT(112) \
DECLARE_INTERRUPT(113) \
DECLARE_INTERRUPT(114) \
DECLARE_INTERRUPT(115) \
DECLARE_INTERRUPT(116) \
DECLARE_INTERRUPT(117) \
DECLARE_INTERRUPT(118) \
DECLARE_INTERRUPT(119) \
DECLARE_INTERRUPT(120) \
DECLARE_INTERRUPT(121) \
DECLARE_INTERRUPT(122) \
DECLARE_INTERRUPT(123) \
DECLARE_INTERRUPT(124) \
DECLARE_INTERRUPT(125) \
DECLARE_INTERRUPT(126) \
DECLARE_INTERRUPT(127) \
DECLARE_INTERRUPT(128) \
DECLARE_INTERRUPT(129) \
DECLARE_INTERRUPT(130) \
DECLARE_INTERRUPT(131) \
DECLARE_INTERRUPT(132) \
DECLARE_INTERRUPT(133) \
DECLARE_INTERRUPT(134) \
DECLARE_INTERRUPT(135) \
DECLARE_INTERRUPT(136) \
DECLARE_INTERRUPT(137) \
DECLARE_INTERRUPT(138) \
DECLARE_INTERRUPT(139) \
DECLARE_INTERRUPT(140) \
DECLARE_INTERRUPT(141) \
DECLARE_INTERRUPT(142) \
DECLARE_INTERRUPT(143) \
DECLARE_INTERRUPT(144) \
DECLARE_INTERRUPT(145) \
DECLARE_INTERRUPT(146) \
DECLARE_INTERRUPT(147) \
DECLARE_INTERRUPT(148) \
DECLARE_INTERRUPT(149) \
DECLARE_INTERRUPT(150) \
DECLARE_INTERRUPT(151) \
DECLARE_INTERRUPT(152) \
DECLARE_INTERRUPT(153) \
DECLARE_INTERRUPT(154) \
DECLARE_INTERRUPT(155) \
DECLARE_INTERRUPT(156) \
DECLARE_INTERRUPT(157) \
DECLARE_INTERRUPT(158) \
DECLARE_INTERRUPT(159) \
DECLARE_INTERRUPT(160) \
DECLARE_INTERRUPT(161) \
DECLARE_INTERRUPT(162) \
DECLARE_INTERRUPT(163) \
DECLARE_INTERRUPT(164) \
DECLARE_INTERRUPT(165) \
DECLARE_INTERRUPT(166) \
DECLARE_INTERRUPT(167) \
DECLARE_INTERRUPT(168) \
DECLARE_INTERRUPT(169) \
DECLARE_INTERRUPT(170) \
DECLARE_INTERRUPT(171) \
DECLARE_INTERRUPT(172) \
DECLARE_INTERRUPT(173) \
DECLARE_INTERRUPT(174) \
DECLARE_INTERRUPT(175) \
DECLARE_INTERRUPT(176) \
DECLARE_INTERRUPT(177) \
DECLARE_INTERRUPT(178) \
DECLARE_INTERRUPT(179) \
DECLARE_INTERRUPT(180) \
DECLARE_INTERRUPT(181) \
DECLARE_INTERRUPT(182) \
DECLARE_INTERRUPT(183) \
DECLARE_INTERRUPT(184) \
DECLARE_INTERRUPT(185) \
DECLARE_INTERRUPT(186) \
DECLARE_INTERRUPT(187) \
DECLARE_INTERRUPT(188) \
DECLARE_INTERRUPT(189) \
DECLARE_INTERRUPT(190) \
DECLARE_INTERRUPT(191) \
DECLARE_INTERRUPT(192) \
DECLARE_INTERRUPT(193) \
DECLARE_INTERRUPT(194) \
DECLARE_INTERRUPT(195) \
DECLARE_INTERRUPT(196) \
DECLARE_INTERRUPT(197) \
DECLARE_INTERRUPT(198) \
DECLARE_INTERRUPT(199) \
DECLARE_INTERRUPT(200) \
DECLARE_INTERRUPT(201) \
DECLARE_INTERRUPT(202) \
DECLARE_INTERRUPT(203) \
DECLARE_INTERRUPT(204) \
DECLARE_INTERRUPT(205) \
DECLARE_INTERRUPT(206) \
DECLARE_INTERRUPT(207) \
DECLARE_INTERRUPT(208) \
DECLARE_INTERRUPT(209) \
DECLARE_INTERRUPT(210) \
DECLARE_INTERRUPT(211) \
DECLARE_INTERRUPT(212) \
DECLARE_INTERRUPT(213) \
DECLARE_INTERRUPT(214) \
DECLARE_INTERRUPT(215) \
DECLARE_INTERRUPT(216) \
DECLARE_INTERRUPT(217) \
DECLARE_INTERRUPT(218) \
DECLARE_INTERRUPT(219) \
DECLARE_INTERRUPT(220) \
DECLARE_INTERRUPT(221) \
DECLARE_INTERRUPT(222) \
DECLARE_INTERRUPT(223) \
DECLARE_INTERRUPT(224) \
DECLARE_INTERRUPT(225) \
DECLARE_INTERRUPT(226) \
DECLARE_INTERRUPT(227) \
DECLARE_INTERRUPT(228) \
DECLARE_INTERRUPT(229) \
DECLARE_INTERRUPT(230) \
DECLARE_INTERRUPT(231) \
DECLARE_INTERRUPT(232) \
DECLARE_INTERRUPT(233) \
DECLARE_INTERRUPT(234) \
DECLARE_INTERRUPT(235) \
DECLARE_INTERRUPT(236) \
DECLARE_INTERRUPT(237) \
DECLARE_INTERRUPT(238) \
DECLARE_INTERRUPT(239) \
DECLARE_INTERRUPT(240) \
DECLARE_INTERRUPT(241) \
DECLARE_INTERRUPT(242) \
DECLARE_INTERRUPT(243) \
DECLARE_INTERRUPT(244) \
DECLARE_INTERRUPT(245) \
DECLARE_INTERRUPT(246) \
DECLARE_INTERRUPT(247) \
DECLARE_INTERRUPT(248) \
DECLARE_INTERRUPT(249) \
DECLARE_INTERRUPT(250) \
DECLARE_INTERRUPT(251) \
DECLARE_INTERRUPT(252) \
DECLARE_INTERRUPT(253) \
DECLARE_INTERRUPT(254) \
DECLARE_INTERRUPT(255));

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <asm/ioapic.h>
#include <asm/lapic.h>
u32 io_apic_read(u32 reg)
{
writel(reg, IO_APIC_INDEX);
return readl(IO_APIC_DATA);
}
void io_apic_write(u32 reg, u32 val)
{
writel(reg, IO_APIC_INDEX);
writel(val, IO_APIC_DATA);
}
void io_apic_set_id(int ioapic_id)
{
int bsp_lapicid = lapicid();
debug("IOAPIC: Initialising IOAPIC at %08x\n", IO_APIC_ADDR);
debug("IOAPIC: Bootstrap Processor Local APIC = %#02x\n", bsp_lapicid);
if (ioapic_id) {
debug("IOAPIC: ID = 0x%02x\n", ioapic_id);
/* Set IOAPIC ID if it has been specified */
io_apic_write(0x00, (io_apic_read(0x00) & 0xf0ffffff) |
(ioapic_id << 24));
}
}

290
u-boot/arch/x86/cpu/irq.c Normal file
View File

@@ -0,0 +1,290 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pci.h>
#include <asm/pirq_routing.h>
#include <asm/tables.h>
DECLARE_GLOBAL_DATA_PTR;
static struct irq_routing_table *pirq_routing_table;
bool pirq_check_irq_routed(struct udevice *dev, int link, u8 irq)
{
struct irq_router *priv = dev_get_priv(dev);
u8 pirq;
int base = priv->link_base;
if (priv->config == PIRQ_VIA_PCI)
dm_pci_read_config8(dev->parent, LINK_N2V(link, base), &pirq);
else
pirq = readb(priv->ibase + LINK_N2V(link, base));
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(struct udevice *dev, int link)
{
struct irq_router *priv = dev_get_priv(dev);
return LINK_V2N(link, priv->link_base);
}
void pirq_assign_irq(struct udevice *dev, int link, u8 irq)
{
struct irq_router *priv = dev_get_priv(dev);
int base = priv->link_base;
/* IRQ# 0/1/2/8/13 are reserved */
if (irq < 3 || irq == 8 || irq == 13)
return;
if (priv->config == PIRQ_VIA_PCI)
dm_pci_write_config8(dev->parent, LINK_N2V(link, base), irq);
else
writeb(irq, priv->ibase + LINK_N2V(link, base));
}
static struct irq_info *check_dup_entry(struct irq_info *slot_base,
int entry_num, int bus, int device)
{
struct irq_info *slot = slot_base;
int i;
for (i = 0; i < entry_num; i++) {
if (slot->bus == bus && slot->devfn == (device << 3))
break;
slot++;
}
return (i == entry_num) ? NULL : slot;
}
static inline void fill_irq_info(struct irq_router *priv, struct irq_info *slot,
int bus, int device, int pin, int pirq)
{
slot->bus = bus;
slot->devfn = (device << 3) | 0;
slot->irq[pin - 1].link = LINK_N2V(pirq, priv->link_base);
slot->irq[pin - 1].bitmap = priv->irq_mask;
}
static int create_pirq_routing_table(struct udevice *dev)
{
struct irq_router *priv = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
int node;
int len, count;
const u32 *cell;
struct irq_routing_table *rt;
struct irq_info *slot, *slot_base;
int irq_entries = 0;
int i;
int ret;
node = dev->of_offset;
/* extract the bdf from fdt_pci_addr */
priv->bdf = dm_pci_get_bdf(dev->parent);
ret = fdt_find_string(blob, node, "intel,pirq-config", "pci");
if (!ret) {
priv->config = PIRQ_VIA_PCI;
} else {
ret = fdt_find_string(blob, node, "intel,pirq-config", "ibase");
if (!ret)
priv->config = PIRQ_VIA_IBASE;
else
return -EINVAL;
}
ret = fdtdec_get_int(blob, node, "intel,pirq-link", -1);
if (ret == -1)
return ret;
priv->link_base = ret;
priv->irq_mask = fdtdec_get_int(blob, node,
"intel,pirq-mask", PIRQ_BITMAP);
if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) {
/* Reserve IRQ9 for SCI */
priv->irq_mask &= ~(1 << 9);
}
if (priv->config == PIRQ_VIA_IBASE) {
int ibase_off;
ibase_off = fdtdec_get_int(blob, node, "intel,ibase-offset", 0);
if (!ibase_off)
return -EINVAL;
/*
* Here we assume that the IBASE register has already been
* properly configured by U-Boot before.
*
* By 'valid' we mean:
* 1) a valid memory space carved within system memory space
* assigned to IBASE register block.
* 2) memory range decoding is enabled.
* Hence we don't do any santify test here.
*/
dm_pci_read_config32(dev->parent, ibase_off, &priv->ibase);
priv->ibase &= ~0xf;
}
priv->actl_8bit = fdtdec_get_bool(blob, node, "intel,actl-8bit");
priv->actl_addr = fdtdec_get_int(blob, node, "intel,actl-addr", 0);
cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
if (!cell || len % sizeof(struct pirq_routing))
return -EINVAL;
count = len / sizeof(struct pirq_routing);
rt = calloc(1, sizeof(struct irq_routing_table));
if (!rt)
return -ENOMEM;
/* Populate the PIRQ table fields */
rt->signature = PIRQ_SIGNATURE;
rt->version = PIRQ_VERSION;
rt->rtr_bus = PCI_BUS(priv->bdf);
rt->rtr_devfn = (PCI_DEV(priv->bdf) << 3) | PCI_FUNC(priv->bdf);
rt->rtr_vendor = PCI_VENDOR_ID_INTEL;
rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31;
slot_base = rt->slots;
/* Now fill in the irq_info entries in the PIRQ table */
for (i = 0; i < count;
i++, cell += sizeof(struct pirq_routing) / sizeof(u32)) {
struct pirq_routing pr;
pr.bdf = fdt_addr_to_cpu(cell[0]);
pr.pin = fdt_addr_to_cpu(cell[1]);
pr.pirq = fdt_addr_to_cpu(cell[2]);
debug("irq_info %d: b.d.f %x.%x.%x INT%c PIRQ%c\n",
i, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
PCI_FUNC(pr.bdf), 'A' + pr.pin - 1,
'A' + pr.pirq);
slot = check_dup_entry(slot_base, irq_entries,
PCI_BUS(pr.bdf), PCI_DEV(pr.bdf));
if (slot) {
debug("found entry for bus %d device %d, ",
PCI_BUS(pr.bdf), PCI_DEV(pr.bdf));
if (slot->irq[pr.pin - 1].link) {
debug("skipping\n");
/*
* Sanity test on the routed PIRQ pin
*
* If they don't match, show a warning to tell
* there might be something wrong with the PIRQ
* routing information in the device tree.
*/
if (slot->irq[pr.pin - 1].link !=
LINK_N2V(pr.pirq, priv->link_base))
debug("WARNING: Inconsistent PIRQ routing information\n");
continue;
}
} else {
slot = slot_base + irq_entries++;
}
debug("writing INT%c\n", 'A' + pr.pin - 1);
fill_irq_info(priv, slot, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
pr.pin, pr.pirq);
}
rt->size = irq_entries * sizeof(struct irq_info) + 32;
/* Fix up the table checksum */
rt->checksum = table_compute_checksum(rt, rt->size);
pirq_routing_table = rt;
return 0;
}
static void irq_enable_sci(struct udevice *dev)
{
struct irq_router *priv = dev_get_priv(dev);
if (priv->actl_8bit) {
/* Bit7 must be turned on to enable ACPI */
dm_pci_write_config8(dev->parent, priv->actl_addr, 0x80);
} else {
/* Write 0 to enable SCI on IRQ9 */
if (priv->config == PIRQ_VIA_PCI)
dm_pci_write_config32(dev->parent, priv->actl_addr, 0);
else
writel(0, priv->ibase + priv->actl_addr);
}
}
int irq_router_common_init(struct udevice *dev)
{
int ret;
ret = create_pirq_routing_table(dev);
if (ret) {
debug("Failed to create pirq routing table\n");
return ret;
}
/* Route PIRQ */
pirq_route_irqs(dev, pirq_routing_table->slots,
get_irq_slot_count(pirq_routing_table));
if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE))
irq_enable_sci(dev);
return 0;
}
int irq_router_probe(struct udevice *dev)
{
return irq_router_common_init(dev);
}
u32 write_pirq_routing_table(u32 addr)
{
if (!pirq_routing_table)
return addr;
return copy_pirq_routing_table(addr, pirq_routing_table);
}
static const struct udevice_id irq_router_ids[] = {
{ .compatible = "intel,irq-router" },
{ }
};
U_BOOT_DRIVER(irq_router_drv) = {
.name = "intel_irq",
.id = UCLASS_IRQ,
.of_match = irq_router_ids,
.probe = irq_router_probe,
.priv_auto_alloc_size = sizeof(struct irq_router),
};
UCLASS_DRIVER(irq) = {
.id = UCLASS_IRQ,
.name = "irq",
};

View File

@@ -0,0 +1,61 @@
#
# From Coreboot src/northbridge/intel/sandybridge/Kconfig
#
# Copyright (C) 2010 Google Inc.
#
# SPDX-License-Identifier: GPL-2.0
config NORTHBRIDGE_INTEL_IVYBRIDGE
bool
select CACHE_MRC_BIN if HAVE_MRC
if NORTHBRIDGE_INTEL_IVYBRIDGE
config DCACHE_RAM_BASE
default 0xff7e0000
config DCACHE_RAM_SIZE
default 0x20000
config DCACHE_RAM_MRC_VAR_SIZE
default 0x4000
config CPU_SPECIFIC_OPTIONS
def_bool y
select SMM_TSEG
select X86_RAMTEST
config SMM_TSEG_SIZE
hex
default 0x800000
config ENABLE_VMX
bool "Enable VMX for virtualization"
default n
help
Virtual Machine Extensions are provided in many x86 CPUs. These
provide various facilities for allowing a host OS to provide an
environment where potentially several guest OSes have only
limited access to the underlying hardware. This is achieved
without resorting to software trapping and/or instruction set
emulation (which would be very slow).
Intel's implementation of this is called VT-x. This option enables
VT-x this so that the OS that is booted by U-Boot can make use of
these facilities. If this option is not enabled, then the host OS
will be unable to support virtualisation, or it will run very
slowly.
config FSP_ADDR
hex
default 0xfff80000
config FSP_USE_UPD
bool
default n
config FSP_BROKEN_HOB
bool
default y
endif

View File

@@ -0,0 +1,19 @@
#
# Copyright (c) 2014 Google, Inc
#
# SPDX-License-Identifier: GPL-2.0+
#
ifdef CONFIG_HAVE_FSP
obj-y += fsp_configs.o ivybridge.o
else
obj-y += cpu.o
obj-y += early_me.o
obj-y += gma.o
obj-y += lpc.o
obj-y += model_206ax.o
obj-y += northbridge.o
obj-y += sata.o
obj-y += sdram.o
endif
obj-y += bd82x6x.o

View File

@@ -0,0 +1,245 @@
/*
* Copyright (C) 2014 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <pch.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/lapic.h>
#include <asm/lpc_common.h>
#include <asm/pci.h>
#include <asm/arch/bd82x6x.h>
#include <asm/arch/model_206ax.h>
#include <asm/arch/pch.h>
#include <asm/arch/sandybridge.h>
#define GPIO_BASE 0x48
#define BIOS_CTRL 0xdc
#ifndef CONFIG_HAVE_FSP
static int pch_revision_id = -1;
static int pch_type = -1;
/**
* pch_silicon_revision() - Read silicon revision ID from the PCH
*
* @dev: PCH device
* @return silicon revision ID
*/
static int pch_silicon_revision(struct udevice *dev)
{
u8 val;
if (pch_revision_id < 0) {
dm_pci_read_config8(dev, PCI_REVISION_ID, &val);
pch_revision_id = val;
}
return pch_revision_id;
}
int pch_silicon_type(struct udevice *dev)
{
u8 val;
if (pch_type < 0) {
dm_pci_read_config8(dev, PCI_DEVICE_ID + 1, &val);
pch_type = val;
}
return pch_type;
}
/**
* pch_silicon_supported() - Check if a certain revision is supported
*
* @dev: PCH device
* @type: PCH type
* @rev: Minimum required resion
* @return 0 if not supported, 1 if supported
*/
static int pch_silicon_supported(struct udevice *dev, int type, int rev)
{
int cur_type = pch_silicon_type(dev);
int cur_rev = pch_silicon_revision(dev);
switch (type) {
case PCH_TYPE_CPT:
/* CougarPoint minimum revision */
if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
return 1;
/* PantherPoint any revision */
if (cur_type == PCH_TYPE_PPT)
return 1;
break;
case PCH_TYPE_PPT:
/* PantherPoint minimum revision */
if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
return 1;
break;
}
return 0;
}
#define IOBP_RETRY 1000
static inline int iobp_poll(void)
{
unsigned try = IOBP_RETRY;
u32 data;
while (try--) {
data = readl(RCB_REG(IOBPS));
if ((data & 1) == 0)
return 1;
udelay(10);
}
printf("IOBP timeout\n");
return 0;
}
void pch_iobp_update(struct udevice *dev, u32 address, u32 andvalue,
u32 orvalue)
{
u32 data;
/* Set the address */
writel(address, RCB_REG(IOBPIRI));
/* READ OPCODE */
if (pch_silicon_supported(dev, PCH_TYPE_CPT, PCH_STEP_B0))
writel(IOBPS_RW_BX, RCB_REG(IOBPS));
else
writel(IOBPS_READ_AX, RCB_REG(IOBPS));
if (!iobp_poll())
return;
/* Read IOBP data */
data = readl(RCB_REG(IOBPD));
if (!iobp_poll())
return;
/* Check for successful transaction */
if ((readl(RCB_REG(IOBPS)) & 0x6) != 0) {
printf("IOBP read 0x%08x failed\n", address);
return;
}
/* Update the data */
data &= andvalue;
data |= orvalue;
/* WRITE OPCODE */
if (pch_silicon_supported(dev, PCH_TYPE_CPT, PCH_STEP_B0))
writel(IOBPS_RW_BX, RCB_REG(IOBPS));
else
writel(IOBPS_WRITE_AX, RCB_REG(IOBPS));
if (!iobp_poll())
return;
/* Write IOBP data */
writel(data, RCB_REG(IOBPD));
if (!iobp_poll())
return;
}
static int bd82x6x_probe(struct udevice *dev)
{
struct udevice *gma_dev;
int ret;
if (!(gd->flags & GD_FLG_RELOC))
return 0;
/* Cause the SATA device to do its init */
uclass_first_device(UCLASS_AHCI, &dev);
ret = syscon_get_by_driver_data(X86_SYSCON_GMA, &gma_dev);
if (ret)
return ret;
ret = gma_func0_init(gma_dev);
if (ret)
return ret;
return 0;
}
#endif /* CONFIG_HAVE_FSP */
static int bd82x6x_pch_get_spi_base(struct udevice *dev, ulong *sbasep)
{
u32 rcba;
dm_pci_read_config32(dev, PCH_RCBA, &rcba);
/* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable */
rcba = rcba & 0xffffc000;
*sbasep = rcba + 0x3800;
return 0;
}
static int bd82x6x_set_spi_protect(struct udevice *dev, bool protect)
{
return lpc_set_spi_protect(dev, BIOS_CTRL, protect);
}
static int bd82x6x_get_gpio_base(struct udevice *dev, u32 *gbasep)
{
u32 base;
/*
* GPIO_BASE moved to its current offset with ICH6, but prior to
* that it was unused (or undocumented). Check that it looks
* okay: not all ones or zeros.
*
* Note we don't need check bit0 here, because the Tunnel Creek
* GPIO base address register bit0 is reserved (read returns 0),
* while on the Ivybridge the bit0 is used to indicate it is an
* I/O space.
*/
dm_pci_read_config32(dev, GPIO_BASE, &base);
if (base == 0x00000000 || base == 0xffffffff) {
debug("%s: unexpected BASE value\n", __func__);
return -ENODEV;
}
/*
* Okay, I guess we're looking at the right device. The actual
* GPIO registers are in the PCI device's I/O space, starting
* at the offset that we just read. Bit 0 indicates that it's
* an I/O address, not a memory address, so mask that off.
*/
*gbasep = base & 1 ? base & ~3 : base & ~15;
return 0;
}
static const struct pch_ops bd82x6x_pch_ops = {
.get_spi_base = bd82x6x_pch_get_spi_base,
.set_spi_protect = bd82x6x_set_spi_protect,
.get_gpio_base = bd82x6x_get_gpio_base,
};
static const struct udevice_id bd82x6x_ids[] = {
{ .compatible = "intel,bd82x6x" },
{ }
};
U_BOOT_DRIVER(bd82x6x_drv) = {
.name = "bd82x6x",
.id = UCLASS_PCH,
.of_match = bd82x6x_ids,
#ifndef CONFIG_HAVE_FSP
.probe = bd82x6x_probe,
#endif
.ops = &bd82x6x_pch_ops,
};

View File

@@ -0,0 +1,195 @@
/*
* Copyright (c) 2014 Google, Inc
* (C) Copyright 2008
* Graeme Russ, graeme.russ@gmail.com.
*
* Some portions from coreboot src/mainboard/google/link/romstage.c
* and src/cpu/intel/model_206ax/bootblock.c
* Copyright (C) 2007-2010 coresystems GmbH
* Copyright (C) 2011 Google Inc.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <pch.h>
#include <asm/cpu.h>
#include <asm/cpu_common.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/lapic.h>
#include <asm/lpc_common.h>
#include <asm/microcode.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
#include <asm/pci.h>
#include <asm/post.h>
#include <asm/processor.h>
#include <asm/arch/model_206ax.h>
#include <asm/arch/pch.h>
#include <asm/arch/sandybridge.h>
DECLARE_GLOBAL_DATA_PTR;
static int set_flex_ratio_to_tdp_nominal(void)
{
/* Minimum CPU revision for configurable TDP support */
if (cpuid_eax(1) < IVB_CONFIG_TDP_MIN_CPUID)
return -EINVAL;
return cpu_set_flex_ratio_to_tdp_nominal();
}
int arch_cpu_init(void)
{
post_code(POST_CPU_INIT);
return x86_cpu_init_f();
}
int arch_cpu_init_dm(void)
{
struct pci_controller *hose;
struct udevice *bus, *dev;
int ret;
post_code(0x70);
ret = uclass_get_device(UCLASS_PCI, 0, &bus);
post_code(0x71);
if (ret)
return ret;
post_code(0x72);
hose = dev_get_uclass_priv(bus);
/* TODO(sjg@chromium.org): Get rid of gd->hose */
gd->hose = hose;
ret = uclass_first_device_err(UCLASS_LPC, &dev);
if (ret)
return ret;
/*
* We should do as little as possible before the serial console is
* up. Perhaps this should move to later. Our next lot of init
* happens in print_cpuinfo() when we have a console
*/
ret = set_flex_ratio_to_tdp_nominal();
if (ret)
return ret;
return 0;
}
#define PCH_EHCI0_TEMP_BAR0 0xe8000000
#define PCH_EHCI1_TEMP_BAR0 0xe8000400
#define PCH_XHCI_TEMP_BAR0 0xe8001000
/*
* Setup USB controller MMIO BAR to prevent the reference code from
* resetting the controller.
*
* The BAR will be re-assigned during device enumeration so these are only
* temporary.
*
* This is used to speed up the resume path.
*/
static void enable_usb_bar(struct udevice *bus)
{
pci_dev_t usb0 = PCH_EHCI1_DEV;
pci_dev_t usb1 = PCH_EHCI2_DEV;
pci_dev_t usb3 = PCH_XHCI_DEV;
ulong cmd;
/* USB Controller 1 */
pci_bus_write_config(bus, usb0, PCI_BASE_ADDRESS_0,
PCH_EHCI0_TEMP_BAR0, PCI_SIZE_32);
pci_bus_read_config(bus, usb0, PCI_COMMAND, &cmd, PCI_SIZE_32);
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
pci_bus_write_config(bus, usb0, PCI_COMMAND, cmd, PCI_SIZE_32);
/* USB Controller 2 */
pci_bus_write_config(bus, usb1, PCI_BASE_ADDRESS_0,
PCH_EHCI1_TEMP_BAR0, PCI_SIZE_32);
pci_bus_read_config(bus, usb1, PCI_COMMAND, &cmd, PCI_SIZE_32);
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
pci_bus_write_config(bus, usb1, PCI_COMMAND, cmd, PCI_SIZE_32);
/* USB3 Controller 1 */
pci_bus_write_config(bus, usb3, PCI_BASE_ADDRESS_0,
PCH_XHCI_TEMP_BAR0, PCI_SIZE_32);
pci_bus_read_config(bus, usb3, PCI_COMMAND, &cmd, PCI_SIZE_32);
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
pci_bus_write_config(bus, usb3, PCI_COMMAND, cmd, PCI_SIZE_32);
}
int print_cpuinfo(void)
{
enum pei_boot_mode_t boot_mode = PEI_BOOT_NONE;
char processor_name[CPU_MAX_NAME_LEN];
struct udevice *dev, *lpc;
const char *name;
uint32_t pm1_cnt;
uint16_t pm1_sts;
int ret;
/* TODO: cmos_post_init() */
if (readl(MCHBAR_REG(SSKPD)) == 0xCAFE) {
debug("soft reset detected\n");
boot_mode = PEI_BOOT_SOFT_RESET;
/* System is not happy after keyboard reset... */
debug("Issuing CF9 warm reset\n");
reset_cpu(0);
}
ret = cpu_common_init();
if (ret)
return ret;
/* Check PM1_STS[15] to see if we are waking from Sx */
pm1_sts = inw(DEFAULT_PMBASE + PM1_STS);
/* Read PM1_CNT[12:10] to determine which Sx state */
pm1_cnt = inl(DEFAULT_PMBASE + PM1_CNT);
if ((pm1_sts & WAK_STS) && ((pm1_cnt >> 10) & 7) == 5) {
debug("Resume from S3 detected, but disabled.\n");
} else {
/*
* TODO: An indication of life might be possible here (e.g.
* keyboard light)
*/
}
post_code(POST_EARLY_INIT);
/* Enable SPD ROMs and DDR-III DRAM */
ret = uclass_first_device_err(UCLASS_I2C, &dev);
if (ret)
return ret;
/* Prepare USB controller early in S3 resume */
if (boot_mode == PEI_BOOT_RESUME) {
uclass_first_device(UCLASS_LPC, &lpc);
enable_usb_bar(pci_get_controller(lpc->parent));
}
gd->arch.pei_boot_mode = boot_mode;
/* Print processor name */
name = cpu_get_name(processor_name);
printf("CPU: %s\n", name);
post_code(POST_CPU_INFO);
return 0;
}
void board_debug_uart_init(void)
{
/* This enables the debug UART */
pci_x86_write_config(NULL, PCH_LPC_DEV, LPC_EN, COMA_LPC_EN,
PCI_SIZE_16);
}

View File

@@ -0,0 +1,173 @@
/*
* From Coreboot src/southbridge/intel/bd82x6x/early_me.c
*
* Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <asm/pci.h>
#include <asm/cpu.h>
#include <asm/processor.h>
#include <asm/arch/me.h>
#include <asm/arch/pch.h>
#include <asm/io.h>
static const char *const me_ack_values[] = {
[ME_HFS_ACK_NO_DID] = "No DID Ack received",
[ME_HFS_ACK_RESET] = "Non-power cycle reset",
[ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset",
[ME_HFS_ACK_S3] = "Go to S3",
[ME_HFS_ACK_S4] = "Go to S4",
[ME_HFS_ACK_S5] = "Go to S5",
[ME_HFS_ACK_GBL_RESET] = "Global Reset",
[ME_HFS_ACK_CONTINUE] = "Continue to boot"
};
int intel_early_me_init(struct udevice *me_dev)
{
int count;
struct me_uma uma;
struct me_hfs hfs;
debug("Intel ME early init\n");
/* Wait for ME UMA SIZE VALID bit to be set */
for (count = ME_RETRY; count > 0; --count) {
pci_read_dword_ptr(me_dev, &uma, PCI_ME_UMA);
if (uma.valid)
break;
udelay(ME_DELAY);
}
if (!count) {
printf("ERROR: ME is not ready!\n");
return -EBUSY;
}
/* Check for valid firmware */
pci_read_dword_ptr(me_dev, &hfs, PCI_ME_HFS);
if (hfs.fpt_bad) {
printf("WARNING: ME has bad firmware\n");
return -EBADF;
}
debug("Intel ME firmware is ready\n");
return 0;
}
int intel_early_me_uma_size(struct udevice *me_dev)
{
struct me_uma uma;
pci_read_dword_ptr(me_dev, &uma, PCI_ME_UMA);
if (uma.valid) {
debug("ME: Requested %uMB UMA\n", uma.size);
return uma.size;
}
debug("ME: Invalid UMA size\n");
return -EINVAL;
}
static inline void set_global_reset(struct udevice *dev, int enable)
{
u32 etr3;
dm_pci_read_config32(dev, ETR3, &etr3);
/* Clear CF9 Without Resume Well Reset Enable */
etr3 &= ~ETR3_CWORWRE;
/* CF9GR indicates a Global Reset */
if (enable)
etr3 |= ETR3_CF9GR;
else
etr3 &= ~ETR3_CF9GR;
dm_pci_write_config32(dev, ETR3, etr3);
}
int intel_early_me_init_done(struct udevice *dev, struct udevice *me_dev,
uint status)
{
int count;
u32 mebase_l, mebase_h;
struct me_hfs hfs;
struct me_did did = {
.init_done = ME_INIT_DONE,
.status = status
};
/* MEBASE from MESEG_BASE[35:20] */
dm_pci_read_config32(PCH_DEV, PCI_CPU_MEBASE_L, &mebase_l);
dm_pci_read_config32(PCH_DEV, PCI_CPU_MEBASE_H, &mebase_h);
mebase_h &= 0xf;
did.uma_base = (mebase_l >> 20) | (mebase_h << 12);
/* Send message to ME */
debug("ME: Sending Init Done with status: %d, UMA base: 0x%04x\n",
status, did.uma_base);
pci_write_dword_ptr(me_dev, &did, PCI_ME_H_GS);
/* Must wait for ME acknowledgement */
for (count = ME_RETRY; count > 0; --count) {
pci_read_dword_ptr(me_dev, &hfs, PCI_ME_HFS);
if (hfs.bios_msg_ack)
break;
udelay(ME_DELAY);
}
if (!count) {
printf("ERROR: ME failed to respond\n");
return -ETIMEDOUT;
}
/* Return the requested BIOS action */
debug("ME: Requested BIOS Action: %s\n", me_ack_values[hfs.ack_data]);
/* Check status after acknowledgement */
intel_me_status(me_dev);
switch (hfs.ack_data) {
case ME_HFS_ACK_CONTINUE:
/* Continue to boot */
return 0;
case ME_HFS_ACK_RESET:
/* Non-power cycle reset */
set_global_reset(dev, 0);
reset_cpu(0);
break;
case ME_HFS_ACK_PWR_CYCLE:
/* Power cycle reset */
set_global_reset(dev, 0);
x86_full_reset();
break;
case ME_HFS_ACK_GBL_RESET:
/* Global reset */
set_global_reset(dev, 1);
x86_full_reset();
break;
case ME_HFS_ACK_S3:
case ME_HFS_ACK_S4:
case ME_HFS_ACK_S5:
break;
}
return -EINVAL;
}
static const struct udevice_id ivybridge_syscon_ids[] = {
{ .compatible = "intel,me", .data = X86_SYSCON_ME },
{ .compatible = "intel,gma", .data = X86_SYSCON_GMA },
{ }
};
U_BOOT_DRIVER(syscon_intel_me) = {
.name = "intel_me_syscon",
.id = UCLASS_SYSCON,
.of_match = ivybridge_syscon_ids,
};

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <fdtdec.h>
#include <asm/fsp/fsp_support.h>
DECLARE_GLOBAL_DATA_PTR;
void update_fsp_configs(struct fsp_config_data *config,
struct fspinit_rtbuf *rt_buf)
{
struct platform_config *plat_config = &config->plat_config;
struct memory_config *mem_config = &config->mem_config;
const void *blob = gd->fdt_blob;
int node;
node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_IVYBRIDGE_FSP);
if (node < 0) {
debug("%s: Cannot find FSP node\n", __func__);
return;
}
plat_config->enable_ht =
fdtdec_get_bool(blob, node, "fsp,enable-ht");
plat_config->enable_turbo =
fdtdec_get_bool(blob, node, "fsp,enable-turbo");
plat_config->enable_memory_down =
fdtdec_get_bool(blob, node, "fsp,enable-memory-down");
plat_config->enable_fast_boot =
fdtdec_get_bool(blob, node, "fsp,enable-fast-boot");
/* Initialize runtime buffer for fsp_init() */
rt_buf->stack_top = config->common.stack_top - 32;
rt_buf->boot_mode = config->common.boot_mode;
rt_buf->plat_config = plat_config;
if (plat_config->enable_memory_down)
rt_buf->mem_config = mem_config;
else
rt_buf->mem_config = NULL;
}

View File

@@ -0,0 +1,850 @@
/*
* From Coreboot file of the same name
*
* Copyright (C) 2011 Chromium OS Authors
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <bios_emul.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <pci_rom.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/mtrr.h>
#include <asm/pci.h>
#include <asm/arch/pch.h>
#include <asm/arch/sandybridge.h>
struct gt_powermeter {
u16 reg;
u32 value;
};
static const struct gt_powermeter snb_pm_gt1[] = {
{ 0xa200, 0xcc000000 },
{ 0xa204, 0x07000040 },
{ 0xa208, 0x0000fe00 },
{ 0xa20c, 0x00000000 },
{ 0xa210, 0x17000000 },
{ 0xa214, 0x00000021 },
{ 0xa218, 0x0817fe19 },
{ 0xa21c, 0x00000000 },
{ 0xa220, 0x00000000 },
{ 0xa224, 0xcc000000 },
{ 0xa228, 0x07000040 },
{ 0xa22c, 0x0000fe00 },
{ 0xa230, 0x00000000 },
{ 0xa234, 0x17000000 },
{ 0xa238, 0x00000021 },
{ 0xa23c, 0x0817fe19 },
{ 0xa240, 0x00000000 },
{ 0xa244, 0x00000000 },
{ 0xa248, 0x8000421e },
{ 0 }
};
static const struct gt_powermeter snb_pm_gt2[] = {
{ 0xa200, 0x330000a6 },
{ 0xa204, 0x402d0031 },
{ 0xa208, 0x00165f83 },
{ 0xa20c, 0xf1000000 },
{ 0xa210, 0x00000000 },
{ 0xa214, 0x00160016 },
{ 0xa218, 0x002a002b },
{ 0xa21c, 0x00000000 },
{ 0xa220, 0x00000000 },
{ 0xa224, 0x330000a6 },
{ 0xa228, 0x402d0031 },
{ 0xa22c, 0x00165f83 },
{ 0xa230, 0xf1000000 },
{ 0xa234, 0x00000000 },
{ 0xa238, 0x00160016 },
{ 0xa23c, 0x002a002b },
{ 0xa240, 0x00000000 },
{ 0xa244, 0x00000000 },
{ 0xa248, 0x8000421e },
{ 0 }
};
static const struct gt_powermeter ivb_pm_gt1[] = {
{ 0xa800, 0x00000000 },
{ 0xa804, 0x00021c00 },
{ 0xa808, 0x00000403 },
{ 0xa80c, 0x02001700 },
{ 0xa810, 0x05000200 },
{ 0xa814, 0x00000000 },
{ 0xa818, 0x00690500 },
{ 0xa81c, 0x0000007f },
{ 0xa820, 0x01002501 },
{ 0xa824, 0x00000300 },
{ 0xa828, 0x01000331 },
{ 0xa82c, 0x0000000c },
{ 0xa830, 0x00010016 },
{ 0xa834, 0x01100101 },
{ 0xa838, 0x00010103 },
{ 0xa83c, 0x00041300 },
{ 0xa840, 0x00000b30 },
{ 0xa844, 0x00000000 },
{ 0xa848, 0x7f000000 },
{ 0xa84c, 0x05000008 },
{ 0xa850, 0x00000001 },
{ 0xa854, 0x00000004 },
{ 0xa858, 0x00000007 },
{ 0xa85c, 0x00000000 },
{ 0xa860, 0x00010000 },
{ 0xa248, 0x0000221e },
{ 0xa900, 0x00000000 },
{ 0xa904, 0x00001c00 },
{ 0xa908, 0x00000000 },
{ 0xa90c, 0x06000000 },
{ 0xa910, 0x09000200 },
{ 0xa914, 0x00000000 },
{ 0xa918, 0x00590000 },
{ 0xa91c, 0x00000000 },
{ 0xa920, 0x04002501 },
{ 0xa924, 0x00000100 },
{ 0xa928, 0x03000410 },
{ 0xa92c, 0x00000000 },
{ 0xa930, 0x00020000 },
{ 0xa934, 0x02070106 },
{ 0xa938, 0x00010100 },
{ 0xa93c, 0x00401c00 },
{ 0xa940, 0x00000000 },
{ 0xa944, 0x00000000 },
{ 0xa948, 0x10000e00 },
{ 0xa94c, 0x02000004 },
{ 0xa950, 0x00000001 },
{ 0xa954, 0x00000004 },
{ 0xa960, 0x00060000 },
{ 0xaa3c, 0x00001c00 },
{ 0xaa54, 0x00000004 },
{ 0xaa60, 0x00060000 },
{ 0 }
};
static const struct gt_powermeter ivb_pm_gt2[] = {
{ 0xa800, 0x10000000 },
{ 0xa804, 0x00033800 },
{ 0xa808, 0x00000902 },
{ 0xa80c, 0x0c002f00 },
{ 0xa810, 0x12000400 },
{ 0xa814, 0x00000000 },
{ 0xa818, 0x00d20800 },
{ 0xa81c, 0x00000002 },
{ 0xa820, 0x03004b02 },
{ 0xa824, 0x00000600 },
{ 0xa828, 0x07000773 },
{ 0xa82c, 0x00000000 },
{ 0xa830, 0x00010032 },
{ 0xa834, 0x1520040d },
{ 0xa838, 0x00020105 },
{ 0xa83c, 0x00083700 },
{ 0xa840, 0x0000151d },
{ 0xa844, 0x00000000 },
{ 0xa848, 0x20001b00 },
{ 0xa84c, 0x0a000010 },
{ 0xa850, 0x00000000 },
{ 0xa854, 0x00000008 },
{ 0xa858, 0x00000008 },
{ 0xa85c, 0x00000000 },
{ 0xa860, 0x00020000 },
{ 0xa248, 0x0000221e },
{ 0xa900, 0x00000000 },
{ 0xa904, 0x00003500 },
{ 0xa908, 0x00000000 },
{ 0xa90c, 0x0c000000 },
{ 0xa910, 0x12000500 },
{ 0xa914, 0x00000000 },
{ 0xa918, 0x00b20000 },
{ 0xa91c, 0x00000000 },
{ 0xa920, 0x08004b02 },
{ 0xa924, 0x00000200 },
{ 0xa928, 0x07000820 },
{ 0xa92c, 0x00000000 },
{ 0xa930, 0x00030000 },
{ 0xa934, 0x050f020d },
{ 0xa938, 0x00020300 },
{ 0xa93c, 0x00903900 },
{ 0xa940, 0x00000000 },
{ 0xa944, 0x00000000 },
{ 0xa948, 0x20001b00 },
{ 0xa94c, 0x0a000010 },
{ 0xa950, 0x00000000 },
{ 0xa954, 0x00000008 },
{ 0xa960, 0x00110000 },
{ 0xaa3c, 0x00003900 },
{ 0xaa54, 0x00000008 },
{ 0xaa60, 0x00110000 },
{ 0 }
};
static const struct gt_powermeter ivb_pm_gt2_17w[] = {
{ 0xa800, 0x20000000 },
{ 0xa804, 0x000e3800 },
{ 0xa808, 0x00000806 },
{ 0xa80c, 0x0c002f00 },
{ 0xa810, 0x0c000800 },
{ 0xa814, 0x00000000 },
{ 0xa818, 0x00d20d00 },
{ 0xa81c, 0x000000ff },
{ 0xa820, 0x03004b02 },
{ 0xa824, 0x00000600 },
{ 0xa828, 0x07000773 },
{ 0xa82c, 0x00000000 },
{ 0xa830, 0x00020032 },
{ 0xa834, 0x1520040d },
{ 0xa838, 0x00020105 },
{ 0xa83c, 0x00083700 },
{ 0xa840, 0x000016ff },
{ 0xa844, 0x00000000 },
{ 0xa848, 0xff000000 },
{ 0xa84c, 0x0a000010 },
{ 0xa850, 0x00000002 },
{ 0xa854, 0x00000008 },
{ 0xa858, 0x0000000f },
{ 0xa85c, 0x00000000 },
{ 0xa860, 0x00020000 },
{ 0xa248, 0x0000221e },
{ 0xa900, 0x00000000 },
{ 0xa904, 0x00003800 },
{ 0xa908, 0x00000000 },
{ 0xa90c, 0x0c000000 },
{ 0xa910, 0x12000800 },
{ 0xa914, 0x00000000 },
{ 0xa918, 0x00b20000 },
{ 0xa91c, 0x00000000 },
{ 0xa920, 0x08004b02 },
{ 0xa924, 0x00000300 },
{ 0xa928, 0x01000820 },
{ 0xa92c, 0x00000000 },
{ 0xa930, 0x00030000 },
{ 0xa934, 0x15150406 },
{ 0xa938, 0x00020300 },
{ 0xa93c, 0x00903900 },
{ 0xa940, 0x00000000 },
{ 0xa944, 0x00000000 },
{ 0xa948, 0x20001b00 },
{ 0xa94c, 0x0a000010 },
{ 0xa950, 0x00000000 },
{ 0xa954, 0x00000008 },
{ 0xa960, 0x00110000 },
{ 0xaa3c, 0x00003900 },
{ 0xaa54, 0x00000008 },
{ 0xaa60, 0x00110000 },
{ 0 }
};
static const struct gt_powermeter ivb_pm_gt2_35w[] = {
{ 0xa800, 0x00000000 },
{ 0xa804, 0x00030400 },
{ 0xa808, 0x00000806 },
{ 0xa80c, 0x0c002f00 },
{ 0xa810, 0x0c000300 },
{ 0xa814, 0x00000000 },
{ 0xa818, 0x00d20d00 },
{ 0xa81c, 0x000000ff },
{ 0xa820, 0x03004b02 },
{ 0xa824, 0x00000600 },
{ 0xa828, 0x07000773 },
{ 0xa82c, 0x00000000 },
{ 0xa830, 0x00020032 },
{ 0xa834, 0x1520040d },
{ 0xa838, 0x00020105 },
{ 0xa83c, 0x00083700 },
{ 0xa840, 0x000016ff },
{ 0xa844, 0x00000000 },
{ 0xa848, 0xff000000 },
{ 0xa84c, 0x0a000010 },
{ 0xa850, 0x00000001 },
{ 0xa854, 0x00000008 },
{ 0xa858, 0x00000008 },
{ 0xa85c, 0x00000000 },
{ 0xa860, 0x00020000 },
{ 0xa248, 0x0000221e },
{ 0xa900, 0x00000000 },
{ 0xa904, 0x00003800 },
{ 0xa908, 0x00000000 },
{ 0xa90c, 0x0c000000 },
{ 0xa910, 0x12000800 },
{ 0xa914, 0x00000000 },
{ 0xa918, 0x00b20000 },
{ 0xa91c, 0x00000000 },
{ 0xa920, 0x08004b02 },
{ 0xa924, 0x00000300 },
{ 0xa928, 0x01000820 },
{ 0xa92c, 0x00000000 },
{ 0xa930, 0x00030000 },
{ 0xa934, 0x15150406 },
{ 0xa938, 0x00020300 },
{ 0xa93c, 0x00903900 },
{ 0xa940, 0x00000000 },
{ 0xa944, 0x00000000 },
{ 0xa948, 0x20001b00 },
{ 0xa94c, 0x0a000010 },
{ 0xa950, 0x00000000 },
{ 0xa954, 0x00000008 },
{ 0xa960, 0x00110000 },
{ 0xaa3c, 0x00003900 },
{ 0xaa54, 0x00000008 },
{ 0xaa60, 0x00110000 },
{ 0 }
};
/*
* Some vga option roms are used for several chipsets but they only have one
* PCI ID in their header. If we encounter such an option rom, we need to do
* the mapping ourselves.
*/
u32 map_oprom_vendev(u32 vendev)
{
u32 new_vendev = vendev;
switch (vendev) {
case 0x80860102: /* GT1 Desktop */
case 0x8086010a: /* GT1 Server */
case 0x80860112: /* GT2 Desktop */
case 0x80860116: /* GT2 Mobile */
case 0x80860122: /* GT2 Desktop >=1.3GHz */
case 0x80860126: /* GT2 Mobile >=1.3GHz */
case 0x80860156: /* IVB */
case 0x80860166: /* IVB */
/* Set to GT1 Mobile */
new_vendev = 0x80860106;
break;
}
return new_vendev;
}
static inline u32 gtt_read(void *bar, u32 reg)
{
return readl(bar + reg);
}
static inline void gtt_write(void *bar, u32 reg, u32 data)
{
writel(data, bar + reg);
}
static void gtt_write_powermeter(void *bar, const struct gt_powermeter *pm)
{
for (; pm && pm->reg; pm++)
gtt_write(bar, pm->reg, pm->value);
}
#define GTT_RETRY 1000
static int gtt_poll(void *bar, u32 reg, u32 mask, u32 value)
{
unsigned try = GTT_RETRY;
u32 data;
while (try--) {
data = gtt_read(bar, reg);
if ((data & mask) == value)
return 1;
udelay(10);
}
printf("GT init timeout\n");
return 0;
}
static int gma_pm_init_pre_vbios(void *gtt_bar, int rev)
{
u32 reg32;
debug("GT Power Management Init, silicon = %#x\n", rev);
if (rev < IVB_STEP_C0) {
/* 1: Enable force wake */
gtt_write(gtt_bar, 0xa18c, 0x00000001);
gtt_poll(gtt_bar, 0x130090, (1 << 0), (1 << 0));
} else {
gtt_write(gtt_bar, 0xa180, 1 << 5);
gtt_write(gtt_bar, 0xa188, 0xffff0001);
gtt_poll(gtt_bar, 0x130040, (1 << 0), (1 << 0));
}
if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
/* 1d: Set GTT+0x42004 [15:14]=11 (SnB C1+) */
reg32 = gtt_read(gtt_bar, 0x42004);
reg32 |= (1 << 14) | (1 << 15);
gtt_write(gtt_bar, 0x42004, reg32);
}
if (rev >= IVB_STEP_A0) {
/* Display Reset Acknowledge Settings */
reg32 = gtt_read(gtt_bar, 0x45010);
reg32 |= (1 << 1) | (1 << 0);
gtt_write(gtt_bar, 0x45010, reg32);
}
/* 2: Get GT SKU from GTT+0x911c[13] */
reg32 = gtt_read(gtt_bar, 0x911c);
if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
if (reg32 & (1 << 13)) {
debug("SNB GT1 Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, snb_pm_gt1);
} else {
debug("SNB GT2 Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, snb_pm_gt2);
}
} else {
u32 unit = readl(MCHBAR_REG(0x5938)) & 0xf;
if (reg32 & (1 << 13)) {
/* GT1 SKU */
debug("IVB GT1 Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, ivb_pm_gt1);
} else {
/* GT2 SKU */
u32 tdp = readl(MCHBAR_REG(0x5930)) & 0x7fff;
tdp /= (1 << unit);
if (tdp <= 17) {
/* <=17W ULV */
debug("IVB GT2 17W Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, ivb_pm_gt2_17w);
} else if ((tdp >= 25) && (tdp <= 35)) {
/* 25W-35W */
debug("IVB GT2 25W-35W Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, ivb_pm_gt2_35w);
} else {
/* All others */
debug("IVB GT2 35W Power Meter Weights\n");
gtt_write_powermeter(gtt_bar, ivb_pm_gt2_35w);
}
}
}
/* 3: Gear ratio map */
gtt_write(gtt_bar, 0xa004, 0x00000010);
/* 4: GFXPAUSE */
gtt_write(gtt_bar, 0xa000, 0x00070020);
/* 5: Dynamic EU trip control */
gtt_write(gtt_bar, 0xa080, 0x00000004);
/* 6: ECO bits */
reg32 = gtt_read(gtt_bar, 0xa180);
reg32 |= (1 << 26) | (1 << 31);
/* (bit 20=1 for SNB step D1+ / IVB A0+) */
if (rev >= SNB_STEP_D1)
reg32 |= (1 << 20);
gtt_write(gtt_bar, 0xa180, reg32);
/* 6a: for SnB step D2+ only */
if (((rev & BASE_REV_MASK) == BASE_REV_SNB) &&
(rev >= SNB_STEP_D2)) {
reg32 = gtt_read(gtt_bar, 0x9400);
reg32 |= (1 << 7);
gtt_write(gtt_bar, 0x9400, reg32);
reg32 = gtt_read(gtt_bar, 0x941c);
reg32 &= 0xf;
reg32 |= (1 << 1);
gtt_write(gtt_bar, 0x941c, reg32);
gtt_poll(gtt_bar, 0x941c, (1 << 1), (0 << 1));
}
if ((rev & BASE_REV_MASK) == BASE_REV_IVB) {
reg32 = gtt_read(gtt_bar, 0x907c);
reg32 |= (1 << 16);
gtt_write(gtt_bar, 0x907c, reg32);
/* 6b: Clocking reset controls */
gtt_write(gtt_bar, 0x9424, 0x00000001);
} else {
/* 6b: Clocking reset controls */
gtt_write(gtt_bar, 0x9424, 0x00000000);
}
/* 7 */
if (gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31))) {
gtt_write(gtt_bar, 0x138128, 0x00000029); /* Mailbox Data */
/* Mailbox Cmd for RC6 VID */
gtt_write(gtt_bar, 0x138124, 0x80000004);
if (gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31)))
gtt_write(gtt_bar, 0x138124, 0x8000000a);
gtt_poll(gtt_bar, 0x138124, (1 << 31), (0 << 31));
}
/* 8 */
gtt_write(gtt_bar, 0xa090, 0x00000000); /* RC Control */
gtt_write(gtt_bar, 0xa098, 0x03e80000); /* RC1e Wake Rate Limit */
gtt_write(gtt_bar, 0xa09c, 0x0028001e); /* RC6/6p Wake Rate Limit */
gtt_write(gtt_bar, 0xa0a0, 0x0000001e); /* RC6pp Wake Rate Limit */
gtt_write(gtt_bar, 0xa0a8, 0x0001e848); /* RC Evaluation Interval */
gtt_write(gtt_bar, 0xa0ac, 0x00000019); /* RC Idle Hysteresis */
/* 9 */
gtt_write(gtt_bar, 0x2054, 0x0000000a); /* Render Idle Max Count */
gtt_write(gtt_bar, 0x12054, 0x0000000a); /* Video Idle Max Count */
gtt_write(gtt_bar, 0x22054, 0x0000000a); /* Blitter Idle Max Count */
/* 10 */
gtt_write(gtt_bar, 0xa0b0, 0x00000000); /* Unblock Ack to Busy */
gtt_write(gtt_bar, 0xa0b4, 0x000003e8); /* RC1e Threshold */
gtt_write(gtt_bar, 0xa0b8, 0x0000c350); /* RC6 Threshold */
gtt_write(gtt_bar, 0xa0bc, 0x000186a0); /* RC6p Threshold */
gtt_write(gtt_bar, 0xa0c0, 0x0000fa00); /* RC6pp Threshold */
/* 11 */
gtt_write(gtt_bar, 0xa010, 0x000f4240); /* RP Down Timeout */
gtt_write(gtt_bar, 0xa014, 0x12060000); /* RP Interrupt Limits */
gtt_write(gtt_bar, 0xa02c, 0x00015f90); /* RP Up Threshold */
gtt_write(gtt_bar, 0xa030, 0x000186a0); /* RP Down Threshold */
gtt_write(gtt_bar, 0xa068, 0x000186a0); /* RP Up EI */
gtt_write(gtt_bar, 0xa06c, 0x000493e0); /* RP Down EI */
gtt_write(gtt_bar, 0xa070, 0x0000000a); /* RP Idle Hysteresis */
/* 11a: Enable Render Standby (RC6) */
if ((rev & BASE_REV_MASK) == BASE_REV_IVB) {
/*
* IvyBridge should also support DeepRenderStandby.
*
* Unfortunately it does not work reliably on all SKUs so
* disable it here and it can be enabled by the kernel.
*/
gtt_write(gtt_bar, 0xa090, 0x88040000); /* HW RC Control */
} else {
gtt_write(gtt_bar, 0xa090, 0x88040000); /* HW RC Control */
}
/* 12: Normal Frequency Request */
/* RPNFREQ_VAL comes from MCHBAR 0x5998 23:16 (8 bits!? use 7) */
reg32 = readl(MCHBAR_REG(0x5998));
reg32 >>= 16;
reg32 &= 0xef;
reg32 <<= 25;
gtt_write(gtt_bar, 0xa008, reg32);
/* 13: RP Control */
gtt_write(gtt_bar, 0xa024, 0x00000592);
/* 14: Enable PM Interrupts */
gtt_write(gtt_bar, 0x4402c, 0x03000076);
/* Clear 0x6c024 [8:6] */
reg32 = gtt_read(gtt_bar, 0x6c024);
reg32 &= ~0x000001c0;
gtt_write(gtt_bar, 0x6c024, reg32);
return 0;
}
int gma_pm_init_post_vbios(struct udevice *dev, int rev, void *gtt_bar)
{
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
u32 reg32, cycle_delay;
debug("GT Power Management Init (post VBIOS)\n");
/* 15: Deassert Force Wake */
if (rev < IVB_STEP_C0) {
gtt_write(gtt_bar, 0xa18c, gtt_read(gtt_bar, 0xa18c) & ~1);
gtt_poll(gtt_bar, 0x130090, (1 << 0), (0 << 0));
} else {
gtt_write(gtt_bar, 0xa188, 0x1fffe);
if (gtt_poll(gtt_bar, 0x130040, (1 << 0), (0 << 0))) {
gtt_write(gtt_bar, 0xa188,
gtt_read(gtt_bar, 0xa188) | 1);
}
}
/* 16: SW RC Control */
gtt_write(gtt_bar, 0xa094, 0x00060000);
/* Setup Digital Port Hotplug */
reg32 = gtt_read(gtt_bar, 0xc4030);
if (!reg32) {
u32 dp_hotplug[3];
if (fdtdec_get_int_array(blob, node, "intel,dp_hotplug",
dp_hotplug, ARRAY_SIZE(dp_hotplug)))
return -EINVAL;
reg32 = (dp_hotplug[0] & 0x7) << 2;
reg32 |= (dp_hotplug[0] & 0x7) << 10;
reg32 |= (dp_hotplug[0] & 0x7) << 18;
gtt_write(gtt_bar, 0xc4030, reg32);
}
/* Setup Panel Power On Delays */
reg32 = gtt_read(gtt_bar, 0xc7208);
if (!reg32) {
reg32 = (unsigned)fdtdec_get_int(blob, node,
"panel-port-select", 0) << 30;
reg32 |= fdtdec_get_int(blob, node, "panel-power-up-delay", 0)
<< 16;
reg32 |= fdtdec_get_int(blob, node,
"panel-power-backlight-on-delay", 0);
gtt_write(gtt_bar, 0xc7208, reg32);
}
/* Setup Panel Power Off Delays */
reg32 = gtt_read(gtt_bar, 0xc720c);
if (!reg32) {
reg32 = fdtdec_get_int(blob, node, "panel-power-down-delay", 0)
<< 16;
reg32 |= fdtdec_get_int(blob, node,
"panel-power-backlight-off-delay", 0);
gtt_write(gtt_bar, 0xc720c, reg32);
}
/* Setup Panel Power Cycle Delay */
cycle_delay = fdtdec_get_int(blob, node,
"intel,panel-power-cycle-delay", 0);
if (cycle_delay) {
reg32 = gtt_read(gtt_bar, 0xc7210);
reg32 &= ~0xff;
reg32 |= cycle_delay;
gtt_write(gtt_bar, 0xc7210, reg32);
}
/* Enable Backlight if needed */
reg32 = fdtdec_get_int(blob, node, "intel,cpu-backlight", 0);
if (reg32) {
gtt_write(gtt_bar, 0x48250, (1 << 31));
gtt_write(gtt_bar, 0x48254, reg32);
}
reg32 = fdtdec_get_int(blob, node, "intel,pch-backlight", 0);
if (reg32) {
gtt_write(gtt_bar, 0xc8250, (1 << 31));
gtt_write(gtt_bar, 0xc8254, reg32);
}
return 0;
}
/*
* Some vga option roms are used for several chipsets but they only have one
* PCI ID in their header. If we encounter such an option rom, we need to do
* the mapping ourselves.
*/
uint32_t board_map_oprom_vendev(uint32_t vendev)
{
switch (vendev) {
case 0x80860102: /* GT1 Desktop */
case 0x8086010a: /* GT1 Server */
case 0x80860112: /* GT2 Desktop */
case 0x80860116: /* GT2 Mobile */
case 0x80860122: /* GT2 Desktop >=1.3GHz */
case 0x80860126: /* GT2 Mobile >=1.3GHz */
case 0x80860156: /* IVB */
case 0x80860166: /* IVB */
return 0x80860106; /* GT1 Mobile */
}
return vendev;
}
static int int15_handler(void)
{
int res = 0;
debug("%s: INT15 function %04x!\n", __func__, M.x86.R_AX);
switch (M.x86.R_AX) {
case 0x5f34:
/*
* Set Panel Fitting Hook:
* bit 2 = Graphics Stretching
* bit 1 = Text Stretching
* bit 0 = Centering (do not set with bit1 or bit2)
* 0 = video bios default
*/
M.x86.R_AX = 0x005f;
M.x86.R_CL = 0x00; /* Use video bios default */
res = 1;
break;
case 0x5f35:
/*
* Boot Display Device Hook:
* bit 0 = CRT
* bit 1 = TV (eDP)
* bit 2 = EFP
* bit 3 = LFP
* bit 4 = CRT2
* bit 5 = TV2 (eDP)
* bit 6 = EFP2
* bit 7 = LFP2
*/
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000; /* Use video bios default */
res = 1;
break;
case 0x5f51:
/*
* Hook to select active LFP configuration:
* 00h = No LVDS, VBIOS does not enable LVDS
* 01h = Int-LVDS, LFP driven by integrated LVDS decoder
* 02h = SVDO-LVDS, LFP driven by SVDO decoder
* 03h = eDP, LFP Driven by Int-DisplayPort encoder
*/
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0003; /* eDP */
res = 1;
break;
case 0x5f70:
switch (M.x86.R_CH) {
case 0:
/* Get Mux */
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000;
res = 1;
break;
case 1:
/* Set Mux */
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000;
res = 1;
break;
case 2:
/* Get SG/Non-SG mode */
M.x86.R_AX = 0x005f;
M.x86.R_CX = 0x0000;
res = 1;
break;
default:
/* Interrupt was not handled */
debug("Unknown INT15 5f70 function: 0x%02x\n",
M.x86.R_CH);
break;
}
break;
case 0x5fac:
res = 1;
break;
default:
debug("Unknown INT15 function %04x!\n", M.x86.R_AX);
break;
}
return res;
}
void sandybridge_setup_graphics(struct udevice *dev, struct udevice *video_dev)
{
u32 reg32;
u16 reg16;
u8 reg8;
dm_pci_read_config16(video_dev, PCI_DEVICE_ID, &reg16);
switch (reg16) {
case 0x0102: /* GT1 Desktop */
case 0x0106: /* GT1 Mobile */
case 0x010a: /* GT1 Server */
case 0x0112: /* GT2 Desktop */
case 0x0116: /* GT2 Mobile */
case 0x0122: /* GT2 Desktop >=1.3GHz */
case 0x0126: /* GT2 Mobile >=1.3GHz */
case 0x0156: /* IvyBridge */
case 0x0166: /* IvyBridge */
break;
default:
debug("Graphics not supported by this CPU/chipset\n");
return;
}
debug("Initialising Graphics\n");
/* Setup IGD memory by setting GGC[7:3] = 1 for 32MB */
dm_pci_read_config16(dev, GGC, &reg16);
reg16 &= ~0x00f8;
reg16 |= 1 << 3;
/* Program GTT memory by setting GGC[9:8] = 2MB */
reg16 &= ~0x0300;
reg16 |= 2 << 8;
/* Enable VGA decode */
reg16 &= ~0x0002;
dm_pci_write_config16(dev, GGC, reg16);
/* Enable 256MB aperture */
dm_pci_read_config8(video_dev, MSAC, &reg8);
reg8 &= ~0x06;
reg8 |= 0x02;
dm_pci_write_config8(video_dev, MSAC, reg8);
/* Erratum workarounds */
reg32 = readl(MCHBAR_REG(0x5f00));
reg32 |= (1 << 9) | (1 << 10);
writel(reg32, MCHBAR_REG(0x5f00));
/* Enable SA Clock Gating */
reg32 = readl(MCHBAR_REG(0x5f00));
writel(reg32 | 1, MCHBAR_REG(0x5f00));
/* GPU RC6 workaround for sighting 366252 */
reg32 = readl(MCHBAR_REG(0x5d14));
reg32 |= (1 << 31);
writel(reg32, MCHBAR_REG(0x5d14));
/* VLW */
reg32 = readl(MCHBAR_REG(0x6120));
reg32 &= ~(1 << 0);
writel(reg32, MCHBAR_REG(0x6120));
reg32 = readl(MCHBAR_REG(0x5418));
reg32 |= (1 << 4) | (1 << 5);
writel(reg32, MCHBAR_REG(0x5418));
}
int gma_func0_init(struct udevice *dev)
{
#ifdef CONFIG_VIDEO
ulong start;
#endif
struct udevice *nbridge;
void *gtt_bar;
ulong base;
u32 reg32;
int ret;
int rev;
/* Enable PCH Display Port */
writew(0x0010, RCB_REG(DISPBDF));
setbits_le32(RCB_REG(FD2), PCH_ENABLE_DBDF);
ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &nbridge);
if (ret)
return ret;
rev = bridge_silicon_revision(nbridge);
sandybridge_setup_graphics(nbridge, dev);
/* IGD needs to be Bus Master */
dm_pci_read_config32(dev, PCI_COMMAND, &reg32);
reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
dm_pci_write_config32(dev, PCI_COMMAND, reg32);
/* Use write-combining for the graphics memory, 256MB */
base = dm_pci_read_bar32(dev, 2);
mtrr_add_request(MTRR_TYPE_WRCOMB, base, 256 << 20);
mtrr_commit(true);
gtt_bar = (void *)dm_pci_read_bar32(dev, 0);
debug("GT bar %p\n", gtt_bar);
ret = gma_pm_init_pre_vbios(gtt_bar, rev);
if (ret)
return ret;
#ifdef CONFIG_VIDEO
start = get_timer(0);
ret = dm_pci_run_vga_bios(dev, int15_handler,
PCI_ROM_USE_NATIVE | PCI_ROM_ALLOW_FALLBACK);
debug("BIOS ran in %lums\n", get_timer(start));
#endif
/* Post VBIOS init */
ret = gma_pm_init_post_vbios(dev, rev, gtt_bar);
if (ret)
return ret;
return 0;
}

View File

@@ -0,0 +1,156 @@
/*
* From Coreboot file of the same name
*
* Copyright (C) 2012 Chromium OS Authors
*
* SPDX-License-Identifier: GPL-2.0
*/
/* mailbox 0: header */
__packed struct opregion_header {
u8 signature[16];
u32 size;
u32 version;
u8 sbios_version[32];
u8 vbios_version[16];
u8 driver_version[16];
u32 mailboxes;
u8 reserved[164];
};
#define IGD_OPREGION_SIGNATURE "IntelGraphicsMem"
#define IGD_OPREGION_VERSION 2
#define IGD_MBOX1 (1 << 0)
#define IGD_MBOX2 (1 << 1)
#define IGD_MBOX3 (1 << 2)
#define IGD_MBOX4 (1 << 3)
#define IGD_MBOX5 (1 << 4)
#define MAILBOXES_MOBILE (IGD_MBOX1 | IGD_MBOX2 | IGD_MBOX3 | \
IGD_MBOX4 | IGD_MBOX5)
#define MAILBOXES_DESKTOP (IGD_MBOX2 | IGD_MBOX4)
#define SBIOS_VERSION_SIZE 32
/* mailbox 1: public acpi methods */
__packed struct opregion_mailbox1 {
u32 drdy;
u32 csts;
u32 cevt;
u8 reserved1[20];
u32 didl[8];
u32 cpdl[8];
u32 cadl[8];
u32 nadl[8];
u32 aslp;
u32 tidx;
u32 chpd;
u32 clid;
u32 cdck;
u32 sxsw;
u32 evts;
u32 cnot;
u32 nrdy;
u8 reserved2[60];
};
/* mailbox 2: software sci interface */
__packed struct opregion_mailbox2 {
u32 scic;
u32 parm;
u32 dslp;
u8 reserved[244];
};
/* mailbox 3: power conservation */
__packed struct opregion_mailbox3 {
u32 ardy;
u32 aslc;
u32 tche;
u32 alsi;
u32 bclp;
u32 pfit;
u32 cblv;
u16 bclm[20];
u32 cpfm;
u32 epfm;
u8 plut[74];
u32 pfmb;
u32 ccdv;
u32 pcft;
u8 reserved[94];
};
#define IGD_BACKLIGHT_BRIGHTNESS 0xff
#define IGD_INITIAL_BRIGHTNESS 0x64
#define IGD_FIELD_VALID (1 << 31)
#define IGD_WORD_FIELD_VALID (1 << 15)
#define IGD_PFIT_STRETCH 6
/* mailbox 4: vbt */
__packed struct {
u8 gvd1[7168];
} opregion_vbt_t;
/* IGD OpRegion */
__packed struct igd_opregion {
opregion_header_t header;
opregion_mailbox1_t mailbox1;
opregion_mailbox2_t mailbox2;
opregion_mailbox3_t mailbox3;
opregion_vbt_t vbt;
};
/* Intel Video BIOS (Option ROM) */
__packed struct optionrom_header {
u16 signature;
u8 size;
u8 reserved[21];
u16 pcir_offset;
u16 vbt_offset;
};
#define OPROM_SIGNATURE 0xaa55
__packed struct optionrom_pcir {
u32 signature;
u16 vendor;
u16 device;
u16 reserved1;
u16 length;
u8 revision;
u8 classcode[3];
u16 imagelength;
u16 coderevision;
u8 codetype;
u8 indicator;
u16 reserved2;
};
__packed struct optionrom_vbt {
u8 hdr_signature[20];
u16 hdr_version;
u16 hdr_size;
u16 hdr_vbt_size;
u8 hdr_vbt_checksum;
u8 hdr_reserved;
u32 hdr_vbt_datablock;
u32 hdr_aim[4];
u8 datahdr_signature[16];
u16 datahdr_version;
u16 datahdr_size;
u16 datahdr_datablocksize;
u8 coreblock_id;
u16 coreblock_size;
u16 coreblock_biossize;
u8 coreblock_biostype;
u8 coreblock_releasestatus;
u8 coreblock_hwsupported;
u8 coreblock_integratedhw;
u8 coreblock_biosbuild[4];
u8 coreblock_biossignon[155];
};
#define VBT_SIGNATURE 0x54425624

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/post.h>
#include <asm/processor.h>
int arch_cpu_init(void)
{
int ret;
post_code(POST_CPU_INIT);
ret = x86_cpu_init_f();
if (ret)
return ret;
return 0;
}

View File

@@ -0,0 +1,534 @@
/*
* From coreboot southbridge/intel/bd82x6x/lpc.c
*
* Copyright (C) 2008-2009 coresystems GmbH
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <rtc.h>
#include <pci.h>
#include <asm/intel_regs.h>
#include <asm/interrupt.h>
#include <asm/io.h>
#include <asm/ioapic.h>
#include <asm/lpc_common.h>
#include <asm/pci.h>
#include <asm/arch/pch.h>
#define NMI_OFF 0
#define ENABLE_ACPI_MODE_IN_COREBOOT 0
#define TEST_SMM_FLASH_LOCKDOWN 0
static int pch_enable_apic(struct udevice *pch)
{
u32 reg32;
int i;
/* Enable ACPI I/O and power management. Set SCI IRQ to IRQ9 */
dm_pci_write_config8(pch, ACPI_CNTL, 0x80);
writel(0, IO_APIC_INDEX);
writel(1 << 25, IO_APIC_DATA);
/* affirm full set of redirection table entries ("write once") */
writel(1, IO_APIC_INDEX);
reg32 = readl(IO_APIC_DATA);
writel(1, IO_APIC_INDEX);
writel(reg32, IO_APIC_DATA);
writel(0, IO_APIC_INDEX);
reg32 = readl(IO_APIC_DATA);
debug("PCH APIC ID = %x\n", (reg32 >> 24) & 0x0f);
if (reg32 != (1 << 25)) {
printf("APIC Error - cannot write to registers\n");
return -EPERM;
}
debug("Dumping IOAPIC registers\n");
for (i = 0; i < 3; i++) {
writel(i, IO_APIC_INDEX);
debug(" reg 0x%04x:", i);
reg32 = readl(IO_APIC_DATA);
debug(" 0x%08x\n", reg32);
}
/* Select Boot Configuration register. */
writel(3, IO_APIC_INDEX);
/* Use Processor System Bus to deliver interrupts. */
writel(1, IO_APIC_DATA);
return 0;
}
static void pch_enable_serial_irqs(struct udevice *pch)
{
u32 value;
/* Set packet length and toggle silent mode bit for one frame. */
value = (1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0);
#ifdef CONFIG_SERIRQ_CONTINUOUS_MODE
dm_pci_write_config8(pch, SERIRQ_CNTL, value);
#else
dm_pci_write_config8(pch, SERIRQ_CNTL, value | (1 << 6));
#endif
}
static int pch_pirq_init(struct udevice *pch)
{
uint8_t route[8], *ptr;
if (fdtdec_get_byte_array(gd->fdt_blob, pch->of_offset,
"intel,pirq-routing", route, sizeof(route)))
return -EINVAL;
ptr = route;
dm_pci_write_config8(pch, PIRQA_ROUT, *ptr++);
dm_pci_write_config8(pch, PIRQB_ROUT, *ptr++);
dm_pci_write_config8(pch, PIRQC_ROUT, *ptr++);
dm_pci_write_config8(pch, PIRQD_ROUT, *ptr++);
dm_pci_write_config8(pch, PIRQE_ROUT, *ptr++);
dm_pci_write_config8(pch, PIRQF_ROUT, *ptr++);
dm_pci_write_config8(pch, PIRQG_ROUT, *ptr++);
dm_pci_write_config8(pch, PIRQH_ROUT, *ptr++);
/*
* TODO(sjg@chromium.org): U-Boot does not set up the interrupts
* here. It's unclear if it is needed
*/
return 0;
}
static int pch_gpi_routing(struct udevice *pch)
{
u8 route[16];
u32 reg;
int gpi;
if (fdtdec_get_byte_array(gd->fdt_blob, pch->of_offset,
"intel,gpi-routing", route, sizeof(route)))
return -EINVAL;
for (reg = 0, gpi = 0; gpi < ARRAY_SIZE(route); gpi++)
reg |= route[gpi] << (gpi * 2);
dm_pci_write_config32(pch, 0xb8, reg);
return 0;
}
static int pch_power_options(struct udevice *pch)
{
const void *blob = gd->fdt_blob;
int node = pch->of_offset;
u8 reg8;
u16 reg16, pmbase;
u32 reg32;
const char *state;
int pwr_on;
int nmi_option;
int ret;
/*
* Which state do we want to goto after g3 (power restored)?
* 0 == S0 Full On
* 1 == S5 Soft Off
*
* If the option is not existent (Laptops), use Kconfig setting.
* TODO(sjg@chromium.org): Make this configurable
*/
pwr_on = MAINBOARD_POWER_ON;
dm_pci_read_config16(pch, GEN_PMCON_3, &reg16);
reg16 &= 0xfffe;
switch (pwr_on) {
case MAINBOARD_POWER_OFF:
reg16 |= 1;
state = "off";
break;
case MAINBOARD_POWER_ON:
reg16 &= ~1;
state = "on";
break;
case MAINBOARD_POWER_KEEP:
reg16 &= ~1;
state = "state keep";
break;
default:
state = "undefined";
}
reg16 &= ~(3 << 4); /* SLP_S4# Assertion Stretch 4s */
reg16 |= (1 << 3); /* SLP_S4# Assertion Stretch Enable */
reg16 &= ~(1 << 10);
reg16 |= (1 << 11); /* SLP_S3# Min Assertion Width 50ms */
reg16 |= (1 << 12); /* Disable SLP stretch after SUS well */
dm_pci_write_config16(pch, GEN_PMCON_3, reg16);
debug("Set power %s after power failure.\n", state);
/* Set up NMI on errors. */
reg8 = inb(0x61);
reg8 &= 0x0f; /* Higher Nibble must be 0 */
reg8 &= ~(1 << 3); /* IOCHK# NMI Enable */
reg8 |= (1 << 2); /* PCI SERR# Disable for now */
outb(reg8, 0x61);
reg8 = inb(0x70);
/* TODO(sjg@chromium.org): Make this configurable */
nmi_option = NMI_OFF;
if (nmi_option) {
debug("NMI sources enabled.\n");
reg8 &= ~(1 << 7); /* Set NMI. */
} else {
debug("NMI sources disabled.\n");
/* Can't mask NMI from PCI-E and NMI_NOW */
reg8 |= (1 << 7);
}
outb(reg8, 0x70);
/* Enable CPU_SLP# and Intel Speedstep, set SMI# rate down */
dm_pci_read_config16(pch, GEN_PMCON_1, &reg16);
reg16 &= ~(3 << 0); /* SMI# rate 1 minute */
reg16 &= ~(1 << 10); /* Disable BIOS_PCI_EXP_EN for native PME */
#if DEBUG_PERIODIC_SMIS
/* Set DEBUG_PERIODIC_SMIS in pch.h to debug using periodic SMIs */
reg16 |= (3 << 0); /* Periodic SMI every 8s */
#endif
dm_pci_write_config16(pch, GEN_PMCON_1, reg16);
/* Set the board's GPI routing. */
ret = pch_gpi_routing(pch);
if (ret)
return ret;
dm_pci_read_config16(pch, 0x40, &pmbase);
pmbase &= 0xfffe;
writel(pmbase + GPE0_EN, fdtdec_get_int(blob, node,
"intel,gpe0-enable", 0));
writew(pmbase + ALT_GP_SMI_EN, fdtdec_get_int(blob, node,
"intel,alt-gp-smi-enable", 0));
/* Set up power management block and determine sleep mode */
reg32 = inl(pmbase + 0x04); /* PM1_CNT */
reg32 &= ~(7 << 10); /* SLP_TYP */
reg32 |= (1 << 0); /* SCI_EN */
outl(reg32, pmbase + 0x04);
/* Clear magic status bits to prevent unexpected wake */
setbits_le32(RCB_REG(0x3310), (1 << 4) | (1 << 5) | (1 << 0));
clrbits_le32(RCB_REG(0x3f02), 0xf);
return 0;
}
static void pch_rtc_init(struct udevice *pch)
{
int rtc_failed;
u8 reg8;
dm_pci_read_config8(pch, GEN_PMCON_3, &reg8);
rtc_failed = reg8 & RTC_BATTERY_DEAD;
if (rtc_failed) {
reg8 &= ~RTC_BATTERY_DEAD;
dm_pci_write_config8(pch, GEN_PMCON_3, reg8);
}
debug("rtc_failed = 0x%x\n", rtc_failed);
/* TODO: Handle power failure */
if (rtc_failed)
printf("RTC power failed\n");
}
/* CougarPoint PCH Power Management init */
static void cpt_pm_init(struct udevice *pch)
{
debug("CougarPoint PM init\n");
dm_pci_write_config8(pch, 0xa9, 0x47);
setbits_le32(RCB_REG(0x2238), (1 << 6) | (1 << 0));
setbits_le32(RCB_REG(0x228c), 1 << 0);
setbits_le32(RCB_REG(0x1100), (1 << 13) | (1 << 14));
setbits_le32(RCB_REG(0x0900), 1 << 14);
writel(0xc0388400, RCB_REG(0x2304));
setbits_le32(RCB_REG(0x2314), (1 << 5) | (1 << 18));
setbits_le32(RCB_REG(0x2320), (1 << 15) | (1 << 1));
clrsetbits_le32(RCB_REG(0x3314), ~0x1f, 0xf);
writel(0x050f0000, RCB_REG(0x3318));
writel(0x04000000, RCB_REG(0x3324));
setbits_le32(RCB_REG(0x3340), 0xfffff);
setbits_le32(RCB_REG(0x3344), 1 << 1);
writel(0x0001c000, RCB_REG(0x3360));
writel(0x00061100, RCB_REG(0x3368));
writel(0x7f8fdfff, RCB_REG(0x3378));
writel(0x000003fc, RCB_REG(0x337c));
writel(0x00001000, RCB_REG(0x3388));
writel(0x0001c000, RCB_REG(0x3390));
writel(0x00000800, RCB_REG(0x33a0));
writel(0x00001000, RCB_REG(0x33b0));
writel(0x00093900, RCB_REG(0x33c0));
writel(0x24653002, RCB_REG(0x33cc));
writel(0x062108fe, RCB_REG(0x33d0));
clrsetbits_le32(RCB_REG(0x33d4), 0x0fff0fff, 0x00670060);
writel(0x01010000, RCB_REG(0x3a28));
writel(0x01010404, RCB_REG(0x3a2c));
writel(0x01041041, RCB_REG(0x3a80));
clrsetbits_le32(RCB_REG(0x3a84), 0x0000ffff, 0x00001001);
setbits_le32(RCB_REG(0x3a84), 1 << 24); /* SATA 2/3 disabled */
setbits_le32(RCB_REG(0x3a88), 1 << 0); /* SATA 4/5 disabled */
writel(0x00000001, RCB_REG(0x3a6c));
clrsetbits_le32(RCB_REG(0x2344), ~0x00ffff00, 0xff00000c);
clrsetbits_le32(RCB_REG(0x80c), 0xff << 20, 0x11 << 20);
writel(0, RCB_REG(0x33c8));
setbits_le32(RCB_REG(0x21b0), 0xf);
}
/* PantherPoint PCH Power Management init */
static void ppt_pm_init(struct udevice *pch)
{
debug("PantherPoint PM init\n");
dm_pci_write_config8(pch, 0xa9, 0x47);
setbits_le32(RCB_REG(0x2238), 1 << 0);
setbits_le32(RCB_REG(0x228c), 1 << 0);
setbits_le16(RCB_REG(0x1100), (1 << 13) | (1 << 14));
setbits_le16(RCB_REG(0x0900), 1 << 14);
writel(0xc03b8400, RCB_REG(0x2304));
setbits_le32(RCB_REG(0x2314), (1 << 5) | (1 << 18));
setbits_le32(RCB_REG(0x2320), (1 << 15) | (1 << 1));
clrsetbits_le32(RCB_REG(0x3314), 0x1f, 0xf);
writel(0x054f0000, RCB_REG(0x3318));
writel(0x04000000, RCB_REG(0x3324));
setbits_le32(RCB_REG(0x3340), 0xfffff);
setbits_le32(RCB_REG(0x3344), (1 << 1) | (1 << 0));
writel(0x0001c000, RCB_REG(0x3360));
writel(0x00061100, RCB_REG(0x3368));
writel(0x7f8fdfff, RCB_REG(0x3378));
writel(0x000003fd, RCB_REG(0x337c));
writel(0x00001000, RCB_REG(0x3388));
writel(0x0001c000, RCB_REG(0x3390));
writel(0x00000800, RCB_REG(0x33a0));
writel(0x00001000, RCB_REG(0x33b0));
writel(0x00093900, RCB_REG(0x33c0));
writel(0x24653002, RCB_REG(0x33cc));
writel(0x067388fe, RCB_REG(0x33d0));
clrsetbits_le32(RCB_REG(0x33d4), 0x0fff0fff, 0x00670060);
writel(0x01010000, RCB_REG(0x3a28));
writel(0x01010404, RCB_REG(0x3a2c));
writel(0x01040000, RCB_REG(0x3a80));
clrsetbits_le32(RCB_REG(0x3a84), 0x0000ffff, 0x00001001);
/* SATA 2/3 disabled */
setbits_le32(RCB_REG(0x3a84), 1 << 24);
/* SATA 4/5 disabled */
setbits_le32(RCB_REG(0x3a88), 1 << 0);
writel(0x00000001, RCB_REG(0x3a6c));
clrsetbits_le32(RCB_REG(0x2344), 0xff0000ff, 0xff00000c);
clrsetbits_le32(RCB_REG(0x80c), 0xff << 20, 0x11 << 20);
setbits_le32(RCB_REG(0x33a4), (1 << 0));
writel(0, RCB_REG(0x33c8));
setbits_le32(RCB_REG(0x21b0), 0xf);
}
static void enable_hpet(void)
{
/* Move HPET to default address 0xfed00000 and enable it */
clrsetbits_le32(RCB_REG(HPTC), 3 << 0, 1 << 7);
}
static void enable_clock_gating(struct udevice *pch)
{
u32 reg32;
u16 reg16;
setbits_le32(RCB_REG(0x2234), 0xf);
dm_pci_read_config16(pch, GEN_PMCON_1, &reg16);
reg16 |= (1 << 2) | (1 << 11);
dm_pci_write_config16(pch, GEN_PMCON_1, reg16);
pch_iobp_update(pch, 0xEB007F07, ~0UL, (1 << 31));
pch_iobp_update(pch, 0xEB004000, ~0UL, (1 << 7));
pch_iobp_update(pch, 0xEC007F07, ~0UL, (1 << 31));
pch_iobp_update(pch, 0xEC004000, ~0UL, (1 << 7));
reg32 = readl(RCB_REG(CG));
reg32 |= (1 << 31);
reg32 |= (1 << 29) | (1 << 28);
reg32 |= (1 << 27) | (1 << 26) | (1 << 25) | (1 << 24);
reg32 |= (1 << 16);
reg32 |= (1 << 17);
reg32 |= (1 << 18);
reg32 |= (1 << 22);
reg32 |= (1 << 23);
reg32 &= ~(1 << 20);
reg32 |= (1 << 19);
reg32 |= (1 << 0);
reg32 |= (0xf << 1);
writel(reg32, RCB_REG(CG));
setbits_le32(RCB_REG(0x38c0), 0x7);
setbits_le32(RCB_REG(0x36d4), 0x6680c004);
setbits_le32(RCB_REG(0x3564), 0x3);
}
static void pch_disable_smm_only_flashing(struct udevice *pch)
{
u8 reg8;
debug("Enabling BIOS updates outside of SMM... ");
dm_pci_read_config8(pch, 0xdc, &reg8); /* BIOS_CNTL */
reg8 &= ~(1 << 5);
dm_pci_write_config8(pch, 0xdc, reg8);
}
static void pch_fixups(struct udevice *pch)
{
u8 gen_pmcon_2;
/* Indicate DRAM init done for MRC S3 to know it can resume */
dm_pci_read_config8(pch, GEN_PMCON_2, &gen_pmcon_2);
gen_pmcon_2 |= (1 << 7);
dm_pci_write_config8(pch, GEN_PMCON_2, gen_pmcon_2);
/* Enable DMI ASPM in the PCH */
clrbits_le32(RCB_REG(0x2304), 1 << 10);
setbits_le32(RCB_REG(0x21a4), (1 << 11) | (1 << 10));
setbits_le32(RCB_REG(0x21a8), 0x3);
}
static void set_spi_speed(void)
{
u32 fdod;
/* Observe SPI Descriptor Component Section 0 */
writel(0x1000, RCB_REG(SPI_DESC_COMP0));
/* Extract the1 Write/Erase SPI Frequency from descriptor */
fdod = readl(RCB_REG(SPI_FREQ_WR_ERA));
fdod >>= 24;
fdod &= 7;
/* Set Software Sequence frequency to match */
clrsetbits_8(RCB_REG(SPI_FREQ_SWSEQ), 7, fdod);
}
static int lpc_init_extra(struct udevice *dev)
{
struct udevice *pch = dev->parent;
const void *blob = gd->fdt_blob;
int node;
debug("pch: lpc_init\n");
dm_pci_write_bar32(pch, 0, 0);
dm_pci_write_bar32(pch, 1, 0xff800000);
dm_pci_write_bar32(pch, 2, 0xfec00000);
dm_pci_write_bar32(pch, 3, 0x800);
dm_pci_write_bar32(pch, 4, 0x900);
node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_PCH);
if (node < 0)
return -ENOENT;
/* Set the value for PCI command register. */
dm_pci_write_config16(pch, PCI_COMMAND, 0x000f);
/* IO APIC initialization. */
pch_enable_apic(pch);
pch_enable_serial_irqs(pch);
/* Setup the PIRQ. */
pch_pirq_init(pch);
/* Setup power options. */
pch_power_options(pch);
/* Initialize power management */
switch (pch_silicon_type(pch)) {
case PCH_TYPE_CPT: /* CougarPoint */
cpt_pm_init(pch);
break;
case PCH_TYPE_PPT: /* PantherPoint */
ppt_pm_init(pch);
break;
default:
printf("Unknown Chipset: %s\n", pch->name);
return -ENOSYS;
}
/* Initialize the real time clock. */
pch_rtc_init(pch);
/* Initialize the High Precision Event Timers, if present. */
enable_hpet();
/* Initialize Clock Gating */
enable_clock_gating(pch);
pch_disable_smm_only_flashing(pch);
pch_fixups(pch);
return 0;
}
static int bd82x6x_lpc_early_init(struct udevice *dev)
{
set_spi_speed();
/* Setting up Southbridge. In the northbridge code. */
debug("Setting up static southbridge registers\n");
dm_pci_write_config32(dev->parent, PCH_RCBA_BASE,
RCB_BASE_ADDRESS | 1);
dm_pci_write_config32(dev->parent, PMBASE, DEFAULT_PMBASE | 1);
/* Enable ACPI BAR */
dm_pci_write_config8(dev->parent, ACPI_CNTL, 0x80);
debug("Disabling watchdog reboot\n");
setbits_le32(RCB_REG(GCS), 1 >> 5); /* No reset */
outw(1 << 11, DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */
dm_pci_write_config32(dev->parent, GPIO_BASE, DEFAULT_GPIOBASE | 1);
dm_pci_write_config32(dev->parent, GPIO_CNTL, 0x10);
return 0;
}
static int bd82x6x_lpc_probe(struct udevice *dev)
{
int ret;
if (!(gd->flags & GD_FLG_RELOC)) {
ret = lpc_common_early_init(dev);
if (ret) {
debug("%s: lpc_early_init() failed\n", __func__);
return ret;
}
return bd82x6x_lpc_early_init(dev);
}
return lpc_init_extra(dev);
}
static const struct udevice_id bd82x6x_lpc_ids[] = {
{ .compatible = "intel,bd82x6x-lpc" },
{ }
};
U_BOOT_DRIVER(bd82x6x_lpc_drv) = {
.name = "lpc",
.id = UCLASS_LPC,
.of_match = bd82x6x_lpc_ids,
.probe = bd82x6x_lpc_probe,
};

View File

@@ -0,0 +1,495 @@
/*
* From Coreboot file of same name
*
* Copyright (C) 2007-2009 coresystems GmbH
* Copyright (C) 2011 The Chromium Authors
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <fdtdec.h>
#include <malloc.h>
#include <asm/cpu.h>
#include <asm/cpu_x86.h>
#include <asm/msr.h>
#include <asm/msr-index.h>
#include <asm/mtrr.h>
#include <asm/processor.h>
#include <asm/speedstep.h>
#include <asm/turbo.h>
#include <asm/arch/bd82x6x.h>
#include <asm/arch/model_206ax.h>
static void enable_vmx(void)
{
struct cpuid_result regs;
#ifdef CONFIG_ENABLE_VMX
int enable = true;
#else
int enable = false;
#endif
msr_t msr;
regs = cpuid(1);
/* Check that the VMX is supported before reading or writing the MSR. */
if (!((regs.ecx & CPUID_VMX) || (regs.ecx & CPUID_SMX)))
return;
msr = msr_read(MSR_IA32_FEATURE_CONTROL);
if (msr.lo & (1 << 0)) {
debug("VMX is locked, so %s will do nothing\n", __func__);
/* VMX locked. If we set it again we get an illegal
* instruction
*/
return;
}
/* The IA32_FEATURE_CONTROL MSR may initialize with random values.
* It must be cleared regardless of VMX config setting.
*/
msr.hi = 0;
msr.lo = 0;
debug("%s VMX\n", enable ? "Enabling" : "Disabling");
/*
* Even though the Intel manual says you must set the lock bit in
* addition to the VMX bit in order for VMX to work, it is incorrect.
* Thus we leave it unlocked for the OS to manage things itself.
* This is good for a few reasons:
* - No need to reflash the bios just to toggle the lock bit.
* - The VMX bits really really should match each other across cores,
* so hard locking it on one while another has the opposite setting
* can easily lead to crashes as code using VMX migrates between
* them.
* - Vendors that want to "upsell" from a bios that disables+locks to
* one that doesn't is sleazy.
* By leaving this to the OS (e.g. Linux), people can do exactly what
* they want on the fly, and do it correctly (e.g. across multiple
* cores).
*/
if (enable) {
msr.lo |= (1 << 2);
if (regs.ecx & CPUID_SMX)
msr.lo |= (1 << 1);
}
msr_write(MSR_IA32_FEATURE_CONTROL, msr);
}
/* Convert time in seconds to POWER_LIMIT_1_TIME MSR value */
static const u8 power_limit_time_sec_to_msr[] = {
[0] = 0x00,
[1] = 0x0a,
[2] = 0x0b,
[3] = 0x4b,
[4] = 0x0c,
[5] = 0x2c,
[6] = 0x4c,
[7] = 0x6c,
[8] = 0x0d,
[10] = 0x2d,
[12] = 0x4d,
[14] = 0x6d,
[16] = 0x0e,
[20] = 0x2e,
[24] = 0x4e,
[28] = 0x6e,
[32] = 0x0f,
[40] = 0x2f,
[48] = 0x4f,
[56] = 0x6f,
[64] = 0x10,
[80] = 0x30,
[96] = 0x50,
[112] = 0x70,
[128] = 0x11,
};
/* Convert POWER_LIMIT_1_TIME MSR value to seconds */
static const u8 power_limit_time_msr_to_sec[] = {
[0x00] = 0,
[0x0a] = 1,
[0x0b] = 2,
[0x4b] = 3,
[0x0c] = 4,
[0x2c] = 5,
[0x4c] = 6,
[0x6c] = 7,
[0x0d] = 8,
[0x2d] = 10,
[0x4d] = 12,
[0x6d] = 14,
[0x0e] = 16,
[0x2e] = 20,
[0x4e] = 24,
[0x6e] = 28,
[0x0f] = 32,
[0x2f] = 40,
[0x4f] = 48,
[0x6f] = 56,
[0x10] = 64,
[0x30] = 80,
[0x50] = 96,
[0x70] = 112,
[0x11] = 128,
};
int cpu_config_tdp_levels(void)
{
struct cpuid_result result;
msr_t platform_info;
/* Minimum CPU revision */
result = cpuid(1);
if (result.eax < IVB_CONFIG_TDP_MIN_CPUID)
return 0;
/* Bits 34:33 indicate how many levels supported */
platform_info = msr_read(MSR_PLATFORM_INFO);
return (platform_info.hi >> 1) & 3;
}
/*
* Configure processor power limits if possible
* This must be done AFTER set of BIOS_RESET_CPL
*/
void set_power_limits(u8 power_limit_1_time)
{
msr_t msr = msr_read(MSR_PLATFORM_INFO);
msr_t limit;
unsigned power_unit;
unsigned tdp, min_power, max_power, max_time;
u8 power_limit_1_val;
if (power_limit_1_time > ARRAY_SIZE(power_limit_time_sec_to_msr))
return;
if (!(msr.lo & PLATFORM_INFO_SET_TDP))
return;
/* Get units */
msr = msr_read(MSR_PKG_POWER_SKU_UNIT);
power_unit = 2 << ((msr.lo & 0xf) - 1);
/* Get power defaults for this SKU */
msr = msr_read(MSR_PKG_POWER_SKU);
tdp = msr.lo & 0x7fff;
min_power = (msr.lo >> 16) & 0x7fff;
max_power = msr.hi & 0x7fff;
max_time = (msr.hi >> 16) & 0x7f;
debug("CPU TDP: %u Watts\n", tdp / power_unit);
if (power_limit_time_msr_to_sec[max_time] > power_limit_1_time)
power_limit_1_time = power_limit_time_msr_to_sec[max_time];
if (min_power > 0 && tdp < min_power)
tdp = min_power;
if (max_power > 0 && tdp > max_power)
tdp = max_power;
power_limit_1_val = power_limit_time_sec_to_msr[power_limit_1_time];
/* Set long term power limit to TDP */
limit.lo = 0;
limit.lo |= tdp & PKG_POWER_LIMIT_MASK;
limit.lo |= PKG_POWER_LIMIT_EN;
limit.lo |= (power_limit_1_val & PKG_POWER_LIMIT_TIME_MASK) <<
PKG_POWER_LIMIT_TIME_SHIFT;
/* Set short term power limit to 1.25 * TDP */
limit.hi = 0;
limit.hi |= ((tdp * 125) / 100) & PKG_POWER_LIMIT_MASK;
limit.hi |= PKG_POWER_LIMIT_EN;
/* Power limit 2 time is only programmable on SNB EP/EX */
msr_write(MSR_PKG_POWER_LIMIT, limit);
/* Use nominal TDP values for CPUs with configurable TDP */
if (cpu_config_tdp_levels()) {
msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
limit.hi = 0;
limit.lo = msr.lo & 0xff;
msr_write(MSR_TURBO_ACTIVATION_RATIO, limit);
}
}
static void configure_c_states(void)
{
struct cpuid_result result;
msr_t msr;
msr = msr_read(MSR_PMG_CST_CONFIG_CTL);
msr.lo |= (1 << 28); /* C1 Auto Undemotion Enable */
msr.lo |= (1 << 27); /* C3 Auto Undemotion Enable */
msr.lo |= (1 << 26); /* C1 Auto Demotion Enable */
msr.lo |= (1 << 25); /* C3 Auto Demotion Enable */
msr.lo &= ~(1 << 10); /* Disable IO MWAIT redirection */
msr.lo |= 7; /* No package C-state limit */
msr_write(MSR_PMG_CST_CONFIG_CTL, msr);
msr = msr_read(MSR_PMG_IO_CAPTURE_ADR);
msr.lo &= ~0x7ffff;
msr.lo |= (PMB0_BASE + 4); /* LVL_2 base address */
msr.lo |= (2 << 16); /* CST Range: C7 is max C-state */
msr_write(MSR_PMG_IO_CAPTURE_ADR, msr);
msr = msr_read(MSR_MISC_PWR_MGMT);
msr.lo &= ~(1 << 0); /* Enable P-state HW_ALL coordination */
msr_write(MSR_MISC_PWR_MGMT, msr);
msr = msr_read(MSR_POWER_CTL);
msr.lo |= (1 << 18); /* Enable Energy Perf Bias MSR 0x1b0 */
msr.lo |= (1 << 1); /* C1E Enable */
msr.lo |= (1 << 0); /* Bi-directional PROCHOT# */
msr_write(MSR_POWER_CTL, msr);
/* C3 Interrupt Response Time Limit */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | 0x50;
msr_write(MSR_PKGC3_IRTL, msr);
/* C6 Interrupt Response Time Limit */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | 0x68;
msr_write(MSR_PKGC6_IRTL, msr);
/* C7 Interrupt Response Time Limit */
msr.hi = 0;
msr.lo = IRTL_VALID | IRTL_1024_NS | 0x6D;
msr_write(MSR_PKGC7_IRTL, msr);
/* Primary Plane Current Limit */
msr = msr_read(MSR_PP0_CURRENT_CONFIG);
msr.lo &= ~0x1fff;
msr.lo |= PP0_CURRENT_LIMIT;
msr_write(MSR_PP0_CURRENT_CONFIG, msr);
/* Secondary Plane Current Limit */
msr = msr_read(MSR_PP1_CURRENT_CONFIG);
msr.lo &= ~0x1fff;
result = cpuid(1);
if (result.eax >= 0x30600)
msr.lo |= PP1_CURRENT_LIMIT_IVB;
else
msr.lo |= PP1_CURRENT_LIMIT_SNB;
msr_write(MSR_PP1_CURRENT_CONFIG, msr);
}
static int configure_thermal_target(struct udevice *dev)
{
int tcc_offset;
msr_t msr;
tcc_offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "tcc-offset",
0);
/* Set TCC activaiton offset if supported */
msr = msr_read(MSR_PLATFORM_INFO);
if ((msr.lo & (1 << 30)) && tcc_offset) {
msr = msr_read(MSR_TEMPERATURE_TARGET);
msr.lo &= ~(0xf << 24); /* Bits 27:24 */
msr.lo |= (tcc_offset & 0xf) << 24;
msr_write(MSR_TEMPERATURE_TARGET, msr);
}
return 0;
}
static void configure_misc(void)
{
msr_t msr;
msr = msr_read(IA32_MISC_ENABLE);
msr.lo |= (1 << 0); /* Fast String enable */
msr.lo |= (1 << 3); /* TM1/TM2/EMTTM enable */
msr.lo |= (1 << 16); /* Enhanced SpeedStep Enable */
msr_write(IA32_MISC_ENABLE, msr);
/* Disable Thermal interrupts */
msr.lo = 0;
msr.hi = 0;
msr_write(IA32_THERM_INTERRUPT, msr);
/* Enable package critical interrupt only */
msr.lo = 1 << 4;
msr.hi = 0;
msr_write(IA32_PACKAGE_THERM_INTERRUPT, msr);
}
static void enable_lapic_tpr(void)
{
msr_t msr;
msr = msr_read(MSR_PIC_MSG_CONTROL);
msr.lo &= ~(1 << 10); /* Enable APIC TPR updates */
msr_write(MSR_PIC_MSG_CONTROL, msr);
}
static void configure_dca_cap(void)
{
struct cpuid_result cpuid_regs;
msr_t msr;
/* Check feature flag in CPUID.(EAX=1):ECX[18]==1 */
cpuid_regs = cpuid(1);
if (cpuid_regs.ecx & (1 << 18)) {
msr = msr_read(IA32_PLATFORM_DCA_CAP);
msr.lo |= 1;
msr_write(IA32_PLATFORM_DCA_CAP, msr);
}
}
static void set_max_ratio(void)
{
msr_t msr, perf_ctl;
perf_ctl.hi = 0;
/* Check for configurable TDP option */
if (cpu_config_tdp_levels()) {
/* Set to nominal TDP ratio */
msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
perf_ctl.lo = (msr.lo & 0xff) << 8;
} else {
/* Platform Info bits 15:8 give max ratio */
msr = msr_read(MSR_PLATFORM_INFO);
perf_ctl.lo = msr.lo & 0xff00;
}
msr_write(MSR_IA32_PERF_CTL, perf_ctl);
debug("model_x06ax: frequency set to %d\n",
((perf_ctl.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK);
}
static void set_energy_perf_bias(u8 policy)
{
msr_t msr;
/* Energy Policy is bits 3:0 */
msr = msr_read(IA32_ENERGY_PERFORMANCE_BIAS);
msr.lo &= ~0xf;
msr.lo |= policy & 0xf;
msr_write(IA32_ENERGY_PERFORMANCE_BIAS, msr);
debug("model_x06ax: energy policy set to %u\n", policy);
}
static void configure_mca(void)
{
msr_t msr;
int i;
msr.lo = 0;
msr.hi = 0;
/* This should only be done on a cold boot */
for (i = 0; i < 7; i++)
msr_write(IA32_MC0_STATUS + (i * 4), msr);
}
#if CONFIG_USBDEBUG
static unsigned ehci_debug_addr;
#endif
static int model_206ax_init(struct udevice *dev)
{
int ret;
/* Clear out pending MCEs */
configure_mca();
#if CONFIG_USBDEBUG
/* Is this caution really needed? */
if (!ehci_debug_addr)
ehci_debug_addr = get_ehci_debug();
set_ehci_debug(0);
#endif
#if CONFIG_USBDEBUG
set_ehci_debug(ehci_debug_addr);
#endif
/* Enable the local cpu apics */
enable_lapic_tpr();
/* Enable virtualization if enabled in CMOS */
enable_vmx();
/* Configure C States */
configure_c_states();
/* Configure Enhanced SpeedStep and Thermal Sensors */
configure_misc();
/* Thermal throttle activation offset */
ret = configure_thermal_target(dev);
if (ret) {
debug("Cannot set thermal target\n");
return ret;
}
/* Enable Direct Cache Access */
configure_dca_cap();
/* Set energy policy */
set_energy_perf_bias(ENERGY_POLICY_NORMAL);
/* Set Max Ratio */
set_max_ratio();
/* Enable Turbo */
turbo_enable();
return 0;
}
static int model_206ax_get_info(struct udevice *dev, struct cpu_info *info)
{
msr_t msr;
msr = msr_read(MSR_IA32_PERF_CTL);
info->cpu_freq = ((msr.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK * 1000000;
info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU |
1 << CPU_FEAT_UCODE;
return 0;
}
static int model_206ax_get_count(struct udevice *dev)
{
return 4;
}
static int cpu_x86_model_206ax_probe(struct udevice *dev)
{
if (dev->seq == 0)
model_206ax_init(dev);
return 0;
}
static const struct cpu_ops cpu_x86_model_206ax_ops = {
.get_desc = cpu_x86_get_desc,
.get_info = model_206ax_get_info,
.get_count = model_206ax_get_count,
};
static const struct udevice_id cpu_x86_model_206ax_ids[] = {
{ .compatible = "intel,core-gen3" },
{ }
};
U_BOOT_DRIVER(cpu_x86_model_206ax_drv) = {
.name = "cpu_x86_model_206ax",
.id = UCLASS_CPU,
.of_match = cpu_x86_model_206ax_ids,
.bind = cpu_x86_bind,
.probe = cpu_x86_model_206ax_probe,
.ops = &cpu_x86_model_206ax_ops,
};

View File

@@ -0,0 +1,238 @@
/*
* From Coreboot northbridge/intel/sandybridge/northbridge.c
*
* Copyright (C) 2007-2009 coresystems GmbH
* Copyright (C) 2011 The Chromium Authors
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <asm/msr.h>
#include <asm/cpu.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/pci.h>
#include <asm/processor.h>
#include <asm/arch/pch.h>
#include <asm/arch/model_206ax.h>
#include <asm/arch/sandybridge.h>
int bridge_silicon_revision(struct udevice *dev)
{
struct cpuid_result result;
u16 bridge_id;
u8 stepping;
result = cpuid(1);
stepping = result.eax & 0xf;
dm_pci_read_config16(dev, PCI_DEVICE_ID, &bridge_id);
bridge_id &= 0xf0;
return bridge_id | stepping;
}
/*
* Reserve everything between A segment and 1MB:
*
* 0xa0000 - 0xbffff: legacy VGA
* 0xc0000 - 0xcffff: VGA OPROM (needed by kernel)
* 0xe0000 - 0xfffff: SeaBIOS, if used, otherwise DMI
*/
static const int legacy_hole_base_k = 0xa0000 / 1024;
static const int legacy_hole_size_k = 384;
static int get_pcie_bar(struct udevice *dev, u32 *base, u32 *len)
{
u32 pciexbar_reg;
*base = 0;
*len = 0;
dm_pci_read_config32(dev, PCIEXBAR, &pciexbar_reg);
if (!(pciexbar_reg & (1 << 0)))
return 0;
switch ((pciexbar_reg >> 1) & 3) {
case 0: /* 256MB */
*base = pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) |
(1 << 28));
*len = 256 * 1024 * 1024;
return 1;
case 1: /* 128M */
*base = pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) |
(1 << 28) | (1 << 27));
*len = 128 * 1024 * 1024;
return 1;
case 2: /* 64M */
*base = pciexbar_reg & ((1 << 31) | (1 << 30) | (1 << 29) |
(1 << 28) | (1 << 27) | (1 << 26));
*len = 64 * 1024 * 1024;
return 1;
}
return 0;
}
static void add_fixed_resources(struct udevice *dev, int index)
{
u32 pcie_config_base, pcie_config_size;
if (get_pcie_bar(dev, &pcie_config_base, &pcie_config_size)) {
debug("Adding PCIe config bar base=0x%08x size=0x%x\n",
pcie_config_base, pcie_config_size);
}
}
static void northbridge_dmi_init(struct udevice *dev, int rev)
{
/* Clear error status bits */
writel(0xffffffff, DMIBAR_REG(0x1c4));
writel(0xffffffff, DMIBAR_REG(0x1d0));
/* Steps prior to DMI ASPM */
if ((rev & BASE_REV_MASK) == BASE_REV_SNB) {
clrsetbits_le32(DMIBAR_REG(0x250), (1 << 22) | (1 << 20),
1 << 21);
}
setbits_le32(DMIBAR_REG(0x238), 1 << 29);
if (rev >= SNB_STEP_D0) {
setbits_le32(DMIBAR_REG(0x1f8), 1 << 16);
} else if (rev >= SNB_STEP_D1) {
clrsetbits_le32(DMIBAR_REG(0x1f8), 1 << 26, 1 << 16);
setbits_le32(DMIBAR_REG(0x1fc), (1 << 12) | (1 << 23));
}
/* Enable ASPM on SNB link, should happen before PCH link */
if ((rev & BASE_REV_MASK) == BASE_REV_SNB)
setbits_le32(DMIBAR_REG(0xd04), 1 << 4);
setbits_le32(DMIBAR_REG(0x88), (1 << 1) | (1 << 0));
}
static void northbridge_init(struct udevice *dev, int rev)
{
u32 bridge_type;
add_fixed_resources(dev, 6);
northbridge_dmi_init(dev, rev);
bridge_type = readl(MCHBAR_REG(0x5f10));
bridge_type &= ~0xff;
if ((rev & BASE_REV_MASK) == BASE_REV_IVB) {
/* Enable Power Aware Interrupt Routing - fixed priority */
clrsetbits_8(MCHBAR_REG(0x5418), 0xf, 0x4);
/* 30h for IvyBridge */
bridge_type |= 0x30;
} else {
/* 20h for Sandybridge */
bridge_type |= 0x20;
}
writel(bridge_type, MCHBAR_REG(0x5f10));
/*
* Set bit 0 of BIOS_RESET_CPL to indicate to the CPU
* that BIOS has initialized memory and power management
*/
setbits_8(MCHBAR_REG(BIOS_RESET_CPL), 1);
debug("Set BIOS_RESET_CPL\n");
/* Configure turbo power limits 1ms after reset complete bit */
mdelay(1);
set_power_limits(28);
/*
* CPUs with configurable TDP also need power limits set
* in MCHBAR. Use same values from MSR_PKG_POWER_LIMIT.
*/
if (cpu_config_tdp_levels()) {
msr_t msr = msr_read(MSR_PKG_POWER_LIMIT);
writel(msr.lo, MCHBAR_REG(0x59A0));
writel(msr.hi, MCHBAR_REG(0x59A4));
}
/* Set here before graphics PM init */
writel(0x00100001, MCHBAR_REG(0x5500));
}
static void sandybridge_setup_northbridge_bars(struct udevice *dev)
{
/* Set up all hardcoded northbridge BARs */
debug("Setting up static registers\n");
dm_pci_write_config32(dev, EPBAR, DEFAULT_EPBAR | 1);
dm_pci_write_config32(dev, EPBAR + 4, (0LL + DEFAULT_EPBAR) >> 32);
dm_pci_write_config32(dev, MCHBAR, MCH_BASE_ADDRESS | 1);
dm_pci_write_config32(dev, MCHBAR + 4, (0LL + MCH_BASE_ADDRESS) >> 32);
/* 64MB - busses 0-63 */
dm_pci_write_config32(dev, PCIEXBAR, DEFAULT_PCIEXBAR | 5);
dm_pci_write_config32(dev, PCIEXBAR + 4,
(0LL + DEFAULT_PCIEXBAR) >> 32);
dm_pci_write_config32(dev, DMIBAR, DEFAULT_DMIBAR | 1);
dm_pci_write_config32(dev, DMIBAR + 4, (0LL + DEFAULT_DMIBAR) >> 32);
/* Set C0000-FFFFF to access RAM on both reads and writes */
dm_pci_write_config8(dev, PAM0, 0x30);
dm_pci_write_config8(dev, PAM1, 0x33);
dm_pci_write_config8(dev, PAM2, 0x33);
dm_pci_write_config8(dev, PAM3, 0x33);
dm_pci_write_config8(dev, PAM4, 0x33);
dm_pci_write_config8(dev, PAM5, 0x33);
dm_pci_write_config8(dev, PAM6, 0x33);
}
static int bd82x6x_northbridge_early_init(struct udevice *dev)
{
const int chipset_type = SANDYBRIDGE_MOBILE;
u32 capid0_a;
u8 reg8;
/* Device ID Override Enable should be done very early */
dm_pci_read_config32(dev, 0xe4, &capid0_a);
if (capid0_a & (1 << 10)) {
dm_pci_read_config8(dev, 0xf3, &reg8);
reg8 &= ~7; /* Clear 2:0 */
if (chipset_type == SANDYBRIDGE_MOBILE)
reg8 |= 1; /* Set bit 0 */
dm_pci_write_config8(dev, 0xf3, reg8);
}
sandybridge_setup_northbridge_bars(dev);
/* Device Enable */
dm_pci_write_config32(dev, DEVEN, DEVEN_HOST | DEVEN_IGD);
return 0;
}
static int bd82x6x_northbridge_probe(struct udevice *dev)
{
int rev;
if (!(gd->flags & GD_FLG_RELOC))
return bd82x6x_northbridge_early_init(dev);
rev = bridge_silicon_revision(dev);
northbridge_init(dev, rev);
return 0;
}
static const struct udevice_id bd82x6x_northbridge_ids[] = {
{ .compatible = "intel,bd82x6x-northbridge" },
{ }
};
U_BOOT_DRIVER(bd82x6x_northbridge_drv) = {
.name = "bd82x6x_northbridge",
.id = UCLASS_NORTHBRIDGE,
.of_match = bd82x6x_northbridge_ids,
.probe = bd82x6x_northbridge_probe,
};

View File

@@ -0,0 +1,239 @@
/*
* From Coreboot
* Copyright (C) 2008-2009 coresystems GmbH
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <asm/io.h>
#include <asm/pch_common.h>
#include <asm/pci.h>
#include <asm/arch/pch.h>
#include <asm/arch/bd82x6x.h>
DECLARE_GLOBAL_DATA_PTR;
static void common_sata_init(struct udevice *dev, unsigned int port_map)
{
u32 reg32;
u16 reg16;
/* Set IDE I/O Configuration */
reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0;
dm_pci_write_config32(dev, IDE_CONFIG, reg32);
/* Port enable */
dm_pci_read_config16(dev, 0x92, &reg16);
reg16 &= ~0x3f;
reg16 |= port_map;
dm_pci_write_config16(dev, 0x92, reg16);
/* SATA Initialization register */
port_map &= 0xff;
dm_pci_write_config32(dev, 0x94, ((port_map ^ 0x3f) << 24) | 0x183);
}
static void bd82x6x_sata_init(struct udevice *dev, struct udevice *pch)
{
unsigned int port_map, speed_support, port_tx;
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
const char *mode;
u32 reg32;
u16 reg16;
debug("SATA: Initializing...\n");
/* SATA configuration */
port_map = fdtdec_get_int(blob, node, "intel,sata-port-map", 0);
speed_support = fdtdec_get_int(blob, node,
"sata_interface_speed_support", 0);
mode = fdt_getprop(blob, node, "intel,sata-mode", NULL);
if (!mode || !strcmp(mode, "ahci")) {
u32 abar;
debug("SATA: Controller in AHCI mode\n");
/* Set timings */
dm_pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
IDE_PPE0 | IDE_IE0 | IDE_TIME0);
dm_pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS);
/* Sync DMA */
dm_pci_write_config16(dev, IDE_SDMA_CNT, IDE_PSDE0);
dm_pci_write_config16(dev, IDE_SDMA_TIM, 0x0001);
common_sata_init(dev, 0x8000 | port_map);
/* Initialize AHCI memory-mapped space */
abar = dm_pci_read_bar32(dev, 5);
debug("ABAR: %08X\n", abar);
/* CAP (HBA Capabilities) : enable power management */
reg32 = readl(abar + 0x00);
reg32 |= 0x0c006000; /* set PSC+SSC+SALP+SSS */
reg32 &= ~0x00020060; /* clear SXS+EMS+PMS */
/* Set ISS, if available */
if (speed_support) {
reg32 &= ~0x00f00000;
reg32 |= (speed_support & 0x03) << 20;
}
writel(reg32, abar + 0x00);
/* PI (Ports implemented) */
writel(port_map, abar + 0x0c);
(void) readl(abar + 0x0c); /* Read back 1 */
(void) readl(abar + 0x0c); /* Read back 2 */
/* CAP2 (HBA Capabilities Extended)*/
reg32 = readl(abar + 0x24);
reg32 &= ~0x00000002;
writel(reg32, abar + 0x24);
/* VSP (Vendor Specific Register */
reg32 = readl(abar + 0xa0);
reg32 &= ~0x00000005;
writel(reg32, abar + 0xa0);
} else if (!strcmp(mode, "combined")) {
debug("SATA: Controller in combined mode\n");
/* No AHCI: clear AHCI base */
dm_pci_write_bar32(dev, 5, 0x00000000);
/* And without AHCI BAR no memory decoding */
dm_pci_read_config16(dev, PCI_COMMAND, &reg16);
reg16 &= ~PCI_COMMAND_MEMORY;
dm_pci_write_config16(dev, PCI_COMMAND, reg16);
dm_pci_write_config8(dev, 0x09, 0x80);
/* Set timings */
dm_pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS);
dm_pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
IDE_PPE0 | IDE_IE0 | IDE_TIME0);
/* Sync DMA */
dm_pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0);
dm_pci_write_config16(dev, IDE_SDMA_TIM, 0x0200);
common_sata_init(dev, port_map);
} else {
debug("SATA: Controller in plain-ide mode\n");
/* No AHCI: clear AHCI base */
dm_pci_write_bar32(dev, 5, 0x00000000);
/* And without AHCI BAR no memory decoding */
dm_pci_read_config16(dev, PCI_COMMAND, &reg16);
reg16 &= ~PCI_COMMAND_MEMORY;
dm_pci_write_config16(dev, PCI_COMMAND, reg16);
/*
* Native mode capable on both primary and secondary (0xa)
* OR'ed with enabled (0x50) = 0xf
*/
dm_pci_write_config8(dev, 0x09, 0x8f);
/* Set timings */
dm_pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
IDE_PPE0 | IDE_IE0 | IDE_TIME0);
dm_pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
IDE_SITRE | IDE_ISP_3_CLOCKS |
IDE_RCT_1_CLOCKS | IDE_IE0 | IDE_TIME0);
/* Sync DMA */
dm_pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0 | IDE_PSDE0);
dm_pci_write_config16(dev, IDE_SDMA_TIM, 0x0201);
common_sata_init(dev, port_map);
}
/* Set Gen3 Transmitter settings if needed */
port_tx = fdtdec_get_int(blob, node, "intel,sata-port0-gen3-tx", 0);
if (port_tx)
pch_iobp_update(pch, SATA_IOBP_SP0G3IR, 0, port_tx);
port_tx = fdtdec_get_int(blob, node, "intel,sata-port1-gen3-tx", 0);
if (port_tx)
pch_iobp_update(pch, SATA_IOBP_SP1G3IR, 0, port_tx);
/* Additional Programming Requirements */
pch_common_sir_write(dev, 0x04, 0x00001600);
pch_common_sir_write(dev, 0x28, 0xa0000033);
reg32 = pch_common_sir_read(dev, 0x54);
reg32 &= 0xff000000;
reg32 |= 0x5555aa;
pch_common_sir_write(dev, 0x54, reg32);
pch_common_sir_write(dev, 0x64, 0xcccc8484);
reg32 = pch_common_sir_read(dev, 0x68);
reg32 &= 0xffff0000;
reg32 |= 0xcccc;
pch_common_sir_write(dev, 0x68, reg32);
reg32 = pch_common_sir_read(dev, 0x78);
reg32 &= 0x0000ffff;
reg32 |= 0x88880000;
pch_common_sir_write(dev, 0x78, reg32);
pch_common_sir_write(dev, 0x84, 0x001c7000);
pch_common_sir_write(dev, 0x88, 0x88338822);
pch_common_sir_write(dev, 0xa0, 0x001c7000);
pch_common_sir_write(dev, 0xc4, 0x0c0c0c0c);
pch_common_sir_write(dev, 0xc8, 0x0c0c0c0c);
pch_common_sir_write(dev, 0xd4, 0x10000000);
pch_iobp_update(pch, 0xea004001, 0x3fffffff, 0xc0000000);
pch_iobp_update(pch, 0xea00408a, 0xfffffcff, 0x00000100);
}
static void bd82x6x_sata_enable(struct udevice *dev)
{
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
unsigned port_map;
const char *mode;
u16 map = 0;
/*
* Set SATA controller mode early so the resource allocator can
* properly assign IO/Memory resources for the controller.
*/
mode = fdt_getprop(blob, node, "intel,sata-mode", NULL);
if (mode && !strcmp(mode, "ahci"))
map = 0x0060;
port_map = fdtdec_get_int(blob, node, "intel,sata-port-map", 0);
map |= (port_map ^ 0x3f) << 8;
dm_pci_write_config16(dev, 0x90, map);
}
static int bd82x6x_sata_probe(struct udevice *dev)
{
struct udevice *pch;
int ret;
ret = uclass_first_device_err(UCLASS_PCH, &pch);
if (ret)
return ret;
if (!(gd->flags & GD_FLG_RELOC))
bd82x6x_sata_enable(dev);
else
bd82x6x_sata_init(dev, pch);
return 0;
}
static const struct udevice_id bd82x6x_ahci_ids[] = {
{ .compatible = "intel,pantherpoint-ahci" },
{ }
};
U_BOOT_DRIVER(ahci_ivybridge_drv) = {
.name = "ahci_ivybridge",
.id = UCLASS_AHCI,
.of_match = bd82x6x_ahci_ids,
.probe = bd82x6x_sata_probe,
};

View File

@@ -0,0 +1,537 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2010,2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* Portions from Coreboot mainboard/google/link/romstage.c
* Copyright (C) 2007-2010 coresystems GmbH
* Copyright (C) 2011 Google Inc.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <net.h>
#include <rtc.h>
#include <spi.h>
#include <spi_flash.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/processor.h>
#include <asm/gpio.h>
#include <asm/global_data.h>
#include <asm/intel_regs.h>
#include <asm/mrccache.h>
#include <asm/mrc_common.h>
#include <asm/mtrr.h>
#include <asm/pci.h>
#include <asm/report_platform.h>
#include <asm/arch/me.h>
#include <asm/arch/pei_data.h>
#include <asm/arch/pch.h>
#include <asm/post.h>
#include <asm/arch/sandybridge.h>
DECLARE_GLOBAL_DATA_PTR;
#define CMOS_OFFSET_MRC_SEED 152
#define CMOS_OFFSET_MRC_SEED_S3 156
#define CMOS_OFFSET_MRC_SEED_CHK 160
ulong board_get_usable_ram_top(ulong total_size)
{
return mrc_common_board_get_usable_ram_top(total_size);
}
void dram_init_banksize(void)
{
mrc_common_dram_init_banksize();
}
static int read_seed_from_cmos(struct pei_data *pei_data)
{
u16 c1, c2, checksum, seed_checksum;
struct udevice *dev;
int ret = 0;
ret = uclass_get_device(UCLASS_RTC, 0, &dev);
if (ret) {
debug("Cannot find RTC: err=%d\n", ret);
return -ENODEV;
}
/*
* Read scrambler seeds from CMOS RAM. We don't want to store them in
* SPI flash since they change on every boot and that would wear down
* the flash too much. So we store these in CMOS and the large MRC
* data in SPI flash.
*/
ret = rtc_read32(dev, CMOS_OFFSET_MRC_SEED, &pei_data->scrambler_seed);
if (!ret) {
ret = rtc_read32(dev, CMOS_OFFSET_MRC_SEED_S3,
&pei_data->scrambler_seed_s3);
}
if (ret) {
debug("Failed to read from RTC %s\n", dev->name);
return ret;
}
debug("Read scrambler seed 0x%08x from CMOS 0x%02x\n",
pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
debug("Read S3 scrambler seed 0x%08x from CMOS 0x%02x\n",
pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3);
/* Compute seed checksum and compare */
c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed,
sizeof(u32));
c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3,
sizeof(u32));
checksum = add_ip_checksums(sizeof(u32), c1, c2);
seed_checksum = rtc_read8(dev, CMOS_OFFSET_MRC_SEED_CHK);
seed_checksum |= rtc_read8(dev, CMOS_OFFSET_MRC_SEED_CHK + 1) << 8;
if (checksum != seed_checksum) {
debug("%s: invalid seed checksum\n", __func__);
pei_data->scrambler_seed = 0;
pei_data->scrambler_seed_s3 = 0;
return -EINVAL;
}
return 0;
}
static int prepare_mrc_cache(struct pei_data *pei_data)
{
struct mrc_data_container *mrc_cache;
struct mrc_region entry;
int ret;
ret = read_seed_from_cmos(pei_data);
if (ret)
return ret;
ret = mrccache_get_region(NULL, &entry);
if (ret)
return ret;
mrc_cache = mrccache_find_current(&entry);
if (!mrc_cache)
return -ENOENT;
pei_data->mrc_input = mrc_cache->data;
pei_data->mrc_input_len = mrc_cache->data_size;
debug("%s: at %p, size %x checksum %04x\n", __func__,
pei_data->mrc_input, pei_data->mrc_input_len,
mrc_cache->checksum);
return 0;
}
static int write_seeds_to_cmos(struct pei_data *pei_data)
{
u16 c1, c2, checksum;
struct udevice *dev;
int ret = 0;
ret = uclass_get_device(UCLASS_RTC, 0, &dev);
if (ret) {
debug("Cannot find RTC: err=%d\n", ret);
return -ENODEV;
}
/* Save the MRC seed values to CMOS */
rtc_write32(dev, CMOS_OFFSET_MRC_SEED, pei_data->scrambler_seed);
debug("Save scrambler seed 0x%08x to CMOS 0x%02x\n",
pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
rtc_write32(dev, CMOS_OFFSET_MRC_SEED_S3, pei_data->scrambler_seed_s3);
debug("Save s3 scrambler seed 0x%08x to CMOS 0x%02x\n",
pei_data->scrambler_seed_s3, CMOS_OFFSET_MRC_SEED_S3);
/* Save a simple checksum of the seed values */
c1 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed,
sizeof(u32));
c2 = compute_ip_checksum((u8 *)&pei_data->scrambler_seed_s3,
sizeof(u32));
checksum = add_ip_checksums(sizeof(u32), c1, c2);
rtc_write8(dev, CMOS_OFFSET_MRC_SEED_CHK, checksum & 0xff);
rtc_write8(dev, CMOS_OFFSET_MRC_SEED_CHK + 1, (checksum >> 8) & 0xff);
return 0;
}
/* Use this hook to save our SDRAM parameters */
int misc_init_r(void)
{
int ret;
ret = mrccache_save();
if (ret)
printf("Unable to save MRC data: %d\n", ret);
return 0;
}
static void post_system_agent_init(struct udevice *dev, struct udevice *me_dev,
struct pei_data *pei_data)
{
uint16_t done;
/*
* Send ME init done for SandyBridge here. This is done inside the
* SystemAgent binary on IvyBridge
*/
dm_pci_read_config16(dev, PCI_DEVICE_ID, &done);
done &= BASE_REV_MASK;
if (BASE_REV_SNB == done)
intel_early_me_init_done(dev, me_dev, ME_INIT_STATUS_SUCCESS);
else
intel_me_status(me_dev);
/* If PCIe init is skipped, set the PEG clock gating */
if (!pei_data->pcie_init)
setbits_le32(MCHBAR_REG(0x7010), 1);
}
static int recovery_mode_enabled(void)
{
return false;
}
static int copy_spd(struct udevice *dev, struct pei_data *peid)
{
const void *data;
int ret;
ret = mrc_locate_spd(dev, sizeof(peid->spd_data[0]), &data);
if (ret)
return ret;
memcpy(peid->spd_data[0], data, sizeof(peid->spd_data[0]));
return 0;
}
/**
* sdram_find() - Find available memory
*
* This is a bit complicated since on x86 there are system memory holes all
* over the place. We create a list of available memory blocks
*
* @dev: Northbridge device
*/
static int sdram_find(struct udevice *dev)
{
struct memory_info *info = &gd->arch.meminfo;
uint32_t tseg_base, uma_size, tolud;
uint64_t tom, me_base, touud;
uint64_t uma_memory_base = 0;
uint64_t uma_memory_size;
unsigned long long tomk;
uint16_t ggc;
u32 val;
/* Total Memory 2GB example:
*
* 00000000 0000MB-1992MB 1992MB RAM (writeback)
* 7c800000 1992MB-2000MB 8MB TSEG (SMRR)
* 7d000000 2000MB-2002MB 2MB GFX GTT (uncached)
* 7d200000 2002MB-2034MB 32MB GFX UMA (uncached)
* 7f200000 2034MB TOLUD
* 7f800000 2040MB MEBASE
* 7f800000 2040MB-2048MB 8MB ME UMA (uncached)
* 80000000 2048MB TOM
* 100000000 4096MB-4102MB 6MB RAM (writeback)
*
* Total Memory 4GB example:
*
* 00000000 0000MB-2768MB 2768MB RAM (writeback)
* ad000000 2768MB-2776MB 8MB TSEG (SMRR)
* ad800000 2776MB-2778MB 2MB GFX GTT (uncached)
* ada00000 2778MB-2810MB 32MB GFX UMA (uncached)
* afa00000 2810MB TOLUD
* ff800000 4088MB MEBASE
* ff800000 4088MB-4096MB 8MB ME UMA (uncached)
* 100000000 4096MB TOM
* 100000000 4096MB-5374MB 1278MB RAM (writeback)
* 14fe00000 5368MB TOUUD
*/
/* Top of Upper Usable DRAM, including remap */
dm_pci_read_config32(dev, TOUUD + 4, &val);
touud = (uint64_t)val << 32;
dm_pci_read_config32(dev, TOUUD, &val);
touud |= val;
/* Top of Lower Usable DRAM */
dm_pci_read_config32(dev, TOLUD, &tolud);
/* Top of Memory - does not account for any UMA */
dm_pci_read_config32(dev, 0xa4, &val);
tom = (uint64_t)val << 32;
dm_pci_read_config32(dev, 0xa0, &val);
tom |= val;
debug("TOUUD %llx TOLUD %08x TOM %llx\n", touud, tolud, tom);
/* ME UMA needs excluding if total memory <4GB */
dm_pci_read_config32(dev, 0x74, &val);
me_base = (uint64_t)val << 32;
dm_pci_read_config32(dev, 0x70, &val);
me_base |= val;
debug("MEBASE %llx\n", me_base);
/* TODO: Get rid of all this shifting by 10 bits */
tomk = tolud >> 10;
if (me_base == tolud) {
/* ME is from MEBASE-TOM */
uma_size = (tom - me_base) >> 10;
/* Increment TOLUD to account for ME as RAM */
tolud += uma_size << 10;
/* UMA starts at old TOLUD */
uma_memory_base = tomk * 1024ULL;
uma_memory_size = uma_size * 1024ULL;
debug("ME UMA base %llx size %uM\n", me_base, uma_size >> 10);
}
/* Graphics memory comes next */
dm_pci_read_config16(dev, GGC, &ggc);
if (!(ggc & 2)) {
debug("IGD decoded, subtracting ");
/* Graphics memory */
uma_size = ((ggc >> 3) & 0x1f) * 32 * 1024ULL;
debug("%uM UMA", uma_size >> 10);
tomk -= uma_size;
uma_memory_base = tomk * 1024ULL;
uma_memory_size += uma_size * 1024ULL;
/* GTT Graphics Stolen Memory Size (GGMS) */
uma_size = ((ggc >> 8) & 0x3) * 1024ULL;
tomk -= uma_size;
uma_memory_base = tomk * 1024ULL;
uma_memory_size += uma_size * 1024ULL;
debug(" and %uM GTT\n", uma_size >> 10);
}
/* Calculate TSEG size from its base which must be below GTT */
dm_pci_read_config32(dev, 0xb8, &tseg_base);
uma_size = (uma_memory_base - tseg_base) >> 10;
tomk -= uma_size;
uma_memory_base = tomk * 1024ULL;
uma_memory_size += uma_size * 1024ULL;
debug("TSEG base 0x%08x size %uM\n", tseg_base, uma_size >> 10);
debug("Available memory below 4GB: %lluM\n", tomk >> 10);
/* Report the memory regions */
mrc_add_memory_area(info, 1 << 20, 2 << 28);
mrc_add_memory_area(info, (2 << 28) + (2 << 20), 4 << 28);
mrc_add_memory_area(info, (4 << 28) + (2 << 20), tseg_base);
mrc_add_memory_area(info, 1ULL << 32, touud);
/* Add MTRRs for memory */
mtrr_add_request(MTRR_TYPE_WRBACK, 0, 2ULL << 30);
mtrr_add_request(MTRR_TYPE_WRBACK, 2ULL << 30, 512 << 20);
mtrr_add_request(MTRR_TYPE_WRBACK, 0xaULL << 28, 256 << 20);
mtrr_add_request(MTRR_TYPE_UNCACHEABLE, tseg_base, 16 << 20);
mtrr_add_request(MTRR_TYPE_UNCACHEABLE, tseg_base + (16 << 20),
32 << 20);
/*
* If >= 4GB installed then memory from TOLUD to 4GB
* is remapped above TOM, TOUUD will account for both
*/
if (touud > (1ULL << 32ULL)) {
debug("Available memory above 4GB: %lluM\n",
(touud >> 20) - 4096);
}
return 0;
}
static void rcba_config(void)
{
/*
* GFX INTA -> PIRQA (MSI)
* D28IP_P3IP WLAN INTA -> PIRQB
* D29IP_E1P EHCI1 INTA -> PIRQD
* D26IP_E2P EHCI2 INTA -> PIRQF
* D31IP_SIP SATA INTA -> PIRQF (MSI)
* D31IP_SMIP SMBUS INTB -> PIRQH
* D31IP_TTIP THRT INTC -> PIRQA
* D27IP_ZIP HDA INTA -> PIRQA (MSI)
*
* TRACKPAD -> PIRQE (Edge Triggered)
* TOUCHSCREEN -> PIRQG (Edge Triggered)
*/
/* Device interrupt pin register (board specific) */
writel((INTC << D31IP_TTIP) | (NOINT << D31IP_SIP2) |
(INTB << D31IP_SMIP) | (INTA << D31IP_SIP), RCB_REG(D31IP));
writel(NOINT << D30IP_PIP, RCB_REG(D30IP));
writel(INTA << D29IP_E1P, RCB_REG(D29IP));
writel(INTA << D28IP_P3IP, RCB_REG(D28IP));
writel(INTA << D27IP_ZIP, RCB_REG(D27IP));
writel(INTA << D26IP_E2P, RCB_REG(D26IP));
writel(NOINT << D25IP_LIP, RCB_REG(D25IP));
writel(NOINT << D22IP_MEI1IP, RCB_REG(D22IP));
/* Device interrupt route registers */
writel(DIR_ROUTE(PIRQB, PIRQH, PIRQA, PIRQC), RCB_REG(D31IR));
writel(DIR_ROUTE(PIRQD, PIRQE, PIRQF, PIRQG), RCB_REG(D29IR));
writel(DIR_ROUTE(PIRQB, PIRQC, PIRQD, PIRQE), RCB_REG(D28IR));
writel(DIR_ROUTE(PIRQA, PIRQH, PIRQA, PIRQB), RCB_REG(D27IR));
writel(DIR_ROUTE(PIRQF, PIRQE, PIRQG, PIRQH), RCB_REG(D26IR));
writel(DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD), RCB_REG(D25IR));
writel(DIR_ROUTE(PIRQA, PIRQB, PIRQC, PIRQD), RCB_REG(D22IR));
/* Enable IOAPIC (generic) */
writew(0x0100, RCB_REG(OIC));
/* PCH BWG says to read back the IOAPIC enable register */
(void)readw(RCB_REG(OIC));
/* Disable unused devices (board specific) */
setbits_le32(RCB_REG(FD), PCH_DISABLE_ALWAYS);
}
int dram_init(void)
{
struct pei_data _pei_data __aligned(8) = {
.pei_version = PEI_VERSION,
.mchbar = MCH_BASE_ADDRESS,
.dmibar = DEFAULT_DMIBAR,
.epbar = DEFAULT_EPBAR,
.pciexbar = CONFIG_PCIE_ECAM_BASE,
.smbusbar = SMBUS_IO_BASE,
.wdbbar = 0x4000000,
.wdbsize = 0x1000,
.hpet_address = CONFIG_HPET_ADDRESS,
.rcba = DEFAULT_RCBABASE,
.pmbase = DEFAULT_PMBASE,
.gpiobase = DEFAULT_GPIOBASE,
.thermalbase = 0xfed08000,
.system_type = 0, /* 0 Mobile, 1 Desktop/Server */
.tseg_size = CONFIG_SMM_TSEG_SIZE,
.ts_addresses = { 0x00, 0x00, 0x00, 0x00 },
.ec_present = 1,
.ddr3lv_support = 1,
/*
* 0 = leave channel enabled
* 1 = disable dimm 0 on channel
* 2 = disable dimm 1 on channel
* 3 = disable dimm 0+1 on channel
*/
.dimm_channel0_disabled = 2,
.dimm_channel1_disabled = 2,
.max_ddr3_freq = 1600,
.usb_port_config = {
/*
* Empty and onboard Ports 0-7, set to un-used pin
* OC3
*/
{ 0, 3, 0x0000 }, /* P0= Empty */
{ 1, 0, 0x0040 }, /* P1= Left USB 1 (OC0) */
{ 1, 1, 0x0040 }, /* P2= Left USB 2 (OC1) */
{ 1, 3, 0x0040 }, /* P3= SDCARD (no OC) */
{ 0, 3, 0x0000 }, /* P4= Empty */
{ 1, 3, 0x0040 }, /* P5= WWAN (no OC) */
{ 0, 3, 0x0000 }, /* P6= Empty */
{ 0, 3, 0x0000 }, /* P7= Empty */
/*
* Empty and onboard Ports 8-13, set to un-used pin
* OC4
*/
{ 1, 4, 0x0040 }, /* P8= Camera (no OC) */
{ 1, 4, 0x0040 }, /* P9= Bluetooth (no OC) */
{ 0, 4, 0x0000 }, /* P10= Empty */
{ 0, 4, 0x0000 }, /* P11= Empty */
{ 0, 4, 0x0000 }, /* P12= Empty */
{ 0, 4, 0x0000 }, /* P13= Empty */
},
};
struct pei_data *pei_data = &_pei_data;
struct udevice *dev, *me_dev;
int ret;
ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &dev);
if (ret)
return ret;
ret = syscon_get_by_driver_data(X86_SYSCON_ME, &me_dev);
if (ret)
return ret;
ret = copy_spd(dev, pei_data);
if (ret)
return ret;
pei_data->boot_mode = gd->arch.pei_boot_mode;
debug("Boot mode %d\n", gd->arch.pei_boot_mode);
debug("mrc_input %p\n", pei_data->mrc_input);
/*
* Do not pass MRC data in for recovery mode boot,
* Always pass it in for S3 resume.
*/
if (!recovery_mode_enabled() ||
pei_data->boot_mode == PEI_BOOT_RESUME) {
ret = prepare_mrc_cache(pei_data);
if (ret)
debug("prepare_mrc_cache failed: %d\n", ret);
}
/* 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");
reset_cpu(0);
}
/* Pass console handler in pei_data */
pei_data->tx_byte = sdram_console_tx_byte;
/* Wait for ME to be ready */
ret = intel_early_me_init(me_dev);
if (ret)
return ret;
ret = intel_early_me_uma_size(me_dev);
if (ret < 0)
return ret;
ret = mrc_common_init(dev, pei_data, false);
if (ret)
return ret;
ret = sdram_find(dev);
if (ret)
return ret;
gd->ram_size = gd->arch.meminfo.total_32bit_memory;
debug("MRC output data length %#x at %p\n", pei_data->mrc_output_len,
pei_data->mrc_output);
post_system_agent_init(dev, me_dev, pei_data);
report_memory_config();
/* S3 resume: don't save scrambler seed or MRC data */
if (pei_data->boot_mode != PEI_BOOT_RESUME) {
/*
* This will be copied to SDRAM in reserve_arch(), then written
* to SPI flash in mrccache_save()
*/
gd->arch.mrc_output = (char *)pei_data->mrc_output;
gd->arch.mrc_output_len = pei_data->mrc_output_len;
ret = write_seeds_to_cmos(pei_data);
if (ret)
debug("Failed to write seeds to CMOS: %d\n", ret);
}
writew(0xCAFE, MCHBAR_REG(SSKPD));
if (ret)
return ret;
rcba_config();
return 0;
}

159
u-boot/arch/x86/cpu/lapic.c Normal file
View File

@@ -0,0 +1,159 @@
/*
* From coreboot file of same name
*
* Copyright (C) 2008-2009 coresystems GmbH
* Copyright (C) 2014 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <asm/io.h>
#include <asm/lapic.h>
#include <asm/msr.h>
#include <asm/msr-index.h>
#include <asm/post.h>
unsigned long lapic_read(unsigned long reg)
{
return readl(LAPIC_DEFAULT_BASE + reg);
}
#define xchg(ptr, v) ((__typeof__(*(ptr)))__xchg((unsigned long)(v), (ptr), \
sizeof(*(ptr))))
struct __xchg_dummy { unsigned long a[100]; };
#define __xg(x) ((struct __xchg_dummy *)(x))
/*
* Note: no "lock" prefix even on SMP. xchg always implies lock anyway.
*
* Note 2: xchg has side effect, so that attribute volatile is necessary,
* but generally the primitive is invalid, *ptr is output argument.
*/
static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
int size)
{
switch (size) {
case 1:
__asm__ __volatile__("xchgb %b0,%1"
: "=q" (x)
: "m" (*__xg(ptr)), "0" (x)
: "memory");
break;
case 2:
__asm__ __volatile__("xchgw %w0,%1"
: "=r" (x)
: "m" (*__xg(ptr)), "0" (x)
: "memory");
break;
case 4:
__asm__ __volatile__("xchgl %0,%1"
: "=r" (x)
: "m" (*__xg(ptr)), "0" (x)
: "memory");
break;
}
return x;
}
void lapic_write(unsigned long reg, unsigned long v)
{
(void)xchg((volatile unsigned long *)(LAPIC_DEFAULT_BASE + reg), v);
}
void enable_lapic(void)
{
if (!IS_ENABLED(CONFIG_INTEL_QUARK)) {
msr_t msr;
msr = msr_read(MSR_IA32_APICBASE);
msr.hi &= 0xffffff00;
msr.lo |= MSR_IA32_APICBASE_ENABLE;
msr.lo &= ~MSR_IA32_APICBASE_BASE;
msr.lo |= LAPIC_DEFAULT_BASE;
msr_write(MSR_IA32_APICBASE, msr);
}
}
void disable_lapic(void)
{
if (!IS_ENABLED(CONFIG_INTEL_QUARK)) {
msr_t msr;
msr = msr_read(MSR_IA32_APICBASE);
msr.lo &= ~MSR_IA32_APICBASE_ENABLE;
msr_write(MSR_IA32_APICBASE, msr);
}
}
unsigned long lapicid(void)
{
return lapic_read(LAPIC_ID) >> 24;
}
static void lapic_wait_icr_idle(void)
{
do { } while (lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY);
}
int lapic_remote_read(int apicid, int reg, unsigned long *pvalue)
{
int timeout;
unsigned long status;
int result;
lapic_wait_icr_idle();
lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid));
lapic_write(LAPIC_ICR, LAPIC_DM_REMRD | (reg >> 4));
timeout = 0;
do {
status = lapic_read(LAPIC_ICR) & LAPIC_ICR_RR_MASK;
} while (status == LAPIC_ICR_RR_INPROG && timeout++ < 1000);
result = -1;
if (status == LAPIC_ICR_RR_VALID) {
*pvalue = lapic_read(LAPIC_RRR);
result = 0;
}
return result;
}
void lapic_setup(void)
{
/* Only Pentium Pro and later have those MSR stuff */
debug("Setting up local apic: ");
/* Enable the local apic */
enable_lapic();
/* Set Task Priority to 'accept all' */
lapic_write(LAPIC_TASKPRI,
lapic_read(LAPIC_TASKPRI) & ~LAPIC_TPRI_MASK);
/* Put the local apic in virtual wire mode */
lapic_write(LAPIC_SPIV, (lapic_read(LAPIC_SPIV) &
~(LAPIC_VECTOR_MASK)) | LAPIC_SPIV_ENABLE);
lapic_write(LAPIC_LVT0, (lapic_read(LAPIC_LVT0) &
~(LAPIC_LVT_MASKED | LAPIC_LVT_LEVEL_TRIGGER |
LAPIC_LVT_REMOTE_IRR | LAPIC_INPUT_POLARITY |
LAPIC_SEND_PENDING | LAPIC_LVT_RESERVED_1 |
LAPIC_DELIVERY_MODE_MASK)) |
(LAPIC_LVT_REMOTE_IRR | LAPIC_SEND_PENDING |
LAPIC_DELIVERY_MODE_EXTINT));
lapic_write(LAPIC_LVT1, (lapic_read(LAPIC_LVT1) &
~(LAPIC_LVT_MASKED | LAPIC_LVT_LEVEL_TRIGGER |
LAPIC_LVT_REMOTE_IRR | LAPIC_INPUT_POLARITY |
LAPIC_SEND_PENDING | LAPIC_LVT_RESERVED_1 |
LAPIC_DELIVERY_MODE_MASK)) |
(LAPIC_LVT_REMOTE_IRR | LAPIC_SEND_PENDING |
LAPIC_DELIVERY_MODE_NMI));
debug("apic_id: 0x%02lx, ", lapicid());
debug("done.\n");
post_code(POST_LAPIC);
}

View File

@@ -0,0 +1,576 @@
/*
* 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 <qfw.h>
#include <asm/atomic.h>
#include <asm/cpu.h>
#include <asm/interrupt.h>
#include <asm/lapic.h>
#include <asm/microcode.h>
#include <asm/mp.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
#include <asm/processor.h>
#include <asm/sipi.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <dm/lists.h>
#include <dm/root.h>
#include <linux/linkage.h>
DECLARE_GLOBAL_DATA_PTR;
/* Total CPUs include BSP */
static int num_cpus;
/* 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);
}
static inline void stop_this_cpu(void)
{
/* Called by an AP when it is ready to halt and wait for a new task */
for (;;)
cpu_hlt();
}
/* 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_apic_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_apic_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, int num_cpus)
{
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 * num_cpus;
stack = memalign(4096, size);
if (!stack)
return -ENOMEM;
params->stack_top = (u32)(stack + size);
#if !defined(CONFIG_QEMU) && !defined(CONFIG_HAVE_FSP)
params->microcode_ptr = ucode_base;
debug("Microcode at %x\n", params->microcode_ptr);
#endif
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, const char *msg)
{
int total = 0;
if (!(lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY))
return 0;
debug("Waiting for %s...", msg);
while (lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY) {
udelay(50);
total += 50;
if (total >= total_delay) {
debug("timed out: aborting\n");
return -ETIMEDOUT;
}
}
debug("done\n");
return 0;
}
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 (apic_wait_timeout(1000, "ICR not to be busy"))
return -ETIMEDOUT;
/* Send INIT IPI to all but self */
lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
lapic_write(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 (apic_wait_timeout(1000, "ICR not to be busy"))
return -ETIMEDOUT;
lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT |
LAPIC_DM_STARTUP | sipi_vector);
if (apic_wait_timeout(10000, "first SIPI to complete"))
return -ETIMEDOUT;
/* Wait for CPUs to check in up to 200 us */
wait_for_aps(num_aps, ap_count, 200, 15);
/* Send 2nd SIPI */
if (apic_wait_timeout(1000, "ICR not to be busy"))
return -ETIMEDOUT;
lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(0));
lapic_write(LAPIC_ICR, LAPIC_DEST_ALLBUT | LAPIC_INT_ASSERT |
LAPIC_DM_STARTUP | sipi_vector);
if (apic_wait_timeout(10000, "second SIPI to complete"))
return -ETIMEDOUT;
/* 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 = 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);
apic_id = lapicid();
ret = find_cpu_by_apic_id(apic_id, devp);
if (ret) {
printf("Cannot find boot CPU, APIC ID %d\n", apic_id);
return ret;
}
return 0;
}
#ifdef CONFIG_QFW
static int qemu_cpu_fixup(void)
{
int ret;
int cpu_num;
int cpu_online;
struct udevice *dev, *pdev;
struct cpu_platdata *plat;
char *cpu;
/* first we need to find '/cpus' */
for (device_find_first_child(dm_root(), &pdev);
pdev;
device_find_next_child(&pdev)) {
if (!strcmp(pdev->name, "cpus"))
break;
}
if (!pdev) {
printf("unable to find cpus device\n");
return -ENODEV;
}
/* calculate cpus that are already bound */
cpu_num = 0;
for (uclass_find_first_device(UCLASS_CPU, &dev);
dev;
uclass_find_next_device(&dev)) {
cpu_num++;
}
/* get actual cpu number */
cpu_online = qemu_fwcfg_online_cpus();
if (cpu_online < 0) {
printf("unable to get online cpu number: %d\n", cpu_online);
return cpu_online;
}
/* bind addtional cpus */
dev = NULL;
for (; cpu_num < cpu_online; cpu_num++) {
/*
* allocate device name here as device_bind_driver() does
* not copy device name, 8 bytes are enough for
* sizeof("cpu@") + 3 digits cpu number + '\0'
*/
cpu = malloc(8);
if (!cpu) {
printf("unable to allocate device name\n");
return -ENOMEM;
}
sprintf(cpu, "cpu@%d", cpu_num);
ret = device_bind_driver(pdev, "cpu_qemu", cpu, &dev);
if (ret) {
printf("binding cpu@%d failed: %d\n", cpu_num, ret);
return ret;
}
plat = dev_get_parent_platdata(dev);
plat->cpu_id = cpu_num;
}
return 0;
}
#endif
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;
#ifdef CONFIG_QFW
ret = qemu_cpu_fixup();
if (ret)
return ret;
#endif
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;
}
num_cpus = cpu_get_count(cpu);
if (num_cpus < 0) {
debug("Cannot get number of CPUs: err=%d\n", num_cpus);
return num_cpus;
}
if (num_cpus < 2)
debug("Warning: Only 1 CPU is detected\n");
ret = check_cpu_devices(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, num_cpus);
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 = 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)
{
struct cpu_platdata *plat = dev_get_parent_platdata(cpu);
/*
* Multiple APs are brought up simultaneously and they may get the same
* seq num in the uclass_resolve_seq() during device_probe(). To avoid
* this, set req_seq to the reg number in the device tree in advance.
*/
cpu->req_seq = fdtdec_get_int(gd->fdt_blob, cpu->of_offset, "reg", -1);
plat->ucode_version = microcode_read_rev();
plat->device_id = gd->arch.x86_device;
return device_probe(cpu);
}

View File

@@ -0,0 +1,95 @@
/*
* (C) Copyright 2014 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*
* Memory Type Range Regsters - these are used to tell the CPU whether
* memory is cacheable and if so the cache write mode to use.
*
* These can speed up booting. See the mtrr command.
*
* Reference: Intel Architecture Software Developer's Manual, Volume 3:
* System Programming
*/
#include <common.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
DECLARE_GLOBAL_DATA_PTR;
/* Prepare to adjust MTRRs */
void mtrr_open(struct mtrr_state *state)
{
if (!gd->arch.has_mtrr)
return;
state->enable_cache = dcache_status();
if (state->enable_cache)
disable_caches();
state->deftype = native_read_msr(MTRR_DEF_TYPE_MSR);
wrmsrl(MTRR_DEF_TYPE_MSR, state->deftype & ~MTRR_DEF_TYPE_EN);
}
/* Clean up after adjusting MTRRs, and enable them */
void mtrr_close(struct mtrr_state *state)
{
if (!gd->arch.has_mtrr)
return;
wrmsrl(MTRR_DEF_TYPE_MSR, state->deftype | MTRR_DEF_TYPE_EN);
if (state->enable_cache)
enable_caches();
}
int mtrr_commit(bool do_caches)
{
struct mtrr_request *req = gd->arch.mtrr_req;
struct mtrr_state state;
uint64_t mask;
int i;
if (!gd->arch.has_mtrr)
return -ENOSYS;
mtrr_open(&state);
for (i = 0; i < gd->arch.mtrr_req_count; i++, req++) {
mask = ~(req->size - 1);
mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1;
wrmsrl(MTRR_PHYS_BASE_MSR(i), req->start | req->type);
wrmsrl(MTRR_PHYS_MASK_MSR(i), mask | MTRR_PHYS_MASK_VALID);
}
/* Clear the ones that are unused */
for (; i < MTRR_COUNT; i++)
wrmsrl(MTRR_PHYS_MASK_MSR(i), 0);
mtrr_close(&state);
return 0;
}
int mtrr_add_request(int type, uint64_t start, uint64_t size)
{
struct mtrr_request *req;
uint64_t mask;
if (!gd->arch.has_mtrr)
return -ENOSYS;
if (gd->arch.mtrr_req_count == MAX_MTRR_REQUESTS)
return -ENOSPC;
req = &gd->arch.mtrr_req[gd->arch.mtrr_req_count++];
req->type = type;
req->start = start;
req->size = size;
debug("%d: type=%d, %08llx %08llx\n", gd->arch.mtrr_req_count - 1,
req->type, req->start, req->size);
mask = ~(req->size - 1);
mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1;
mask |= MTRR_PHYS_MASK_VALID;
debug(" %016llx %016llx\n", req->start | req->type, mask);
return 0;
}

88
u-boot/arch/x86/cpu/pci.c Normal file
View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2008,2009
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <pci.h>
#include <asm/io.h>
#include <asm/pci.h>
DECLARE_GLOBAL_DATA_PTR;
int pci_x86_read_config(struct udevice *bus, pci_dev_t bdf, uint offset,
ulong *valuep, enum pci_size_t size)
{
outl(bdf | (offset & 0xfc) | PCI_CFG_EN, PCI_REG_ADDR);
switch (size) {
case PCI_SIZE_8:
*valuep = inb(PCI_REG_DATA + (offset & 3));
break;
case PCI_SIZE_16:
*valuep = inw(PCI_REG_DATA + (offset & 2));
break;
case PCI_SIZE_32:
*valuep = inl(PCI_REG_DATA);
break;
}
return 0;
}
int pci_x86_write_config(struct udevice *bus, pci_dev_t bdf, uint offset,
ulong value, enum pci_size_t size)
{
outl(bdf | (offset & 0xfc) | PCI_CFG_EN, PCI_REG_ADDR);
switch (size) {
case PCI_SIZE_8:
outb(value, PCI_REG_DATA + (offset & 3));
break;
case PCI_SIZE_16:
outw(value, PCI_REG_DATA + (offset & 2));
break;
case PCI_SIZE_32:
outl(value, PCI_REG_DATA);
break;
}
return 0;
}
void pci_assign_irqs(int bus, int device, u8 irq[4])
{
pci_dev_t bdf;
int func;
u16 vendor;
u8 pin, line;
for (func = 0; func < 8; func++) {
bdf = PCI_BDF(bus, device, func);
pci_read_config16(bdf, PCI_VENDOR_ID, &vendor);
if (vendor == 0xffff || vendor == 0x0000)
continue;
pci_read_config8(bdf, PCI_INTERRUPT_PIN, &pin);
/* PCI spec says all values except 1..4 are reserved */
if ((pin < 1) || (pin > 4))
continue;
line = irq[pin - 1];
if (!line)
continue;
debug("Assigning IRQ %d to PCI device %d.%x.%d (INT%c)\n",
line, bus, device, func, 'A' + pin - 1);
pci_write_config8(bdf, PCI_INTERRUPT_LINE, line);
}
}

View File

@@ -0,0 +1,27 @@
#
# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
#
# SPDX-License-Identifier: GPL-2.0+
#
config QEMU
bool
if QEMU
config SYS_CAR_ADDR
hex
default 0xd0000
config SYS_CAR_SIZE
hex
default 0x10000
config ACPI_PM1_BASE
hex
default 0xe400
help
ACPI Power Managment 1 (PM1) i/o-mapped base address.
This device is defined in ACPI specification, with 16 bytes in size.
endif

View File

@@ -0,0 +1,11 @@
#
# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
#
# SPDX-License-Identifier: GPL-2.0+
#
ifndef CONFIG_EFI_STUB
obj-y += car.o dram.o
endif
obj-y += qemu.o
obj-$(CONFIG_QFW) += cpu.o e820.o

View File

@@ -0,0 +1,26 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <asm/post.h>
.globl car_init
car_init:
/* Save the BIST result */
movl %eax, %ebp
post_code(POST_CAR_START)
/*
* Since we know we are running inside emulator,
* we can do nothing here for CAR initialization.
*/
/* Restore the BIST result */
movl %ebp, %eax
post_code(POST_CAR_CPU_CACHE)
jmp car_init_ret

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2015, Miao Yan <yanmiaobest@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <errno.h>
#include <qfw.h>
#include <asm/cpu.h>
DECLARE_GLOBAL_DATA_PTR;
int cpu_qemu_get_desc(struct udevice *dev, char *buf, int size)
{
if (size < CPU_MAX_NAME_LEN)
return -ENOSPC;
cpu_get_name(buf);
return 0;
}
static int cpu_qemu_get_count(struct udevice *dev)
{
return qemu_fwcfg_online_cpus();
}
static const struct cpu_ops cpu_qemu_ops = {
.get_desc = cpu_qemu_get_desc,
.get_count = cpu_qemu_get_count,
};
static const struct udevice_id cpu_qemu_ids[] = {
{ .compatible = "cpu-qemu" },
{ }
};
U_BOOT_DRIVER(cpu_qemu_drv) = {
.name = "cpu_qemu",
.id = UCLASS_CPU,
.of_match = cpu_qemu_ids,
.ops = &cpu_qemu_ops,
};

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/post.h>
#include <asm/arch/qemu.h>
DECLARE_GLOBAL_DATA_PTR;
int dram_init(void)
{
u32 ram;
outb(HIGH_RAM_ADDR, CMOS_ADDR_PORT);
ram = ((u32)inb(CMOS_DATA_PORT)) << 14;
outb(LOW_RAM_ADDR, CMOS_ADDR_PORT);
ram |= ((u32)inb(CMOS_DATA_PORT)) << 6;
ram += 16 * 1024;
gd->ram_size = ram * 1024;
post_code(POST_DRAM);
return 0;
}
void dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = 0;
gd->bd->bi_dram[0].size = gd->ram_size;
}
/*
* This function looks for the highest region of memory lower than 4GB which
* has enough space for U-Boot where U-Boot is aligned on a page boundary.
* It overrides the default implementation found elsewhere which simply
* picks the end of ram, wherever that may be. The location of the stack,
* the relocation address, and how far U-Boot is moved by relocation are
* set in the global data structure.
*/
ulong board_get_usable_ram_top(ulong total_size)
{
return gd->ram_size;
}

View File

@@ -0,0 +1,43 @@
/*
* (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/e820.h>
unsigned install_e820_map(unsigned max_entries, struct e820entry *entries)
{
entries[0].addr = 0;
entries[0].size = ISA_START_ADDRESS;
entries[0].type = E820_RAM;
entries[1].addr = ISA_START_ADDRESS;
entries[1].size = ISA_END_ADDRESS - ISA_START_ADDRESS;
entries[1].type = E820_RESERVED;
/*
* since we use memalign(malloc) to allocate high memory for
* storing ACPI tables, we need to reserve them in e820 tables,
* otherwise kernel will reclaim them and data will be corrupted
*/
entries[2].addr = ISA_END_ADDRESS;
entries[2].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS;
entries[2].type = E820_RAM;
/* for simplicity, reserve entire malloc space */
entries[3].addr = gd->relocaddr - TOTAL_MALLOC_LEN;
entries[3].size = TOTAL_MALLOC_LEN;
entries[3].type = E820_RESERVED;
entries[4].addr = gd->relocaddr;
entries[4].size = gd->ram_size - gd->relocaddr;
entries[4].type = E820_RESERVED;
entries[5].addr = CONFIG_PCIE_ECAM_BASE;
entries[5].size = CONFIG_PCIE_ECAM_SIZE;
entries[5].type = E820_RESERVED;
return 6;
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <pci.h>
#include <qfw.h>
#include <asm/irq.h>
#include <asm/post.h>
#include <asm/processor.h>
#include <asm/arch/device.h>
#include <asm/arch/qemu.h>
static bool i440fx;
#ifdef CONFIG_QFW
/* on x86, the qfw registers are all IO ports */
#define FW_CONTROL_PORT 0x510
#define FW_DATA_PORT 0x511
#define FW_DMA_PORT_LOW 0x514
#define FW_DMA_PORT_HIGH 0x518
static void qemu_x86_fwcfg_read_entry_pio(uint16_t entry,
uint32_t size, void *address)
{
uint32_t i = 0;
uint8_t *data = address;
/*
* writting FW_CFG_INVALID will cause read operation to resume at
* last offset, otherwise read will start at offset 0
*
* Note: on platform where the control register is IO port, the
* endianness is little endian.
*/
if (entry != FW_CFG_INVALID)
outw(cpu_to_le16(entry), FW_CONTROL_PORT);
/* the endianness of data register is string-preserving */
while (size--)
data[i++] = inb(FW_DATA_PORT);
}
static void qemu_x86_fwcfg_read_entry_dma(struct fw_cfg_dma_access *dma)
{
/* the DMA address register is big endian */
outl(cpu_to_be32((uint32_t)dma), FW_DMA_PORT_HIGH);
while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR)
__asm__ __volatile__ ("pause");
}
static struct fw_cfg_arch_ops fwcfg_x86_ops = {
.arch_read_pio = qemu_x86_fwcfg_read_entry_pio,
.arch_read_dma = qemu_x86_fwcfg_read_entry_dma
};
#endif
static void enable_pm_piix(void)
{
u8 en;
u16 cmd;
/* Set the PM I/O base */
pci_write_config32(PIIX_PM, PMBA, CONFIG_ACPI_PM1_BASE | 1);
/* Enable access to the PM I/O space */
pci_read_config16(PIIX_PM, PCI_COMMAND, &cmd);
cmd |= PCI_COMMAND_IO;
pci_write_config16(PIIX_PM, PCI_COMMAND, cmd);
/* PM I/O Space Enable (PMIOSE) */
pci_read_config8(PIIX_PM, PMREGMISC, &en);
en |= PMIOSE;
pci_write_config8(PIIX_PM, PMREGMISC, en);
}
static void enable_pm_ich9(void)
{
/* Set the PM I/O base */
pci_write_config32(ICH9_PM, PMBA, CONFIG_ACPI_PM1_BASE | 1);
}
static void qemu_chipset_init(void)
{
u16 device, xbcs;
int pam, i;
/*
* i440FX and Q35 chipset have different PAM register offset, but with
* the same bitfield layout. Here we determine the offset based on its
* PCI device ID.
*/
pci_read_config16(PCI_BDF(0, 0, 0), PCI_DEVICE_ID, &device);
i440fx = (device == PCI_DEVICE_ID_INTEL_82441);
pam = i440fx ? I440FX_PAM : Q35_PAM;
/*
* Initialize Programmable Attribute Map (PAM) Registers
*
* Configure legacy segments C/D/E/F to system RAM
*/
for (i = 0; i < PAM_NUM; i++)
pci_write_config8(PCI_BDF(0, 0, 0), pam + i, PAM_RW);
if (i440fx) {
/*
* Enable legacy IDE I/O ports decode
*
* Note: QEMU always decode legacy IDE I/O port on PIIX chipset.
* However Linux ata_piix driver does sanity check on these two
* registers to see whether legacy ports decode is turned on.
* This is to make Linux ata_piix driver happy.
*/
pci_write_config16(PIIX_IDE, IDE0_TIM, IDE_DECODE_EN);
pci_write_config16(PIIX_IDE, IDE1_TIM, IDE_DECODE_EN);
/* Enable I/O APIC */
pci_read_config16(PIIX_ISA, XBCS, &xbcs);
xbcs |= APIC_EN;
pci_write_config16(PIIX_ISA, XBCS, xbcs);
enable_pm_piix();
} else {
/* Configure PCIe ECAM base address */
pci_write_config32(PCI_BDF(0, 0, 0), PCIEX_BAR,
CONFIG_PCIE_ECAM_BASE | BAR_EN);
enable_pm_ich9();
}
#ifdef CONFIG_QFW
qemu_fwcfg_init(&fwcfg_x86_ops);
#endif
}
int arch_cpu_init(void)
{
int ret;
post_code(POST_CPU_INIT);
ret = x86_cpu_init_f();
if (ret)
return ret;
return 0;
}
#ifndef CONFIG_EFI_STUB
int print_cpuinfo(void)
{
post_code(POST_CPU_INFO);
return default_print_cpuinfo();
}
#endif
void reset_cpu(ulong addr)
{
/* cold reset */
x86_full_reset();
}
int arch_early_init_r(void)
{
qemu_chipset_init();
return 0;
}
#ifdef CONFIG_GENERATE_MP_TABLE
int mp_determine_pci_dstirq(int bus, int dev, int func, int pirq)
{
u8 irq;
if (i440fx) {
/*
* Not like most x86 platforms, the PIRQ[A-D] on PIIX3 are not
* connected to I/O APIC INTPIN#16-19. Instead they are routed
* to an irq number controled by the PIRQ routing register.
*/
pci_read_config8(PCI_BDF(bus, dev, func),
PCI_INTERRUPT_LINE, &irq);
} else {
/*
* ICH9's PIRQ[A-H] are not consecutive numbers from 0 to 7.
* PIRQ[A-D] still maps to [0-3] but PIRQ[E-H] maps to [8-11].
*/
irq = pirq < 8 ? pirq + 16 : pirq + 12;
}
return irq;
}
#endif

View File

@@ -0,0 +1,121 @@
#
# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
#
# SPDX-License-Identifier: GPL-2.0+
#
config INTEL_QUARK
bool
select HAVE_RMU
if INTEL_QUARK
config HAVE_RMU
bool "Add a Remote Management Unit (RMU) binary"
help
Select this option to add a Remote Management Unit (RMU) binary
to the resulting U-Boot image. It is a data block (up to 64K) of
machine-specific code which must be put in the flash for the RMU
within the Quark SoC processor to access when powered up before
system BIOS is executed.
config RMU_FILE
string "Remote Management Unit (RMU) binary filename"
depends on HAVE_RMU
default "rmu.bin"
help
The filename of the file to use as Remote Management Unit (RMU)
binary in the board directory.
config RMU_ADDR
hex "Remote Management Unit (RMU) binary location"
depends on HAVE_RMU
default 0xfff00000
help
The location of the RMU binary is determined by a strap. It must be
put in flash at a location matching the strap-determined base address.
The default base address of 0xfff00000 indicates that the binary must
be located at offset 0 from the beginning of a 1MB flash device.
config HAVE_CMC
bool
default HAVE_RMU
config CMC_FILE
string
depends on HAVE_CMC
default RMU_FILE
config CMC_ADDR
hex
depends on HAVE_CMC
default RMU_ADDR
config ESRAM_BASE
hex
default 0x80000000
help
Embedded SRAM (eSRAM) memory-mapped base address.
config PCIE_ECAM_BASE
hex
default 0xe0000000
config RCBA_BASE
hex
default 0xfed1c000
help
Root Complex register block memory-mapped base address.
config ACPI_PM1_BASE
hex
default 0x1000
help
ACPI Power Managment 1 (PM1) i/o-mapped base address.
This device is defined in ACPI specification, with 16 bytes in size.
config ACPI_PBLK_BASE
hex
default 0x1010
help
ACPI Processor Block (PBLK) i/o-mapped base address.
This device is defined in ACPI specification, with 16 bytes in size.
config SPI_DMA_BASE
hex
default 0x1020
help
SPI DMA i/o-mapped base address.
config GPIO_BASE
hex
default 0x1080
help
GPIO i/o-mapped base address.
config ACPI_GPE0_BASE
hex
default 0x1100
help
ACPI General Purpose Event 0 (GPE0) i/o-mapped base address.
This device is defined in ACPI specification, with 64 bytes in size.
config WDT_BASE
hex
default 0x1140
help
Watchdog timer i/o-mapped base address.
config SYS_CAR_ADDR
hex
default ESRAM_BASE
config SYS_CAR_SIZE
hex
default 0x8000
help
Space in bytes in eSRAM used as Cache-As-ARM (CAR).
Note this size must not exceed eSRAM's total size.
endif

View File

@@ -0,0 +1,9 @@
#
# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += car.o dram.o irq.o msg_port.o quark.o
obj-y += mrc.o mrc_util.o hte.o smc.o
obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o

View File

@@ -0,0 +1,163 @@
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/acpi_table.h>
#include <asm/ioapic.h>
#include <asm/mpspec.h>
#include <asm/tables.h>
#include <asm/arch/iomap.h>
void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs,
void *dsdt)
{
struct acpi_table_header *header = &(fadt->header);
u16 pmbase = ACPI_PM1_BASE_ADDRESS;
memset((void *)fadt, 0, sizeof(struct acpi_fadt));
acpi_fill_header(header, "FACP");
header->length = sizeof(struct acpi_fadt);
header->revision = 4;
fadt->firmware_ctrl = (u32)facs;
fadt->dsdt = (u32)dsdt;
fadt->preferred_pm_profile = ACPI_PM_UNSPECIFIED;
fadt->sci_int = 9;
fadt->smi_cmd = 0;
fadt->acpi_enable = 0;
fadt->acpi_disable = 0;
fadt->s4bios_req = 0;
fadt->pstate_cnt = 0;
fadt->pm1a_evt_blk = pmbase;
fadt->pm1b_evt_blk = 0x0;
fadt->pm1a_cnt_blk = pmbase + 0x4;
fadt->pm1b_cnt_blk = 0x0;
fadt->pm2_cnt_blk = 0x0;
fadt->pm_tmr_blk = pmbase + 0x8;
fadt->gpe0_blk = ACPI_GPE0_BASE_ADDRESS;
fadt->gpe1_blk = 0;
fadt->pm1_evt_len = 4;
fadt->pm1_cnt_len = 2;
fadt->pm2_cnt_len = 0;
fadt->pm_tmr_len = 4;
fadt->gpe0_blk_len = 8;
fadt->gpe1_blk_len = 0;
fadt->gpe1_base = 0;
fadt->cst_cnt = 0;
fadt->p_lvl2_lat = ACPI_FADT_C2_NOT_SUPPORTED;
fadt->p_lvl3_lat = ACPI_FADT_C3_NOT_SUPPORTED;
fadt->flush_size = 0;
fadt->flush_stride = 0;
fadt->duty_offset = 1;
fadt->duty_width = 3;
fadt->day_alrm = 0x00;
fadt->mon_alrm = 0x00;
fadt->century = 0x00;
fadt->iapc_boot_arch = ACPI_FADT_LEGACY_DEVICES;
fadt->flags = ACPI_FADT_WBINVD | ACPI_FADT_C1_SUPPORTED |
ACPI_FADT_POWER_BUTTON | ACPI_FADT_SLEEP_BUTTON |
ACPI_FADT_S4_RTC_WAKE | ACPI_FADT_RESET_REGISTER |
ACPI_FADT_PLATFORM_CLOCK;
fadt->reset_reg.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->reset_reg.bit_width = 8;
fadt->reset_reg.bit_offset = 0;
fadt->reset_reg.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
fadt->reset_reg.addrl = IO_PORT_RESET;
fadt->reset_reg.addrh = 0;
fadt->reset_value = SYS_RST | RST_CPU;
fadt->x_firmware_ctl_l = (u32)facs;
fadt->x_firmware_ctl_h = 0;
fadt->x_dsdt_l = (u32)dsdt;
fadt->x_dsdt_h = 0;
fadt->x_pm1a_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8;
fadt->x_pm1a_evt_blk.bit_offset = 0;
fadt->x_pm1a_evt_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
fadt->x_pm1a_evt_blk.addrl = fadt->pm1a_evt_blk;
fadt->x_pm1a_evt_blk.addrh = 0x0;
fadt->x_pm1b_evt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm1b_evt_blk.bit_width = 0;
fadt->x_pm1b_evt_blk.bit_offset = 0;
fadt->x_pm1b_evt_blk.access_size = 0;
fadt->x_pm1b_evt_blk.addrl = 0x0;
fadt->x_pm1b_evt_blk.addrh = 0x0;
fadt->x_pm1a_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm1a_cnt_blk.bit_width = fadt->pm1_cnt_len * 8;
fadt->x_pm1a_cnt_blk.bit_offset = 0;
fadt->x_pm1a_cnt_blk.access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
fadt->x_pm1a_cnt_blk.addrl = fadt->pm1a_cnt_blk;
fadt->x_pm1a_cnt_blk.addrh = 0x0;
fadt->x_pm1b_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm1b_cnt_blk.bit_width = 0;
fadt->x_pm1b_cnt_blk.bit_offset = 0;
fadt->x_pm1b_cnt_blk.access_size = 0;
fadt->x_pm1b_cnt_blk.addrl = 0x0;
fadt->x_pm1b_cnt_blk.addrh = 0x0;
fadt->x_pm2_cnt_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm2_cnt_blk.bit_width = fadt->pm2_cnt_len * 8;
fadt->x_pm2_cnt_blk.bit_offset = 0;
fadt->x_pm2_cnt_blk.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
fadt->x_pm2_cnt_blk.addrl = fadt->pm2_cnt_blk;
fadt->x_pm2_cnt_blk.addrh = 0x0;
fadt->x_pm_tmr_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_pm_tmr_blk.bit_width = fadt->pm_tmr_len * 8;
fadt->x_pm_tmr_blk.bit_offset = 0;
fadt->x_pm_tmr_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
fadt->x_pm_tmr_blk.addrl = fadt->pm_tmr_blk;
fadt->x_pm_tmr_blk.addrh = 0x0;
fadt->x_gpe0_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_gpe0_blk.bit_width = fadt->gpe0_blk_len * 8;
fadt->x_gpe0_blk.bit_offset = 0;
fadt->x_gpe0_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
fadt->x_gpe0_blk.addrl = fadt->gpe0_blk;
fadt->x_gpe0_blk.addrh = 0x0;
fadt->x_gpe1_blk.space_id = ACPI_ADDRESS_SPACE_IO;
fadt->x_gpe1_blk.bit_width = 0;
fadt->x_gpe1_blk.bit_offset = 0;
fadt->x_gpe1_blk.access_size = 0;
fadt->x_gpe1_blk.addrl = 0x0;
fadt->x_gpe1_blk.addrh = 0x0;
header->checksum = table_compute_checksum(fadt, header->length);
}
static int acpi_create_madt_irq_overrides(u32 current)
{
struct acpi_madt_irqoverride *irqovr;
u16 sci_flags = MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_HIGH;
int length = 0;
irqovr = (void *)current;
length += acpi_create_madt_irqoverride(irqovr, 0, 0, 2, 0);
irqovr = (void *)(current + length);
length += acpi_create_madt_irqoverride(irqovr, 0, 9, 9, sci_flags);
return length;
}
u32 acpi_fill_madt(u32 current)
{
current += acpi_create_madt_lapics(current);
current += acpi_create_madt_ioapic((struct acpi_madt_ioapic *)current,
io_apic_read(IO_APIC_ID) >> 24, IO_APIC_ADDR, 0);
current += acpi_create_madt_irq_overrides(current);
return current;
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <asm/pci.h>
#include <asm/post.h>
#include <asm/arch/quark.h>
#include <asm/arch/msg_port.h>
.globl car_init
car_init:
post_code(POST_CAR_START)
/*
* Quark SoC contains an embedded 512KiB SRAM (eSRAM) that is
* initialized by hardware. eSRAM is the ideal place to be used
* for Cache-As-RAM (CAR) before system memory is available.
*
* Relocate this eSRAM to a suitable location in the physical
* memory map and enable it.
*/
/* Host Memory Bound Register P03h:R08h */
mov $((MSG_PORT_HOST_BRIDGE << 16) | (HM_BOUND << 8)), %eax
mov $(DRAM_BASE + DRAM_MAX_SIZE + ESRAM_SIZE), %edx
lea 1f, %esp
jmp msg_port_write
1:
/* eSRAM Block Page Control Register P05h:R82h */
mov $((MSG_PORT_MEM_MGR << 16) | (ESRAM_BLK_CTRL << 8)), %eax
mov $(ESRAM_BLOCK_MODE | (CONFIG_ESRAM_BASE >> 24)), %edx
lea 2f, %esp
jmp msg_port_write
2:
post_code(POST_CAR_CPU_CACHE)
jmp car_init_ret
msg_port_read:
/*
* Parameter:
* eax[23:16] - Message Port ID
* eax[15:08] - Register Address
*
* Return Value:
* eax - Message Port Register value
*
* Return Address: esp
*/
or $((MSG_OP_READ << 24) | MSG_BYTE_ENABLE), %eax
mov %eax, %ebx
/* Write MCR B0:D0:F0:RD0 */
mov $(PCI_CFG_EN | MSG_CTRL_REG), %eax
mov $PCI_REG_ADDR, %dx
out %eax, %dx
mov $PCI_REG_DATA, %dx
mov %ebx, %eax
out %eax, %dx
/* Read MDR B0:D0:F0:RD4 */
mov $(PCI_CFG_EN | MSG_DATA_REG), %eax
mov $PCI_REG_ADDR, %dx
out %eax, %dx
mov $PCI_REG_DATA, %dx
in %dx, %eax
jmp *%esp
msg_port_write:
/*
* Parameter:
* eax[23:16] - Message Port ID
* eax[15:08] - Register Address
* edx - Message Port Register value to write
*
* Return Address: esp
*/
or $((MSG_OP_WRITE << 24) | MSG_BYTE_ENABLE), %eax
mov %eax, %esi
mov %edx, %edi
/* Write MDR B0:D0:F0:RD4 */
mov $(PCI_CFG_EN | MSG_DATA_REG), %eax
mov $PCI_REG_ADDR, %dx
out %eax, %dx
mov $PCI_REG_DATA, %dx
mov %edi, %eax
out %eax, %dx
/* Write MCR B0:D0:F0:RD0 */
mov $(PCI_CFG_EN | MSG_CTRL_REG), %eax
mov $PCI_REG_ADDR, %dx
out %eax, %dx
mov $PCI_REG_DATA, %dx
mov %esi, %eax
out %eax, %dx
jmp *%esp

View File

@@ -0,0 +1,182 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <asm/mrccache.h>
#include <asm/mtrr.h>
#include <asm/post.h>
#include <asm/arch/mrc.h>
#include <asm/arch/msg_port.h>
#include <asm/arch/quark.h>
DECLARE_GLOBAL_DATA_PTR;
static __maybe_unused int prepare_mrc_cache(struct mrc_params *mrc_params)
{
struct mrc_data_container *cache;
struct mrc_region entry;
int ret;
ret = mrccache_get_region(NULL, &entry);
if (ret)
return ret;
cache = mrccache_find_current(&entry);
if (!cache)
return -ENOENT;
debug("%s: mrc cache at %p, size %x checksum %04x\n", __func__,
cache->data, cache->data_size, cache->checksum);
/* copy mrc cache to the mrc_params */
memcpy(&mrc_params->timings, cache->data, cache->data_size);
return 0;
}
static int mrc_configure_params(struct mrc_params *mrc_params)
{
const void *blob = gd->fdt_blob;
int node;
int mrc_flags;
node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_QRK_MRC);
if (node < 0) {
debug("%s: Cannot find MRC node\n", __func__);
return -EINVAL;
}
#ifdef CONFIG_ENABLE_MRC_CACHE
mrc_params->boot_mode = prepare_mrc_cache(mrc_params);
if (mrc_params->boot_mode)
mrc_params->boot_mode = BM_COLD;
else
mrc_params->boot_mode = BM_FAST;
#else
mrc_params->boot_mode = BM_COLD;
#endif
/*
* TODO:
*
* We need determine ECC by pin strap state
*
* Disable ECC by default for now
*/
mrc_params->ecc_enables = 0;
mrc_flags = fdtdec_get_int(blob, node, "flags", 0);
if (mrc_flags & MRC_FLAG_SCRAMBLE_EN)
mrc_params->scrambling_enables = 1;
else
mrc_params->scrambling_enables = 0;
mrc_params->dram_width = fdtdec_get_int(blob, node, "dram-width", 0);
mrc_params->ddr_speed = fdtdec_get_int(blob, node, "dram-speed", 0);
mrc_params->ddr_type = fdtdec_get_int(blob, node, "dram-type", 0);
mrc_params->rank_enables = fdtdec_get_int(blob, node, "rank-mask", 0);
mrc_params->channel_enables = fdtdec_get_int(blob, node,
"chan-mask", 0);
mrc_params->channel_width = fdtdec_get_int(blob, node,
"chan-width", 0);
mrc_params->address_mode = fdtdec_get_int(blob, node, "addr-mode", 0);
mrc_params->refresh_rate = fdtdec_get_int(blob, node,
"refresh-rate", 0);
mrc_params->sr_temp_range = fdtdec_get_int(blob, node,
"sr-temp-range", 0);
mrc_params->ron_value = fdtdec_get_int(blob, node,
"ron-value", 0);
mrc_params->rtt_nom_value = fdtdec_get_int(blob, node,
"rtt-nom-value", 0);
mrc_params->rd_odt_value = fdtdec_get_int(blob, node,
"rd-odt-value", 0);
mrc_params->params.density = fdtdec_get_int(blob, node,
"dram-density", 0);
mrc_params->params.cl = fdtdec_get_int(blob, node, "dram-cl", 0);
mrc_params->params.ras = fdtdec_get_int(blob, node, "dram-ras", 0);
mrc_params->params.wtr = fdtdec_get_int(blob, node, "dram-wtr", 0);
mrc_params->params.rrd = fdtdec_get_int(blob, node, "dram-rrd", 0);
mrc_params->params.faw = fdtdec_get_int(blob, node, "dram-faw", 0);
debug("MRC dram_width %d\n", mrc_params->dram_width);
debug("MRC rank_enables %d\n", mrc_params->rank_enables);
debug("MRC ddr_speed %d\n", mrc_params->ddr_speed);
debug("MRC flags: %s\n",
(mrc_params->scrambling_enables) ? "SCRAMBLE_EN" : "");
debug("MRC density=%d tCL=%d tRAS=%d tWTR=%d tRRD=%d tFAW=%d\n",
mrc_params->params.density, mrc_params->params.cl,
mrc_params->params.ras, mrc_params->params.wtr,
mrc_params->params.rrd, mrc_params->params.faw);
return 0;
}
int dram_init(void)
{
struct mrc_params mrc_params;
#ifdef CONFIG_ENABLE_MRC_CACHE
char *cache;
#endif
int ret;
memset(&mrc_params, 0, sizeof(struct mrc_params));
ret = mrc_configure_params(&mrc_params);
if (ret)
return ret;
/* Set up the DRAM by calling the memory reference code */
mrc_init(&mrc_params);
if (mrc_params.status)
return -EIO;
gd->ram_size = mrc_params.mem_size;
post_code(POST_DRAM);
/* variable range MTRR#2: RAM area */
disable_caches();
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_VAR_PHYBASE(MTRR_VAR_RAM),
0 | MTRR_TYPE_WRBACK);
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_VAR_PHYMASK(MTRR_VAR_RAM),
(~(gd->ram_size - 1)) | MTRR_PHYS_MASK_VALID);
enable_caches();
#ifdef CONFIG_ENABLE_MRC_CACHE
cache = malloc(sizeof(struct mrc_timings));
if (cache) {
memcpy(cache, &mrc_params.timings, sizeof(struct mrc_timings));
gd->arch.mrc_output = cache;
gd->arch.mrc_output_len = sizeof(struct mrc_timings);
}
#endif
return 0;
}
void dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = 0;
gd->bd->bi_dram[0].size = gd->ram_size;
}
/*
* This function looks for the highest region of memory lower than 4GB which
* has enough space for U-Boot where U-Boot is aligned on a page boundary.
* It overrides the default implementation found elsewhere which simply
* picks the end of ram, wherever that may be. The location of the stack,
* the relocation address, and how far U-Boot is moved by relocation are
* set in the global data structure.
*/
ulong board_get_usable_ram_top(ulong total_size)
{
return gd->ram_size;
}

View File

@@ -0,0 +1,396 @@
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* Ported from Intel released Quark UEFI BIOS
* QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei
*
* SPDX-License-Identifier: Intel
*/
#include <common.h>
#include <asm/arch/mrc.h>
#include <asm/arch/msg_port.h>
#include "mrc_util.h"
#include "hte.h"
/**
* Enable HTE to detect all possible errors for the given training parameters
* (per-bit or full byte lane).
*/
static void hte_enable_all_errors(void)
{
msg_port_write(HTE, 0x000200a2, 0xffffffff);
msg_port_write(HTE, 0x000200a3, 0x000000ff);
msg_port_write(HTE, 0x000200a4, 0x00000000);
}
/**
* Go and read the HTE register in order to find any error
*
* @return: The errors detected in the HTE status register
*/
static u32 hte_check_errors(void)
{
return msg_port_read(HTE, 0x000200a7);
}
/**
* Wait until HTE finishes
*/
static void hte_wait_for_complete(void)
{
u32 tmp;
ENTERFN();
do {} while ((msg_port_read(HTE, 0x00020012) & (1 << 30)) != 0);
tmp = msg_port_read(HTE, 0x00020011);
tmp |= (1 << 9);
tmp &= ~((1 << 12) | (1 << 13));
msg_port_write(HTE, 0x00020011, tmp);
LEAVEFN();
}
/**
* Clear registers related with errors in the HTE
*/
static void hte_clear_error_regs(void)
{
u32 tmp;
/*
* Clear all HTE errors and enable error checking
* for burst and chunk.
*/
tmp = msg_port_read(HTE, 0x000200a1);
tmp |= (1 << 8);
msg_port_write(HTE, 0x000200a1, tmp);
}
/**
* Execute a basic single-cache-line memory write/read/verify test using simple
* constant pattern, different for READ_TRAIN and WRITE_TRAIN modes.
*
* See hte_basic_write_read() which is the external visible wrapper.
*
* @mrc_params: host structure for all MRC global data
* @addr: memory adress being tested (must hit specific channel/rank)
* @first_run: if set then the HTE registers are configured, otherwise it is
* assumed configuration is done and we just re-run the test
* @mode: READ_TRAIN or WRITE_TRAIN (the difference is in the pattern)
*
* @return: byte lane failure on each bit (for Quark only bit0 and bit1)
*/
static u16 hte_basic_data_cmp(struct mrc_params *mrc_params, u32 addr,
u8 first_run, u8 mode)
{
u32 pattern;
u32 offset;
if (first_run) {
msg_port_write(HTE, 0x00020020, 0x01b10021);
msg_port_write(HTE, 0x00020021, 0x06000000);
msg_port_write(HTE, 0x00020022, addr >> 6);
msg_port_write(HTE, 0x00020062, 0x00800015);
msg_port_write(HTE, 0x00020063, 0xaaaaaaaa);
msg_port_write(HTE, 0x00020064, 0xcccccccc);
msg_port_write(HTE, 0x00020065, 0xf0f0f0f0);
msg_port_write(HTE, 0x00020061, 0x00030008);
if (mode == WRITE_TRAIN)
pattern = 0xc33c0000;
else /* READ_TRAIN */
pattern = 0xaa5555aa;
for (offset = 0x80; offset <= 0x8f; offset++)
msg_port_write(HTE, offset, pattern);
}
msg_port_write(HTE, 0x000200a1, 0xffff1000);
msg_port_write(HTE, 0x00020011, 0x00011000);
msg_port_write(HTE, 0x00020011, 0x00011100);
hte_wait_for_complete();
/*
* Return bits 15:8 of HTE_CH0_ERR_XSTAT to check for
* any bytelane errors.
*/
return (hte_check_errors() >> 8) & 0xff;
}
/**
* Examine a single-cache-line memory with write/read/verify test using multiple
* data patterns (victim-aggressor algorithm).
*
* See hte_write_stress_bit_lanes() which is the external visible wrapper.
*
* @mrc_params: host structure for all MRC global data
* @addr: memory adress being tested (must hit specific channel/rank)
* @loop_cnt: number of test iterations
* @seed_victim: victim data pattern seed
* @seed_aggressor: aggressor data pattern seed
* @victim_bit: should be 0 as auto-rotate feature is in use
* @first_run: if set then the HTE registers are configured, otherwise it is
* assumed configuration is done and we just re-run the test
*
* @return: byte lane failure on each bit (for Quark only bit0 and bit1)
*/
static u16 hte_rw_data_cmp(struct mrc_params *mrc_params, u32 addr,
u8 loop_cnt, u32 seed_victim, u32 seed_aggressor,
u8 victim_bit, u8 first_run)
{
u32 offset;
u32 tmp;
if (first_run) {
msg_port_write(HTE, 0x00020020, 0x00910024);
msg_port_write(HTE, 0x00020023, 0x00810024);
msg_port_write(HTE, 0x00020021, 0x06070000);
msg_port_write(HTE, 0x00020024, 0x06070000);
msg_port_write(HTE, 0x00020022, addr >> 6);
msg_port_write(HTE, 0x00020025, addr >> 6);
msg_port_write(HTE, 0x00020062, 0x0000002a);
msg_port_write(HTE, 0x00020063, seed_victim);
msg_port_write(HTE, 0x00020064, seed_aggressor);
msg_port_write(HTE, 0x00020065, seed_victim);
/*
* Write the pattern buffers to select the victim bit
*
* Start with bit0
*/
for (offset = 0x80; offset <= 0x8f; offset++) {
if ((offset % 8) == victim_bit)
msg_port_write(HTE, offset, 0x55555555);
else
msg_port_write(HTE, offset, 0xcccccccc);
}
msg_port_write(HTE, 0x00020061, 0x00000000);
msg_port_write(HTE, 0x00020066, 0x03440000);
msg_port_write(HTE, 0x000200a1, 0xffff1000);
}
tmp = 0x10001000 | (loop_cnt << 16);
msg_port_write(HTE, 0x00020011, tmp);
msg_port_write(HTE, 0x00020011, tmp | (1 << 8));
hte_wait_for_complete();
/*
* Return bits 15:8 of HTE_CH0_ERR_XSTAT to check for
* any bytelane errors.
*/
return (hte_check_errors() >> 8) & 0xff;
}
/**
* Use HW HTE engine to initialize or test all memory attached to a given DUNIT.
* If flag is MRC_MEM_INIT, this routine writes 0s to all memory locations to
* initialize ECC. If flag is MRC_MEM_TEST, this routine will send an 5AA55AA5
* pattern to all memory locations on the RankMask and then read it back.
* Then it sends an A55AA55A pattern to all memory locations on the RankMask
* and reads it back.
*
* @mrc_params: host structure for all MRC global data
* @flag: MRC_MEM_INIT or MRC_MEM_TEST
*
* @return: errors register showing HTE failures. Also prints out which rank
* failed the HTE test if failure occurs. For rank detection to work,
* the address map must be left in its default state. If MRC changes
* the address map, this function must be modified to change it back
* to default at the beginning, then restore it at the end.
*/
u32 hte_mem_init(struct mrc_params *mrc_params, u8 flag)
{
u32 offset;
int test_num;
int i;
/*
* Clear out the error registers at the start of each memory
* init or memory test run.
*/
hte_clear_error_regs();
msg_port_write(HTE, 0x00020062, 0x00000015);
for (offset = 0x80; offset <= 0x8f; offset++)
msg_port_write(HTE, offset, ((offset & 1) ? 0xa55a : 0x5aa5));
msg_port_write(HTE, 0x00020021, 0x00000000);
msg_port_write(HTE, 0x00020022, (mrc_params->mem_size >> 6) - 1);
msg_port_write(HTE, 0x00020063, 0xaaaaaaaa);
msg_port_write(HTE, 0x00020064, 0xcccccccc);
msg_port_write(HTE, 0x00020065, 0xf0f0f0f0);
msg_port_write(HTE, 0x00020066, 0x03000000);
switch (flag) {
case MRC_MEM_INIT:
/*
* Only 1 write pass through memory is needed
* to initialize ECC
*/
test_num = 1;
break;
case MRC_MEM_TEST:
/* Write/read then write/read with inverted pattern */
test_num = 4;
break;
default:
DPF(D_INFO, "Unknown parameter for flag: %d\n", flag);
return 0xffffffff;
}
DPF(D_INFO, "hte_mem_init");
for (i = 0; i < test_num; i++) {
DPF(D_INFO, ".");
if (i == 0) {
msg_port_write(HTE, 0x00020061, 0x00000000);
msg_port_write(HTE, 0x00020020, 0x00110010);
} else if (i == 1) {
msg_port_write(HTE, 0x00020061, 0x00000000);
msg_port_write(HTE, 0x00020020, 0x00010010);
} else if (i == 2) {
msg_port_write(HTE, 0x00020061, 0x00010100);
msg_port_write(HTE, 0x00020020, 0x00110010);
} else {
msg_port_write(HTE, 0x00020061, 0x00010100);
msg_port_write(HTE, 0x00020020, 0x00010010);
}
msg_port_write(HTE, 0x00020011, 0x00111000);
msg_port_write(HTE, 0x00020011, 0x00111100);
hte_wait_for_complete();
/* If this is a READ pass, check for errors at the end */
if ((i % 2) == 1) {
/* Return immediately if error */
if (hte_check_errors())
break;
}
}
DPF(D_INFO, "done\n");
return hte_check_errors();
}
/**
* Execute a basic single-cache-line memory write/read/verify test using simple
* constant pattern, different for READ_TRAIN and WRITE_TRAIN modes.
*
* @mrc_params: host structure for all MRC global data
* @addr: memory adress being tested (must hit specific channel/rank)
* @first_run: if set then the HTE registers are configured, otherwise it is
* assumed configuration is done and we just re-run the test
* @mode: READ_TRAIN or WRITE_TRAIN (the difference is in the pattern)
*
* @return: byte lane failure on each bit (for Quark only bit0 and bit1)
*/
u16 hte_basic_write_read(struct mrc_params *mrc_params, u32 addr,
u8 first_run, u8 mode)
{
u16 errors;
ENTERFN();
/* Enable all error reporting in preparation for HTE test */
hte_enable_all_errors();
hte_clear_error_regs();
errors = hte_basic_data_cmp(mrc_params, addr, first_run, mode);
LEAVEFN();
return errors;
}
/**
* Examine a single-cache-line memory with write/read/verify test using multiple
* data patterns (victim-aggressor algorithm).
*
* @mrc_params: host structure for all MRC global data
* @addr: memory adress being tested (must hit specific channel/rank)
* @first_run: if set then the HTE registers are configured, otherwise it is
* assumed configuration is done and we just re-run the test
*
* @return: byte lane failure on each bit (for Quark only bit0 and bit1)
*/
u16 hte_write_stress_bit_lanes(struct mrc_params *mrc_params,
u32 addr, u8 first_run)
{
u16 errors;
u8 victim_bit = 0;
ENTERFN();
/* Enable all error reporting in preparation for HTE test */
hte_enable_all_errors();
hte_clear_error_regs();
/*
* Loop through each bit in the bytelane.
*
* Each pass creates a victim bit while keeping all other bits the same
* as aggressors. AVN HTE adds an auto-rotate feature which allows us
* to program the entire victim/aggressor sequence in 1 step.
*
* The victim bit rotates on each pass so no need to have software
* implement a victim bit loop like on VLV.
*/
errors = hte_rw_data_cmp(mrc_params, addr, HTE_LOOP_CNT,
HTE_LFSR_VICTIM_SEED, HTE_LFSR_AGRESSOR_SEED,
victim_bit, first_run);
LEAVEFN();
return errors;
}
/**
* Execute a basic single-cache-line memory write or read.
* This is just for receive enable / fine write-levelling purpose.
*
* @addr: memory adress being tested (must hit specific channel/rank)
* @first_run: if set then the HTE registers are configured, otherwise it is
* assumed configuration is done and we just re-run the test
* @is_write: when non-zero memory write operation executed, otherwise read
*/
void hte_mem_op(u32 addr, u8 first_run, u8 is_write)
{
u32 offset;
u32 tmp;
hte_enable_all_errors();
hte_clear_error_regs();
if (first_run) {
tmp = is_write ? 0x01110021 : 0x01010021;
msg_port_write(HTE, 0x00020020, tmp);
msg_port_write(HTE, 0x00020021, 0x06000000);
msg_port_write(HTE, 0x00020022, addr >> 6);
msg_port_write(HTE, 0x00020062, 0x00800015);
msg_port_write(HTE, 0x00020063, 0xaaaaaaaa);
msg_port_write(HTE, 0x00020064, 0xcccccccc);
msg_port_write(HTE, 0x00020065, 0xf0f0f0f0);
msg_port_write(HTE, 0x00020061, 0x00030008);
for (offset = 0x80; offset <= 0x8f; offset++)
msg_port_write(HTE, offset, 0xc33c0000);
}
msg_port_write(HTE, 0x000200a1, 0xffff1000);
msg_port_write(HTE, 0x00020011, 0x00011000);
msg_port_write(HTE, 0x00020011, 0x00011100);
hte_wait_for_complete();
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* Ported from Intel released Quark UEFI BIOS
* QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei
*
* SPDX-License-Identifier: Intel
*/
#ifndef _HTE_H_
#define _HTE_H_
enum {
MRC_MEM_INIT,
MRC_MEM_TEST
};
enum {
READ_TRAIN,
WRITE_TRAIN
};
/*
* EXP_LOOP_CNT field of HTE_CMD_CTL
*
* This CANNOT be less than 4!
*/
#define HTE_LOOP_CNT 5
/* random seed for victim */
#define HTE_LFSR_VICTIM_SEED 0xf294ba21
/* random seed for aggressor */
#define HTE_LFSR_AGRESSOR_SEED 0xeba7492d
u32 hte_mem_init(struct mrc_params *mrc_params, u8 flag);
u16 hte_basic_write_read(struct mrc_params *mrc_params, u32 addr,
u8 first_run, u8 mode);
u16 hte_write_stress_bit_lanes(struct mrc_params *mrc_params,
u32 addr, u8 first_run);
void hte_mem_op(u32 addr, u8 first_run, u8 is_write);
#endif /* _HTE_H_ */

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
* Copyright (C) 2015 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <asm/irq.h>
#include <asm/arch/device.h>
#include <asm/arch/quark.h>
int quark_irq_router_probe(struct udevice *dev)
{
struct quark_rcba *rcba;
u32 base;
qrk_pci_read_config_dword(QUARK_LEGACY_BRIDGE, LB_RCBA, &base);
base &= ~MEM_BAR_EN;
rcba = (struct quark_rcba *)base;
/*
* Route Quark PCI device interrupt pin to PIRQ
*
* Route device#23's INTA/B/C/D to PIRQA/B/C/D
* Route device#20,21's INTA/B/C/D to PIRQE/F/G/H
*/
writew(PIRQC, &rcba->rmu_ir);
writew(PIRQA | (PIRQB << 4) | (PIRQC << 8) | (PIRQD << 12),
&rcba->d23_ir);
writew(PIRQD, &rcba->core_ir);
writew(PIRQE | (PIRQF << 4) | (PIRQG << 8) | (PIRQH << 12),
&rcba->d20d21_ir);
return irq_router_common_init(dev);
}
static const struct udevice_id quark_irq_router_ids[] = {
{ .compatible = "intel,quark-irq-router" },
{ }
};
U_BOOT_DRIVER(quark_irq_router_drv) = {
.name = "quark_intel_irq",
.id = UCLASS_IRQ,
.of_match = quark_irq_router_ids,
.probe = quark_irq_router_probe,
};

View File

@@ -0,0 +1,205 @@
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* Ported from Intel released Quark UEFI BIOS
* QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei
*
* SPDX-License-Identifier: Intel
*/
/*
* This is the main Quark Memory Reference Code (MRC)
*
* These functions are generic and should work for any Quark-based board.
*
* MRC requires two data structures to be passed in which are initialized by
* mrc_adjust_params().
*
* The basic flow is as follows:
* 01) Check for supported DDR speed configuration
* 02) Set up Memory Manager buffer as pass-through (POR)
* 03) Set Channel Interleaving Mode and Channel Stride to the most aggressive
* setting possible
* 04) Set up the Memory Controller logic
* 05) Set up the DDR_PHY logic
* 06) Initialise the DRAMs (JEDEC)
* 07) Perform the Receive Enable Calibration algorithm
* 08) Perform the Write Leveling algorithm
* 09) Perform the Read Training algorithm (includes internal Vref)
* 10) Perform the Write Training algorithm
* 11) Set Channel Interleaving Mode and Channel Stride to the desired settings
*
* DRAM unit configuration based on Valleyview MRC.
*/
#include <common.h>
#include <version.h>
#include <asm/arch/mrc.h>
#include <asm/arch/msg_port.h>
#include "mrc_util.h"
#include "smc.h"
static const struct mem_init init[] = {
{ 0x0101, BM_COLD | BM_FAST | BM_WARM | BM_S3, clear_self_refresh },
{ 0x0200, BM_COLD | BM_FAST | BM_WARM | BM_S3, prog_ddr_timing_control },
{ 0x0103, BM_COLD | BM_FAST , prog_decode_before_jedec },
{ 0x0104, BM_COLD | BM_FAST , perform_ddr_reset },
{ 0x0300, BM_COLD | BM_FAST | BM_S3, ddrphy_init },
{ 0x0400, BM_COLD | BM_FAST , perform_jedec_init },
{ 0x0105, BM_COLD | BM_FAST , set_ddr_init_complete },
{ 0x0106, BM_FAST | BM_WARM | BM_S3, restore_timings },
{ 0x0106, BM_COLD , default_timings },
{ 0x0500, BM_COLD , rcvn_cal },
{ 0x0600, BM_COLD , wr_level },
{ 0x0120, BM_COLD , prog_page_ctrl },
{ 0x0700, BM_COLD , rd_train },
{ 0x0800, BM_COLD , wr_train },
{ 0x010b, BM_COLD , store_timings },
{ 0x010c, BM_COLD | BM_FAST | BM_WARM | BM_S3, enable_scrambling },
{ 0x010d, BM_COLD | BM_FAST | BM_WARM | BM_S3, prog_ddr_control },
{ 0x010e, BM_COLD | BM_FAST | BM_WARM | BM_S3, prog_dra_drb },
{ 0x010f, BM_WARM | BM_S3, perform_wake },
{ 0x0110, BM_COLD | BM_FAST | BM_WARM | BM_S3, change_refresh_period },
{ 0x0111, BM_COLD | BM_FAST | BM_WARM | BM_S3, set_auto_refresh },
{ 0x0112, BM_COLD | BM_FAST | BM_WARM | BM_S3, ecc_enable },
{ 0x0113, BM_COLD | BM_FAST , memory_test },
{ 0x0114, BM_COLD | BM_FAST | BM_WARM | BM_S3, lock_registers }
};
/* Adjust configuration parameters before initialization sequence */
static void mrc_adjust_params(struct mrc_params *mrc_params)
{
const struct dram_params *dram_params;
uint8_t dram_width;
uint32_t rank_enables;
uint32_t channel_width;
ENTERFN();
/* initially expect success */
mrc_params->status = MRC_SUCCESS;
dram_width = mrc_params->dram_width;
rank_enables = mrc_params->rank_enables;
channel_width = mrc_params->channel_width;
/*
* Setup board layout (must be reviewed as is selecting static timings)
* 0 == R0 (DDR3 x16), 1 == R1 (DDR3 x16),
* 2 == DV (DDR3 x8), 3 == SV (DDR3 x8).
*/
if (dram_width == X8)
mrc_params->board_id = 2; /* select x8 layout */
else
mrc_params->board_id = 0; /* select x16 layout */
/* initially no memory */
mrc_params->mem_size = 0;
/* begin of channel settings */
dram_params = &mrc_params->params;
/*
* Determine column bits:
*
* Column: 11 for 8Gbx8, else 10
*/
mrc_params->column_bits[0] =
(dram_params[0].density == 4) &&
(dram_width == X8) ? 11 : 10;
/*
* Determine row bits:
*
* 512Mbx16=12 512Mbx8=13
* 1Gbx16=13 1Gbx8=14
* 2Gbx16=14 2Gbx8=15
* 4Gbx16=15 4Gbx8=16
* 8Gbx16=16 8Gbx8=16
*/
mrc_params->row_bits[0] = 12 + dram_params[0].density +
(dram_params[0].density < 4) &&
(dram_width == X8) ? 1 : 0;
/*
* Determine per-channel memory size:
*
* (For 2 RANKs, multiply by 2)
* (For 16 bit data bus, divide by 2)
*
* DENSITY WIDTH MEM_AVAILABLE
* 512Mb x16 0x008000000 ( 128MB)
* 512Mb x8 0x010000000 ( 256MB)
* 1Gb x16 0x010000000 ( 256MB)
* 1Gb x8 0x020000000 ( 512MB)
* 2Gb x16 0x020000000 ( 512MB)
* 2Gb x8 0x040000000 (1024MB)
* 4Gb x16 0x040000000 (1024MB)
* 4Gb x8 0x080000000 (2048MB)
*/
mrc_params->channel_size[0] = 1 << dram_params[0].density;
mrc_params->channel_size[0] *= (dram_width == X8) ? 2 : 1;
mrc_params->channel_size[0] *= (rank_enables == 0x3) ? 2 : 1;
mrc_params->channel_size[0] *= (channel_width == X16) ? 1 : 2;
/* Determine memory size (convert number of 64MB/512Mb units) */
mrc_params->mem_size += mrc_params->channel_size[0] << 26;
LEAVEFN();
}
static void mrc_mem_init(struct mrc_params *mrc_params)
{
int i;
ENTERFN();
/* MRC started */
mrc_post_code(0x01, 0x00);
if (mrc_params->boot_mode != BM_COLD) {
if (mrc_params->ddr_speed != mrc_params->timings.ddr_speed) {
/* full training required as frequency changed */
mrc_params->boot_mode = BM_COLD;
}
}
for (i = 0; i < ARRAY_SIZE(init); i++) {
uint64_t my_tsc;
if (mrc_params->boot_mode & init[i].boot_path) {
uint8_t major = init[i].post_code >> 8 & 0xff;
uint8_t minor = init[i].post_code >> 0 & 0xff;
mrc_post_code(major, minor);
my_tsc = rdtsc();
init[i].init_fn(mrc_params);
DPF(D_TIME, "Execution time %llx", rdtsc() - my_tsc);
}
}
/* display the timings */
print_timings(mrc_params);
/* MRC complete */
mrc_post_code(0x01, 0xff);
LEAVEFN();
}
void mrc_init(struct mrc_params *mrc_params)
{
ENTERFN();
DPF(D_INFO, "MRC Version %04x %s %s\n", MRC_VERSION,
U_BOOT_DATE, U_BOOT_TIME);
/* Set up the data structures used by mrc_mem_init() */
mrc_adjust_params(mrc_params);
/* Initialize system memory */
mrc_mem_init(mrc_params);
LEAVEFN();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* Ported from Intel released Quark UEFI BIOS
* QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei
*
* SPDX-License-Identifier: Intel
*/
#ifndef _MRC_UTIL_H_
#define _MRC_UTIL_H_
/* Turn on this macro to enable MRC debugging output */
#undef MRC_DEBUG
/* MRC Debug Support */
#define DPF debug_cond
/* debug print type */
#ifdef MRC_DEBUG
#define D_ERROR 0x0001
#define D_INFO 0x0002
#define D_REGRD 0x0004
#define D_REGWR 0x0008
#define D_FCALL 0x0010
#define D_TRN 0x0020
#define D_TIME 0x0040
#else
#define D_ERROR 0
#define D_INFO 0
#define D_REGRD 0
#define D_REGWR 0
#define D_FCALL 0
#define D_TRN 0
#define D_TIME 0
#endif
#define ENTERFN(...) debug_cond(D_FCALL, "<%s>\n", __func__)
#define LEAVEFN(...) debug_cond(D_FCALL, "</%s>\n", __func__)
#define REPORTFN(...) debug_cond(D_FCALL, "<%s/>\n", __func__)
/* Message Bus Port */
#define MEM_CTLR 0x01
#define HOST_BRIDGE 0x03
#define MEM_MGR 0x05
#define HTE 0x11
#define DDRPHY 0x12
/* number of sample points */
#define SAMPLE_CNT 3
/* number of PIs to increment per sample */
#define SAMPLE_DLY 26
enum {
/* indicates to decrease delays when looking for edge */
BACKWARD,
/* indicates to increase delays when looking for edge */
FORWARD
};
enum {
RCVN,
WDQS,
WDQX,
RDQS,
VREF,
WCMD,
WCTL,
WCLK,
MAX_ALGOS,
};
void mrc_write_mask(u32 unit, u32 addr, u32 data, u32 mask);
void mrc_alt_write_mask(u32 unit, u32 addr, u32 data, u32 mask);
void mrc_post_code(uint8_t major, uint8_t minor);
void delay_n(uint32_t ns);
void delay_u(uint32_t ms);
void select_mem_mgr(void);
void select_hte(void);
void dram_init_command(uint32_t data);
void dram_wake_command(void);
void training_message(uint8_t channel, uint8_t rank, uint8_t byte_lane);
void set_rcvn(uint8_t channel, uint8_t rank,
uint8_t byte_lane, uint32_t pi_count);
uint32_t get_rcvn(uint8_t channel, uint8_t rank, uint8_t byte_lane);
void set_rdqs(uint8_t channel, uint8_t rank,
uint8_t byte_lane, uint32_t pi_count);
uint32_t get_rdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane);
void set_wdqs(uint8_t channel, uint8_t rank,
uint8_t byte_lane, uint32_t pi_count);
uint32_t get_wdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane);
void set_wdq(uint8_t channel, uint8_t rank,
uint8_t byte_lane, uint32_t pi_count);
uint32_t get_wdq(uint8_t channel, uint8_t rank, uint8_t byte_lane);
void set_wcmd(uint8_t channel, uint32_t pi_count);
uint32_t get_wcmd(uint8_t channel);
void set_wclk(uint8_t channel, uint8_t rank, uint32_t pi_count);
uint32_t get_wclk(uint8_t channel, uint8_t rank);
void set_wctl(uint8_t channel, uint8_t rank, uint32_t pi_count);
uint32_t get_wctl(uint8_t channel, uint8_t rank);
void set_vref(uint8_t channel, uint8_t byte_lane, uint32_t setting);
uint32_t get_vref(uint8_t channel, uint8_t byte_lane);
uint32_t get_addr(uint8_t channel, uint8_t rank);
uint32_t sample_dqs(struct mrc_params *mrc_params, uint8_t channel,
uint8_t rank, bool rcvn);
void find_rising_edge(struct mrc_params *mrc_params, uint32_t delay[],
uint8_t channel, uint8_t rank, bool rcvn);
uint32_t byte_lane_mask(struct mrc_params *mrc_params);
uint32_t check_rw_coarse(struct mrc_params *mrc_params, uint32_t address);
uint32_t check_bls_ex(struct mrc_params *mrc_params, uint32_t address);
void lfsr32(uint32_t *lfsr_ptr);
void clear_pointers(void);
void print_timings(struct mrc_params *mrc_params);
#endif /* _MRC_UTIL_H_ */

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/arch/device.h>
#include <asm/arch/msg_port.h>
#include <asm/arch/quark.h>
void msg_port_setup(int op, int port, int reg)
{
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_REG,
(((op) << 24) | ((port) << 16) |
(((reg) << 8) & 0xff00) | MSG_BYTE_ENABLE));
}
u32 msg_port_read(u8 port, u32 reg)
{
u32 value;
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG,
reg & 0xffffff00);
msg_port_setup(MSG_OP_READ, port, reg);
qrk_pci_read_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, &value);
return value;
}
void msg_port_write(u8 port, u32 reg, u32 value)
{
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, value);
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG,
reg & 0xffffff00);
msg_port_setup(MSG_OP_WRITE, port, reg);
}
u32 msg_port_alt_read(u8 port, u32 reg)
{
u32 value;
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG,
reg & 0xffffff00);
msg_port_setup(MSG_OP_ALT_READ, port, reg);
qrk_pci_read_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, &value);
return value;
}
void msg_port_alt_write(u8 port, u32 reg, u32 value)
{
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, value);
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG,
reg & 0xffffff00);
msg_port_setup(MSG_OP_ALT_WRITE, port, reg);
}
u32 msg_port_io_read(u8 port, u32 reg)
{
u32 value;
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG,
reg & 0xffffff00);
msg_port_setup(MSG_OP_IO_READ, port, reg);
qrk_pci_read_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, &value);
return value;
}
void msg_port_io_write(u8 port, u32 reg, u32 value)
{
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, value);
qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG,
reg & 0xffffff00);
msg_port_setup(MSG_OP_IO_WRITE, port, reg);
}

View File

@@ -0,0 +1,366 @@
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/ioapic.h>
#include <asm/mrccache.h>
#include <asm/mtrr.h>
#include <asm/pci.h>
#include <asm/post.h>
#include <asm/arch/device.h>
#include <asm/arch/msg_port.h>
#include <asm/arch/quark.h>
static struct pci_device_id mmc_supported[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_SDIO },
{},
};
static void quark_setup_mtrr(void)
{
u32 base, mask;
int i;
disable_caches();
/* mark the VGA RAM area as uncacheable */
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_FIX_16K_A0000,
MTRR_FIX_TYPE(MTRR_TYPE_UNCACHEABLE));
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_FIX_16K_B0000,
MTRR_FIX_TYPE(MTRR_TYPE_UNCACHEABLE));
/* mark other fixed range areas as cacheable */
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_FIX_64K_00000,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_FIX_64K_40000,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_FIX_16K_80000,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_FIX_16K_90000,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
for (i = MTRR_FIX_4K_C0000; i <= MTRR_FIX_4K_FC000; i++)
msg_port_write(MSG_PORT_HOST_BRIDGE, i,
MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
/* variable range MTRR#0: ROM area */
mask = ~(CONFIG_SYS_MONITOR_LEN - 1);
base = CONFIG_SYS_TEXT_BASE & mask;
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_VAR_PHYBASE(MTRR_VAR_ROM),
base | MTRR_TYPE_WRBACK);
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_VAR_PHYMASK(MTRR_VAR_ROM),
mask | MTRR_PHYS_MASK_VALID);
/* variable range MTRR#1: eSRAM area */
mask = ~(ESRAM_SIZE - 1);
base = CONFIG_ESRAM_BASE & mask;
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_VAR_PHYBASE(MTRR_VAR_ESRAM),
base | MTRR_TYPE_WRBACK);
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_VAR_PHYMASK(MTRR_VAR_ESRAM),
mask | MTRR_PHYS_MASK_VALID);
/* enable both variable and fixed range MTRRs */
msg_port_write(MSG_PORT_HOST_BRIDGE, MTRR_DEF_TYPE,
MTRR_DEF_TYPE_EN | MTRR_DEF_TYPE_FIX_EN);
enable_caches();
}
static void quark_setup_bars(void)
{
/* GPIO - D31:F0:R44h */
qrk_pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_GBA,
CONFIG_GPIO_BASE | IO_BAR_EN);
/* ACPI PM1 Block - D31:F0:R48h */
qrk_pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_PM1BLK,
CONFIG_ACPI_PM1_BASE | IO_BAR_EN);
/* GPE0 - D31:F0:R4Ch */
qrk_pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_GPE0BLK,
CONFIG_ACPI_GPE0_BASE | IO_BAR_EN);
/* WDT - D31:F0:R84h */
qrk_pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_WDTBA,
CONFIG_WDT_BASE | IO_BAR_EN);
/* RCBA - D31:F0:RF0h */
qrk_pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_RCBA,
CONFIG_RCBA_BASE | MEM_BAR_EN);
/* ACPI P Block - Msg Port 04:R70h */
msg_port_write(MSG_PORT_RMU, PBLK_BA,
CONFIG_ACPI_PBLK_BASE | IO_BAR_EN);
/* SPI DMA - Msg Port 04:R7Ah */
msg_port_write(MSG_PORT_RMU, SPI_DMA_BA,
CONFIG_SPI_DMA_BASE | IO_BAR_EN);
/* PCIe ECAM */
msg_port_write(MSG_PORT_MEM_ARBITER, AEC_CTRL,
CONFIG_PCIE_ECAM_BASE | MEM_BAR_EN);
msg_port_write(MSG_PORT_HOST_BRIDGE, HEC_REG,
CONFIG_PCIE_ECAM_BASE | MEM_BAR_EN);
}
static void quark_pcie_early_init(void)
{
/*
* Step1: Assert PCIe signal PERST#
*
* The CPU interface to the PERST# signal is platform dependent.
* Call the board-specific codes to perform this task.
*/
board_assert_perst();
/* Step2: PHY common lane reset */
msg_port_alt_setbits(MSG_PORT_SOC_UNIT, PCIE_CFG, PCIE_PHY_LANE_RST);
/* wait 1 ms for PHY common lane reset */
mdelay(1);
/* Step3: PHY sideband interface reset and controller main reset */
msg_port_alt_setbits(MSG_PORT_SOC_UNIT, PCIE_CFG,
PCIE_PHY_SB_RST | PCIE_CTLR_MAIN_RST);
/* wait 80ms for PLL to lock */
mdelay(80);
/* Step4: Controller sideband interface reset */
msg_port_alt_setbits(MSG_PORT_SOC_UNIT, PCIE_CFG, PCIE_CTLR_SB_RST);
/* wait 20ms for controller sideband interface reset */
mdelay(20);
/* Step5: De-assert PERST# */
board_deassert_perst();
/* Step6: Controller primary interface reset */
msg_port_alt_setbits(MSG_PORT_SOC_UNIT, PCIE_CFG, PCIE_CTLR_PRI_RST);
/* Mixer Load Lane 0 */
msg_port_io_clrbits(MSG_PORT_PCIE_AFE, PCIE_RXPICTRL0_L0,
(1 << 6) | (1 << 7));
/* Mixer Load Lane 1 */
msg_port_io_clrbits(MSG_PORT_PCIE_AFE, PCIE_RXPICTRL0_L1,
(1 << 6) | (1 << 7));
}
static void quark_usb_early_init(void)
{
/* The sequence below comes from Quark firmware writer guide */
msg_port_alt_clrsetbits(MSG_PORT_USB_AFE, USB2_GLOBAL_PORT,
1 << 1, (1 << 6) | (1 << 7));
msg_port_alt_clrsetbits(MSG_PORT_USB_AFE, USB2_COMPBG,
(1 << 8) | (1 << 9), (1 << 7) | (1 << 10));
msg_port_alt_setbits(MSG_PORT_USB_AFE, USB2_PLL2, 1 << 29);
msg_port_alt_setbits(MSG_PORT_USB_AFE, USB2_PLL1, 1 << 1);
msg_port_alt_clrsetbits(MSG_PORT_USB_AFE, USB2_PLL1,
(1 << 3) | (1 << 4) | (1 << 5), 1 << 6);
msg_port_alt_clrbits(MSG_PORT_USB_AFE, USB2_PLL2, 1 << 29);
msg_port_alt_setbits(MSG_PORT_USB_AFE, USB2_PLL2, 1 << 24);
}
static void quark_thermal_early_init(void)
{
/* The sequence below comes from Quark firmware writer guide */
/* thermal sensor mode config */
msg_port_alt_clrsetbits(MSG_PORT_SOC_UNIT, TS_CFG1,
(1 << 3) | (1 << 4) | (1 << 5), 1 << 5);
msg_port_alt_clrsetbits(MSG_PORT_SOC_UNIT, TS_CFG1,
(1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) |
(1 << 12), 1 << 9);
msg_port_alt_setbits(MSG_PORT_SOC_UNIT, TS_CFG1, 1 << 14);
msg_port_alt_clrbits(MSG_PORT_SOC_UNIT, TS_CFG1, 1 << 17);
msg_port_alt_clrbits(MSG_PORT_SOC_UNIT, TS_CFG1, 1 << 18);
msg_port_alt_clrsetbits(MSG_PORT_SOC_UNIT, TS_CFG2, 0xffff, 0x011f);
msg_port_alt_clrsetbits(MSG_PORT_SOC_UNIT, TS_CFG3, 0xff, 0x17);
msg_port_alt_clrsetbits(MSG_PORT_SOC_UNIT, TS_CFG3,
(1 << 8) | (1 << 9), 1 << 8);
msg_port_alt_clrbits(MSG_PORT_SOC_UNIT, TS_CFG3, 0xff000000);
msg_port_alt_clrsetbits(MSG_PORT_SOC_UNIT, TS_CFG4,
0x7ff800, 0xc8 << 11);
/* thermal monitor catastrophic trip set point (105 celsius) */
msg_port_clrsetbits(MSG_PORT_RMU, TS_TRIP, 0xff, 155);
/* thermal monitor catastrophic trip clear point (0 celsius) */
msg_port_clrsetbits(MSG_PORT_RMU, TS_TRIP, 0xff0000, 50 << 16);
/* take thermal sensor out of reset */
msg_port_alt_clrbits(MSG_PORT_SOC_UNIT, TS_CFG4, 1 << 0);
/* enable thermal monitor */
msg_port_setbits(MSG_PORT_RMU, TS_MODE, 1 << 15);
/* lock all thermal configuration */
msg_port_setbits(MSG_PORT_RMU, RMU_CTRL, (1 << 5) | (1 << 6));
}
static void quark_enable_legacy_seg(void)
{
msg_port_setbits(MSG_PORT_HOST_BRIDGE, HMISC2,
HMISC2_SEGE | HMISC2_SEGF | HMISC2_SEGAB);
}
int arch_cpu_init(void)
{
int ret;
post_code(POST_CPU_INIT);
ret = x86_cpu_init_f();
if (ret)
return ret;
/*
* Quark SoC does not support MSR MTRRs. Fixed and variable range MTRRs
* are accessed indirectly via the message port and not the traditional
* MSR mechanism. Only UC, WT and WB cache types are supported.
*/
quark_setup_mtrr();
/*
* Quark SoC has some non-standard BARs (excluding PCI standard BARs)
* which need be initialized with suggested values
*/
quark_setup_bars();
/* Initialize USB2 PHY */
quark_usb_early_init();
/* Initialize thermal sensor */
quark_thermal_early_init();
/* Turn on legacy segments (A/B/E/F) decode to system RAM */
quark_enable_legacy_seg();
return 0;
}
int arch_cpu_init_dm(void)
{
/*
* Initialize PCIe controller
*
* Quark SoC holds the PCIe controller in reset following a power on.
* U-Boot needs to release the PCIe controller from reset. The PCIe
* controller (D23:F0/F1) will not be visible in PCI configuration
* space and any access to its PCI configuration registers will cause
* system hang while it is held in reset.
*/
quark_pcie_early_init();
return 0;
}
int print_cpuinfo(void)
{
post_code(POST_CPU_INFO);
return default_print_cpuinfo();
}
void reset_cpu(ulong addr)
{
/* cold reset */
x86_full_reset();
}
static void quark_pcie_init(void)
{
u32 val;
/* PCIe upstream non-posted & posted request size */
qrk_pci_write_config_dword(QUARK_PCIE0, PCIE_RP_CCFG,
CCFG_UPRS | CCFG_UNRS);
qrk_pci_write_config_dword(QUARK_PCIE1, PCIE_RP_CCFG,
CCFG_UPRS | CCFG_UNRS);
/* PCIe packet fast transmit mode (IPF) */
qrk_pci_write_config_dword(QUARK_PCIE0, PCIE_RP_MPC2, MPC2_IPF);
qrk_pci_write_config_dword(QUARK_PCIE1, PCIE_RP_MPC2, MPC2_IPF);
/* PCIe message bus idle counter (SBIC) */
qrk_pci_read_config_dword(QUARK_PCIE0, PCIE_RP_MBC, &val);
val |= MBC_SBIC;
qrk_pci_write_config_dword(QUARK_PCIE0, PCIE_RP_MBC, val);
qrk_pci_read_config_dword(QUARK_PCIE1, PCIE_RP_MBC, &val);
val |= MBC_SBIC;
qrk_pci_write_config_dword(QUARK_PCIE1, PCIE_RP_MBC, val);
}
static void quark_usb_init(void)
{
u32 bar;
/* Change USB EHCI packet buffer OUT/IN threshold */
qrk_pci_read_config_dword(QUARK_USB_EHCI, PCI_BASE_ADDRESS_0, &bar);
writel((0x7f << 16) | 0x7f, bar + EHCI_INSNREG01);
/* Disable USB device interrupts */
qrk_pci_read_config_dword(QUARK_USB_DEVICE, PCI_BASE_ADDRESS_0, &bar);
writel(0x7f, bar + USBD_INT_MASK);
writel((0xf << 16) | 0xf, bar + USBD_EP_INT_MASK);
writel((0xf << 16) | 0xf, bar + USBD_EP_INT_STS);
}
int arch_early_init_r(void)
{
quark_pcie_init();
quark_usb_init();
return 0;
}
int cpu_mmc_init(bd_t *bis)
{
return pci_mmc_init("Quark SDHCI", mmc_supported);
}
int arch_misc_init(void)
{
#ifdef CONFIG_ENABLE_MRC_CACHE
/*
* We intend not to check any return value here, as even MRC cache
* is not saved successfully, it is not a severe error that will
* prevent system from continuing to boot.
*/
mrccache_save();
#endif
/* Assign a unique I/O APIC ID */
io_apic_set_id(1);
return 0;
}
void board_final_cleanup(void)
{
struct quark_rcba *rcba;
u32 base, val;
qrk_pci_read_config_dword(QUARK_LEGACY_BRIDGE, LB_RCBA, &base);
base &= ~MEM_BAR_EN;
rcba = (struct quark_rcba *)base;
/* Initialize 'Component ID' to zero */
val = readl(&rcba->esd);
val &= ~0xff0000;
writel(val, &rcba->esd);
/* Lock HMBOUND for security */
msg_port_setbits(MSG_PORT_HOST_BRIDGE, HM_BOUND, HM_BOUND_LOCK);
return;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,533 @@
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*
* Ported from Intel released Quark UEFI BIOS
* QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei
*
* SPDX-License-Identifier: Intel
*/
#ifndef _SMC_H_
#define _SMC_H_
/* System Memory Controller Register Defines */
/* Memory Controller Message Bus Registers Offsets */
#define DRP 0x00
#define DTR0 0x01
#define DTR1 0x02
#define DTR2 0x03
#define DTR3 0x04
#define DTR4 0x05
#define DPMC0 0x06
#define DPMC1 0x07
#define DRFC 0x08
#define DSCH 0x09
#define DCAL 0x0a
#define DRMC 0x0b
#define PMSTS 0x0c
#define DCO 0x0f
#define DSTAT 0x20
#define SSKPD0 0x4a
#define SSKPD1 0x4b
#define DECCCTRL 0x60
#define DECCSTAT 0x61
#define DECCSBECNT 0x62
#define DECCSBECA 0x68
#define DECCSBECS 0x69
#define DECCDBECA 0x6a
#define DECCDBECS 0x6b
#define DFUSESTAT 0x70
#define SCRMSEED 0x80
#define SCRMLO 0x81
#define SCRMHI 0x82
/* DRP register defines */
#define DRP_RKEN0 (1 << 0)
#define DRP_RKEN1 (1 << 1)
#define DRP_PRI64BSPLITEN (1 << 13)
#define DRP_ADDRMAP_MAP0 (1 << 14)
#define DRP_ADDRMAP_MAP1 (1 << 15)
#define DRP_ADDRMAP_MASK 0x0000c000
/* DTR0 register defines */
#define DTR0_DFREQ_MASK 0x00000003
#define DTR0_TRP_MASK 0x000000f0
#define DTR0_TRCD_MASK 0x00000f00
#define DTR0_TCL_MASK 0x00007000
/* DTR1 register defines */
#define DTR1_TWCL_MASK 0x00000007
#define DTR1_TCMD_MASK 0x00000030
#define DTR1_TWTP_MASK 0x00000f00
#define DTR1_TCCD_12CLK (1 << 12)
#define DTR1_TCCD_18CLK (1 << 13)
#define DTR1_TCCD_MASK 0x00003000
#define DTR1_TFAW_MASK 0x000f0000
#define DTR1_TRAS_MASK 0x00f00000
#define DTR1_TRRD_MASK 0x03000000
#define DTR1_TRTP_MASK 0x70000000
/* DTR2 register defines */
#define DTR2_TRRDR_MASK 0x00000007
#define DTR2_TWWDR_MASK 0x00000700
#define DTR2_TRWDR_MASK 0x000f0000
/* DTR3 register defines */
#define DTR3_TWRDR_MASK 0x00000007
#define DTR3_TXXXX_MASK 0x00000070
#define DTR3_TRWSR_MASK 0x00000f00
#define DTR3_TWRSR_MASK 0x0001e000
#define DTR3_TXP_MASK 0x00c00000
/* DTR4 register defines */
#define DTR4_WRODTSTRT_MASK 0x00000003
#define DTR4_WRODTSTOP_MASK 0x00000070
#define DTR4_XXXX1_MASK 0x00000700
#define DTR4_XXXX2_MASK 0x00007000
#define DTR4_ODTDIS (1 << 15)
#define DTR4_TRGSTRDIS (1 << 16)
/* DPMC0 register defines */
#define DPMC0_PCLSTO_MASK 0x00070000
#define DPMC0_PREAPWDEN (1 << 21)
#define DPMC0_DYNSREN (1 << 23)
#define DPMC0_CLKGTDIS (1 << 24)
#define DPMC0_DISPWRDN (1 << 25)
#define DPMC0_ENPHYCLKGATE (1 << 29)
/* DRFC register defines */
#define DRFC_TREFI_MASK 0x00007000
#define DRFC_REFDBTCLR (1 << 21)
/* DSCH register defines */
#define DSCH_OOODIS (1 << 8)
#define DSCH_OOOST3DIS (1 << 9)
#define DSCH_NEWBYPDIS (1 << 12)
/* DCAL register defines */
#define DCAL_ZQCINT_MASK 0x00000700
#define DCAL_SRXZQCL_MASK 0x00003000
/* DRMC register defines */
#define DRMC_CKEMODE (1 << 4)
#define DRMC_ODTMODE (1 << 12)
#define DRMC_COLDWAKE (1 << 16)
/* PMSTS register defines */
#define PMSTS_DISR (1 << 0)
/* DCO register defines */
#define DCO_DRPLOCK (1 << 0)
#define DCO_CPGCLOCK (1 << 8)
#define DCO_PMICTL (1 << 28)
#define DCO_PMIDIS (1 << 29)
#define DCO_IC (1 << 31)
/* DECCCTRL register defines */
#define DECCCTRL_SBEEN (1 << 0)
#define DECCCTRL_DBEEN (1 << 1)
#define DECCCTRL_ENCBGEN (1 << 17)
/* DRAM init command */
#define DCMD_MRS1(rnk, dat) (0 | ((rnk) << 22) | (1 << 3) | ((dat) << 6))
#define DCMD_REF(rnk) (1 | ((rnk) << 22))
#define DCMD_PRE(rnk) (2 | ((rnk) << 22))
#define DCMD_PREA(rnk) (2 | ((rnk) << 22) | (0x400 << 6))
#define DCMD_ACT(rnk, row) (3 | ((rnk) << 22) | ((row) << 6))
#define DCMD_WR(rnk, col) (4 | ((rnk) << 22) | ((col) << 6))
#define DCMD_RD(rnk, col) (5 | ((rnk) << 22) | ((col) << 6))
#define DCMD_ZQCS(rnk) (6 | ((rnk) << 22))
#define DCMD_ZQCL(rnk) (6 | ((rnk) << 22) | (0x400 << 6))
#define DCMD_NOP(rnk) (7 | ((rnk) << 22))
#define DDR3_EMRS1_DIC_40 0
#define DDR3_EMRS1_DIC_34 1
#define DDR3_EMRS1_RTTNOM_0 0
#define DDR3_EMRS1_RTTNOM_60 0x04
#define DDR3_EMRS1_RTTNOM_120 0x40
#define DDR3_EMRS1_RTTNOM_40 0x44
#define DDR3_EMRS1_RTTNOM_20 0x200
#define DDR3_EMRS1_RTTNOM_30 0x204
#define DDR3_EMRS2_RTTWR_60 (1 << 9)
#define DDR3_EMRS2_RTTWR_120 (1 << 10)
/* BEGIN DDRIO Registers */
/* DDR IOs & COMPs */
#define DDRIODQ_BL_OFFSET 0x0800
#define DDRIODQ_CH_OFFSET ((NUM_BYTE_LANES / 2) * DDRIODQ_BL_OFFSET)
#define DDRIOCCC_CH_OFFSET 0x0800
#define DDRCOMP_CH_OFFSET 0x0100
/* CH0-BL01-DQ */
#define DQOBSCKEBBCTL 0x0000
#define DQDLLTXCTL 0x0004
#define DQDLLRXCTL 0x0008
#define DQMDLLCTL 0x000c
#define B0RXIOBUFCTL 0x0010
#define B0VREFCTL 0x0014
#define B0RXOFFSET1 0x0018
#define B0RXOFFSET0 0x001c
#define B1RXIOBUFCTL 0x0020
#define B1VREFCTL 0x0024
#define B1RXOFFSET1 0x0028
#define B1RXOFFSET0 0x002c
#define DQDFTCTL 0x0030
#define DQTRAINSTS 0x0034
#define B1DLLPICODER0 0x0038
#define B0DLLPICODER0 0x003c
#define B1DLLPICODER1 0x0040
#define B0DLLPICODER1 0x0044
#define B1DLLPICODER2 0x0048
#define B0DLLPICODER2 0x004c
#define B1DLLPICODER3 0x0050
#define B0DLLPICODER3 0x0054
#define B1RXDQSPICODE 0x0058
#define B0RXDQSPICODE 0x005c
#define B1RXDQPICODER32 0x0060
#define B1RXDQPICODER10 0x0064
#define B0RXDQPICODER32 0x0068
#define B0RXDQPICODER10 0x006c
#define B01PTRCTL0 0x0070
#define B01PTRCTL1 0x0074
#define B01DBCTL0 0x0078
#define B01DBCTL1 0x007c
#define B0LATCTL0 0x0080
#define B1LATCTL0 0x0084
#define B01LATCTL1 0x0088
#define B0ONDURCTL 0x008c
#define B1ONDURCTL 0x0090
#define B0OVRCTL 0x0094
#define B1OVRCTL 0x0098
#define DQCTL 0x009c
#define B0RK2RKCHGPTRCTRL 0x00a0
#define B1RK2RKCHGPTRCTRL 0x00a4
#define DQRK2RKCTL 0x00a8
#define DQRK2RKPTRCTL 0x00ac
#define B0RK2RKLAT 0x00b0
#define B1RK2RKLAT 0x00b4
#define DQCLKALIGNREG0 0x00b8
#define DQCLKALIGNREG1 0x00bc
#define DQCLKALIGNREG2 0x00c0
#define DQCLKALIGNSTS0 0x00c4
#define DQCLKALIGNSTS1 0x00c8
#define DQCLKGATE 0x00cc
#define B0COMPSLV1 0x00d0
#define B1COMPSLV1 0x00d4
#define B0COMPSLV2 0x00d8
#define B1COMPSLV2 0x00dc
#define B0COMPSLV3 0x00e0
#define B1COMPSLV3 0x00e4
#define DQVISALANECR0TOP 0x00e8
#define DQVISALANECR1TOP 0x00ec
#define DQVISACONTROLCRTOP 0x00f0
#define DQVISALANECR0BL 0x00f4
#define DQVISALANECR1BL 0x00f8
#define DQVISACONTROLCRBL 0x00fc
#define DQTIMINGCTRL 0x010c
/* CH0-ECC */
#define ECCDLLTXCTL 0x2004
#define ECCDLLRXCTL 0x2008
#define ECCMDLLCTL 0x200c
#define ECCB1DLLPICODER0 0x2038
#define ECCB1DLLPICODER1 0x2040
#define ECCB1DLLPICODER2 0x2048
#define ECCB1DLLPICODER3 0x2050
#define ECCB01DBCTL0 0x2078
#define ECCB01DBCTL1 0x207c
#define ECCCLKALIGNREG0 0x20b8
#define ECCCLKALIGNREG1 0x20bc
#define ECCCLKALIGNREG2 0x20c0
/* CH0-CMD */
#define CMDOBSCKEBBCTL 0x4800
#define CMDDLLTXCTL 0x4808
#define CMDDLLRXCTL 0x480c
#define CMDMDLLCTL 0x4810
#define CMDRCOMPODT 0x4814
#define CMDDLLPICODER0 0x4820
#define CMDDLLPICODER1 0x4824
#define CMDCFGREG0 0x4840
#define CMDPTRREG 0x4844
#define CMDCLKALIGNREG0 0x4850
#define CMDCLKALIGNREG1 0x4854
#define CMDCLKALIGNREG2 0x4858
#define CMDPMCONFIG0 0x485c
#define CMDPMDLYREG0 0x4860
#define CMDPMDLYREG1 0x4864
#define CMDPMDLYREG2 0x4868
#define CMDPMDLYREG3 0x486c
#define CMDPMDLYREG4 0x4870
#define CMDCLKALIGNSTS0 0x4874
#define CMDCLKALIGNSTS1 0x4878
#define CMDPMSTS0 0x487c
#define CMDPMSTS1 0x4880
#define CMDCOMPSLV 0x4884
#define CMDBONUS0 0x488c
#define CMDBONUS1 0x4890
#define CMDVISALANECR0 0x4894
#define CMDVISALANECR1 0x4898
#define CMDVISACONTROLCR 0x489c
#define CMDCLKGATE 0x48a0
#define CMDTIMINGCTRL 0x48a4
/* CH0-CLK-CTL */
#define CCOBSCKEBBCTL 0x5800
#define CCRCOMPIO 0x5804
#define CCDLLTXCTL 0x5808
#define CCDLLRXCTL 0x580c
#define CCMDLLCTL 0x5810
#define CCRCOMPODT 0x5814
#define CCDLLPICODER0 0x5820
#define CCDLLPICODER1 0x5824
#define CCDDR3RESETCTL 0x5830
#define CCCFGREG0 0x5838
#define CCCFGREG1 0x5840
#define CCPTRREG 0x5844
#define CCCLKALIGNREG0 0x5850
#define CCCLKALIGNREG1 0x5854
#define CCCLKALIGNREG2 0x5858
#define CCPMCONFIG0 0x585c
#define CCPMDLYREG0 0x5860
#define CCPMDLYREG1 0x5864
#define CCPMDLYREG2 0x5868
#define CCPMDLYREG3 0x586c
#define CCPMDLYREG4 0x5870
#define CCCLKALIGNSTS0 0x5874
#define CCCLKALIGNSTS1 0x5878
#define CCPMSTS0 0x587c
#define CCPMSTS1 0x5880
#define CCCOMPSLV1 0x5884
#define CCCOMPSLV2 0x5888
#define CCCOMPSLV3 0x588c
#define CCBONUS0 0x5894
#define CCBONUS1 0x5898
#define CCVISALANECR0 0x589c
#define CCVISALANECR1 0x58a0
#define CCVISACONTROLCR 0x58a4
#define CCCLKGATE 0x58a8
#define CCTIMINGCTL 0x58ac
/* COMP */
#define CMPCTRL 0x6800
#define SOFTRSTCNTL 0x6804
#define MSCNTR 0x6808
#define NMSCNTRL 0x680c
#define LATCH1CTL 0x6814
#define COMPVISALANECR0 0x681c
#define COMPVISALANECR1 0x6820
#define COMPVISACONTROLCR 0x6824
#define COMPBONUS0 0x6830
#define TCOCNTCTRL 0x683c
#define DQANAODTPUCTL 0x6840
#define DQANAODTPDCTL 0x6844
#define DQANADRVPUCTL 0x6848
#define DQANADRVPDCTL 0x684c
#define DQANADLYPUCTL 0x6850
#define DQANADLYPDCTL 0x6854
#define DQANATCOPUCTL 0x6858
#define DQANATCOPDCTL 0x685c
#define CMDANADRVPUCTL 0x6868
#define CMDANADRVPDCTL 0x686c
#define CMDANADLYPUCTL 0x6870
#define CMDANADLYPDCTL 0x6874
#define CLKANAODTPUCTL 0x6880
#define CLKANAODTPDCTL 0x6884
#define CLKANADRVPUCTL 0x6888
#define CLKANADRVPDCTL 0x688c
#define CLKANADLYPUCTL 0x6890
#define CLKANADLYPDCTL 0x6894
#define CLKANATCOPUCTL 0x6898
#define CLKANATCOPDCTL 0x689c
#define DQSANAODTPUCTL 0x68a0
#define DQSANAODTPDCTL 0x68a4
#define DQSANADRVPUCTL 0x68a8
#define DQSANADRVPDCTL 0x68ac
#define DQSANADLYPUCTL 0x68b0
#define DQSANADLYPDCTL 0x68b4
#define DQSANATCOPUCTL 0x68b8
#define DQSANATCOPDCTL 0x68bc
#define CTLANADRVPUCTL 0x68c8
#define CTLANADRVPDCTL 0x68cc
#define CTLANADLYPUCTL 0x68d0
#define CTLANADLYPDCTL 0x68d4
#define CHNLBUFSTATIC 0x68f0
#define COMPOBSCNTRL 0x68f4
#define COMPBUFFDBG0 0x68f8
#define COMPBUFFDBG1 0x68fc
#define CFGMISCCH0 0x6900
#define COMPEN0CH0 0x6904
#define COMPEN1CH0 0x6908
#define COMPEN2CH0 0x690c
#define STATLEGEN0CH0 0x6910
#define STATLEGEN1CH0 0x6914
#define DQVREFCH0 0x6918
#define CMDVREFCH0 0x691c
#define CLKVREFCH0 0x6920
#define DQSVREFCH0 0x6924
#define CTLVREFCH0 0x6928
#define TCOVREFCH0 0x692c
#define DLYSELCH0 0x6930
#define TCODRAMBUFODTCH0 0x6934
#define CCBUFODTCH0 0x6938
#define RXOFFSETCH0 0x693c
#define DQODTPUCTLCH0 0x6940
#define DQODTPDCTLCH0 0x6944
#define DQDRVPUCTLCH0 0x6948
#define DQDRVPDCTLCH0 0x694c
#define DQDLYPUCTLCH0 0x6950
#define DQDLYPDCTLCH0 0x6954
#define DQTCOPUCTLCH0 0x6958
#define DQTCOPDCTLCH0 0x695c
#define CMDDRVPUCTLCH0 0x6968
#define CMDDRVPDCTLCH0 0x696c
#define CMDDLYPUCTLCH0 0x6970
#define CMDDLYPDCTLCH0 0x6974
#define CLKODTPUCTLCH0 0x6980
#define CLKODTPDCTLCH0 0x6984
#define CLKDRVPUCTLCH0 0x6988
#define CLKDRVPDCTLCH0 0x698c
#define CLKDLYPUCTLCH0 0x6990
#define CLKDLYPDCTLCH0 0x6994
#define CLKTCOPUCTLCH0 0x6998
#define CLKTCOPDCTLCH0 0x699c
#define DQSODTPUCTLCH0 0x69a0
#define DQSODTPDCTLCH0 0x69a4
#define DQSDRVPUCTLCH0 0x69a8
#define DQSDRVPDCTLCH0 0x69ac
#define DQSDLYPUCTLCH0 0x69b0
#define DQSDLYPDCTLCH0 0x69b4
#define DQSTCOPUCTLCH0 0x69b8
#define DQSTCOPDCTLCH0 0x69bc
#define CTLDRVPUCTLCH0 0x69c8
#define CTLDRVPDCTLCH0 0x69cc
#define CTLDLYPUCTLCH0 0x69d0
#define CTLDLYPDCTLCH0 0x69d4
#define FNLUPDTCTLCH0 0x69f0
/* PLL */
#define MPLLCTRL0 0x7800
#define MPLLCTRL1 0x7808
#define MPLLCSR0 0x7810
#define MPLLCSR1 0x7814
#define MPLLCSR2 0x7820
#define MPLLDFT 0x7828
#define MPLLMON0CTL 0x7830
#define MPLLMON1CTL 0x7838
#define MPLLMON2CTL 0x783c
#define SFRTRIM 0x7850
#define MPLLDFTOUT0 0x7858
#define MPLLDFTOUT1 0x785c
#define MASTERRSTN 0x7880
#define PLLLOCKDEL 0x7884
#define SFRDEL 0x7888
#define CRUVISALANECR0 0x78f0
#define CRUVISALANECR1 0x78f4
#define CRUVISACONTROLCR 0x78f8
#define IOSFVISALANECR0 0x78fc
#define IOSFVISALANECR1 0x7900
#define IOSFVISACONTROLCR 0x7904
/* END DDRIO Registers */
/* DRAM Specific Message Bus OpCodes */
#define MSG_OP_DRAM_INIT 0x68
#define MSG_OP_DRAM_WAKE 0xca
#define SAMPLE_SIZE 6
/* must be less than this number to enable early deadband */
#define EARLY_DB 0x12
/* must be greater than this number to enable late deadband */
#define LATE_DB 0x34
#define CHX_REGS (11 * 4)
#define FULL_CLK 128
#define HALF_CLK 64
#define QRTR_CLK 32
#define MCEIL(num, den) ((uint8_t)((num + den - 1) / den))
#define MMAX(a, b) ((a) > (b) ? (a) : (b))
#define DEAD_LOOP() for (;;);
#define MIN_RDQS_EYE 10 /* in PI Codes */
#define MIN_VREF_EYE 10 /* in VREF Codes */
/* how many RDQS codes to jump while margining */
#define RDQS_STEP 1
/* how many VREF codes to jump while margining */
#define VREF_STEP 1
/* offset into "vref_codes[]" for minimum allowed VREF setting */
#define VREF_MIN 0x00
/* offset into "vref_codes[]" for maximum allowed VREF setting */
#define VREF_MAX 0x3f
#define RDQS_MIN 0x00 /* minimum RDQS delay value */
#define RDQS_MAX 0x3f /* maximum RDQS delay value */
/* how many WDQ codes to jump while margining */
#define WDQ_STEP 1
enum {
B, /* BOTTOM VREF */
T /* TOP VREF */
};
enum {
L, /* LEFT RDQS */
R /* RIGHT RDQS */
};
/* Memory Options */
/* enable STATIC timing settings for RCVN (BACKUP_MODE) */
#undef BACKUP_RCVN
/* enable STATIC timing settings for WDQS (BACKUP_MODE) */
#undef BACKUP_WDQS
/* enable STATIC timing settings for RDQS (BACKUP_MODE) */
#undef BACKUP_RDQS
/* enable STATIC timing settings for WDQ (BACKUP_MODE) */
#undef BACKUP_WDQ
/* enable *COMP overrides (BACKUP_MODE) */
#undef BACKUP_COMPS
/* enable the RD_TRAIN eye check */
#undef RX_EYE_CHECK
/* enable Host to Memory Clock Alignment */
#define HMC_TEST
/* enable multi-rank support via rank2rank sharing */
#define R2R_SHARING
/* disable signals not used in 16bit mode of DDRIO */
#define FORCE_16BIT_DDRIO
#define PLATFORM_ID 1
void clear_self_refresh(struct mrc_params *mrc_params);
void prog_ddr_timing_control(struct mrc_params *mrc_params);
void prog_decode_before_jedec(struct mrc_params *mrc_params);
void perform_ddr_reset(struct mrc_params *mrc_params);
void ddrphy_init(struct mrc_params *mrc_params);
void perform_jedec_init(struct mrc_params *mrc_params);
void set_ddr_init_complete(struct mrc_params *mrc_params);
void restore_timings(struct mrc_params *mrc_params);
void default_timings(struct mrc_params *mrc_params);
void rcvn_cal(struct mrc_params *mrc_params);
void wr_level(struct mrc_params *mrc_params);
void prog_page_ctrl(struct mrc_params *mrc_params);
void rd_train(struct mrc_params *mrc_params);
void wr_train(struct mrc_params *mrc_params);
void store_timings(struct mrc_params *mrc_params);
void enable_scrambling(struct mrc_params *mrc_params);
void prog_ddr_control(struct mrc_params *mrc_params);
void prog_dra_drb(struct mrc_params *mrc_params);
void perform_wake(struct mrc_params *mrc_params);
void change_refresh_period(struct mrc_params *mrc_params);
void set_auto_refresh(struct mrc_params *mrc_params);
void ecc_enable(struct mrc_params *mrc_params);
void memory_test(struct mrc_params *mrc_params);
void lock_registers(struct mrc_params *mrc_params);
#endif /* _SMC_H_ */

View File

@@ -0,0 +1,53 @@
#
# Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
#
# SPDX-License-Identifier: GPL-2.0+
#
config INTEL_QUEENSBAY
bool
select HAVE_FSP
select HAVE_CMC
if INTEL_QUEENSBAY
config HAVE_CMC
bool "Add a Chipset Micro Code state machine binary"
help
Select this option to add a Chipset Micro Code state machine binary
to the resulting U-Boot image. It is a 64K data block of machine
specific code which must be put in the flash for the processor to
access when powered up before system BIOS is executed.
config CMC_FILE
string "Chipset Micro Code state machine filename"
depends on HAVE_CMC
default "cmc.bin"
help
The filename of the file to use as Chipset Micro Code state machine
binary in the board directory.
config CMC_ADDR
hex "Chipset Micro Code state machine binary location"
depends on HAVE_CMC
default 0xfffb0000
help
The location of the CMC binary is determined by a strap. It must be
put in flash at a location matching the strap-determined base address.
The default base address of 0xfffb0000 indicates that the binary must
be located at offset 0xb0000 from the beginning of a 1MB flash device.
config CPU_ADDR_BITS
int
default 32
config DISABLE_IGD
bool "Disable Integrated Graphics Device (IGD)"
help
Disable the Integrated Graphics Device (IGD) so that it does not
show in the PCI configuration space as a VGA disaplay controller.
This gives a chance for U-Boot to run PCI/PCIe based graphics
card's VGA BIOS and use that card for the graphics console.
endif

View File

@@ -0,0 +1,8 @@
#
# Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += fsp_configs.o irq.o
obj-y += tnc.o topcliff.o

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: Intel
*/
#include <common.h>
#include <asm/fsp/fsp_support.h>
void update_fsp_configs(struct fsp_config_data *config,
struct fspinit_rtbuf *rt_buf)
{
/* Initialize runtime buffer for fsp_init() */
rt_buf->common.stack_top = config->common.stack_top - 32;
rt_buf->common.boot_mode = config->common.boot_mode;
rt_buf->common.upd_data = &config->fsp_upd;
/* Override any UPD setting if required */
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
* Copyright (C) 2015 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pci.h>
#include <asm/arch/device.h>
#include <asm/arch/tnc.h>
int queensbay_irq_router_probe(struct udevice *dev)
{
struct tnc_rcba *rcba;
u32 base;
dm_pci_read_config32(dev->parent, LPC_RCBA, &base);
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 have to route PCIe
* root ports' INTx to PIRQ A/B/C/D as well. For other devices
* on TunneCreek, route them to PIRQ E/F/G/H.
*/
writew(PIRQE, &rcba->d02ir);
writew(PIRQF, &rcba->d03ir);
writew(PIRQG, &rcba->d27ir);
writew(PIRQH, &rcba->d31ir);
writew(PIRQA, &rcba->d23ir);
writew(PIRQB, &rcba->d24ir);
writew(PIRQC, &rcba->d25ir);
writew(PIRQD, &rcba->d26ir);
return irq_router_common_init(dev);
}
static const struct udevice_id queensbay_irq_router_ids[] = {
{ .compatible = "intel,queensbay-irq-router" },
{ }
};
U_BOOT_DRIVER(queensbay_irq_router_drv) = {
.name = "queensbay_intel_irq",
.id = UCLASS_IRQ,
.of_match = queensbay_irq_router_ids,
.probe = queensbay_irq_router_probe,
};

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <pci.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/post.h>
#include <asm/arch/device.h>
#include <asm/arch/tnc.h>
#include <asm/fsp/fsp_support.h>
#include <asm/processor.h>
static int __maybe_unused disable_igd(void)
{
struct udevice *igd, *sdvo;
int ret;
ret = dm_pci_bus_find_bdf(TNC_IGD, &igd);
if (ret)
return ret;
if (!igd)
return 0;
ret = dm_pci_bus_find_bdf(TNC_SDVO, &sdvo);
if (ret)
return ret;
if (!sdvo)
return 0;
/*
* According to Atom E6xx datasheet, setting VGA Disable (bit17)
* of Graphics Controller register (offset 0x50) prevents IGD
* (D2:F0) from reporting itself as a VGA display controller
* class in the PCI configuration space, and should also prevent
* it from responding to VGA legacy memory range and I/O addresses.
*
* However test result shows that with just VGA Disable bit set and
* a PCIe graphics card connected to one of the PCIe controllers on
* the E6xx, accessing the VGA legacy space still causes system hang.
* After a number of attempts, it turns out besides VGA Disable bit,
* the SDVO (D3:F0) device should be disabled to make it work.
*
* To simplify, use the Function Disable register (offset 0xc4)
* to disable both IGD (D2:F0) and SDVO (D3:F0) devices. Now these
* two devices will be completely disabled (invisible in the PCI
* configuration space) unless a system reset is performed.
*/
dm_pci_write_config32(igd, IGD_FD, FUNC_DISABLE);
dm_pci_write_config32(sdvo, IGD_FD, FUNC_DISABLE);
/*
* After setting the function disable bit, IGD and SDVO devices will
* disappear in the PCI configuration space. This however creates an
* inconsistent state from a driver model PCI controller point of view,
* as these two PCI devices are still attached to its parent's child
* device list as maintained by the driver model. Some driver model PCI
* APIs like dm_pci_find_class(), are referring to the list to speed up
* the finding process instead of re-enumerating the whole PCI bus, so
* it gets the stale cached data which is wrong.
*
* Note x86 PCI enueration normally happens twice, in pre-relocation
* phase and post-relocation. One option might be to call disable_igd()
* in one of the pre-relocation initialization hooks so that it gets
* disabled in the first round, and when it comes to the second round
* driver model PCI will construct a correct list. Unfortunately this
* does not work as Intel FSP is used on this platform to perform low
* level initialization, and fsp_init_phase_pci() is called only once
* in the post-relocation phase. If we disable IGD and SDVO devices,
* fsp_init_phase_pci() simply hangs and never returns.
*
* So the only option we have is to manually remove these two devices.
*/
ret = device_remove(igd);
if (ret)
return ret;
ret = device_unbind(igd);
if (ret)
return ret;
ret = device_remove(sdvo);
if (ret)
return ret;
ret = device_unbind(sdvo);
if (ret)
return ret;
return 0;
}
int arch_cpu_init(void)
{
int ret;
post_code(POST_CPU_INIT);
ret = x86_cpu_init_f();
if (ret)
return ret;
return 0;
}
int arch_early_init_r(void)
{
int ret = 0;
#ifdef CONFIG_DISABLE_IGD
ret = disable_igd();
#endif
return ret;
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <mmc.h>
#include <pci_ids.h>
static struct pci_device_id mmc_supported[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0 },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1 },
{},
};
int cpu_mmc_init(bd_t *bis)
{
return pci_mmc_init("Topcliff SDHCI", mmc_supported);
}

View File

@@ -0,0 +1,22 @@
/*
* U-Boot - x86 Startup Code
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* SPDX-License-Identifier: GPL-2.0+
*/
/* Reset vector, jumps to start16.S */
.extern start16
.section .resetvec, "ax"
.code16
reset_vector:
cli
cld
jmp start16
.org 0xf
nop

View File

@@ -0,0 +1,217 @@
/*
* 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, %esi
call *%esi
/* This matches struct sipi_param */
.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

302
u-boot/arch/x86/cpu/start.S Normal file
View File

@@ -0,0 +1,302 @@
/*
* U-Boot - x86 Startup Code
*
* (C) Copyright 2008-2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <asm/global_data.h>
#include <asm/post.h>
#include <asm/processor.h>
#include <asm/processor-flags.h>
#include <generated/generic-asm-offsets.h>
#include <generated/asm-offsets.h>
/*
* Define this to boot U-Boot from a 32-bit program which sets the GDT
* differently. This can be used to boot directly from any stage of coreboot,
* for example, bypassing the normal payload-loading feature.
* This is only useful for development.
*/
#undef LOAD_FROM_32_BIT
.section .text
.code32
.globl _start
.type _start, @function
.globl _x86boot_start
_x86boot_start:
/*
* This is the fail-safe 32-bit bootstrap entry point.
*
* This code is used when booting from another boot loader like
* coreboot or EFI. So we repeat some of the same init found in
* start16.
*/
cli
cld
/* Turn off cache (this might require a 486-class CPU) */
movl %cr0, %eax
orl $(X86_CR0_NW | X86_CR0_CD), %eax
movl %eax, %cr0
wbinvd
/* Tell 32-bit code it is being entered from an in-RAM copy */
movl $GD_FLG_WARM_BOOT, %ebx
/*
* Zero the BIST (Built-In Self Test) value since we don't have it.
* It must be 0 or the previous loader would have reported an error.
*/
movl $0, %ebp
jmp 1f
/* Add a way for tools to discover the _start entry point */
.align 4
.long 0x12345678
_start:
/*
* This is the 32-bit cold-reset entry point, coming from start16.
* Set %ebx to GD_FLG_COLD_BOOT to indicate this.
*/
movl $GD_FLG_COLD_BOOT, %ebx
/* Save BIST */
movl %eax, %ebp
1:
/* Save table pointer */
movl %ecx, %esi
#ifdef LOAD_FROM_32_BIT
lgdt gdt_ptr2
#endif
/* Load the segement registers to match the GDT loaded in start16.S */
movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax
movw %ax, %fs
movw %ax, %ds
movw %ax, %gs
movw %ax, %es
movw %ax, %ss
/* Clear the interrupt vectors */
lidt blank_idt_ptr
/*
* Critical early platform init - generally not used, we prefer init
* to happen later when we have a console, in case something goes
* wrong.
*/
jmp early_board_init
.globl early_board_init_ret
early_board_init_ret:
post_code(POST_START)
/* Initialise Cache-As-RAM */
jmp car_init
.globl car_init_ret
car_init_ret:
#ifndef CONFIG_HAVE_FSP
/*
* We now have CONFIG_SYS_CAR_SIZE bytes of Cache-As-RAM (or SRAM,
* or fully initialised SDRAM - we really don't care which)
* starting at CONFIG_SYS_CAR_ADDR to be used as a temporary stack
* and early malloc() area. The MRC requires some space at the top.
*
* Stack grows down from top of CAR. We have:
*
* top-> CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE
* MRC area
* global_data with x86 global descriptor table
* early malloc area
* stack
* bottom-> CONFIG_SYS_CAR_ADDR
*/
movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %esp
#ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE
subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %esp
#endif
#else
/*
* U-Boot enters here twice. For the first time it comes from
* car_init_done() with esp points to a temporary stack and esi
* set to zero. For the second time it comes from fsp_init_done()
* with esi holding the HOB list address returned by the FSP.
*/
#endif
/* Set up global data */
mov %esp, %eax
call board_init_f_alloc_reserve
mov %eax, %esp
call board_init_f_init_reserve
#ifdef CONFIG_DEBUG_UART
call debug_uart_init
#endif
/* Get address of global_data */
mov %fs:0, %edx
#ifdef CONFIG_HAVE_FSP
/* Store the HOB list if we have one */
test %esi, %esi
jz skip_hob
movl %esi, GD_HOB_LIST(%edx)
/*
* After fsp_init() returns, the stack has already been switched to a
* place within system memory as defined by CONFIG_FSP_TEMP_RAM_ADDR.
* Enlarge the size of malloc() pool before relocation since we have
* plenty of memory now.
*/
subl $CONFIG_FSP_SYS_MALLOC_F_LEN, %esp
movl %esp, GD_MALLOC_BASE(%edx)
skip_hob:
#else
/* Store table pointer */
movl %esi, GD_TABLE(%edx)
#endif
/* Store BIST */
movl %ebp, GD_BIST(%edx)
/* Set parameter to board_init_f() to boot flags */
post_code(POST_START_DONE)
xorl %eax, %eax
/* Enter, U-Boot! */
call board_init_f
/* indicate (lack of) progress */
movw $0x85, %ax
jmp die
.globl board_init_f_r_trampoline
.type board_init_f_r_trampoline, @function
board_init_f_r_trampoline:
/*
* SDRAM has been initialised, U-Boot code has been copied into
* RAM, BSS has been cleared and relocation adjustments have been
* made. It is now time to jump into the in-RAM copy of U-Boot
*
* %eax = Address of top of new stack
*/
/* Stack grows down from top of SDRAM */
movl %eax, %esp
/* See if we need to disable CAR */
.weak car_uninit
movl $car_uninit, %eax
cmpl $0, %eax
jz 1f
call car_uninit
1:
/* Re-enter U-Boot by calling board_init_f_r() */
call board_init_f_r
die:
hlt
jmp die
hlt
blank_idt_ptr:
.word 0 /* limit */
.long 0 /* base */
.p2align 2 /* force 4-byte alignment */
/* Add a multiboot header so U-Boot can be loaded by GRUB2 */
multiboot_header:
/* magic */
.long 0x1badb002
/* flags */
.long (1 << 16)
/* checksum */
.long -0x1BADB002 - (1 << 16)
/* header addr */
.long multiboot_header - _x86boot_start + CONFIG_SYS_TEXT_BASE
/* load addr */
.long CONFIG_SYS_TEXT_BASE
/* load end addr */
.long 0
/* bss end addr */
.long 0
/* entry addr */
.long CONFIG_SYS_TEXT_BASE
#ifdef LOAD_FROM_32_BIT
/*
* The following Global Descriptor Table is just enough to get us into
* 'Flat Protected Mode' - It will be discarded as soon as the final
* GDT is setup in a safe location in RAM
*/
gdt_ptr2:
.word 0x1f /* limit (31 bytes = 4 GDT entries - 1) */
.long gdt_rom2 /* base */
/* Some CPUs are picky about GDT alignment... */
.align 16
.globl gdt_rom2
gdt_rom2:
/*
* The GDT table ...
*
* Selector Type
* 0x00 NULL
* 0x08 Unused
* 0x10 32bit code
* 0x18 32bit data/stack
*/
/* The NULL Desciptor - Mandatory */
.word 0x0000 /* limit_low */
.word 0x0000 /* base_low */
.byte 0x00 /* base_middle */
.byte 0x00 /* access */
.byte 0x00 /* flags + limit_high */
.byte 0x00 /* base_high */
/* Unused Desciptor - (matches Linux) */
.word 0x0000 /* limit_low */
.word 0x0000 /* base_low */
.byte 0x00 /* base_middle */
.byte 0x00 /* access */
.byte 0x00 /* flags + limit_high */
.byte 0x00 /* base_high */
/*
* The Code Segment Descriptor:
* - Base = 0x00000000
* - Size = 4GB
* - Access = Present, Ring 0, Exec (Code), Readable
* - Flags = 4kB Granularity, 32-bit
*/
.word 0xffff /* limit_low */
.word 0x0000 /* base_low */
.byte 0x00 /* base_middle */
.byte 0x9b /* access */
.byte 0xcf /* flags + limit_high */
.byte 0x00 /* base_high */
/*
* The Data Segment Descriptor:
* - Base = 0x00000000
* - Size = 4GB
* - Access = Present, Ring 0, Non-Exec (Data), Writable
* - Flags = 4kB Granularity, 32-bit
*/
.word 0xffff /* limit_low */
.word 0x0000 /* base_low */
.byte 0x00 /* base_middle */
.byte 0x93 /* access */
.byte 0xcf /* flags + limit_high */
.byte 0x00 /* base_high */
#endif

View File

@@ -0,0 +1,131 @@
/*
* U-Boot - x86 Startup Code
*
* (C) Copyright 2008-2011
* Graeme Russ, <graeme.russ@gmail.com>
*
* (C) Copyright 2002,2003
* Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/global_data.h>
#include <asm/processor-flags.h>
#define BOOT_SEG 0xffff0000 /* linear segment of boot code */
#define a32 .byte 0x67;
#define o32 .byte 0x66;
.section .start16, "ax"
.code16
.globl start16
start16:
/* Save BIST */
movl %eax, %ecx
/* Set the Cold Boot / Hard Reset flag */
movl $GD_FLG_COLD_BOOT, %ebx
xorl %eax, %eax
movl %eax, %cr3 /* Invalidate TLB */
/* Turn off cache (this might require a 486-class CPU) */
movl %cr0, %eax
orl $(X86_CR0_NW | X86_CR0_CD), %eax
movl %eax, %cr0
wbinvd
/* load the temporary Global Descriptor Table */
o32 cs lidt idt_ptr
o32 cs lgdt gdt_ptr
/* Now, we enter protected mode */
movl %cr0, %eax
orl $X86_CR0_PE, %eax
movl %eax, %cr0
/* Flush the prefetch queue */
jmp ff
ff:
/* Finally restore BIST and jump to the 32-bit initialization code */
movw $code32start, %ax
movw %ax, %bp
movl %ecx, %eax
o32 cs ljmp *(%bp)
/* 48-bit far pointer */
code32start:
.long _start /* offset */
.word 0x10 /* segment */
idt_ptr:
.word 0 /* limit */
.long 0 /* base */
/*
* The following Global Descriptor Table is just enough to get us into
* 'Flat Protected Mode' - It will be discarded as soon as the final
* GDT is setup in a safe location in RAM
*/
gdt_ptr:
.word 0x1f /* limit (31 bytes = 4 GDT entries - 1) */
.long BOOT_SEG + gdt_rom /* base */
/* Some CPUs are picky about GDT alignment... */
.align 16
.globl gdt_rom
gdt_rom:
/*
* The GDT table ...
*
* Selector Type
* 0x00 NULL
* 0x08 Unused
* 0x10 32bit code
* 0x18 32bit data/stack
*/
/* The NULL Desciptor - Mandatory */
.word 0x0000 /* limit_low */
.word 0x0000 /* base_low */
.byte 0x00 /* base_middle */
.byte 0x00 /* access */
.byte 0x00 /* flags + limit_high */
.byte 0x00 /* base_high */
/* Unused Desciptor - (matches Linux) */
.word 0x0000 /* limit_low */
.word 0x0000 /* base_low */
.byte 0x00 /* base_middle */
.byte 0x00 /* access */
.byte 0x00 /* flags + limit_high */
.byte 0x00 /* base_high */
/*
* The Code Segment Descriptor:
* - Base = 0x00000000
* - Size = 4GB
* - Access = Present, Ring 0, Exec (Code), Readable
* - Flags = 4kB Granularity, 32-bit
*/
.word 0xffff /* limit_low */
.word 0x0000 /* base_low */
.byte 0x00 /* base_middle */
.byte 0x9b /* access */
.byte 0xcf /* flags + limit_high */
.byte 0x00 /* base_high */
/*
* The Data Segment Descriptor:
* - Base = 0x00000000
* - Size = 4GB
* - Access = Present, Ring 0, Non-Exec (Data), Writable
* - Flags = 4kB Granularity, 32-bit
*/
.word 0xffff /* limit_low */
.word 0x0000 /* base_low */
.byte 0x00 /* base_middle */
.byte 0x93 /* access */
.byte 0xcf /* flags + limit_high */
.byte 0x00 /* base_high */

Some files were not shown because too many files have changed in this diff Show More