GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,72 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# (C) Copyright 2002
# Daniel Engström, Omicron Ceti AB, daniel@omicron.se.
ifeq ($(CONFIG_$(SPL_)X86_64),y)
extra-y = start64.o
else
ifeq ($(CONFIG_$(SPL_TPL_)X86_16BIT_INIT),y)
extra-y = start.o
else
ifndef CONFIG_SPL
extra-y = start.o
else
ifdef CONFIG_SPL_BUILD
extra-y = start_from_tpl.o
else
extra-y = start_from_spl.o
endif
endif
endif
endif
extra-$(CONFIG_$(SPL_TPL_)X86_16BIT_INIT) += resetvec.o start16.o
obj-y += cpu.o
ifndef CONFIG_TPL_BUILD
obj-y += cpu_x86.o
endif
ifndef CONFIG_$(SPL_)X86_64
AFLAGS_REMOVE_call32.o := -mregparm=3 \
$(if $(CONFIG_EFI_STUB_64BIT),-march=i386 -m32)
AFLAGS_call32.o := -fpic -fshort-wchar \
$(if $(CONFIG_EFI_STUB_64BIT),-m64)
extra-y += call32.o
endif
obj-y += intel_common/
obj-$(CONFIG_INTEL_BAYTRAIL) += baytrail/
obj-$(CONFIG_INTEL_BRASWELL) += braswell/
obj-$(CONFIG_INTEL_BROADWELL) += broadwell/
obj-$(CONFIG_SYS_COREBOOT) += coreboot/
obj-$(CONFIG_SYS_SLIMBOOTLOADER) += slimbootloader/
obj-$(CONFIG_EFI) += efi/
obj-$(CONFIG_QEMU) += qemu/
obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/
obj-$(CONFIG_INTEL_QUARK) += quark/
obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/
obj-$(CONFIG_INTEL_TANGIER) += tangier/
obj-$(CONFIG_APIC) += lapic.o ioapic.o
obj-y += irq.o
ifndef CONFIG_$(SPL_)X86_64
obj-$(CONFIG_SMP) += mp_init.o
endif
obj-y += mtrr.o
obj-$(CONFIG_PCI) += pci.o
ifndef CONFIG_$(SPL_)X86_64
obj-$(CONFIG_SMP) += sipi_vector.o
endif
obj-y += turbo.o
obj-$(CONFIG_HAVE_ACPI_RESUME) += wakeup.o
ifeq ($(CONFIG_$(SPL_)X86_64),y)
obj-y += x86_64/
else
obj-y += i386/
endif
@@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015 Google, Inc
config INTEL_BAYTRAIL
bool
select HAVE_FSP
select ARCH_MISC_INIT
select CPU_INTEL_TURBO_NOT_PACKAGE_SCOPED
imply HAVE_INTEL_ME
imply ENABLE_MRC_CACHE
imply AHCI_PCI
imply ICH_SPI
imply INTEL_ICH6_GPIO
imply PINCTRL_ICH6
imply MMC
imply MMC_PCI
imply MMC_SDHCI
imply MMC_SDHCI_SDMA
imply SCSI
imply SCSI_AHCI
imply SPI_FLASH
imply SYS_NS16550
imply USB
imply USB_EHCI_HCD
imply USB_XHCI_HCD
imply VIDEO_VESA
if INTEL_BAYTRAIL
config INTERNAL_UART
bool "Enable the SoC integrated legacy UART"
help
There is a legacy UART integrated into the Bay Trail SoC.
A maximum baud rate of 115200 bps is supported. For this
reason, it is recommended that the UART port be used for
debug purposes only, eg: U-Boot console.
config DEBUG_UART
bool
select DEBUG_UART_BOARD_INIT
endif
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015 Google, Inc
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
@@ -0,0 +1,206 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <acpi_s3.h>
#include <cpu.h>
#include <dm.h>
#include <dm/uclass-internal.h>
#include <asm/acpi_table.h>
#include <asm/io.h>
#include <asm/tables.h>
#include <asm/arch/global_nvs.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 | FULL_RST;
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);
}
void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
{
struct udevice *dev;
int ret;
/* at least we have one processor */
gnvs->pcnt = 1;
/* override the processor count with actual number */
ret = uclass_find_first_device(UCLASS_CPU, &dev);
if (ret == 0 && dev != NULL) {
ret = cpu_get_count(dev);
if (ret > 0)
gnvs->pcnt = ret;
}
/* determine whether internal uart is on */
if (IS_ENABLED(CONFIG_INTERNAL_UART))
gnvs->iuart_en = 1;
else
gnvs->iuart_en = 0;
}
#ifdef CONFIG_HAVE_ACPI_RESUME
/*
* The following two routines are called at a very early stage, even before
* FSP 2nd phase API fsp_init() is called. Registers off ACPI_BASE_ADDRESS
* and PMC_BASE_ADDRESS are accessed, so we need make sure the base addresses
* of these two blocks are programmed by either U-Boot or FSP.
*
* It has been verified that 1st phase API (see arch/x86/lib/fsp1/fsp_car.S)
* on Intel BayTrail SoC already initializes these two base addresses so
* we are safe to access these registers here.
*/
enum acpi_sleep_state chipset_prev_sleep_state(void)
{
u32 pm1_sts;
u32 pm1_cnt;
u32 gen_pmcon1;
enum acpi_sleep_state prev_sleep_state = ACPI_S0;
/* Read Power State */
pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS);
pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
gen_pmcon1 = readl(PMC_BASE_ADDRESS + GEN_PMCON1);
debug("PM1_STS = 0x%x PM1_CNT = 0x%x GEN_PMCON1 = 0x%x\n",
pm1_sts, pm1_cnt, gen_pmcon1);
if (pm1_sts & WAK_STS)
prev_sleep_state = acpi_sleep_from_pm1(pm1_cnt);
if (gen_pmcon1 & (PWR_FLR | SUS_PWR_FLR))
prev_sleep_state = ACPI_S5;
return prev_sleep_state;
}
void chipset_clear_sleep_state(void)
{
u32 pm1_cnt;
pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
outl(pm1_cnt & ~(SLP_TYP), ACPI_BASE_ADDRESS + PM1_CNT);
}
#endif
@@ -0,0 +1,207 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Google, Inc
*
* Based on code from coreboot
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <pci.h>
#include <asm/cpu.h>
#include <asm/cpu_x86.h>
#include <asm/io.h>
#include <asm/lapic.h>
#include <asm/msr.h>
#include <asm/turbo.h>
#define BYT_PRV_CLK 0x800
#define BYT_PRV_CLK_EN (1 << 0)
#define BYT_PRV_CLK_M_VAL_SHIFT 1
#define BYT_PRV_CLK_N_VAL_SHIFT 16
#define BYT_PRV_CLK_UPDATE (1 << 31)
static void hsuart_clock_set(void *base)
{
u32 m, n, reg;
/*
* Configure the BayTrail UART clock for the internal HS UARTs
* (PCI devices) to 58982400 Hz
*/
m = 0x2400;
n = 0x3d09;
reg = (m << BYT_PRV_CLK_M_VAL_SHIFT) | (n << BYT_PRV_CLK_N_VAL_SHIFT);
writel(reg, base + BYT_PRV_CLK);
reg |= BYT_PRV_CLK_EN | BYT_PRV_CLK_UPDATE;
writel(reg, base + BYT_PRV_CLK);
}
/*
* Configure the internal clock of both SIO HS-UARTs, if they are enabled
* via FSP
*/
int arch_cpu_init_dm(void)
{
struct udevice *dev;
void *base;
int ret;
int i;
/* Loop over the 2 HS-UARTs */
for (i = 0; i < 2; i++) {
ret = dm_pci_bus_find_bdf(PCI_BDF(0, 0x1e, 3 + i), &dev);
if (!ret) {
base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
PCI_REGION_MEM);
hsuart_clock_set(base);
}
}
return 0;
}
static void set_max_freq(void)
{
msr_t perf_ctl;
msr_t msr;
/* Enable speed step */
msr = msr_read(MSR_IA32_MISC_ENABLE);
msr.lo |= MISC_ENABLE_ENHANCED_SPEEDSTEP;
msr_write(MSR_IA32_MISC_ENABLE, 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 [22: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,
.get_vendor = cpu_x86_get_vendor,
};
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,
.flags = DM_FLAG_PRE_RELOC,
};
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Google, Inc
*/
#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;
}
void board_debug_uart_init(void)
{
setup_internal_uart(1);
}
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: Intel
/*
* Copyright (C) 2013, Intel Corporation
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
* Copyright (C) 2015, Kodak Alaris, Inc
*/
#include <common.h>
#include <fdtdec.h>
#include <asm/fsp1/fsp_support.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* Override the FSP's Azalia configuration data
*
* @azalia: pointer to be updated to point to a ROM address where Azalia
* configuration data is stored
*/
__weak void update_fsp_azalia_configs(struct azalia_config **azalia)
{
*azalia = NULL;
}
/**
* 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 fsp_update_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;
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",
MRC_INIT_TSEG_SIZE_1MB);
fsp_upd->mrc_init_mmio_size = fdtdec_get_int(blob, node,
"fsp,mrc-init-mmio-size",
MRC_INIT_MMIO_SIZE_2048MB);
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",
EMMC_BOOT_MODE_EMMC41);
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",
SATA_MODE_AHCI);
fsp_upd->enable_azalia = fdtdec_get_bool(blob, node,
"fsp,enable-azalia");
if (fsp_upd->enable_azalia)
update_fsp_azalia_configs(&fsp_upd->azalia_cfg_ptr);
fsp_upd->enable_xhci = fdtdec_get_bool(blob, node, "fsp,enable-xhci");
fsp_upd->lpe_mode = fdtdec_get_int(blob, node, "fsp,lpe-mode",
LPE_MODE_PCI);
fsp_upd->lpss_sio_mode = fdtdec_get_int(blob, node, "fsp,lpss-sio-mode",
LPSS_SIO_MODE_PCI);
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", IGD_DVMT50_PRE_ALLOC_64MB);
fsp_upd->aperture_size = fdtdec_get_int(blob, node, "fsp,aperture-size",
APERTURE_SIZE_256MB);
fsp_upd->gtt_size = fdtdec_get_int(blob, node, "fsp,gtt-size",
GTT_SIZE_2MB);
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_mode = fdtdec_get_int(blob, node, "fsp,scc-mode",
SCC_MODE_PCI);
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",
OS_SELECTION_LINUX);
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",
DRAM_SPEED_1333MTS);
mem->dram_type = fdtdec_get_int(blob, node,
"fsp,dram-type",
DRAM_TYPE_DDR3L);
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",
DIMM_WIDTH_X8);
mem->dimm_density = fdtdec_get_int(blob, node,
"fsp,dimm-density",
DIMM_DENSITY_2GBIT);
mem->dimm_bus_width = fdtdec_get_int(blob, node,
"fsp,dimm-bus-width",
DIMM_BUS_WIDTH_64BITS);
mem->dimm_sides = fdtdec_get_int(blob, node,
"fsp,dimm-sides",
DIMM_SIDES_1RANKS);
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);
}
}
}
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <mmc.h>
#include <pci_ids.h>
#include <asm/irq.h>
#include <asm/mrccache.h>
#include <asm/post.h>
#include <asm/arch/iomap.h>
/* GPIO SUS */
#define GPIO_SUS_PAD_BASE (IO_BASE_ADDRESS + IO_BASE_OFFSET_GPSSUS)
#define GPIO_SUS_DFX5_CONF0 0x150
#define BYT_TRIG_LVL BIT(24)
#define BYT_TRIG_POS BIT(25)
int arch_cpu_init(void)
{
post_code(POST_CPU_INIT);
return x86_cpu_init_f();
}
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
/*
* For some unknown reason, FSP (gold4) for BayTrail configures
* the GPIO DFX5 PAD to enable level interrupt (bit 24 and 25).
* This does not cause any issue when Linux kernel runs w/ or w/o
* the pinctrl driver for BayTrail. However this causes unstable
* S3 resume if the pinctrl driver is included in the kernel build.
* As this pin keeps generating interrupts during an S3 resume,
* and there is no IRQ requester in the kernel to handle it, the
* kernel seems to hang and does not continue resuming.
*
* Clear the mysterious interrupt bits for this pin.
*/
clrbits_le32(GPIO_SUS_PAD_BASE + GPIO_SUS_DFX5_CONF0,
BYT_TRIG_LVL | BYT_TRIG_POS);
return 0;
}
@@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
config INTEL_BRASWELL
bool
select HAVE_FSP
select ARCH_MISC_INIT
select CPU_INTEL_TURBO_NOT_PACKAGE_SCOPED
imply HAVE_INTEL_ME
imply HAVE_VBT
imply ENABLE_MRC_CACHE
imply AHCI_PCI
imply ICH_SPI
imply MMC
imply MMC_PCI
imply MMC_SDHCI
imply MMC_SDHCI_SDMA
imply SCSI
imply SCSI_AHCI
imply SPI_FLASH
imply SYS_NS16550
imply USB
imply USB_XHCI_HCD
imply VIDEO_FSP
if INTEL_BRASWELL
config FSP_ADDR
hex
default 0xfff20000
endif
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
obj-y += braswell.o early_uart.o fsp_configs.o
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <asm/mrccache.h>
#include <asm/post.h>
int arch_cpu_init(void)
{
post_code(POST_CPU_INIT);
return x86_cpu_init_f();
}
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
return 0;
}
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.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
/* UART PAD definitions */
#define UART_RXD_COMMUITY 1
#define UART_TXD_COMMUITY 1
#define UART_RXD_FAMILY 4
#define UART_TXD_FAMILY 4
#define UART_RXD_PAD 2
#define UART_TXD_PAD 7
#define UART_RXD_FUNC 3
#define UART_TXD_FUNC 3
/* IO Memory */
#define IO_BASE_ADDRESS 0xfed80000
static inline uint32_t gpio_pconf0(int community, int family, int pad)
{
return IO_BASE_ADDRESS + community * 0x8000 + 0x4400 +
family * 0x400 + pad * 8;
}
static void gpio_select_func(int community, int family, int pad, int func)
{
uint32_t pconf0_addr = gpio_pconf0(community, family, pad);
clrsetbits_le32(pconf0_addr, 0xf << 16, func << 16);
}
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
*/
gpio_select_func(UART_RXD_COMMUITY, UART_RXD_FAMILY,
UART_RXD_PAD, UART_RXD_FUNC);
gpio_select_func(UART_TXD_COMMUITY, UART_TXD_FAMILY,
UART_TXD_PAD, UART_TXD_FUNC);
return 0;
}
void board_debug_uart_init(void)
{
setup_internal_uart(1);
}
@@ -0,0 +1,163 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <fdtdec.h>
#include <asm/fsp1/fsp_support.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* Override the FSP's Azalia configuration data
*
* @azalia: pointer to be updated to point to a ROM address where Azalia
* configuration data is stored
*/
__weak void update_fsp_azalia_configs(struct azalia_config **azalia)
{
*azalia = NULL;
}
/**
* Override the FSP's GPIO configuration data
*
* @family: pointer to be updated to point to a ROM address where GPIO
* family configuration data is stored
* @pad: pointer to be updated to point to a ROM address where GPIO
* pad configuration data is stored
*/
__weak void update_fsp_gpio_configs(struct gpio_family **family,
struct gpio_pad **pad)
{
*family = NULL;
*pad = NULL;
}
/**
* Override the FSP's configuration data.
* If the device tree does not specify an integer setting, use the default
* provided in Intel's Braswell release FSP/BraswellFsp.bsf file.
*/
void fsp_update_configs(struct fsp_config_data *config,
struct fspinit_rtbuf *rt_buf)
{
struct upd_region *fsp_upd = &config->fsp_upd;
struct memory_upd *memory_upd = &fsp_upd->memory_upd;
struct silicon_upd *silicon_upd = &fsp_upd->silicon_upd;
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;
node = fdt_node_offset_by_compatible(blob, 0, "intel,braswell-fsp");
if (node < 0) {
debug("%s: Cannot find FSP node\n", __func__);
return;
}
node = fdt_node_offset_by_compatible(blob, node,
"intel,braswell-fsp-memory");
if (node < 0) {
debug("%s: Cannot find FSP memory node\n", __func__);
return;
}
/* Override memory UPD contents */
memory_upd->mrc_init_tseg_size = fdtdec_get_int(blob, node,
"fsp,mrc-init-tseg-size", MRC_INIT_TSEG_SIZE_4MB);
memory_upd->mrc_init_mmio_size = fdtdec_get_int(blob, node,
"fsp,mrc-init-mmio-size", MRC_INIT_MMIO_SIZE_2048MB);
memory_upd->mrc_init_spd_addr1 = fdtdec_get_int(blob, node,
"fsp,mrc-init-spd-addr1", 0xa0);
memory_upd->mrc_init_spd_addr2 = fdtdec_get_int(blob, node,
"fsp,mrc-init-spd-addr2", 0xa2);
memory_upd->igd_dvmt50_pre_alloc = fdtdec_get_int(blob, node,
"fsp,igd-dvmt50-pre-alloc", IGD_DVMT50_PRE_ALLOC_32MB);
memory_upd->aperture_size = fdtdec_get_int(blob, node,
"fsp,aperture-size", APERTURE_SIZE_256MB);
memory_upd->gtt_size = fdtdec_get_int(blob, node,
"fsp,gtt-size", GTT_SIZE_1MB);
memory_upd->legacy_seg_decode = fdtdec_get_bool(blob, node,
"fsp,legacy-seg-decode");
memory_upd->enable_dvfs = fdtdec_get_bool(blob, node,
"fsp,enable-dvfs");
memory_upd->memory_type = fdtdec_get_int(blob, node,
"fsp,memory-type", DRAM_TYPE_DDR3);
memory_upd->enable_ca_mirror = fdtdec_get_bool(blob, node,
"fsp,enable-ca-mirror");
node = fdt_node_offset_by_compatible(blob, node,
"intel,braswell-fsp-silicon");
if (node < 0) {
debug("%s: Cannot find FSP silicon node\n", __func__);
return;
}
/* Override silicon UPD contents */
silicon_upd->sdcard_mode = fdtdec_get_int(blob, node,
"fsp,sdcard-mode", SDCARD_MODE_PCI);
silicon_upd->enable_hsuart0 = fdtdec_get_bool(blob, node,
"fsp,enable-hsuart0");
silicon_upd->enable_hsuart1 = fdtdec_get_bool(blob, node,
"fsp,enable-hsuart1");
silicon_upd->enable_azalia = fdtdec_get_bool(blob, node,
"fsp,enable-azalia");
if (silicon_upd->enable_azalia)
update_fsp_azalia_configs(&silicon_upd->azalia_cfg_ptr);
silicon_upd->enable_sata = fdtdec_get_bool(blob, node,
"fsp,enable-sata");
silicon_upd->enable_xhci = fdtdec_get_bool(blob, node,
"fsp,enable-xhci");
silicon_upd->lpe_mode = fdtdec_get_int(blob, node,
"fsp,lpe-mode", LPE_MODE_PCI);
silicon_upd->enable_dma0 = fdtdec_get_bool(blob, node,
"fsp,enable-dma0");
silicon_upd->enable_dma1 = fdtdec_get_bool(blob, node,
"fsp,enable-dma1");
silicon_upd->enable_i2c0 = fdtdec_get_bool(blob, node,
"fsp,enable-i2c0");
silicon_upd->enable_i2c1 = fdtdec_get_bool(blob, node,
"fsp,enable-i2c1");
silicon_upd->enable_i2c2 = fdtdec_get_bool(blob, node,
"fsp,enable-i2c2");
silicon_upd->enable_i2c3 = fdtdec_get_bool(blob, node,
"fsp,enable-i2c3");
silicon_upd->enable_i2c4 = fdtdec_get_bool(blob, node,
"fsp,enable-i2c4");
silicon_upd->enable_i2c5 = fdtdec_get_bool(blob, node,
"fsp,enable-i2c5");
silicon_upd->enable_i2c6 = fdtdec_get_bool(blob, node,
"fsp,enable-i2c6");
#ifdef CONFIG_HAVE_VBT
silicon_upd->graphics_config_ptr = CONFIG_VBT_ADDR;
#endif
update_fsp_gpio_configs(&silicon_upd->gpio_familiy_ptr,
&silicon_upd->gpio_pad_ptr);
/*
* For Braswell B0 stepping, disable_punit_pwr_config must be set to 1
* otherwise it just hangs in fsp_init().
*/
if (gd->arch.x86_mask == 2)
silicon_upd->disable_punit_pwr_config = 1;
silicon_upd->emmc_mode = fdtdec_get_int(blob, node,
"fsp,emmc-mode", EMMC_MODE_PCI);
silicon_upd->sata_speed = fdtdec_get_int(blob, node,
"fsp,sata-speed", SATA_SPEED_GEN3);
silicon_upd->pmic_i2c_bus = fdtdec_get_int(blob, node,
"fsp,pmic-i2c-bus", 0);
silicon_upd->enable_isp = fdtdec_get_bool(blob, node,
"fsp,enable-isp");
silicon_upd->isp_pci_dev_config = fdtdec_get_int(blob, node,
"fsp,isp-pci-dev-config", ISP_PCI_DEV_CONFIG_2);
silicon_upd->turbo_mode = fdtdec_get_bool(blob, node,
"fsp,turbo-mode");
silicon_upd->pnp_settings = fdtdec_get_int(blob, node,
"fsp,pnp-settings", PNP_SETTING_POWER_AND_PERF);
silicon_upd->sd_detect_chk = fdtdec_get_bool(blob, node,
"fsp,sd-detect-chk");
}
@@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2016 Google Inc.
#
config INTEL_BROADWELL
bool
select CACHE_MRC_BIN
select ARCH_EARLY_INIT_R
imply HAVE_INTEL_ME
imply ENABLE_MRC_CACHE
imply AHCI_PCI
imply ICH_SPI
imply INTEL_BROADWELL_GPIO
imply SCSI
imply SCSI_AHCI
imply SPI_FLASH
imply USB
imply USB_EHCI_HCD
imply VIDEO_BROADWELL_IGD
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
@@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (c) 2016 Google, Inc
obj-y += adsp.o
obj-$(CONFIG_$(SPL_TPL_)X86_16BIT_INIT) += cpu.o
obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += cpu_full.o
ifdef CONFIG_SPL
ifndef CONFIG_SPL_BUILD
obj-y += cpu_from_spl.o
obj-y += cpu_full.o
obj-y += refcode.o
endif
ifndef CONFIG_SPL_BUILD
# obj-y += cpu_from_spl.o
endif
endif
ifeq ($(CONFIG_$(SPL_TPL_)X86_32BIT_INIT),)
#obj-y += cpu_from_spl.o
endif
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-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += refcode.o
obj-y += sata.o
obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += sdram.o
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Support for Intel Application Digital Signal Processor
*
* Copyright 2019 Google LLC
*
* Modified from coreboot file of the same name
*/
#define LOG_CATEGORY UCLASS_SYSCON
#include <common.h>
#include <dm.h>
#include <pci.h>
#include <asm/io.h>
#include <asm/cpu.h>
#include <asm/intel_regs.h>
#include <asm/arch/adsp.h>
#include <asm/arch/pch.h>
#include <asm/arch/rcb.h>
enum pci_type_t {
LYNX_POINT,
WILDCAT_POINT,
};
struct broadwell_adsp_priv {
bool adsp_d3_pg_enable;
bool adsp_sram_pg_enable;
bool sio_acpi_mode;
};
static int broadwell_adsp_probe(struct udevice *dev)
{
struct broadwell_adsp_priv *priv = dev_get_priv(dev);
enum pci_type_t type;
u32 bar0, bar1;
u32 tmp32;
/* Find BAR0 and BAR1 */
bar0 = dm_pci_read_bar32(dev, 0);
if (!bar0)
return -EINVAL;
bar1 = dm_pci_read_bar32(dev, 1);
if (!bar1)
return -EINVAL;
/*
* Set LTR value in DSP shim LTR control register to 3ms
* SNOOP_REQ[13]=1b SNOOP_SCALE[12:10]=100b (1ms) SNOOP_VAL[9:0]=3h
*/
type = dev_get_driver_data(dev);
tmp32 = type == WILDCAT_POINT ? ADSP_SHIM_BASE_WPT : ADSP_SHIM_BASE_LPT;
writel(ADSP_SHIM_LTRC_VALUE, bar0 + tmp32);
/* Program VDRTCTL2 D19:F0:A8[31:0] = 0x00000fff */
dm_pci_write_config32(dev, ADSP_PCI_VDRTCTL2, ADSP_VDRTCTL2_VALUE);
/* Program ADSP IOBP VDLDAT1 to 0x040100 */
pch_iobp_write(ADSP_IOBP_VDLDAT1, ADSP_VDLDAT1_VALUE);
/* Set D3 Power Gating Enable in D19:F0:A0 based on PCH type */
dm_pci_read_config32(dev, ADSP_PCI_VDRTCTL0, &tmp32);
if (type == WILDCAT_POINT) {
if (priv->adsp_d3_pg_enable) {
tmp32 &= ~ADSP_VDRTCTL0_D3PGD_WPT;
if (priv->adsp_sram_pg_enable)
tmp32 &= ~ADSP_VDRTCTL0_D3SRAMPGD_WPT;
else
tmp32 |= ADSP_VDRTCTL0_D3SRAMPGD_WPT;
} else {
tmp32 |= ADSP_VDRTCTL0_D3PGD_WPT;
}
} else {
if (priv->adsp_d3_pg_enable) {
tmp32 &= ~ADSP_VDRTCTL0_D3PGD_LPT;
if (priv->adsp_sram_pg_enable)
tmp32 &= ~ADSP_VDRTCTL0_D3SRAMPGD_LPT;
else
tmp32 |= ADSP_VDRTCTL0_D3SRAMPGD_LPT;
} else {
tmp32 |= ADSP_VDRTCTL0_D3PGD_LPT;
}
}
dm_pci_write_config32(dev, ADSP_PCI_VDRTCTL0, tmp32);
/* Set PSF Snoop to SA, RCBA+0x3350[10]=1b */
setbits_le32(RCB_REG(0x3350), 1 << 10);
/* Set DSP IOBP PMCTL 0x1e0=0x3f */
pch_iobp_write(ADSP_IOBP_PMCTL, ADSP_PMCTL_VALUE);
if (priv->sio_acpi_mode) {
/* Configure for ACPI mode */
log_info("ADSP: Enable ACPI Mode IRQ3\n");
/* Set interrupt de-assert/assert opcode override to IRQ3 */
pch_iobp_write(ADSP_IOBP_VDLDAT2, ADSP_IOBP_ACPI_IRQ3);
/* Enable IRQ3 in RCBA */
setbits_le32(RCB_REG(ACPIIRQEN), ADSP_ACPI_IRQEN);
/* Set ACPI Interrupt Enable Bit */
pch_iobp_update(ADSP_IOBP_PCICFGCTL, ~ADSP_PCICFGCTL_SPCBAD,
ADSP_PCICFGCTL_ACPIIE);
/* Put ADSP in D3hot */
clrbits_le32(bar1 + PCH_PCS, PCH_PCS_PS_D3HOT);
} else {
log_info("ADSP: Enable PCI Mode IRQ23\n");
/* Configure for PCI mode */
dm_pci_write_config32(dev, PCI_INTERRUPT_LINE, ADSP_PCI_IRQ);
/* Clear ACPI Interrupt Enable Bit */
pch_iobp_update(ADSP_IOBP_PCICFGCTL,
~(ADSP_PCICFGCTL_SPCBAD |
ADSP_PCICFGCTL_ACPIIE), 0);
}
return 0;
}
static int broadwell_adsp_ofdata_to_platdata(struct udevice *dev)
{
struct broadwell_adsp_priv *priv = dev_get_priv(dev);
priv->adsp_d3_pg_enable = dev_read_bool(dev, "intel,adsp-d3-pg-enable");
priv->adsp_sram_pg_enable = dev_read_bool(dev,
"intel,adsp-sram-pg-enable");
priv->sio_acpi_mode = dev_read_bool(dev, "intel,sio-acpi-mode");
return 0;
}
static const struct udevice_id broadwell_adsp_ids[] = {
{ .compatible = "intel,wildcatpoint-adsp", .data = WILDCAT_POINT },
{ }
};
U_BOOT_DRIVER(broadwell_adsp_drv) = {
.name = "adsp",
.id = UCLASS_SYSCON,
.ofdata_to_platdata = broadwell_adsp_ofdata_to_platdata,
.of_match = broadwell_adsp_ids,
.bind = dm_scan_fdt_dev,
.probe = broadwell_adsp_probe,
};
static struct pci_device_id broadwell_adsp_supported[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_WILDCATPOINT_ADSP) },
{ },
};
U_BOOT_PCI_DEVICE(broadwell_adsp_drv, broadwell_adsp_supported);
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*
* 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/lpc_common.h>
#include <asm/msr.h>
#include <asm/pci.h>
#include <asm/post.h>
#include <asm/turbo.h>
#include <asm/arch/cpu.h>
#include <asm/arch/pch.h>
#include <asm/arch/rcb.h>
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;
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;
}
perf_ctl.hi = 0;
msr_write(MSR_IA32_PERF_CTL, perf_ctl);
debug("CPU: frequency set to %d MHz\n",
((perf_ctl.lo >> 8) & 0xff) * INTEL_BCLK_MHZ);
}
int arch_cpu_init(void)
{
post_code(POST_CPU_INIT);
#ifdef CONFIG_TPL
/* Do a mini-init if TPL has already done the full init */
return x86_cpu_reinit_f();
#else
return x86_cpu_init_f();
#endif
}
int checkcpu(void)
{
int ret;
set_max_freq();
ret = cpu_common_init();
if (ret)
return ret;
gd->arch.pei_boot_mode = PEI_BOOT_NONE;
return 0;
}
int print_cpuinfo(void)
{
char processor_name[CPU_MAX_NAME_LEN];
const char *name;
/* Print processor name */
name = cpu_get_name(processor_name);
printf("CPU: %s\n", name);
return 0;
}
void board_debug_uart_init(void)
{
/* com1 / com2 decode range */
pci_x86_write_config(PCH_DEV_LPC, LPC_IO_DEC, 1 << 4, PCI_SIZE_16);
pci_x86_write_config(PCH_DEV_LPC, LPC_EN, COMA_LPC_EN, PCI_SIZE_16);
}
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2016 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <bloblist.h>
#include <cpu_func.h>
#include <debug_uart.h>
#include <handoff.h>
#include <asm/mtrr.h>
int misc_init_r(void)
{
return 0;
}
int dram_init(void)
{
struct spl_handoff *ho;
ho = bloblist_find(BLOBLISTT_SPL_HANDOFF, sizeof(*ho));
if (!ho)
return log_msg_ret("Missing SPL hand-off info", -ENOENT);
handoff_load_dram_size(ho);
#ifdef CONFIG_TPL
/* TODO(sjg@chromium.org): MTRR cannot be adjusted without a hang */
mtrr_add_request(MTRR_TYPE_WRBACK, 0, 2ULL << 30);
#else
mtrr_add_request(MTRR_TYPE_WRBACK, 0, gd->ram_size);
mtrr_commit(true);
#endif
return 0;
}
int checkcpu(void)
{
return 0;
}
int print_cpuinfo(void)
{
return 0;
}
void board_debug_uart_init(void)
{
}
int dram_init_banksize(void)
{
#ifdef CONFIG_NR_DRAM_BANKS
struct spl_handoff *ho;
ho = bloblist_find(BLOBLISTT_SPL_HANDOFF, sizeof(*ho));
if (!ho)
return log_msg_ret("Missing SPL hand-off info", -ENOENT);
handoff_load_dram_banks(ho);
#endif
return 0;
}
@@ -0,0 +1,667 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*
* 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,
};
#if defined(CONFIG_SPL_BUILD) && !defined(CONFIG_TPL_BUILD)
int arch_cpu_init(void)
{
return 0;
}
#endif
/*
* 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(dev),
"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(dev),
"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;
}
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_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(MSR_IA32_PERF_CTL, perf_ctl);
debug("cpu: frequency set to %d\n",
((perf_ctl.lo >> 8) & 0xff) * INTEL_BCLK_MHZ);
}
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 |= MISC_ENABLE_FAST_STRING;
msr.lo |= MISC_ENABLE_TM1;
msr.lo |= MISC_ENABLE_ENHANCED_SPEEDSTEP;
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_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 */
cpu_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;
uint power_unit;
uint 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)
{
return cpu_intel_get_info(info, INTEL_BCLK_MHZ);
}
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,
.get_vendor = cpu_x86_get_vendor,
};
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),
.flags = DM_FLAG_PRE_RELOC,
};
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*
* Modified from coreboot
*/
#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;
}
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*
* From coreboot broadwell support
*/
#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,
};
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*
* 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;
}
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2011 The Chromium Authors
*/
#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <asm/mrc_common.h>
#include <asm/arch/iomap.h>
#include <asm/arch/pch.h>
#include <asm/arch/pei_data.h>
__weak asmlinkage void sdram_console_tx_byte(unsigned char byte)
{
#ifdef DEBUG
putc(byte);
#endif
}
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 void pei_data_usb2_port(struct pei_data *pei_data, int port, uint length,
uint enable, uint oc_pin, uint 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 void pei_data_usb3_port(struct pei_data *pei_data, int port, uint enable,
uint oc_pin, uint 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 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,
};
@@ -0,0 +1,681 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*/
#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/serialio.h>
#include <asm/arch/spi.h>
#include <dm/uclass-internal.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(dev),
"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(dev),
"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);
}
/* Enable LTR Auto Mode for D21:F1-F6 */
static void serialio_d21_ltr(u32 bar0)
{
/* 1. Program BAR0 + 808h[2] = 0b */
clrbits_le32(bar0 + SIO_REG_PPR_GEN, SIO_REG_PPR_GEN_LTR_MODE_MASK);
/* 2. Program BAR0 + 804h[1:0] = 00b */
clrbits_le32(bar0 + SIO_REG_PPR_RST, SIO_REG_PPR_RST_ASSERT);
/* 3. Program BAR0 + 804h[1:0] = 11b */
setbits_le32(bar0 + SIO_REG_PPR_RST, SIO_REG_PPR_RST_ASSERT);
/* 4. Program BAR0 + 814h[31:0] = 00000000h */
writel(0, bar0 + SIO_REG_AUTO_LTR);
}
/* Select I2C voltage of 1.8V or 3.3V */
static void serialio_i2c_voltage_sel(u32 bar0, uint voltage)
{
clrsetbits_le32(bar0 + SIO_REG_PPR_GEN, SIO_REG_PPR_GEN_VOLTAGE_MASK,
SIO_REG_PPR_GEN_VOLTAGE(voltage));
}
/* Put Serial IO D21:F0-F6 device into desired mode */
static void serialio_d21_mode(int sio_index, int int_pin, bool acpi_mode)
{
u32 portctrl = SIO_IOBP_PORTCTRL_PM_CAP_PRSNT;
/* Snoop select 1 */
portctrl |= SIO_IOBP_PORTCTRL_SNOOP_SELECT(1);
/* Set interrupt pin */
portctrl |= SIO_IOBP_PORTCTRL_INT_PIN(int_pin);
if (acpi_mode) {
/* Enable ACPI interrupt mode */
portctrl |= SIO_IOBP_PORTCTRL_ACPI_IRQ_EN;
}
pch_iobp_update(SIO_IOBP_PORTCTRLX(sio_index), 0, portctrl);
}
/* Init sequence to be run once, done as part of D21:F0 (SDMA) init */
static void serialio_init_once(bool acpi_mode)
{
if (acpi_mode) {
/* Enable ACPI IRQ for IRQ13, IRQ7, IRQ6, IRQ5 in RCBA */
setbits_le32(RCB_REG(ACPIIRQEN),
1 << 13 | 1 << 7 | 1 << 6 | 1 << 5);
}
/* Program IOBP CB000154h[12,9:8,4:0] = 1001100011111b */
pch_iobp_update(SIO_IOBP_GPIODF, ~0x0000131f, 0x0000131f);
/* Program IOBP CB000180h[5:0] = 111111b (undefined register) */
pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f);
}
/**
* pch_serialio_init() - set up serial I/O devices
*
* @return 0 if OK, -ve on error
*/
static int pch_serialio_init(void)
{
struct udevice *dev, *hda;
bool acpi_mode = true;
u32 bar0, bar1;
int ret;
ret = uclass_find_first_device(UCLASS_I2C, &dev);
if (ret)
return ret;
bar0 = dm_pci_read_bar32(dev, 0);
if (!bar0)
return -EINVAL;
bar1 = dm_pci_read_bar32(dev, 1);
if (!bar1)
return -EINVAL;
serialio_init_once(acpi_mode);
serialio_d21_mode(SIO_ID_SDMA, SIO_PIN_INTB, acpi_mode);
serialio_d21_ltr(bar0);
serialio_i2c_voltage_sel(bar0, 1); /* Select 1.8V always */
serialio_d21_mode(SIO_ID_I2C0, SIO_PIN_INTC, acpi_mode);
setbits_le32(bar1 + PCH_PCS, PCH_PCS_PS_D3HOT);
clrbits_le32(bar1 + PCH_PCS, PCH_PCS_PS_D3HOT);
setbits_le32(bar0 + SIO_REG_PPR_CLOCK, SIO_REG_PPR_CLOCK_EN);
/* Manually find the High-definition audio, to turn it off */
ret = dm_pci_bus_find_bdf(PCI_BDF(0, 0x1b, 0), &hda);
if (ret)
return -ENOENT;
dm_pci_clrset_config8(hda, 0x43, 0, 0x6f);
/* Route I/O buffers to ADSP function */
dm_pci_clrset_config8(hda, 0x42, 0, 1 << 7 | 1 << 6);
log_debug("HDA disabled, I/O buffers routed to ADSP\n");
return 0;
}
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);
ret = pch_serialio_init();
if (ret)
return ret;
systemagent_init();
return 0;
}
static int broadwell_pch_probe(struct udevice *dev)
{
if (CONFIG_IS_ENABLED(X86_32BIT_INIT)) {
if (!(gd->flags & GD_FLG_RELOC))
return broadwell_pch_early_init(dev);
else
return broadwell_pch_init(dev);
} else if (IS_ENABLED(CONFIG_SPL) && !IS_ENABLED(CONFIG_SPL_BUILD)) {
return broadwell_pch_init(dev);
} else {
return 0;
}
}
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 int broadwell_ioctl(struct udevice *dev, enum pch_req_t req, void *data,
int size)
{
switch (req) {
case PCH_REQ_PMBASE_INFO: {
struct pch_pmbase_info *pm = data;
int ret;
/* Find the base address of the powermanagement registers */
ret = dm_pci_read_config16(dev, 0x40, &pm->base);
if (ret)
return ret;
pm->base &= 0xfffe;
pm->gpio0_en_ofs = GPE0_EN(0);
pm->pm1_sts_ofs = PM1_STS;
pm->pm1_cnt_ofs = PM1_CNT;
return 0;
}
default:
return -ENOSYS;
}
}
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,
.ioctl = broadwell_ioctl,
};
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,
};
@@ -0,0 +1,278 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Google, Inc
*/
#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>
#include <dm/uclass-internal.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(dev));
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(dev));
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_find_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,
};
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From coreboot src/soc/intel/broadwell/romstage/power_state.c
*
* Copyright (C) 2016 Google, Inc.
*/
#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);
}
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Read a coreboot rmodule and execute it.
* The rmodule_header struct is from coreboot.
*
* Copyright (c) 2016 Google, Inc
*/
#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;
/**
* cpu_run_reference_code() - Run the platform reference code
*
* Some platforms require a binary blob to be executed once SDRAM is
* available. This is used to set up various platform features, such as the
* platform controller hub (PCH). This function should be implemented by the
* CPU-specific code.
*
* @return 0 on success, -ve on failure
*/
static 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;
}
int arch_early_init_r(void)
{
return cpu_run_reference_code();
}
@@ -0,0 +1,268 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*
* From coreboot src/soc/intel/broadwell/sata.c
*/
#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(dev);
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),
};
@@ -0,0 +1,207 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*
* From coreboot src/soc/intel/broadwell/romstage/raminit.c
*/
#include <common.h>
#include <dm.h>
#include <init.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);
}
int dram_init_banksize(void)
{
mrc_common_dram_init_banksize();
return 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) {
debug("Cannot get ME (err=%d)\n", ret);
return ret;
}
intel_me_status(me_dev);
/* Save ME HSIO version */
ret = uclass_first_device_err(UCLASS_PCH, &pch_dev);
if (ret) {
debug("Cannot get PCH (err=%d)\n", ret);
return ret;
}
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_err(UCLASS_NORTHBRIDGE, &dev);
if (ret) {
debug("Cannot get Northbridge (err=%d)\n", ret);
return ret;
}
size = 256;
ret = mrc_locate_spd(dev, size, &spd_data);
if (ret) {
debug("Cannot locate SPD (err=%d)\n", 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) {
debug("mrc_common_init() failed(err=%d)\n", ret);
return ret;
}
debug("Memory init done\n");
ret = sdram_find(dev);
if (ret) {
debug("sdram_find() failed (err=%d)\n", 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;
}
static const struct udevice_id broadwell_syscon_ids[] = {
{ .compatible = "intel,me", .data = X86_SYSCON_ME },
{ }
};
U_BOOT_DRIVER(syscon_intel_me) = {
.name = "intel_me_syscon",
.id = UCLASS_SYSCON,
.of_match = broadwell_syscon_ids,
};
@@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#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
@@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0+
#
# (C) Copyright 2002
# Daniel Engström, Omicron Ceti AB, daniel@omicron.se.
CROSS_COMPILE ?= i386-linux-
# DO NOT MODIFY THE FOLLOWING UNLESS YOU REALLY KNOW WHAT YOU ARE DOING!
LDPPFLAGS += -DRESET_SEG_START=$(CONFIG_RESET_SEG_START)
LDPPFLAGS += -DRESET_VEC_LOC=$(CONFIG_RESET_VEC_LOC)
LDPPFLAGS += -DSTART_16=$(CONFIG_SYS_X86_START16)
ifdef CONFIG_X86_64
ifndef CONFIG_SPL_BUILD
LDSCRIPT = $(srctree)/arch/x86/cpu/u-boot-64.lds
endif
endif
@@ -0,0 +1,28 @@
if TARGET_COREBOOT
config SYS_COREBOOT
bool
default y
imply SYS_NS16550
imply SCSI
imply SCSI_AHCI
imply AHCI_PCI
imply MMC
imply MMC_PCI
imply MMC_SDHCI
imply MMC_SDHCI_SDMA
imply USB
imply USB_EHCI_HCD
imply USB_XHCI_HCD
imply USB_STORAGE
imply USB_KEYBOARD
imply VIDEO_COREBOOT
imply E1000
imply ETH_DESIGNWARE
imply PCH_GBE
imply RTL8169
imply CMD_CBFS
imply FS_CBFS
imply CBMEM_CONSOLE
endif
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0+
#
# 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.
obj-y += car.o
obj-y += coreboot.o
obj-y += tables.o
obj-y += sdram.o
obj-y += timestamp.o
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2010-2011
* Graeme Russ, <graeme.russ@gmail.com>
*/
.section .text
.globl car_init
car_init:
jmp car_init_ret
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2008
* Graeme Russ, graeme.russ@gmail.com.
*/
#include <common.h>
#include <cpu_func.h>
#include <fdtdec.h>
#include <usb.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 checkcpu(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, true);
wrmsrl(MTRR_PHYS_BASE_MSR(top_mtrr), 0);
wrmsrl(MTRR_PHYS_MASK_MSR(top_mtrr), 0);
mtrr_close(&state, true);
}
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)
{
/* start usb so that usb keyboard can be used as input device */
if (CONFIG_IS_ENABLED(USB_KEYBOARD))
usb_init();
board_final_cleanup();
return 0;
}
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011 The Chromium OS Authors.
* (C) Copyright 2010,2011
* Graeme Russ, <graeme.russ@gmail.com>
*/
#include <common.h>
#include <init.h>
#include <asm/e820.h>
#include <asm/arch/sysinfo.h>
DECLARE_GLOBAL_DATA_PTR;
unsigned int install_e820_map(unsigned int max_entries,
struct e820_entry *entries)
{
unsigned int num_entries;
int i;
num_entries = min((unsigned int)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;
}
int 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;
}
}
}
return 0;
}
@@ -0,0 +1,240 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 Advanced Micro Devices, Inc.
* Copyright (C) 2009 coresystems GmbH
*/
#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;
}
__weak void cb_parse_unhandled(u32 tag, unsigned char *ptr)
{
}
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;
default:
cb_parse_unhandled(rec->tag, ptr);
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;
}
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
*/
#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;
}
@@ -0,0 +1,285 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (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
*/
#include <common.h>
#include <acpi_s3.h>
#include <command.h>
#include <cpu_func.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <syscon.h>
#include <asm/acpi.h>
#include <asm/acpi_table.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;
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",
};
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;
}
int x86_init_cache(void)
{
enable_caches();
return 0;
}
int init_cache(void) __attribute__((weak, alias("x86_init_cache")));
void flush_cache(unsigned long dummy1, unsigned long dummy2)
{
asm("wbinvd\n");
}
/* 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;
}
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])
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);
#ifdef CONFIG_HAVE_ACPI_RESUME
debug("ACPI previous sleep state: %s\n",
acpi_ss_string(gd->arch.prev_sleep_state));
#endif
return 0;
}
void show_boot_progress(int val)
{
outb(val, POST_PORT);
}
#if !defined(CONFIG_SYS_COREBOOT) && !defined(CONFIG_EFI_STUB)
/*
* 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)
{
struct acpi_fadt __maybe_unused *fadt;
board_final_cleanup();
#ifdef CONFIG_HAVE_ACPI_RESUME
fadt = acpi_find_fadt();
if (fadt && gd->arch.prev_sleep_state == ACPI_S3)
acpi_resume(fadt);
#endif
write_tables();
#ifdef CONFIG_GENERATE_ACPI_TABLE
fadt = acpi_find_fadt();
/* Don't touch ACPI hardware on HW reduced platforms */
if (fadt && !(fadt->flags & ACPI_FADT_HW_REDUCED_ACPI)) {
/*
* Other than waiting for OSPM to request us to switch to ACPI
* mode, do it by ourselves, since SMI will not be triggered.
*/
enter_acpi_mode(fadt->pm1a_cnt_blk);
}
#endif
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
#ifdef CONFIG_HAVE_ACPI_RESUME
acpi_s3_reserve();
#ifdef CONFIG_HAVE_FSP
/*
* Save stack address to CMOS so that at next S3 boot,
* we can use it as the stack address for fsp_contiue()
*/
fsp_save_s3_stack();
#endif /* CONFIG_HAVE_FSP */
#endif /* CONFIG_HAVE_ACPI_RESUME */
return 0;
}
#endif
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#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);
struct cpuid_result res;
plat->cpu_id = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
"intel,apic-id", -1);
plat->family = gd->arch.x86;
res = cpuid(1);
plat->id[0] = res.eax;
plat->id[1] = res.edx;
return 0;
}
int cpu_x86_get_vendor(struct udevice *dev, char *buf, int size)
{
const char *vendor = cpu_vendor_name(gd->arch.x86_vendor);
if (size < (strlen(vendor) + 1))
return -ENOSPC;
strcpy(buf, vendor);
return 0;
}
int cpu_x86_get_desc(struct udevice *dev, char *buf, int size)
{
char *ptr;
if (size < CPU_MAX_NAME_LEN)
return -ENOSPC;
ptr = cpu_get_name(buf);
if (ptr != buf)
strcpy(buf, ptr);
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,
.get_vendor = cpu_x86_get_vendor,
};
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,
.flags = DM_FLAG_PRE_RELOC,
};
@@ -0,0 +1,11 @@
if EFI
config SYS_CAR_ADDR
hex
default 0x100000
config SYS_CAR_SIZE
hex
default 0x20000
endif
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (c) 2015 Google, Inc
ifdef CONFIG_EFI_APP
obj-y += app.o
obj-y += sdram.o
endif
ifdef CONFIG_EFI_STUB
obj-y += car.o
obj-y += payload.o
endif
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
*/
#include <common.h>
#include <cpu_func.h>
#include <fdtdec.h>
#include <netdev.h>
int arch_cpu_init(void)
{
return x86_cpu_init_f();
}
int checkcpu(void)
{
return 0;
}
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}
void board_final_cleanup(void)
{
}
int misc_init_r(void)
{
return 0;
}
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
.globl car_init
car_init:
jmp car_init_ret
@@ -0,0 +1,292 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
* Written by Simon Glass <sjg@chromium.org>
*/
#include <common.h>
#include <cpu_func.h>
#include <efi.h>
#include <errno.h>
#include <init.h>
#include <usb.h>
#include <asm/bootparam.h>
#include <asm/e820.h>
#include <asm/post.h>
DECLARE_GLOBAL_DATA_PTR;
/*
* 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)
{
struct efi_mem_desc *desc, *end;
struct efi_entry_memmap *map;
int ret, size;
uintptr_t dest_addr = 0;
struct efi_mem_desc *largest = NULL;
/*
* Find largest area of memory below 4GB. We could
* call efi_build_mem_table() for a more accurate picture since it
* merges areas together where possible. But that function uses more
* pre-relocation memory, and it's not critical that we find the
* absolute largest region.
*/
ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size);
if (ret) {
/* We should have stopped in dram_init(), something is wrong */
debug("%s: Missing memory map\n", __func__);
goto err;
}
end = (struct efi_mem_desc *)((ulong)map + size);
desc = map->desc;
for (; desc < end; desc = efi_get_next_mem_desc(map, desc)) {
if (desc->type != EFI_CONVENTIONAL_MEMORY ||
desc->physical_start >= 1ULL << 32)
continue;
if (!largest || desc->num_pages > largest->num_pages)
largest = desc;
}
/* If no suitable area was found, return an error. */
assert(largest);
if (!largest || (largest->num_pages << EFI_PAGE_SHIFT) < (2 << 20))
goto err;
dest_addr = largest->physical_start + (largest->num_pages <<
EFI_PAGE_SHIFT);
return (ulong)dest_addr;
err:
panic("No available memory found for relocation");
return 0;
}
int dram_init(void)
{
struct efi_mem_desc *desc, *end;
struct efi_entry_memmap *map;
int size, ret;
ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size);
if (ret) {
printf("Cannot find EFI memory map tables, ret=%d\n", ret);
return -ENODEV;
}
end = (struct efi_mem_desc *)((ulong)map + size);
gd->ram_size = 0;
desc = map->desc;
for (; desc < end; desc = efi_get_next_mem_desc(map, desc)) {
if (desc->type < EFI_MMAP_IO)
gd->ram_size += desc->num_pages << EFI_PAGE_SHIFT;
}
return 0;
}
int dram_init_banksize(void)
{
struct efi_mem_desc *desc, *end;
struct efi_entry_memmap *map;
int ret, size;
int num_banks;
ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size);
if (ret) {
/* We should have stopped in dram_init(), something is wrong */
debug("%s: Missing memory map\n", __func__);
return -ENXIO;
}
end = (struct efi_mem_desc *)((ulong)map + size);
desc = map->desc;
for (num_banks = 0;
desc < end && num_banks < CONFIG_NR_DRAM_BANKS;
desc = efi_get_next_mem_desc(map, desc)) {
/*
* We only use conventional memory and ignore
* anything less than 1MB.
*/
if (desc->type != EFI_CONVENTIONAL_MEMORY ||
(desc->num_pages << EFI_PAGE_SHIFT) < 1 << 20)
continue;
gd->bd->bi_dram[num_banks].start = desc->physical_start;
gd->bd->bi_dram[num_banks].size = desc->num_pages <<
EFI_PAGE_SHIFT;
num_banks++;
}
return 0;
}
int arch_cpu_init(void)
{
post_code(POST_CPU_INIT);
return x86_cpu_init_f();
}
int checkcpu(void)
{
return 0;
}
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}
/* Find any available tables and copy them to a safe place */
int reserve_arch(void)
{
struct efi_info_hdr *hdr;
debug("table=%lx\n", gd->arch.table);
if (!gd->arch.table)
return 0;
hdr = (struct efi_info_hdr *)gd->arch.table;
gd->start_addr_sp -= hdr->total_size;
memcpy((void *)gd->start_addr_sp, hdr, hdr->total_size);
debug("Stashing EFI table at %lx to %lx, size %x\n",
gd->arch.table, gd->start_addr_sp, hdr->total_size);
gd->arch.table = gd->start_addr_sp;
return 0;
}
int last_stage_init(void)
{
/* start usb so that usb keyboard can be used as input device */
if (CONFIG_IS_ENABLED(USB_KEYBOARD))
usb_init();
return 0;
}
unsigned int install_e820_map(unsigned int max_entries,
struct e820_entry *entries)
{
struct efi_mem_desc *desc, *end;
struct efi_entry_memmap *map;
int size, ret;
efi_physical_addr_t last_end_addr = 0;
struct e820_entry *last_entry = NULL;
__u32 e820_type;
unsigned int num_entries = 0;
ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size);
if (ret) {
printf("Cannot find EFI memory map tables, ret=%d\n", ret);
return -ENODEV;
}
end = (struct efi_mem_desc *)((ulong)map + size);
for (desc = map->desc; desc < end;
desc = efi_get_next_mem_desc(map, desc)) {
if (desc->num_pages == 0)
continue;
switch (desc->type) {
case EFI_LOADER_CODE:
case EFI_LOADER_DATA:
case EFI_BOOT_SERVICES_CODE:
case EFI_BOOT_SERVICES_DATA:
case EFI_CONVENTIONAL_MEMORY:
e820_type = E820_RAM;
break;
case EFI_RESERVED_MEMORY_TYPE:
case EFI_RUNTIME_SERVICES_CODE:
case EFI_RUNTIME_SERVICES_DATA:
case EFI_MMAP_IO:
case EFI_MMAP_IO_PORT:
case EFI_PAL_CODE:
e820_type = E820_RESERVED;
break;
case EFI_ACPI_RECLAIM_MEMORY:
e820_type = E820_ACPI;
break;
case EFI_ACPI_MEMORY_NVS:
e820_type = E820_NVS;
break;
case EFI_UNUSABLE_MEMORY:
e820_type = E820_UNUSABLE;
break;
default:
printf("Invalid EFI memory descriptor type (0x%x)!\n",
desc->type);
continue;
}
if (last_entry != NULL && last_entry->type == e820_type &&
desc->physical_start == last_end_addr) {
last_entry->size += (desc->num_pages << EFI_PAGE_SHIFT);
last_end_addr += (desc->num_pages << EFI_PAGE_SHIFT);
} else {
if (num_entries >= E820MAX)
break;
entries[num_entries].addr = desc->physical_start;
entries[num_entries].size = desc->num_pages;
entries[num_entries].size <<= EFI_PAGE_SHIFT;
entries[num_entries].type = e820_type;
last_entry = &entries[num_entries];
last_end_addr = last_entry->addr + last_entry->size;
num_entries++;
}
}
return num_entries;
}
void setup_efi_info(struct efi_info *efi_info)
{
struct efi_entry_systable *table;
struct efi_entry_memmap *map;
char *signature;
int size, ret;
memset(efi_info, 0, sizeof(struct efi_info));
ret = efi_info_get(EFIET_SYS_TABLE, (void **)&table, &size);
if (ret) {
printf("Cannot find EFI system table, ret=%d\n", ret);
return;
}
efi_info->efi_systab = (u32)(table->sys_table);
ret = efi_info_get(EFIET_MEMORY_MAP, (void **)&map, &size);
if (ret) {
printf("Cannot find EFI memory map tables, ret=%d\n", ret);
return;
}
efi_info->efi_memdesc_size = map->desc_size;
efi_info->efi_memdesc_version = map->version;
efi_info->efi_memmap = (u32)(map->desc);
efi_info->efi_memmap_size = size - sizeof(struct efi_entry_memmap);
#ifdef CONFIG_EFI_STUB_64BIT
efi_info->efi_systab_hi = table->sys_table >> 32;
efi_info->efi_memmap_hi = (u64)(u32)(map->desc) >> 32;
signature = EFI64_LOADER_SIGNATURE;
#else
signature = EFI32_LOADER_SIGNATURE;
#endif
memcpy(&efi_info->efi_loader_signature, signature, 4);
}
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2015 Google, Inc
*/
#include <common.h>
#include <efi.h>
#include <init.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;
}
int dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = efi_get_ram_base();
gd->bd->bi_dram[0].size = CONFIG_EFI_RAM_SIZE;
return 0;
}
@@ -0,0 +1,9 @@
#
# (C) Copyright 2016 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
obj-y += call64.o
obj-y += cpu.o
obj-y += interrupt.o
obj-y += setjmp.o
@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (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
*/
#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 */
.globl call64_stub_size
call64_stub_size:
.long . - cpu_call64
.data
.align 16
.globl gdt64
gdt64:
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:
@@ -0,0 +1,620 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (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
*/
#include <common.h>
#include <cpu_func.h>
#include <malloc.h>
#include <asm/control_regs.h>
#include <asm/cpu.h>
#include <asm/mp.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
#include <asm/processor-flags.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 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 = (ulong)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
/*
* 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;
}
/* initialise FPU, reset EM, set MP and NE */
static void setup_cpu_features(void)
{
const u32 em_rst = ~X86_CR0_EM;
const u32 mp_ne_set = X86_CR0_MP | X86_CR0_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");
}
static void setup_identity(void)
{
/* 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 */
static void setup_pci_ram_top(void)
{
gd->pci_ram_top = 0x80000000U;
}
static void setup_mtrr(void)
{
u64 mtrr_cap;
/* Configure fixed range MTRRs for some legacy regions */
if (!gd->arch.has_mtrr)
return;
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);
}
}
int x86_cpu_init_tpl(void)
{
setup_cpu_features();
setup_identity();
return 0;
}
int x86_cpu_init_f(void)
{
if (ll_boot_init())
setup_cpu_features();
setup_identity();
setup_mtrr();
setup_pci_ram_top();
/* Set up the i8254 timer if required */
if (IS_ENABLED(CONFIG_I8254_TIMER))
i8254_init();
return 0;
}
int x86_cpu_reinit_f(void)
{
setup_identity();
setup_pci_ram_top();
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 dcache_status(void)
{
return !(read_cr0() & X86_CR0_CD);
}
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();
}
#define PAGETABLE_BASE 0x80000
#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] = (ulong)&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] = (ulong)&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;
}
/*
* Jump from SPL to U-Boot
*
* This function is work-in-progress with many issues to resolve.
*
* It works by setting up several regions:
* ptr - a place to put the code that jumps into 64-bit mode
* gdt - a place to put the global descriptor table
* pgtable - a place to put the page tables
*
* The cpu_call64() code is copied from ROM and then manually patched so that
* it has the correct GDT address in RAM. U-Boot is copied from ROM into
* its pre-relocation address. Then we jump to the cpu_call64() code in RAM,
* which changes to 64-bit mode and starts U-Boot.
*/
int cpu_jump_to_64bit_uboot(ulong target)
{
typedef void (*func_t)(ulong pgtable, ulong setup_base, ulong target);
uint32_t *pgtable;
func_t func;
char *ptr;
pgtable = (uint32_t *)PAGETABLE_BASE;
build_pagetable(pgtable);
extern long call64_stub_size;
ptr = malloc(call64_stub_size);
if (!ptr) {
printf("Failed to allocate the cpu_call64 stub\n");
return -ENOMEM;
}
memcpy(ptr, cpu_call64, call64_stub_size);
func = (func_t)ptr;
/*
* Copy U-Boot from ROM
* TODO(sjg@chromium.org): Figure out a way to get the text base
* correctly here, and in the device-tree binman definition.
*
* Also consider using FIT so we get the correct image length and
* parameters.
*/
memcpy((char *)target, (char *)0xfff00000, 0x100000);
/* Jump to U-Boot */
func((ulong)pgtable, 0, (ulong)target);
return -EFAULT;
}
#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),
};
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
@@ -0,0 +1,631 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (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
*/
#include <common.h>
#include <dm.h>
#include <efi_loader.h>
#include <irq_func.h>
#include <asm/control_regs.h>
#include <asm/i8259.h>
#include <asm/interrupt.h>
#include <asm/io.h>
#include <asm/lapic.h>
#include <asm/processor-flags.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.d32 irq_common_entry\n"
static char *exceptions[] = {
"Divide Error",
"Debug",
"NMI Interrupt",
"Breakpoint",
"Overflow",
"BOUND Range Exceeded",
"Invalid Opcode (Undefined Opcode)",
"Device Not Available (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"
};
/**
* show_efi_loaded_images() - show loaded UEFI images
*
* List all loaded UEFI images.
*
* @eip: instruction pointer
*/
static void show_efi_loaded_images(uintptr_t eip)
{
efi_print_image_infos((void *)eip);
}
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;
}
show_efi_loaded_images(eip);
}
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)((ulong)(routine) >> 16);
idt[intnum].base_low = (u16)((ulong)(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;
#if CONFIG_IS_ENABLED(X86_64)
asm volatile ("pushfq ; popq %0 ; cli\n" : "=g" (flags) : );
#else
asm volatile ("pushfl ; popl %0 ; cli\n" : "=g" (flags) : );
#endif
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
#ifdef CONFIG_APIC
lapic_setup();
#endif
/* 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));
@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Written by H. Peter Anvin <hpa@zytor.com>
* Brought in from Linux v4.4 and modified for U-Boot
* From Linux arch/um/sys-i386/setjmp.S
*/
#define _REGPARM
/*
* The jmp_buf is assumed to contain the following, in order:
* %ebx
* %esp
* %ebp
* %esi
* %edi
* <return address>
*/
.text
.align 4
.globl setjmp
.type setjmp, @function
setjmp:
#ifdef _REGPARM
movl %eax, %edx
#else
movl 4(%esp), %edx
#endif
popl %ecx /* Return address, and adjust the stack */
xorl %eax, %eax /* Return value */
movl %ebx, (%edx)
movl %esp, 4(%edx) /* Post-return %esp! */
pushl %ecx /* Make the call/return stack happy */
movl %ebp, 8(%edx)
movl %esi, 12(%edx)
movl %edi, 16(%edx)
movl %ecx, 20(%edx) /* Return address */
ret
/* Provide function size if needed */
.size setjmp, .-setjmp
.align 4
.globl longjmp
.type longjmp, @function
longjmp:
#ifdef _REGPARM
xchgl %eax, %edx
#else
movl 4(%esp), %edx /* jmp_ptr address */
#endif
movl (%edx), %ebx
movl 4(%edx), %esp
movl 8(%edx), %ebp
movl 12(%edx), %esi
movl 16(%edx), %edi
jmp *20(%edx)
.size longjmp, .-longjmp
@@ -0,0 +1,25 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (c) 2016 Google, Inc
ifdef CONFIG_HAVE_MRC
obj-$(CONFIG_$(SPL_TPL_)X86_16BIT_INIT) += car.o
obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += me_status.o
obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += report_platform.o
obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += mrc.o
endif
obj-y += cpu.o
obj-y += lpc.o
ifndef CONFIG_TARGET_EFI_APP
obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += microcode.o
ifndef CONFIG_$(SPL_)X86_64
obj-y += microcode.o
endif
endif
obj-y += pch.o
ifdef CONFIG_SPL
ifndef CONFIG_SPL_BUILD
obj-y += cpu_from_spl.o
endif
endif
@@ -0,0 +1,244 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* 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>
*/
#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 binman */
.globl ucode_base
ucode_base: /* Declared in microcode.h */
.long 0 /* microcode base */
.globl ucode_size
ucode_size: /* Declared in microcode.h */
.long 0 /* microcode size */
@@ -0,0 +1,228 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014 Google Inc.
* Copyright (c) 2016 Google, Inc
* Copyright (C) 2015-2018 Intel Corporation.
* Copyright (C) 2018 Siemens AG
* Some code taken from coreboot cpulib.c
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <errno.h>
#include <asm/cpu.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) {
debug("%s: Microcode update failure (err=%d)\n", __func__, ret);
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;
}
int cpu_intel_get_info(struct cpu_info *info, int bclk)
{
msr_t msr;
msr = msr_read(MSR_IA32_PERF_CTL);
info->cpu_freq = ((msr.lo >> 8) & 0xff) * bclk * 1000000;
info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU |
1 << CPU_FEAT_UCODE | 1 << CPU_FEAT_DEVICE_ID;
return 0;
}
int cpu_configure_thermal_target(struct udevice *dev)
{
u32 tcc_offset;
msr_t msr;
int ret;
ret = dev_read_u32(dev, "tcc-offset", &tcc_offset);
if (!ret)
return -ENOENT;
/* Set TCC activaiton offset if supported */
msr = msr_read(MSR_PLATFORM_INFO);
if (msr.lo & (1 << 30)) {
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;
}
void cpu_set_perf_control(uint clk_ratio)
{
msr_t perf_ctl;
perf_ctl.lo = (clk_ratio & 0xff) << 8;
perf_ctl.hi = 0;
msr_write(MSR_IA32_PERF_CTL, perf_ctl);
debug("CPU: frequency set to %d MHz\n", clk_ratio * INTEL_BCLK_MHZ);
}
bool 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) != 0;
}
void cpu_set_p_state_to_turbo_ratio(void)
{
msr_t msr;
msr = msr_read(MSR_TURBO_RATIO_LIMIT);
cpu_set_perf_control(msr.lo);
}
enum burst_mode_t cpu_get_burst_mode_state(void)
{
enum burst_mode_t state;
int burst_en, burst_cap;
msr_t msr;
uint eax;
eax = cpuid_eax(0x6);
burst_cap = eax & 0x2;
msr = msr_read(MSR_IA32_MISC_ENABLE);
burst_en = !(msr.hi & BURST_MODE_DISABLE);
if (!burst_cap && burst_en)
state = BURST_MODE_UNAVAILABLE;
else if (burst_cap && !burst_en)
state = BURST_MODE_DISABLED;
else if (burst_cap && burst_en)
state = BURST_MODE_ENABLED;
else
state = BURST_MODE_UNKNOWN;
return state;
}
void cpu_set_burst_mode(bool burst_mode)
{
msr_t msr;
msr = msr_read(MSR_IA32_MISC_ENABLE);
if (burst_mode)
msr.hi &= ~BURST_MODE_DISABLE;
else
msr.hi |= BURST_MODE_DISABLE;
msr_write(MSR_IA32_MISC_ENABLE, msr);
}
void cpu_set_eist(bool eist_status)
{
msr_t msr;
msr = msr_read(MSR_IA32_MISC_ENABLE);
if (eist_status)
msr.lo |= MISC_ENABLE_ENHANCED_SPEEDSTEP;
else
msr.lo &= ~MISC_ENABLE_ENHANCED_SPEEDSTEP;
msr_write(MSR_IA32_MISC_ENABLE, msr);
}
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <handoff.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;
int arch_cpu_init(void)
{
int ret;
#if CONFIG_IS_ENABLED(HANDOFF) && IS_ENABLED(CONFIG_USE_HOB)
struct spl_handoff *ho = gd->spl_handoff;
gd->arch.hob_list = ho->arch.hob_list;
#endif
ret = x86_cpu_reinit_f();
return ret;
}
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*/
#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(dev),
"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;
}
@@ -0,0 +1,212 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From Coreboot src/southbridge/intel/bd82x6x/me_status.c
*
* Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
*/
#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);
}
@@ -0,0 +1,186 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Google, Inc
* Copyright (C) 2000 Ronald G. Minnich
*
* Microcode update for Intel PIII and later CPUs
*/
#include <common.h>
#include <errno.h>
#include <fdtdec.h>
#include <linux/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 -ENOENT;
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;
ulong address;
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 == -ENOENT && ucode_base) {
/*
* The microcode has been removed from the device tree
* in the build system. In that case it will have
* already been updated in car_init().
*/
debug("%s: Microcode data not available\n", __func__);
skipped++;
continue;
}
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;
}
address = (ulong)update.data + UCODE_HEADER_LEN;
wrmsr(MSR_IA32_UCODE_WRITE, address, 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++;
if (!ucode_base) {
ucode_base = (ulong)update.data;
ucode_size = update.size;
}
} while (1);
}
@@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <init.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(dev));
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);
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;
}
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*/
#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);
}
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From Coreboot src/northbridge/intel/sandybridge/report_platform.c
*
* Copyright (C) 2012 Google Inc.
*/
#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);
}
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#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));
}
}
@@ -0,0 +1,377 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#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;
/**
* pirq_reg_to_linkno() - Convert a PIRQ routing register offset to link number
*
* @priv: IRQ router driver's priv data
* @reg: PIRQ routing register offset from the base address
* @return: PIRQ link number (0 for PIRQA, 1 for PIRQB, etc)
*/
static inline int pirq_reg_to_linkno(struct irq_router *priv, int reg)
{
int linkno = 0;
if (priv->has_regmap) {
struct pirq_regmap *map = priv->regmap;
int i;
for (i = 0; i < priv->link_num; i++) {
if (reg - priv->link_base == map->offset) {
linkno = map->link;
break;
}
map++;
}
} else {
linkno = reg - priv->link_base;
}
return linkno;
}
/**
* pirq_linkno_to_reg() - Convert a PIRQ link number to routing register offset
*
* @priv: IRQ router driver's priv data
* @linkno: PIRQ link number (0 for PIRQA, 1 for PIRQB, etc)
* @return: PIRQ routing register offset from the base address
*/
static inline int pirq_linkno_to_reg(struct irq_router *priv, int linkno)
{
int reg = 0;
if (priv->has_regmap) {
struct pirq_regmap *map = priv->regmap;
int i;
for (i = 0; i < priv->link_num; i++) {
if (linkno == map->link) {
reg = map->offset + priv->link_base;
break;
}
map++;
}
} else {
reg = linkno + priv->link_base;
}
return reg;
}
bool pirq_check_irq_routed(struct udevice *dev, int link, u8 irq)
{
struct irq_router *priv = dev_get_priv(dev);
u8 pirq;
if (priv->config == PIRQ_VIA_PCI)
dm_pci_read_config8(dev->parent,
pirq_linkno_to_reg(priv, link), &pirq);
else
pirq = readb((uintptr_t)priv->ibase +
pirq_linkno_to_reg(priv, link));
pirq &= 0xf;
/* IRQ# 0/1/2/8/13 are reserved */
if (pirq < 3 || pirq == 8 || pirq == 13)
return false;
return pirq == irq ? true : false;
}
int pirq_translate_link(struct udevice *dev, int link)
{
struct irq_router *priv = dev_get_priv(dev);
return pirq_reg_to_linkno(priv, link);
}
void pirq_assign_irq(struct udevice *dev, int link, u8 irq)
{
struct irq_router *priv = dev_get_priv(dev);
/* 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,
pirq_linkno_to_reg(priv, link), irq);
else
writeb(irq, (uintptr_t)priv->ibase +
pirq_linkno_to_reg(priv, link));
}
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 = pirq_linkno_to_reg(priv, pirq);
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 pirq_regmap *map;
struct irq_routing_table *rt;
struct irq_info *slot, *slot_base;
int irq_entries = 0;
int i;
int ret;
node = dev_of_offset(dev);
/* extract the bdf from fdt_pci_addr */
priv->bdf = dm_pci_get_bdf(dev->parent);
ret = fdt_stringlist_search(blob, node, "intel,pirq-config", "pci");
if (!ret) {
priv->config = PIRQ_VIA_PCI;
} else {
ret = fdt_stringlist_search(blob, node, "intel,pirq-config",
"ibase");
if (!ret)
priv->config = PIRQ_VIA_IBASE;
else
return -EINVAL;
}
cell = fdt_getprop(blob, node, "intel,pirq-link", &len);
if (!cell || len != 8)
return -EINVAL;
priv->link_base = fdt_addr_to_cpu(cell[0]);
priv->link_num = fdt_addr_to_cpu(cell[1]);
if (priv->link_num > CONFIG_MAX_PIRQ_LINKS) {
debug("Limiting supported PIRQ link number from %d to %d\n",
priv->link_num, CONFIG_MAX_PIRQ_LINKS);
priv->link_num = CONFIG_MAX_PIRQ_LINKS;
}
cell = fdt_getprop(blob, node, "intel,pirq-regmap", &len);
if (cell) {
if (len % sizeof(struct pirq_regmap))
return -EINVAL;
count = len / sizeof(struct pirq_regmap);
if (count < priv->link_num) {
printf("Number of pirq-regmap entires is wrong\n");
return -EINVAL;
}
count = priv->link_num;
priv->regmap = calloc(count, sizeof(struct pirq_regmap));
if (!priv->regmap)
return -ENOMEM;
priv->has_regmap = true;
map = priv->regmap;
for (i = 0; i < count; i++) {
map->link = fdt_addr_to_cpu(cell[0]);
map->offset = fdt_addr_to_cpu(cell[1]);
cell += sizeof(struct pirq_regmap) / sizeof(u32);
map++;
}
}
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 !=
pirq_linkno_to_reg(priv, pr.pirq))
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);
gd->arch.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, (uintptr_t)priv->ibase + priv->actl_addr);
}
}
int irq_router_probe(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, gd->arch.pirq_routing_table->slots,
get_irq_slot_count(gd->arch.pirq_routing_table));
if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE))
irq_enable_sci(dev);
return 0;
}
ulong write_pirq_routing_table(ulong addr)
{
if (!gd->arch.pirq_routing_table)
return addr;
return copy_pirq_routing_table(addr, gd->arch.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",
};
@@ -0,0 +1,75 @@
# SPDX-License-Identifier: GPL-2.0
#
# From Coreboot src/northbridge/intel/sandybridge/Kconfig
#
# Copyright (C) 2010 Google Inc.
#
config NORTHBRIDGE_INTEL_IVYBRIDGE
bool
select CACHE_MRC_BIN if HAVE_MRC
imply HAVE_INTEL_ME
imply ENABLE_MRC_CACHE
imply AHCI_PCI
imply ICH_SPI
imply INTEL_ICH6_GPIO
imply PINCTRL_ICH6
imply SCSI
imply SCSI_AHCI
imply SPI_FLASH
imply USB
imply USB_EHCI_HCD
imply USB_XHCI_HCD
imply VIDEO_VESA
imply SOUND_IVYBRIDGE
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
@@ -0,0 +1,21 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (c) 2014 Google, Inc
ifdef CONFIG_HAVE_FSP
obj-y += fsp_configs.o ivybridge.o
else
obj-$(CONFIG_$(SPL_)X86_32BIT_INIT) += cpu.o
obj-y += early_me.o
obj-y += lpc.o
obj-y += northbridge.o
ifndef CONFIG_SPL_BUILD
obj-y += sata.o
endif
obj-$(CONFIG_$(SPL_)X86_32BIT_INIT) += sdram.o
ifndef CONFIG_$(SPL_)X86_32BIT_INIT
obj-y += sdram_nop.o
endif
endif
obj-y += model_206ax.o
obj-y += bd82x6x.o
@@ -0,0 +1,272 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2014 Google, Inc
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <pch.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/model_206ax.h>
#include <asm/arch/pch.h>
#include <asm/arch/sandybridge.h>
DECLARE_GLOBAL_DATA_PTR;
#define GPIO_BASE 0x48
#define BIOS_CTRL 0xdc
#define RCBA_AUDIO_CONFIG 0x2030
#define RCBA_AUDIO_CONFIG_HDA BIT(31)
#define RCBA_AUDIO_CONFIG_MASK 0xfe
#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)
{
if (!(gd->flags & GD_FLG_RELOC))
return 0;
/* Cause the SATA device to do its init */
uclass_first_device(UCLASS_AHCI, &dev);
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 int bd82x6x_ioctl(struct udevice *dev, enum pch_req_t req, void *data,
int size)
{
u32 rcba, val;
switch (req) {
case PCH_REQ_HDA_CONFIG:
dm_pci_read_config32(dev, PCH_RCBA, &rcba);
val = readl(rcba + RCBA_AUDIO_CONFIG);
if (!(val & RCBA_AUDIO_CONFIG_HDA))
return -ENOENT;
return val & RCBA_AUDIO_CONFIG_MASK;
case PCH_REQ_PMBASE_INFO: {
struct pch_pmbase_info *pm = data;
int ret;
/* Find the base address of the powermanagement registers */
ret = dm_pci_read_config16(dev, 0x40, &pm->base);
if (ret)
return ret;
pm->base &= 0xfffe;
pm->gpio0_en_ofs = GPE0_EN;
pm->pm1_sts_ofs = PM1_STS;
pm->pm1_cnt_ofs = PM1_CNT;
return 0;
}
default:
return -ENOSYS;
}
}
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,
.ioctl = bd82x6x_ioctl,
};
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,
};
@@ -0,0 +1,204 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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.
*/
#include <common.h>
#include <cpu_func.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 checkcpu() 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 checkcpu(void)
{
enum pei_boot_mode_t boot_mode = PEI_BOOT_NONE;
struct udevice *dev, *lpc;
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) {
debug("%s: cpu_common_init() failed\n", __func__);
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) {
debug("%s: Failed to get I2C (ret=%d)\n", __func__, 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;
return 0;
}
int print_cpuinfo(void)
{
char processor_name[CPU_MAX_NAME_LEN];
const char *name;
/* 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(PCH_LPC_DEV, LPC_EN, COMA_LPC_EN, PCI_SIZE_16);
}
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From Coreboot src/southbridge/intel/bd82x6x/early_me.c
*
* Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <sysreset.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);
sysreset_walk_halt(SYSRESET_COLD);
break;
case ME_HFS_ACK_PWR_CYCLE:
/* Power cycle reset */
set_global_reset(dev, 0);
sysreset_walk_halt(SYSRESET_COLD);
break;
case ME_HFS_ACK_GBL_RESET:
/* Global reset */
set_global_reset(dev, 1);
sysreset_walk_halt(SYSRESET_COLD);
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 },
{ }
};
U_BOOT_DRIVER(syscon_intel_me) = {
.name = "intel_me_syscon",
.id = UCLASS_SYSCON,
.of_match = ivybridge_syscon_ids,
};
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <fdtdec.h>
#include <asm/fsp1/fsp_support.h>
DECLARE_GLOBAL_DATA_PTR;
void fsp_update_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;
}
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <asm/post.h>
#include <asm/processor.h>
int arch_cpu_init(void)
{
post_code(POST_CPU_INIT);
return x86_cpu_init_f();
}
@@ -0,0 +1,529 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From coreboot southbridge/intel/bd82x6x/lpc.c
*
* Copyright (C) 2008-2009 coresystems GmbH
*/
#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>
DECLARE_GLOBAL_DATA_PTR;
#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, dev_of_offset(pch),
"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, dev_of_offset(pch),
"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 = dev_of_offset(pch);
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(fdtdec_get_int(blob, node, "intel,gpe0-enable", 0),
(ulong)pmbase + GPE0_EN);
writew(fdtdec_get_int(blob, node, "intel,alt-gp-smi-enable", 0),
(ulong)pmbase + ALT_GP_SMI_EN);
/* 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, ~0U, 1 << 31);
pch_iobp_update(pch, 0xeb004000, ~0U, 1 << 7);
pch_iobp_update(pch, 0xec007f07, ~0U, 1 << 31);
pch_iobp_update(pch, 0xec004000, ~0U, 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;
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);
/* 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,
};
@@ -0,0 +1,452 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From Coreboot file of same name
*
* Copyright (C) 2007-2009 coresystems GmbH
* Copyright (C) 2011 The Chromium Authors
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <fdtdec.h>
#include <malloc.h>
#include <asm/cpu.h>
#include <asm/cpu_common.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/model_206ax.h>
DECLARE_GLOBAL_DATA_PTR;
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,
};
bool cpu_ivybridge_config_tdp_levels(void)
{
struct cpuid_result result;
/* Minimum CPU revision */
result = cpuid(1);
if (result.eax < IVB_CONFIG_TDP_MIN_CPUID)
return false;
return cpu_config_tdp_levels();
}
/*
* 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_ivybridge_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 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;
uint ratio;
/* Check for configurable TDP option */
if (cpu_ivybridge_config_tdp_levels()) {
/* Set to nominal TDP ratio */
msr = msr_read(MSR_CONFIG_TDP_NOMINAL);
ratio = msr.lo & 0xff;
} else {
/* Platform Info bits 15:8 give max ratio */
msr = msr_read(MSR_PLATFORM_INFO);
ratio = (msr.lo & 0xff00) >> 8;
}
cpu_set_perf_control(ratio);
}
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);
}
static int model_206ax_init(struct udevice *dev)
{
int ret;
/* Clear out pending MCEs */
configure_mca();
/* 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 = cpu_configure_thermal_target(dev);
if (ret) {
debug("Cannot set thermal target\n");
if (ret != -ENOENT)
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)
{
return cpu_intel_get_info(info, INTEL_BCLK_MHZ);
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,
.get_vendor = cpu_x86_get_vendor,
};
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,
.flags = DM_FLAG_PRE_RELOC,
};
@@ -0,0 +1,261 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From Coreboot northbridge/intel/sandybridge/northbridge.c
*
* Copyright (C) 2007-2009 coresystems GmbH
* Copyright (C) 2011 The Chromium Authors
*/
#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>
DECLARE_GLOBAL_DATA_PTR;
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;
}
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_ivybridge_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);
}
/**
* sandybridge_init_iommu() - Set up IOMMU so that azalia can be used
*
* It is not obvious where these values come from. They may be undocumented.
*/
static void sandybridge_init_iommu(struct udevice *dev)
{
u32 capid0_a;
dm_pci_read_config32(dev, 0xe4, &capid0_a);
if (capid0_a & (1 << 23)) {
log_debug("capid0_a not needed\n");
return;
}
/* setup BARs */
writel(IOMMU_BASE1 >> 32, MCHBAR_REG(0x5404));
writel(IOMMU_BASE1 | 1, MCHBAR_REG(0x5400));
writel(IOMMU_BASE2 >> 32, MCHBAR_REG(0x5414));
writel(IOMMU_BASE2 | 1, MCHBAR_REG(0x5410));
/* lock policies */
writel(0x80000000, IOMMU_BASE1 + 0xff0);
/* Enable azalia sound */
writel(0x20000000, IOMMU_BASE2 + 0xff0);
writel(0xa0000000, IOMMU_BASE2 + 0xff0);
}
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);
/* Setup IOMMU BARs */
sandybridge_init_iommu(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,
};
@@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From Coreboot
* Copyright (C) 2008-2009 coresystems GmbH
*/
#include <common.h>
#include <ahci.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>
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(dev);
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")) {
ulong 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: %08lx\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(dev);
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_bind(struct udevice *dev)
{
struct udevice *scsi_dev;
int ret;
if (gd->flags & GD_FLG_RELOC) {
ret = ahci_bind_scsi(dev, &scsi_dev);
if (ret)
return ret;
}
return 0;
}
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);
ret = ahci_probe_scsi_pci(dev);
if (ret)
return ret;
}
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,
.bind = bd82x6x_sata_bind,
.probe = bd82x6x_sata_probe,
};
@@ -0,0 +1,559 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 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.
*/
#include <common.h>
#include <errno.h>
#include <fdtdec.h>
#include <init.h>
#include <malloc.h>
#include <net.h>
#include <rtc.h>
#include <spi.h>
#include <spi_flash.h>
#include <syscon.h>
#include <sysreset.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);
}
int dram_init_banksize(void)
{
mrc_common_dram_init_banksize();
return 0;
}
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) {
debug("%s: Could not locate SPD (ret=%d)\n", __func__, 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;
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;
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;
/* GTT Graphics Stolen Memory Size (GGMS) */
uma_size = ((ggc >> 8) & 0x3) * 1024ULL;
tomk -= uma_size;
uma_memory_base = tomk * 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;
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;
/* We need the pinctrl set up early */
ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev);
if (ret) {
debug("%s: Could not get pinconf (ret=%d)\n", __func__, ret);
return ret;
}
ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &dev);
if (ret) {
debug("%s: Could not get northbridge (ret=%d)\n", __func__,
ret);
return ret;
}
ret = syscon_get_by_driver_data(X86_SYSCON_ME, &me_dev);
if (ret) {
debug("%s: Could not get ME (ret=%d)\n", __func__, ret);
return ret;
}
ret = copy_spd(dev, pei_data);
if (ret) {
debug("%s: Could not get SPD (ret=%d)\n", __func__, 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");
sysreset_walk_halt(SYSRESET_COLD);
}
/* 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) {
debug("%s: Could not init ME (ret=%d)\n", __func__, ret);
return ret;
}
ret = intel_early_me_uma_size(me_dev);
if (ret < 0) {
debug("%s: Could not get UMA size (ret=%d)\n", __func__, ret);
return ret;
}
ret = mrc_common_init(dev, pei_data, false);
if (ret) {
debug("%s: mrc_common_init() failed (ret=%d)\n", __func__, ret);
return ret;
}
ret = sdram_find(dev);
if (ret) {
debug("%s: sdram_find() failed (ret=%d)\n", __func__, 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;
}
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2016 Google, Inc
*/
#include <common.h>
DECLARE_GLOBAL_DATA_PTR;
int dram_init(void)
{
gd->ram_size = 1ULL << 31;
gd->bd->bi_dram[0].start = 0;
gd->bd->bi_dram[0].size = gd->ram_size;
return 0;
}
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: GPL-2.0
/*
* From coreboot file of same name
*
* Copyright (C) 2008-2009 coresystems GmbH
* Copyright (C) 2014 Google, Inc
*/
#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);
}
@@ -0,0 +1,577 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015 Google, Inc
*
* 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) && \
!defined(CONFIG_INTEL_MID)
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 -ENOSPC;
}
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 -EIO;
}
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 = -ETIMEDOUT;
}
}
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 -EINVAL;
}
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 -ENOENT;
/*
* 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, dev_of_offset(cpu), "reg",
-1);
plat->ucode_version = microcode_read_rev();
plat->device_id = gd->arch.x86_device;
return device_probe(cpu);
}
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2014 Google, Inc
*
* 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
*/
/*
* Note that any console output (e.g. debug()) in this file will likely fail
* since the MTRR registers are sometimes in flux.
*/
#include <common.h>
#include <cpu_func.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, bool do_caches)
{
if (!gd->arch.has_mtrr)
return;
if (do_caches) {
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, bool do_caches)
{
if (!gd->arch.has_mtrr)
return;
wrmsrl(MTRR_DEF_TYPE_MSR, state->deftype | MTRR_DEF_TYPE_EN);
if (do_caches && state->enable_cache)
enable_caches();
}
static void set_var_mtrr(uint reg, uint type, uint64_t start, uint64_t size)
{
u64 mask;
wrmsrl(MTRR_PHYS_BASE_MSR(reg), start | type);
mask = ~(size - 1);
mask &= (1ULL << CONFIG_CPU_ADDR_BITS) - 1;
wrmsrl(MTRR_PHYS_MASK_MSR(reg), mask | MTRR_PHYS_MASK_VALID);
}
int mtrr_commit(bool do_caches)
{
struct mtrr_request *req = gd->arch.mtrr_req;
struct mtrr_state state;
int i;
debug("%s: enabled=%d, count=%d\n", __func__, gd->arch.has_mtrr,
gd->arch.mtrr_req_count);
if (!gd->arch.has_mtrr)
return -ENOSYS;
debug("open\n");
mtrr_open(&state, do_caches);
debug("open done\n");
for (i = 0; i < gd->arch.mtrr_req_count; i++, req++)
set_var_mtrr(i, req->type, req->start, req->size);
/* Clear the ones that are unused */
debug("clear\n");
for (; i < MTRR_COUNT; i++)
wrmsrl(MTRR_PHYS_MASK_MSR(i), 0);
debug("close\n");
mtrr_close(&state, do_caches);
debug("mtrr done\n");
return 0;
}
int mtrr_add_request(int type, uint64_t start, uint64_t size)
{
struct mtrr_request *req;
uint64_t mask;
debug("%s: count=%d\n", __func__, gd->arch.mtrr_req_count);
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;
}
static int get_var_mtrr_count(void)
{
return msr_read(MSR_MTRR_CAP_MSR).lo & MSR_MTRR_CAP_VCNT;
}
static int get_free_var_mtrr(void)
{
struct msr_t maskm;
int vcnt;
int i;
vcnt = get_var_mtrr_count();
/* Identify the first var mtrr which is not valid */
for (i = 0; i < vcnt; i++) {
maskm = msr_read(MTRR_PHYS_MASK_MSR(i));
if ((maskm.lo & MTRR_PHYS_MASK_VALID) == 0)
return i;
}
/* No free var mtrr */
return -ENOSPC;
}
int mtrr_set_next_var(uint type, uint64_t start, uint64_t size)
{
int mtrr;
mtrr = get_free_var_mtrr();
if (mtrr < 0)
return mtrr;
set_var_mtrr(mtrr, type, start, size);
debug("MTRR %x: start=%x, size=%x\n", mtrr, (uint)start, (uint)size);
return 0;
}
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* 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>
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <pci.h>
#include <asm/io.h>
#include <asm/pci.h>
int pci_x86_read_config(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(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;
}
int pci_x86_clrset_config(pci_dev_t bdf, uint offset, ulong clr, ulong set,
enum pci_size_t size)
{
ulong value;
int ret;
ret = pci_x86_read_config(bdf, offset, &value, size);
if (ret)
return ret;
value &= ~clr;
value |= set;
return pci_x86_write_config(bdf, offset, value, size);
}
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);
}
}
@@ -0,0 +1,34 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
config QEMU
bool
select ARCH_EARLY_INIT_R
imply AHCI_PCI
imply E1000
imply SCSI
imply SCSI_AHCI
imply SYS_NS16550
imply USB
imply USB_EHCI_HCD
imply VIDEO_VESA
if QEMU
config SYS_CAR_ADDR
hex
default 0x10000
config SYS_CAR_SIZE
hex
default 0x10000
config ACPI_PM1_BASE
hex
default 0xe400
help
ACPI Power Management 1 (PM1) i/o-mapped base address.
This device is defined in ACPI specification, with 16 bytes in size.
endif
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
ifndef CONFIG_$(SPL_)X86_64
obj-y += car.o
endif
obj-y += dram.o
obj-y += qemu.o
obj-$(CONFIG_QFW) += cpu.o e820.o
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#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
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Miao Yan <yanmiaobest@gmail.com>
*/
#include <common.h>
#include <cpu.h>
#include <dm.h>
#include <errno.h>
#include <qfw.h>
#include <asm/cpu.h>
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,
};
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <init.h>
#include <asm/post.h>
#include <asm/arch/qemu.h>
DECLARE_GLOBAL_DATA_PTR;
u32 qemu_get_low_memory_size(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;
return ram * 1024;
}
u64 qemu_get_high_memory_size(void)
{
u64 ram;
outb(HIGH_HIGHRAM_ADDR, CMOS_ADDR_PORT);
ram = ((u64)inb(CMOS_DATA_PORT)) << 22;
outb(MID_HIGHRAM_ADDR, CMOS_ADDR_PORT);
ram |= ((u64)inb(CMOS_DATA_PORT)) << 14;
outb(LOW_HIGHRAM_ADDR, CMOS_ADDR_PORT);
ram |= ((u64)inb(CMOS_DATA_PORT)) << 6;
return ram * 1024;
}
int dram_init(void)
{
gd->ram_size = qemu_get_low_memory_size();
gd->ram_size += qemu_get_high_memory_size();
post_code(POST_DRAM);
return 0;
}
int dram_init_banksize(void)
{
u64 high_mem_size;
gd->bd->bi_dram[0].start = 0;
gd->bd->bi_dram[0].size = qemu_get_low_memory_size();
high_mem_size = qemu_get_high_memory_size();
if (high_mem_size) {
gd->bd->bi_dram[1].start = SZ_4G;
gd->bd->bi_dram[1].size = high_mem_size;
}
return 0;
}
/*
* 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 qemu_get_low_memory_size();
}
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* QEMU x86 specific E820 table generation
*
* (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com>
* (C) Copyright 2019 Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <env_internal.h>
#include <asm/e820.h>
#include <asm/arch/qemu.h>
DECLARE_GLOBAL_DATA_PTR;
unsigned int install_e820_map(unsigned int max_entries,
struct e820_entry *entries)
{
u64 high_mem_size;
int n = 0;
entries[n].addr = 0;
entries[n].size = ISA_START_ADDRESS;
entries[n].type = E820_RAM;
n++;
entries[n].addr = ISA_START_ADDRESS;
entries[n].size = ISA_END_ADDRESS - ISA_START_ADDRESS;
entries[n].type = E820_RESERVED;
n++;
/*
* 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[n].addr = ISA_END_ADDRESS;
entries[n].size = gd->relocaddr - TOTAL_MALLOC_LEN - ISA_END_ADDRESS;
entries[n].type = E820_RAM;
n++;
/* for simplicity, reserve entire malloc space */
entries[n].addr = gd->relocaddr - TOTAL_MALLOC_LEN;
entries[n].size = TOTAL_MALLOC_LEN;
entries[n].type = E820_RESERVED;
n++;
entries[n].addr = gd->relocaddr;
entries[n].size = qemu_get_low_memory_size() - gd->relocaddr;
entries[n].type = E820_RESERVED;
n++;
entries[n].addr = CONFIG_PCIE_ECAM_BASE;
entries[n].size = CONFIG_PCIE_ECAM_SIZE;
entries[n].type = E820_RESERVED;
n++;
high_mem_size = qemu_get_high_memory_size();
if (high_mem_size) {
entries[n].addr = SZ_4G;
entries[n].size = high_mem_size;
entries[n].type = E820_RAM;
n++;
}
return n;
}
@@ -0,0 +1,190 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <cpu_func.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((uintptr_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
}
#if !CONFIG_IS_ENABLED(SPL_X86_32BIT_INIT)
int arch_cpu_init(void)
{
post_code(POST_CPU_INIT);
return x86_cpu_init_f();
}
int checkcpu(void)
{
return 0;
}
int print_cpuinfo(void)
{
post_code(POST_CPU_INFO);
return default_print_cpuinfo();
}
#endif
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
@@ -0,0 +1,137 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
config INTEL_QUARK
bool
select HAVE_RMU
select ARCH_EARLY_INIT_R
select ARCH_MISC_INIT
imply ENABLE_MRC_CACHE
imply ETH_DESIGNWARE
imply ICH_SPI
imply INTEL_ICH6_GPIO
imply MMC
imply MMC_PCI
imply MMC_SDHCI
imply MMC_SDHCI_SDMA
imply SPI_FLASH
imply SYS_NS16550
imply USB
imply USB_EHCI_HCD
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 Management 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.
config X86_TSC_TIMER_EARLY_FREQ
int
default 400
endif
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0+
#
# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
obj-y += car.o dram.o msg_port.o quark.o
obj-y += mrc.o mrc_util.o hte.o smc.o
obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <asm/acpi_table.h>
#include <asm/tables.h>
#include <asm/arch/global_nvs.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 | FULL_RST;
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);
}
void acpi_create_gnvs(struct acpi_global_nvs *gnvs)
{
/* quark is a uni-processor */
gnvs->pcnt = 1;
}
@@ -0,0 +1,104 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#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
@@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <cpu_func.h>
#include <errno.h>
#include <fdtdec.h>
#include <init.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;
}
int dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = 0;
gd->bd->bi_dram[0].size = gd->ram_size;
return 0;
}
/*
* 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;
}
@@ -0,0 +1,395 @@
// SPDX-License-Identifier: Intel
/*
* 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
*/
#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();
}
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: Intel */
/*
* 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
*/
#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_ */
@@ -0,0 +1,204 @@
// SPDX-License-Identifier: Intel
/*
* 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
*/
/*
* 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
@@ -0,0 +1,118 @@
/* SPDX-License-Identifier: Intel */
/*
* 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
*/
#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_ */
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#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);
}
@@ -0,0 +1,381 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
*/
#include <common.h>
#include <cpu_func.h>
#include <mmc.h>
#include <asm/io.h>
#include <asm/ioapic.h>
#include <asm/irq.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 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 checkcpu(void)
{
return 0;
}
int print_cpuinfo(void)
{
post_code(POST_CPU_INFO);
return default_print_cpuinfo();
}
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);
}
static void quark_irq_init(void)
{
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);
}
int arch_early_init_r(void)
{
quark_pcie_init();
quark_usb_init();
quark_irq_init();
return 0;
}
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

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