diff options
Diffstat (limited to 'roms/u-boot/arch/x86/cpu')
163 files changed, 29241 insertions, 0 deletions
diff --git a/roms/u-boot/arch/x86/cpu/Makefile b/roms/u-boot/arch/x86/cpu/Makefile new file mode 100644 index 000000000..16e67e3da --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/Makefile @@ -0,0 +1,77 @@ +# 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_APOLLOLAKE) += apollolake/ +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-$(CONFIG_$(SPL_TPL_)ACPI_GPE) += acpi_gpe.o +obj-$(CONFIG_QFW) += qfw_cpu.o +ifndef CONFIG_SYS_COREBOOT +obj-$(CONFIG_$(SPL_TPL_)X86_32BIT_INIT) += irq.o +endif +ifndef CONFIG_$(SPL_)X86_64 +obj-$(CONFIG_$(SPL_)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 diff --git a/roms/u-boot/arch/x86/cpu/acpi_gpe.c b/roms/u-boot/arch/x86/cpu/acpi_gpe.c new file mode 100644 index 000000000..da01e7133 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/acpi_gpe.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google, LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY UCLASS_IRQ + +#include <common.h> +#include <dm.h> +#include <irq.h> +#include <log.h> +#include <acpi/acpi_device.h> +#include <asm/io.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/interrupt-controller/x86-irq.h> + +/** + * struct acpi_gpe_priv - private driver information + * + * @acpi_base: Base I/O address of ACPI registers + */ +struct acpi_gpe_priv { + ulong acpi_base; +}; + +#define GPE0_STS(x) (0x20 + ((x) * 4)) + +static int acpi_gpe_read_and_clear(struct irq *irq) +{ + struct acpi_gpe_priv *priv = dev_get_priv(irq->dev); + u32 mask, sts; + ulong start; + int ret = 0; + int bank; + + bank = irq->id / 32; + mask = 1 << (irq->id % 32); + + /* Wait up to 1ms for GPE status to clear */ + start = get_timer(0); + do { + if (get_timer(start) > 1) + return ret; + + sts = inl(priv->acpi_base + GPE0_STS(bank)); + if (sts & mask) { + outl(mask, priv->acpi_base + GPE0_STS(bank)); + ret = 1; + } + } while (sts & mask); + + return ret; +} + +static int acpi_gpe_of_to_plat(struct udevice *dev) +{ + struct acpi_gpe_priv *priv = dev_get_priv(dev); + + priv->acpi_base = dev_read_addr(dev); + if (!priv->acpi_base || priv->acpi_base == FDT_ADDR_T_NONE) + return log_msg_ret("acpi_base", -EINVAL); + + return 0; +} + +static int acpi_gpe_of_xlate(struct irq *irq, struct ofnode_phandle_args *args) +{ + irq->id = args->args[0]; + irq->flags = args->args[1]; + + return 0; +} + +#if CONFIG_IS_ENABLED(ACPIGEN) +static int acpi_gpe_get_acpi(const struct irq *irq, struct acpi_irq *acpi_irq) +{ + memset(acpi_irq, '\0', sizeof(*acpi_irq)); + acpi_irq->pin = irq->id; + acpi_irq->mode = irq->flags & IRQ_TYPE_EDGE_BOTH ? + ACPI_IRQ_EDGE_TRIGGERED : ACPI_IRQ_LEVEL_TRIGGERED; + acpi_irq->polarity = irq->flags & + (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW) ? + ACPI_IRQ_ACTIVE_LOW : ACPI_IRQ_ACTIVE_HIGH; + acpi_irq->shared = irq->flags & X86_IRQ_TYPE_SHARED ? + ACPI_IRQ_SHARED : ACPI_IRQ_EXCLUSIVE; + acpi_irq->wake = irq->flags & X86_IRQ_TYPE_WAKE ? ACPI_IRQ_WAKE : + ACPI_IRQ_NO_WAKE; + + return 0; +} +#endif + +static const struct irq_ops acpi_gpe_ops = { + .read_and_clear = acpi_gpe_read_and_clear, + .of_xlate = acpi_gpe_of_xlate, +#if CONFIG_IS_ENABLED(ACPIGEN) + .get_acpi = acpi_gpe_get_acpi, +#endif +}; + +static const struct udevice_id acpi_gpe_ids[] = { + { .compatible = "intel,acpi-gpe", .data = X86_IRQT_ACPI_GPE }, + { } +}; + +U_BOOT_DRIVER(intel_acpi_gpe) = { + .name = "intel_acpi_gpe", + .id = UCLASS_IRQ, + .of_match = acpi_gpe_ids, + .ops = &acpi_gpe_ops, + .of_to_plat = acpi_gpe_of_to_plat, + .priv_auto = sizeof(struct acpi_gpe_priv), +}; diff --git a/roms/u-boot/arch/x86/cpu/apollolake/Kconfig b/roms/u-boot/arch/x86/cpu/apollolake/Kconfig new file mode 100644 index 000000000..b3ce05317 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/Kconfig @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright 2019 Google LLC +# + +config INTEL_APOLLOLAKE + bool + select FSP_VERSION2 + select HAVE_FSP + select ARCH_MISC_INIT + select USE_CAR + select INTEL_SOC + select INTEL_PMC + select TPL_X86_TSC_TIMER_NATIVE + select SPL_PCH_SUPPORT + select TPL_PCH_SUPPORT + select PCIEX_LENGTH_256MB + select PCH_SUPPORT + select P2SB + select SMP_AP_WORK + select INTEL_GMA_SWSMISCI + select ACPI_GNVS_EXTERNAL + select TPL_OF_PLATDATA_PARENT + select TPL_OF_PLATDATA_INST + select TPL_READ_ONLY + imply ENABLE_MRC_CACHE + imply AHCI_PCI + imply SCSI + imply SCSI_AHCI + imply SPI_FLASH + imply USB + imply USB_EHCI_HCD + imply TPL + imply SPL + imply TPL_X86_16BIT_INIT + imply TPL_OF_PLATDATA + imply ACPI_PMC + imply MMC + imply DM_MMC + imply MMC_PCI + imply MMC_SDHCI + imply CMD_MMC + imply VIDEO_FSP + imply PINCTRL_INTEL + imply PINCTRL_INTEL_APL + imply HAVE_VBT + imply HAVE_X86_FIT + imply INTEL_GPIO + imply SMP + imply HAVE_ITSS + imply HAVE_P2SB + imply CLK + imply CMD_CLK + imply CLK_INTEL + imply ACPI_GPE + imply INTEL_GMA_ACPI + +if INTEL_APOLLOLAKE + +config DCACHE_RAM_BASE + default 0xfef00000 + +config DCACHE_RAM_SIZE + default 0xc0000 + +config DCACHE_RAM_MRC_VAR_SIZE + default 0xb0000 + +config CPU_SPECIFIC_OPTIONS + def_bool y + select SMM_TSEG + select X86_RAMTEST + +config SMM_TSEG_SIZE + hex + default 0x800000 + +config MMCONF_BASE_ADDRESS + hex + default 0xe0000000 + +config TPL_SIZE_LIMIT + default 0x7800 + +config CPU_ADDR_BITS + default 39 + +config APL_SPI_FLASH_BOOT + bool "Support booting with SPI-flash driver instead memory-mapped SPI" + select TPL_SPI_FLASH_SUPPORT + select TPL_SPI_SUPPORT + select TPL_DM_SPI + select TPL_DM_SPI_FLASH + help + This enables SPI and SPI flash in TPL. Without the this only + available boot method is to use memory-mapped SPI. Since this is + actually fast and produces a TPL which is 7KB smaller, memory-mapped + SPI is the default. + +config APL_BOOT_FROM_FAST_SPI_FLASH + bool "Boot using SPI flash driver" + select APL_SPI_FLASH_BOOT + help + This option is separate from APL_SPI_FLASH_BOOT since it is useful to + be able to compare booting speed with the same build. Enable this to + use the SPI-flash driver to load SPL, U-Boot and FSP-M. For technical + reasons FSP-S is currently always loaded from memory-mapped SPI. See + Apollo Lake's arch_fsp_init_r() for details about that. + +config VBT_ADDR + default 0xff3f1000 + +endif diff --git a/roms/u-boot/arch/x86/cpu/apollolake/Makefile b/roms/u-boot/arch/x86/cpu/apollolake/Makefile new file mode 100644 index 000000000..2ddf4af62 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/Makefile @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2019 Google LLC + +obj-$(CONFIG_SPL_BUILD) += cpu_spl.o +obj-$(CONFIG_SPL_BUILD) += spl.o +obj-$(CONFIG_SPL_BUILD) += systemagent.o +obj-y += cpu_common.o + +ifndef CONFIG_TPL_BUILD +obj-y += cpu.o +obj-y += punit.o +obj-y += fsp_bindings.o +ifdef CONFIG_SPL_BUILD +obj-y += fsp_m.o +endif +endif +ifndef CONFIG_SPL_BUILD +obj-y += acpi.o +obj-y += fsp_s.o +endif + +obj-y += hostbridge.o +obj-y += lpc.o +obj-y += pch.o +obj-y += pmc.o +obj-y += uart.o diff --git a/roms/u-boot/arch/x86/cpu/apollolake/acpi.c b/roms/u-boot/arch/x86/cpu/apollolake/acpi.c new file mode 100644 index 000000000..fd21c0b49 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/acpi.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Intel Corp. + * Copyright (C) 2017-2019 Siemens AG + * (Written by Lance Zhao <lijian.zhao@intel.com> for Intel Corp.) + * Copyright 2019 Google LLC + * + * Modified from coreboot apollolake/acpi.c + */ + +#define LOG_CATEGORY LOGC_ACPI + +#include <common.h> +#include <cpu.h> +#include <dm.h> +#include <log.h> +#include <p2sb.h> +#include <pci.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_s3.h> +#include <asm/acpi_table.h> +#include <asm/cpu_common.h> +#include <asm/intel_acpi.h> +#include <asm/intel_gnvs.h> +#include <asm/intel_pinctrl.h> +#include <asm/intel_pinctrl_defs.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/mpspec.h> +#include <asm/tables.h> +#include <asm/arch/iomap.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pm.h> +#include <asm/arch/systemagent.h> +#include <dm/acpi.h> +#include <dm/uclass-internal.h> +#include <power/acpi_pmc.h> + +int arch_read_sci_irq_select(void) +{ + struct acpi_pmc_upriv *upriv; + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_ACPI_PMC, &dev); + if (ret) + return log_msg_ret("pmc", ret); + upriv = dev_get_uclass_priv(dev); + + return readl(upriv->pmc_bar0 + IRQ_REG); +} + +int arch_write_sci_irq_select(uint scis) +{ + struct acpi_pmc_upriv *upriv; + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_ACPI_PMC, &dev); + if (ret) + return log_msg_ret("pmc", ret); + upriv = dev_get_uclass_priv(dev); + writel(scis, upriv->pmc_bar0 + IRQ_REG); + + return 0; +} + +/** + * chromeos_init_acpi() - Initialise basic data to boot Chrome OS + * + * This tells Chrome OS to boot in developer mode + * + * @cros: Structure to initialise + */ +static void chromeos_init_acpi(struct chromeos_acpi_gnvs *cros) +{ + cros->active_main_fw = 1; + cros->active_main_fw = 1; /* A */ + cros->switches = CHSW_DEVELOPER_SWITCH; + cros->main_fw_type = 2; /* Developer */ +} + +int acpi_create_gnvs(struct acpi_global_nvs *gnvs) +{ + struct udevice *cpu; + int ret; + + /* Clear out GNV */ + memset(gnvs, '\0', sizeof(*gnvs)); + + /* TODO(sjg@chromium.org): Add the console log to gnvs->cbmc */ + + if (IS_ENABLED(CONFIG_CHROMEOS)) + chromeos_init_acpi(&gnvs->chromeos); + + /* Set unknown wake source */ + gnvs->pm1i = ~0ULL; + + /* CPU core count */ + gnvs->pcnt = 1; + ret = uclass_find_first_device(UCLASS_CPU, &cpu); + if (cpu) { + ret = cpu_get_count(cpu); + if (ret > 0) + gnvs->pcnt = ret; + } + + gnvs->dpte = 1; + + return 0; +} + +uint32_t acpi_fill_soc_wake(uint32_t generic_pm1_en) +{ + /* + * WAK_STS bit is set when the system is in one of the sleep states + * (via the SLP_EN bit) and an enabled wake event occurs. Upon setting + * this bit, the PMC will transition the system to the ON state and + * can only be set by hardware and can only be cleared by writing a one + * to this bit position. + */ + generic_pm1_en |= WAK_STS | RTC_EN | PWRBTN_EN; + + return generic_pm1_en; +} + +int arch_madt_sci_irq_polarity(int sci) +{ + return MP_IRQ_POLARITY_LOW; +} + +void fill_fadt(struct acpi_fadt *fadt) +{ + fadt->pm_tmr_blk = IOMAP_ACPI_BASE + PM1_TMR; + + fadt->p_lvl2_lat = ACPI_FADT_C2_NOT_SUPPORTED; + fadt->p_lvl3_lat = ACPI_FADT_C3_NOT_SUPPORTED; + + fadt->pm_tmr_len = 4; + fadt->duty_width = 3; + + fadt->iapc_boot_arch = ACPI_FADT_LEGACY_DEVICES | ACPI_FADT_8042; + + fadt->x_pm_tmr_blk.space_id = 1; + fadt->x_pm_tmr_blk.bit_width = fadt->pm_tmr_len * 8; + fadt->x_pm_tmr_blk.addrl = IOMAP_ACPI_BASE + PM1_TMR; +} + +void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, + void *dsdt) +{ + struct acpi_table_header *header = &fadt->header; + + acpi_fadt_common(fadt, facs, dsdt); + intel_acpi_fill_fadt(fadt); + fill_fadt(fadt); + header->checksum = table_compute_checksum(fadt, header->length); +} + +int apl_acpi_fill_dmar(struct acpi_ctx *ctx) +{ + struct udevice *dev, *sa_dev; + u64 gfxvtbar = readq(MCHBAR_REG(GFXVTBAR)) & VTBAR_MASK; + u64 defvtbar = readq(MCHBAR_REG(DEFVTBAR)) & VTBAR_MASK; + bool gfxvten = readl(MCHBAR_REG(GFXVTBAR)) & VTBAR_ENABLED; + bool defvten = readl(MCHBAR_REG(DEFVTBAR)) & VTBAR_ENABLED; + void *tmp; + int ret; + + uclass_find_first_device(UCLASS_VIDEO, &dev); + ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &sa_dev); + if (ret) + return log_msg_ret("no sa", ret); + + /* IGD has to be enabled, GFXVTBAR set and enabled */ + if (dev && device_active(dev) && gfxvtbar && gfxvten) { + tmp = ctx->current; + + acpi_create_dmar_drhd(ctx, 0, 0, gfxvtbar); + ret = acpi_create_dmar_ds_pci(ctx, PCI_BDF(0, 2, 0)); + if (ret) + return log_msg_ret("ds_pci", ret); + acpi_dmar_drhd_fixup(ctx, tmp); + + /* Add RMRR entry */ + tmp = ctx->current; + acpi_create_dmar_rmrr(ctx->current, 0, sa_get_gsm_base(sa_dev), + sa_get_tolud_base(sa_dev) - 1); + acpi_create_dmar_ds_pci(ctx->current, PCI_BDF(0, 2, 0)); + acpi_dmar_rmrr_fixup(ctx, tmp); + } + + /* DEFVTBAR has to be set and enabled */ + if (defvtbar && defvten) { + struct udevice *p2sb_dev; + u16 ibdf, hbdf; + uint ioapic, hpet; + int ret; + + tmp = ctx->current; + /* + * P2SB may already be hidden. There's no clear rule, when. + * It is needed to get bus, device and function for IOAPIC and + * HPET device which is stored in P2SB device. So unhide it to + * get the info and hide it again when done. + * + * TODO(sjg@chromium.org): p2sb_unhide() ? + */ + ret = uclass_first_device_err(UCLASS_P2SB, &p2sb_dev); + if (ret) + return log_msg_ret("p2sb", ret); + + dm_pci_read_config16(p2sb_dev, PCH_P2SB_IBDF, &ibdf); + ioapic = PCI_TO_BDF(ibdf); + dm_pci_read_config16(p2sb_dev, PCH_P2SB_HBDF, &hbdf); + hpet = PCI_TO_BDF(hbdf); + /* TODO(sjg@chromium.org): p2sb_hide() ? */ + + acpi_create_dmar_drhd(ctx, DRHD_INCLUDE_PCI_ALL, 0, defvtbar); + acpi_create_dmar_ds_ioapic(ctx, 2, ioapic); + acpi_create_dmar_ds_msi_hpet(ctx, 0, hpet); + acpi_dmar_drhd_fixup(tmp, ctx->current); + } + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/apollolake/cpu.c b/roms/u-boot/arch/x86/cpu/apollolake/cpu.c new file mode 100644 index 000000000..647c9df6a --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/cpu.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <cpu.h> +#include <dm.h> +#include <log.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_table.h> +#include <asm/cpu_common.h> +#include <asm/cpu_x86.h> +#include <asm/global_data.h> +#include <asm/intel_acpi.h> +#include <asm/msr.h> +#include <asm/mtrr.h> +#include <asm/arch/cpu.h> +#include <asm/arch/iomap.h> +#include <dm/acpi.h> + +#ifdef CONFIG_ACPIGEN +#define CSTATE_RES(address_space, width, offset, address) \ + { \ + .space_id = address_space, \ + .bit_width = width, \ + .bit_offset = offset, \ + .addrl = address, \ + } + +static struct acpi_cstate cstate_map[] = { + { + /* C1 */ + .ctype = 1, /* ACPI C1 */ + .latency = 1, + .power = 1000, + .resource = { + .space_id = ACPI_ADDRESS_SPACE_FIXED, + }, + }, { + .ctype = 2, /* ACPI C2 */ + .latency = 50, + .power = 10, + .resource = { + .space_id = ACPI_ADDRESS_SPACE_IO, + .bit_width = 8, + .addrl = 0x415, + }, + }, { + .ctype = 3, /* ACPI C3 */ + .latency = 150, + .power = 10, + .resource = { + .space_id = ACPI_ADDRESS_SPACE_IO, + .bit_width = 8, + .addrl = 0x419, + }, + }, +}; + +static int acpi_cpu_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + uint core_id = dev_seq(dev); + int cores_per_package; + int ret; + + cores_per_package = cpu_get_cores_per_package(); + ret = acpi_generate_cpu_header(ctx, core_id, cstate_map, + ARRAY_SIZE(cstate_map)); + + /* Generate P-state tables */ + generate_p_state_entries(ctx, core_id, cores_per_package); + + /* Generate T-state tables */ + generate_t_state_entries(ctx, core_id, cores_per_package, NULL, 0); + + acpigen_pop_len(ctx); + + if (device_is_last_sibling(dev)) { + ret = acpi_generate_cpu_package_final(ctx, cores_per_package); + + if (ret) + return ret; + } + + return 0; +} +#endif /* CONFIG_ACPIGEN */ + +static int apl_get_info(const struct udevice *dev, struct cpu_info *info) +{ + return cpu_intel_get_info(info, INTEL_BCLK_MHZ); +} + +static void update_fixed_mtrrs(void) +{ + native_write_msr(MTRR_FIX_64K_00000_MSR, + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK), + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK)); + native_write_msr(MTRR_FIX_16K_80000_MSR, + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK), + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK)); + native_write_msr(MTRR_FIX_4K_E0000_MSR, + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK), + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK)); + native_write_msr(MTRR_FIX_4K_E8000_MSR, + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK), + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK)); + native_write_msr(MTRR_FIX_4K_F0000_MSR, + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK), + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK)); + native_write_msr(MTRR_FIX_4K_F8000_MSR, + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK), + MTRR_FIX_TYPE(MTRR_TYPE_WRBACK)); +} + +static void setup_core_msrs(void) +{ + wrmsrl(MSR_PMG_CST_CONFIG_CONTROL, + PKG_C_STATE_LIMIT_C2_MASK | CORE_C_STATE_LIMIT_C10_MASK | + IO_MWAIT_REDIRECT_MASK | CST_CFG_LOCK_MASK); + /* Power Management I/O base address for I/O trapping to C-states */ + wrmsrl(MSR_PMG_IO_CAPTURE_ADR, ACPI_PMIO_CST_REG | + (PMG_IO_BASE_CST_RNG_BLK_SIZE << 16)); + /* Disable C1E */ + msr_clrsetbits_64(MSR_POWER_CTL, 0x2, 0); + /* Disable support for MONITOR and MWAIT instructions */ + msr_clrsetbits_64(MSR_IA32_MISC_ENABLE, MISC_ENABLE_MWAIT, 0); + /* + * Enable and Lock the Advanced Encryption Standard (AES-NI) + * feature register + */ + msr_clrsetbits_64(MSR_FEATURE_CONFIG, FEATURE_CONFIG_RESERVED_MASK, + FEATURE_CONFIG_LOCK); + + update_fixed_mtrrs(); +} + +static int soc_core_init(void) +{ + struct udevice *pmc; + int ret; + + /* Clear out pending MCEs */ + cpu_mca_configure(); + + /* Set core MSRs */ + setup_core_msrs(); + /* + * Enable ACPI PM timer emulation, which also lets microcode know + * location of ACPI_BASE_ADDRESS. This also enables other features + * implemented in microcode. + */ + ret = uclass_first_device_err(UCLASS_ACPI_PMC, &pmc); + if (ret) + return log_msg_ret("PMC", ret); + enable_pm_timer_emulation(pmc); + + return 0; +} + +static int cpu_apl_probe(struct udevice *dev) +{ + if (gd->flags & GD_FLG_RELOC) { + int ret; + + ret = soc_core_init(); + if (ret) + return log_ret(ret); + } + + return 0; +} + +#ifdef CONFIG_ACPIGEN +struct acpi_ops apl_cpu_acpi_ops = { + .fill_ssdt = acpi_cpu_fill_ssdt, +}; +#endif + +static const struct cpu_ops cpu_x86_apl_ops = { + .get_desc = cpu_x86_get_desc, + .get_info = apl_get_info, + .get_count = cpu_x86_get_count, + .get_vendor = cpu_x86_get_vendor, +}; + +static const struct udevice_id cpu_x86_apl_ids[] = { + { .compatible = "intel,apl-cpu" }, + { } +}; + +U_BOOT_DRIVER(intel_apl_cpu) = { + .name = "intel_apl_cpu", + .id = UCLASS_CPU, + .of_match = cpu_x86_apl_ids, + .bind = cpu_x86_bind, + .probe = cpu_apl_probe, + .ops = &cpu_x86_apl_ops, + ACPI_OPS_PTR(&apl_cpu_acpi_ops) + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/roms/u-boot/arch/x86/cpu/apollolake/cpu_common.c b/roms/u-boot/arch/x86/cpu/apollolake/cpu_common.c new file mode 100644 index 000000000..5d7d26b14 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/cpu_common.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <asm/cpu_common.h> +#include <asm/io.h> +#include <asm/msr.h> +#include <asm/pci.h> +#include <asm/arch/cpu.h> +#include <asm/arch/iomap.h> +#include <asm/arch/uart.h> +#include <power/acpi_pmc.h> + +/* Define this here to avoid referencing any drivers for the debug UART 1 */ +#define PCH_DEV_P2SB PCI_BDF(0, 0x0d, 0) + +void cpu_flush_l1d_to_l2(void) +{ + struct msr_t msr; + + msr = msr_read(MSR_POWER_MISC); + msr.lo |= FLUSH_DL1_L2; + msr_write(MSR_POWER_MISC, msr); +} + +void enable_pm_timer_emulation(const struct udevice *pmc) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(pmc); + msr_t msr; + + /* + * The derived frequency is calculated as follows: + * (CTC_FREQ * msr[63:32]) >> 32 = target frequency. + * + * Back-solve the multiplier so the 3.579545MHz ACPI timer frequency is + * used. + */ + msr.hi = (3579545ULL << 32) / CTC_FREQ; + + /* Set PM1 timer IO port and enable */ + msr.lo = EMULATE_PM_TMR_EN | (upriv->acpi_base + R_ACPI_PM1_TMR); + debug("PM timer %x %x\n", msr.hi, msr.lo); + msr_write(MSR_EMULATE_PM_TIMER, msr); +} + +static void pch_uart_init(void) +{ + /* + * Set up the pinmux so that the UART rx/tx signals are connected + * outside the SoC. + * + * There are about 500 lines of code required to program the GPIO + * configuration for the UARTs. But it boils down to four writes, and + * for the debug UART we want the minimum possible amount of code before + * the UART is running. So just add the magic writes here. See + * apl_hostbridge_early_init_pinctrl() for the full horror. + */ + if (PCI_FUNC(PCH_DEV_UART) == 1) { + writel(0x40000402, 0xd0c50650); + writel(0x3c47, 0xd0c50654); + writel(0x40000400, 0xd0c50658); + writel(0x3c48, 0xd0c5065c); + } else { /* UART2 */ + writel(0x40000402, 0xd0c50670); + writel(0x3c4b, 0xd0c50674); + writel(0x40000400, 0xd0c50678); + writel(0x3c4c, 0xd0c5067c); + } + +#ifdef CONFIG_DEBUG_UART + apl_uart_init(PCH_DEV_UART, CONFIG_DEBUG_UART_BASE); +#endif +} + +static void p2sb_enable_bar(ulong bar) +{ + /* Enable PCR Base address in PCH */ + pci_x86_write_config(PCH_DEV_P2SB, PCI_BASE_ADDRESS_0, bar, + PCI_SIZE_32); + pci_x86_write_config(PCH_DEV_P2SB, PCI_BASE_ADDRESS_1, 0, PCI_SIZE_32); + + /* Enable P2SB MSE */ + pci_x86_write_config(PCH_DEV_P2SB, PCI_COMMAND, + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY, + PCI_SIZE_8); +} + +/* + * board_debug_uart_init() - Init the debug UART ready for use + * + * This is the minimum init needed to get the UART running. It avoids any + * drivers or complex code, so that the UART is running as soon as possible. + */ +void board_debug_uart_init(void) +{ + p2sb_enable_bar(IOMAP_P2SB_BAR); + pch_uart_init(); +} diff --git a/roms/u-boot/arch/x86/cpu/apollolake/cpu_spl.c b/roms/u-boot/arch/x86/cpu/apollolake/cpu_spl.c new file mode 100644 index 000000000..8f48457ee --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/cpu_spl.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + * + * Portions taken from coreboot + */ + +#include <common.h> +#include <dm.h> +#include <ec_commands.h> +#include <init.h> +#include <log.h> +#include <spi_flash.h> +#include <spl.h> +#include <syscon.h> +#include <acpi/acpi_s3.h> +#include <asm/cpu.h> +#include <asm/cpu_common.h> +#include <asm/cpu_x86.h> +#include <asm/fast_spi.h> +#include <asm/global_data.h> +#include <asm/intel_pinctrl.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/msr.h> +#include <asm/mtrr.h> +#include <asm/pci.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> +#include <asm/arch/iomap.h> +#include <asm/arch/lpc.h> +#include <asm/arch/pch.h> +#include <asm/arch/systemagent.h> +#include <asm/fsp2/fsp_api.h> +#include <linux/sizes.h> +#include <power/acpi_pmc.h> + +static int fast_spi_cache_bios_region(void) +{ + uint map_size, offset; + ulong map_base, base; + int ret; + + ret = fast_spi_early_init(PCH_DEV_SPI, IOMAP_SPI_BASE); + if (ret) + return log_msg_ret("early_init", ret); + + ret = fast_spi_get_bios_mmap(PCH_DEV_SPI, &map_base, &map_size, + &offset); + if (ret) + return log_msg_ret("get_mmap", ret); + + base = SZ_4G - map_size; + mtrr_set_next_var(MTRR_TYPE_WRPROT, base, map_size); + log_debug("BIOS cache base=%lx, size=%x\n", base, (uint)map_size); + + return 0; +} + +static void google_chromeec_ioport_range(uint *out_basep, uint *out_sizep) +{ + uint base; + uint size; + + if (IS_ENABLED(CONFIG_EC_GOOGLE_CHROMEEC_MEC)) { + base = MEC_EMI_BASE; + size = MEC_EMI_SIZE; + } else { + base = EC_HOST_CMD_REGION0; + size = 2 * EC_HOST_CMD_REGION_SIZE; + /* Make sure MEMMAP region follows host cmd region */ + assert(base + size == EC_LPC_ADDR_MEMMAP); + size += EC_MEMMAP_SIZE; + } + + *out_basep = base; + *out_sizep = size; +} + +static void early_ec_init(void) +{ + uint base, size; + + /* + * Set up LPC decoding for the Chrome OS EC I/O port ranges: + * - Ports 62/66, 60/64, and 200->208 + * - Chrome OS EC communication I/O ports + */ + lpc_enable_fixed_io_ranges(LPC_IOE_EC_62_66 | LPC_IOE_KBC_60_64 | + LPC_IOE_LGE_200); + google_chromeec_ioport_range(&base, &size); + lpc_open_pmio_window(base, size); +} + +static int arch_cpu_init_tpl(void) +{ + struct udevice *pmc, *sa, *p2sb, *serial, *spi, *lpc; + int ret; + + ret = uclass_first_device_err(UCLASS_ACPI_PMC, &pmc); + if (ret) + return log_msg_ret("PMC", ret); + + /* Clear global reset promotion bit */ + ret = pmc_global_reset_set_enable(pmc, false); + if (ret) + return log_msg_ret("disable global reset", ret); + + enable_pm_timer_emulation(pmc); + + ret = uclass_first_device_err(UCLASS_P2SB, &p2sb); + if (ret) + return log_msg_ret("p2sb", ret); + ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &sa); + if (ret) + return log_msg_ret("northbridge", ret); + gd->baudrate = CONFIG_BAUDRATE; + ret = uclass_first_device_err(UCLASS_SERIAL, &serial); + if (ret) + return log_msg_ret("serial", ret); + if (CONFIG_IS_ENABLED(SPI_FLASH_SUPPORT)) { + ret = uclass_first_device_err(UCLASS_SPI, &spi); + if (ret) + return log_msg_ret("SPI", ret); + } else { + /* Alternative code if we don't have SPI in TPL */ + if (IS_ENABLED(CONFIG_APL_BOOT_FROM_FAST_SPI_FLASH)) + printf("Warning: Enable APL_SPI_FLASHBOOT to use SPI-flash driver in TPL"); + ret = fast_spi_cache_bios_region(); + if (ret) + return log_msg_ret("BIOS cache", ret); + } + ret = pmc_disable_tco(pmc); + if (ret) + return log_msg_ret("disable TCO", ret); + ret = pmc_gpe_init(pmc); + if (ret) + return log_msg_ret("pmc_gpe", ret); + ret = uclass_first_device_err(UCLASS_LPC, &lpc); + if (ret) + return log_msg_ret("lpc", ret); + + early_ec_init(); + + return 0; +} + +/* + * Enables several BARs and devices which are needed for memory init + * - MCH_BASE_ADDR is needed in order to talk to the memory controller + * - HPET is enabled because FSP wants to store a pointer to global data in the + * HPET comparator register + */ +static int arch_cpu_init_spl(void) +{ + struct udevice *pmc, *p2sb; + int ret; + + ret = uclass_first_device_err(UCLASS_ACPI_PMC, &pmc); + if (ret) + return log_msg_ret("Could not probe PMC", ret); + ret = uclass_first_device_err(UCLASS_P2SB, &p2sb); + if (ret) + return log_msg_ret("Cannot set up p2sb", ret); + + lpc_io_setup_comm_a_b(); + + /* TODO(sjg@chromium.org): Enable upper RTC bank here */ + + ret = pmc_init(pmc); + if (ret < 0) + return log_msg_ret("Could not init PMC", ret); + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) { + ret = pmc_prev_sleep_state(pmc); + if (ret < 0) + return log_msg_ret("Could not get PMC sleep state", + ret); + gd->arch.prev_sleep_state = ret; + } + + return 0; +} + +int arch_cpu_init(void) +{ + int ret = 0; + + if (spl_phase() == PHASE_TPL) + ret = arch_cpu_init_tpl(); + else if (spl_phase() == PHASE_SPL) + ret = arch_cpu_init_spl(); + if (ret) + printf("%s: Error %d\n", __func__, ret); + + return ret; +} diff --git a/roms/u-boot/arch/x86/cpu/apollolake/fsp_bindings.c b/roms/u-boot/arch/x86/cpu/apollolake/fsp_bindings.c new file mode 100644 index 000000000..319c78b95 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/fsp_bindings.c @@ -0,0 +1,1856 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020 B&R Industrial Automation GmbH - http://www.br-automation.com + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <asm/arch/fsp_bindings.h> + +/** + * read_u8_prop() - Read an u8 property from devicetree (scalar or array) + * @node: Valid node reference to read property from + * @name: Name of the property to read from + * @count: If the property is expected to be an array, this is the + * number of expected elements + * Set to 0 if the property is expected to be a scalar + * @dst: Pointer to destination of where to save the value(s) read + * from devicetree + */ +static void read_u8_prop(ofnode node, char *name, size_t count, u8 *dst) +{ + u32 tmp; + const u8 *buf; + int ret; + + if (count == 0) { + ret = ofnode_read_u32(node, name, &tmp); + if (ret == 0) + *dst = tmp; + } else { + buf = ofnode_read_u8_array_ptr(node, name, count); + if (buf) + memcpy(dst, buf, count); + } +} + +/** + * read_u16_prop() - Read an u16 property from devicetree (scalar or array) + * @node: Valid node reference to read property from + * @name: Name of the property to read from + * @count: If the property is expected to be an array, this is the + * number of expected elements + * Set to 0 if the property is expected to be a scalar + * @dst: Pointer to destination of where to save the value(s) read + * from devicetree + * @return 0 on success, -ve on error + */ +static int read_u16_prop(ofnode node, char *name, size_t count, u16 *dst) +{ + u32 tmp; + u32 buf[32]; + int ret; + + if (ARRAY_SIZE(buf) < count) { + debug("ERROR: %s buffer to small!\n", __func__); + return -ENOSPC; + } + + if (count == 0) { + ret = ofnode_read_u32(node, name, &tmp); + if (ret == 0) + *dst = tmp; + } else { + ret = ofnode_read_u32_array(node, name, buf, count); + if (ret == 0) + for (int i = 0; i < count; i++) + dst[i] = buf[i]; + } + + return 0; +} + +/** + * read_u32_prop() - Read an u32 property from devicetree (scalar or array) + * @node: Valid node reference to read property from + * @name: Name of the property to read from + * @count: If the property is expected to be an array, this is the + * number of expected elements + * set to 0 if the property is expected to be a scalar + * @dst: Pointer to destination of where to save the value(s) read + * from devicetree + */ +static void read_u32_prop(ofnode node, char *name, size_t count, u32 *dst) +{ + if (count == 0) + ofnode_read_u32(node, name, dst); + else + ofnode_read_u32_array(node, name, dst, count); +} + +/** + * read_u64_prop() - Read an u64 property from devicetree (scalar or array) + * @node: Valid node reference to read property from + * @name: Name of the property to read from + * @count: If the property is expected to be an array, this is the + * number of expected elements + * set to 0 if the property is expected to be a scalar + * @dst: Pointer to destination of where to save the value(s) read + * from devicetree + */ +static int read_u64_prop(ofnode node, char *name, size_t count, u64 *dst) +{ + if (count == 0) { + ofnode_read_u64(node, name, dst); + } else { + debug("ERROR: %s u64 arrays not supported!\n", __func__); + return -EINVAL; + } + + return 0; +} + +/** + * read_string_prop() - Read a string property from devicetree + * @node: Valid node reference to read property from + * @name: Name of the property to read from + * @count: Size of the destination buffer + * @dst: Pointer to destination of where to save the values read + * from devicetree + */ +static void read_string_prop(ofnode node, char *name, size_t count, char *dst) +{ + const char *string_buf; + + if (count > 0) { + string_buf = ofnode_read_string(node, name); + if (string_buf) + strlcpy(dst, string_buf, count); + } +} + +/** + * read_swizzle_prop() - Read a swizzle property from devicetree + * @node: Valid node reference to read property from + * @name: Name of the property to read from + * @count: Number of elements in the swizzle configuration + * @dst: pointer to destination of where to save the values read + * from devicetree + */ +static void read_swizzle_prop(ofnode node, char *name, size_t count, u8 *dst) +{ + const struct lpddr4_chan_swizzle_cfg *sch; + /* Number of bytes to copy per DQS */ + const size_t sz = DQ_BITS_PER_DQS; + const struct lpddr4_swizzle_cfg *swizzle_cfg; + + swizzle_cfg = (const struct lpddr4_swizzle_cfg *) + ofnode_read_u8_array_ptr(node, name, count); + + if (!swizzle_cfg) + return; + /* + * CH0_DQB byte lanes in the bit swizzle configuration field are + * not 1:1. The mapping within the swizzling field is: + * indices [0:7] - byte lane 1 (DQS1) DQ[8:15] + * indices [8:15] - byte lane 0 (DQS0) DQ[0:7] + * indices [16:23] - byte lane 3 (DQS3) DQ[24:31] + * indices [24:31] - byte lane 2 (DQS2) DQ[16:23] + */ + sch = &swizzle_cfg->phys[LP4_PHYS_CH0B]; + memcpy(&dst[0 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS1], sz); + memcpy(&dst[1 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS0], sz); + memcpy(&dst[2 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS3], sz); + memcpy(&dst[3 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS2], sz); + + /* + * CH0_DQA byte lanes in the bit swizzle configuration field are 1:1. + */ + sch = &swizzle_cfg->phys[LP4_PHYS_CH0A]; + memcpy(&dst[4 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS0], sz); + memcpy(&dst[5 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS1], sz); + memcpy(&dst[6 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS2], sz); + memcpy(&dst[7 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS3], sz); + + sch = &swizzle_cfg->phys[LP4_PHYS_CH1B]; + memcpy(&dst[8 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS1], sz); + memcpy(&dst[9 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS0], sz); + memcpy(&dst[10 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS3], sz); + memcpy(&dst[11 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS2], sz); + + /* + * CH0_DQA byte lanes in the bit swizzle configuration field are 1:1. + */ + sch = &swizzle_cfg->phys[LP4_PHYS_CH1A]; + memcpy(&dst[12 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS0], sz); + memcpy(&dst[13 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS1], sz); + memcpy(&dst[14 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS2], sz); + memcpy(&dst[15 * DQ_BITS_PER_DQS], &sch->dqs[LP4_DQS3], sz); +} + +/** + * fsp_update_config_from_dtb() - Read FSP config from devicetree node + * @node: Valid node reference to read property from + * @cfg: Pointer to FSP config structure + * @fsp_bindings: Binding describing which devicetree properties should + * be stored where in the FSP configuration structure + * The end of the list is declared by a NULL pointer in propname + * @return 0 on success, -ve on error + * + * This function reads the configuration for FSP from the provided + * devicetree node and saves it in the FSP configuration structure. + * Configuration options that are not present in the devicetree are + * left at their current value. + */ +__maybe_unused +static int fsp_update_config_from_dtb(ofnode node, u8 *cfg, + const struct fsp_binding *fsp_bindings) +{ + const struct fsp_binding *fspb; + int ret; + + for (int i = 0; fsp_bindings[i].propname; i++) { + fspb = &fsp_bindings[i]; + + switch (fspb->type) { + case FSP_UINT8: + read_u8_prop(node, fspb->propname, fspb->count, + &cfg[fspb->offset]); + break; + case FSP_UINT16: + ret = read_u16_prop(node, fspb->propname, fspb->count, + (u16 *)&cfg[fspb->offset]); + if (ret) + return ret; + break; + case FSP_UINT32: + read_u32_prop(node, fspb->propname, fspb->count, + (u32 *)&cfg[fspb->offset]); + break; + case FSP_UINT64: + ret = read_u64_prop(node, fspb->propname, fspb->count, + (u64 *)&cfg[fspb->offset]); + if (ret) + return ret; + break; + case FSP_STRING: + read_string_prop(node, fspb->propname, fspb->count, + (char *)&cfg[fspb->offset]); + break; + case FSP_LPDDR4_SWIZZLE: + read_swizzle_prop(node, fspb->propname, fspb->count, + &cfg[fspb->offset]); + break; + } + } + + return 0; +} + +#if defined(CONFIG_SPL_BUILD) +const struct fsp_binding fsp_m_bindings[] = { + { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_m_config, serial_debug_port_address), + .propname = "fspm,serial-debug-port-address", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, serial_debug_port_type), + .propname = "fspm,serial-debug-port-type", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, serial_debug_port_device), + .propname = "fspm,serial-debug-port-device", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, serial_debug_port_stride_size), + .propname = "fspm,serial-debug-port-stride-size", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, mrc_fast_boot), + .propname = "fspm,mrc-fast-boot", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, igd), + .propname = "fspm,igd", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, igd_dvmt50_pre_alloc), + .propname = "fspm,igd-dvmt50-pre-alloc", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, igd_aperture_size), + .propname = "fspm,igd-aperture-size", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, gtt_size), + .propname = "fspm,gtt-size", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, primary_video_adaptor), + .propname = "fspm,primary-video-adaptor", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, package), + .propname = "fspm,package", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, profile), + .propname = "fspm,profile", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, memory_down), + .propname = "fspm,memory-down", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, ddr3_l_page_size), + .propname = "fspm,ddr3-l-page-size", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, ddr3_lasr), + .propname = "fspm,ddr3-lasr", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, scrambler_support), + .propname = "fspm,scrambler-support", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, interleaved_mode), + .propname = "fspm,interleaved-mode", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_m_config, channel_hash_mask), + .propname = "fspm,channel-hash-mask", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_m_config, slice_hash_mask), + .propname = "fspm,slice-hash-mask", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, channels_slices_enable), + .propname = "fspm,channels-slices-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, min_ref_rate2x_enable), + .propname = "fspm,min-ref-rate2x-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, dual_rank_support_enable), + .propname = "fspm,dual-rank-support-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, rmt_mode), + .propname = "fspm,rmt-mode", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_m_config, memory_size_limit), + .propname = "fspm,memory-size-limit", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_m_config, low_memory_max_value), + .propname = "fspm,low-memory-max-value", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_m_config, high_memory_max_value), + .propname = "fspm,high-memory-max-value", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, disable_fast_boot), + .propname = "fspm,disable-fast-boot", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, dimm0_spd_address), + .propname = "fspm,dimm0-spd-address", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, dimm1_spd_address), + .propname = "fspm,dimm1-spd-address", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[0].rank_enable), + .propname = "fspm,ch0-rank-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[0].device_width), + .propname = "fspm,ch0-device-width", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[0].dram_density), + .propname = "fspm,ch0-dram-density", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[0].option), + .propname = "fspm,ch0-option", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[0].odt_config), + .propname = "fspm,ch0-odt-config", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[0].tristate_clk1), + .propname = "fspm,ch0-tristate-clk1", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[0].mode2_n), + .propname = "fspm,ch0-mode2-n", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[0].odt_levels), + .propname = "fspm,ch0-odt-levels", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[1].rank_enable), + .propname = "fspm,ch1-rank-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[1].device_width), + .propname = "fspm,ch1-device-width", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[1].dram_density), + .propname = "fspm,ch1-dram-density", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[1].option), + .propname = "fspm,ch1-option", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[1].odt_config), + .propname = "fspm,ch1-odt-config", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[1].tristate_clk1), + .propname = "fspm,ch1-tristate-clk1", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[1].mode2_n), + .propname = "fspm,ch1-mode2-n", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[1].odt_levels), + .propname = "fspm,ch1-odt-levels", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[2].rank_enable), + .propname = "fspm,ch2-rank-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[2].device_width), + .propname = "fspm,ch2-device-width", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[2].dram_density), + .propname = "fspm,ch2-dram-density", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[2].option), + .propname = "fspm,ch2-option", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[2].odt_config), + .propname = "fspm,ch2-odt-config", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[2].tristate_clk1), + .propname = "fspm,ch2-tristate-clk1", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[2].mode2_n), + .propname = "fspm,ch2-mode2-n", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[2].odt_levels), + .propname = "fspm,ch2-odt-levels", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[3].rank_enable), + .propname = "fspm,ch3-rank-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[3].device_width), + .propname = "fspm,ch3-device-width", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[3].dram_density), + .propname = "fspm,ch3-dram-density", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[3].option), + .propname = "fspm,ch3-option", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[3].odt_config), + .propname = "fspm,ch3-odt-config", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[3].tristate_clk1), + .propname = "fspm,ch3-tristate-clk1", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[3].mode2_n), + .propname = "fspm,ch3-mode2-n", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, chan[3].odt_levels), + .propname = "fspm,ch3-odt-levels", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, rmt_check_run), + .propname = "fspm,rmt-check-run", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_m_config, + rmt_margin_check_scale_high_threshold), + .propname = "fspm,rmt-margin-check-scale-high-threshold", + }, { + .type = FSP_LPDDR4_SWIZZLE, + .offset = offsetof(struct fsp_m_config, ch_bit_swizzling), + .propname = "fspm,ch-bit-swizzling", + .count = SIZE_OF_MEMBER(struct fsp_m_config, ch_bit_swizzling) / + SIZE_OF_MEMBER(struct fsp_m_config, ch_bit_swizzling[0][0]) + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_m_config, msg_level_mask), + .propname = "fspm,msg-level-mask", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, pre_mem_gpio_table_pin_num), + .propname = "fspm,pre-mem-gpio-table-pin-num", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_m_config, + pre_mem_gpio_table_pin_num), + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_m_config, pre_mem_gpio_table_ptr), + .propname = "fspm,pre-mem-gpio-table-ptr", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, pre_mem_gpio_table_entry_num), + .propname = "fspm,pre-mem-gpio-table-entry-num", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, enhance_port8xh_decoding), + .propname = "fspm,enhance-port8xh-decoding", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, spd_write_enable), + .propname = "fspm,spd-write-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, mrc_data_saving), + .propname = "fspm,mrc-data-saving", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_m_config, oem_loading_base), + .propname = "fspm,oem-loading-base", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, oem_file_name), + .propname = "fspm,oem-file-name", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_m_config, oem_file_name), + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_m_config, mrc_boot_data_ptr), + .propname = "fspm,mrc-boot-data-ptr", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, e_mmc_trace_len), + .propname = "fspm,emmc-trace-len", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, skip_cse_rbp), + .propname = "fspm,skip-cse-rbp", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, npk_en), + .propname = "fspm,npk-en", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, fw_trace_en), + .propname = "fspm,fw-trace-en", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, fw_trace_destination), + .propname = "fspm,fw-trace-destination", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, recover_dump), + .propname = "fspm,recover-dump", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, msc0_wrap), + .propname = "fspm,msc0-wrap", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, msc1_wrap), + .propname = "fspm,msc1-wrap", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_m_config, msc0_size), + .propname = "fspm,msc0-size", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_m_config, msc1_size), + .propname = "fspm,msc1-size", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, pti_mode), + .propname = "fspm,pti-mode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, pti_training), + .propname = "fspm,pti-training", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, pti_speed), + .propname = "fspm,pti-speed", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, punit_mlvl), + .propname = "fspm,punit-mlvl", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, pmc_mlvl), + .propname = "fspm,pmc-mlvl", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, sw_trace_en), + .propname = "fspm,sw-trace-en", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, periodic_retraining_disable), + .propname = "fspm,periodic-retraining-disable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, enable_reset_system), + .propname = "fspm,enable-reset-system", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, enable_s3_heci2), + .propname = "fspm,enable-s3-heci2", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_m_config, variable_nvs_buffer_ptr), + .propname = "fspm,variable-nvs-buffer-ptr", + }, { + .type = FSP_UINT64, + .offset = offsetof(struct fsp_m_config, start_timer_ticker_of_pfet_assert), + .propname = "fspm,start-timer-ticker-of-pfet-assert", + }, { + .type = FSP_UINT8, .offset = offsetof(struct fsp_m_config, rt_en), + .propname = "fspm,rt-en", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_m_config, skip_pcie_power_sequence), + .propname = "fspm,skip-pcie-power-sequence", + }, { + .propname = NULL + } +}; + +int fsp_m_update_config_from_dtb(ofnode node, struct fsp_m_config *cfg) +{ + return fsp_update_config_from_dtb(node, (u8 *)cfg, fsp_m_bindings); +} +#endif + +#if !defined(CONFIG_SPL_BUILD) && !defined(CONFIG_TPL_BUILD) +const struct fsp_binding fsp_s_bindings[] = { + { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, active_processor_cores), + .propname = "fsps,active-processor-cores", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, disable_core1), + .propname = "fsps,disable-core1", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, disable_core2), + .propname = "fsps,disable-core2", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, disable_core3), + .propname = "fsps,disable-core3", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, vmx_enable), + .propname = "fsps,vmx-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, proc_trace_mem_size), + .propname = "fsps,proc-trace-mem-size", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, proc_trace_enable), + .propname = "fsps,proc-trace-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, eist), + .propname = "fsps,eist", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, boot_p_state), + .propname = "fsps,boot-p-state", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, enable_cx), + .propname = "fsps,enable-cx", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, c1e), + .propname = "fsps,c1e", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, bi_proc_hot), + .propname = "fsps,bi-proc-hot", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pkg_c_state_limit), + .propname = "fsps,pkg-c-state-limit", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, c_state_auto_demotion), + .propname = "fsps,c-state-auto-demotion", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, c_state_un_demotion), + .propname = "fsps,c-state-un-demotion", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, max_core_c_state), + .propname = "fsps,max-core-c-state", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pkg_c_state_demotion), + .propname = "fsps,pkg-c-state-demotion", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pkg_c_state_un_demotion), + .propname = "fsps,pkg-c-state-un-demotion", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, turbo_mode), + .propname = "fsps,turbo-mode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hda_verb_table_entry_num), + .propname = "fsps,hda-verb-table-entry-num", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, hda_verb_table_ptr), + .propname = "fsps,hda-verb-table-ptr", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, p2sb_unhide), + .propname = "fsps,p2sb-unhide", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, ipu_en), + .propname = "fsps,ipu-en", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, ipu_acpi_mode), + .propname = "fsps,ipu-acpi-mode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, force_wake), + .propname = "fsps,force-wake", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, gtt_mm_adr), + .propname = "fsps,gtt-mm-adr", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, gm_adr), + .propname = "fsps,gm-adr", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pavp_lock), + .propname = "fsps,pavp-lock", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, graphics_freq_modify), + .propname = "fsps,graphics-freq-modify", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, graphics_freq_req), + .propname = "fsps,graphics-freq-req", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, graphics_video_freq), + .propname = "fsps,graphics-video-freq", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pm_lock), + .propname = "fsps,pm-lock", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dop_clock_gating), + .propname = "fsps,dop-clock-gating", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, unsolicited_attack_override), + .propname = "fsps,unsolicited-attack-override", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, wopcm_support), + .propname = "fsps,wopcm-support", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, wopcm_size), + .propname = "fsps,wopcm-size", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, power_gating), + .propname = "fsps,power-gating", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, unit_level_clock_gating), + .propname = "fsps,unit-level-clock-gating", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, fast_boot), + .propname = "fsps,fast-boot", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dyn_sr), + .propname = "fsps,dyn-sr", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sa_ipu_enable), + .propname = "fsps,sa-ipu-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pm_support), + .propname = "fsps,pm-support", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, enable_render_standby), + .propname = "fsps,enable-render-standby", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, logo_size), + .propname = "fsps,logo-size", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, logo_ptr), + .propname = "fsps,logo-ptr", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, graphics_config_ptr), + .propname = "fsps,graphics-config-ptr", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pavp_enable), + .propname = "fsps,pavp-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pavp_pr3), + .propname = "fsps,pavp-pr3", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, cd_clock), + .propname = "fsps,cd-clock", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pei_graphics_peim_init), + .propname = "fsps,pei-graphics-peim-init", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, write_protection_enable), + .propname = "fsps,write-protection-enable", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + write_protection_enable), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, read_protection_enable), + .propname = "fsps,read-protection-enable", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + read_protection_enable), + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, protected_range_limit), + .propname = "fsps,protected-range-limit", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + protected_range_limit), + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, protected_range_base), + .propname = "fsps,protected-range-base", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + protected_range_base), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, gmm), + .propname = "fsps,gmm", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_pgcb_clk_trunk), + .propname = "fsps,clk-gating-pgcb-clk-trunk", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_sb), + .propname = "fsps,clk-gating-sb", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_sb_clk_trunk), + .propname = "fsps,clk-gating-sb-clk-trunk", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_sb_clk_partition), + .propname = "fsps,clk-gating-sb-clk-partition", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_core), + .propname = "fsps,clk-gating-core", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_dma), + .propname = "fsps,clk-gating-dma", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_reg_access), + .propname = "fsps,clk-gating-reg-access", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_host), + .propname = "fsps,clk-gating-host", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_partition), + .propname = "fsps,clk-gating-partition", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, clk_gating_trunk), + .propname = "fsps,clk-gating-trunk", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hda_enable), + .propname = "fsps,hda-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dsp_enable), + .propname = "fsps,dsp-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pme), + .propname = "fsps,pme", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_io_buffer_ownership), + .propname = "fsps,hd-audio-io-buffer-ownership", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_io_buffer_voltage), + .propname = "fsps,hd-audio-io-buffer-voltage", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_vc_type), + .propname = "fsps,hd-audio-vc-type", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_link_frequency), + .propname = "fsps,hd-audio-link-frequency", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_i_disp_link_frequency), + .propname = "fsps,hd-audio-i-disp-link-frequency", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_i_disp_link_tmode), + .propname = "fsps,hd-audio-i-disp-link-tmode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dsp_endpoint_dmic), + .propname = "fsps,dsp-endpoint-dmic", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dsp_endpoint_bluetooth), + .propname = "fsps,dsp-endpoint-bluetooth", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dsp_endpoint_i2s_skp), + .propname = "fsps,dsp-endpoint-i2s-skp", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dsp_endpoint_i2s_hp), + .propname = "fsps,dsp-endpoint-i2s-hp", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, audio_ctl_pwr_gate), + .propname = "fsps,audio-ctl-pwr-gate", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, audio_dsp_pwr_gate), + .propname = "fsps,audio-dsp-pwr-gate", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, mmt), + .propname = "fsps,mmt", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hmt), + .propname = "fsps,hmt", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_pwr_gate), + .propname = "fsps,hd-audio-pwr-gate", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_clk_gate), + .propname = "fsps,hd-audio-clk-gate", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, dsp_feature_mask), + .propname = "fsps,dsp-feature-mask", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, dsp_pp_module_mask), + .propname = "fsps,dsp-pp-module-mask", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, bios_cfg_lock_down), + .propname = "fsps,bios-cfg-lock-down", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hpet), + .propname = "fsps,hpet", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hpet_bdf_valid), + .propname = "fsps,hpet-bdf-valid", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hpet_bus_number), + .propname = "fsps,hpet-bus-number", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hpet_device_number), + .propname = "fsps,hpet-device-number", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hpet_function_number), + .propname = "fsps,hpet-function-number", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, io_apic_bdf_valid), + .propname = "fsps,io-apic-bdf-valid", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, io_apic_bus_number), + .propname = "fsps,io-apic-bus-number", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, io_apic_device_number), + .propname = "fsps,io-apic-device-number", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, io_apic_function_number), + .propname = "fsps,io-apic-function-number", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, io_apic_entry24_119), + .propname = "fsps,io-apic-entry24-119", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, io_apic_id), + .propname = "fsps,io-apic-id", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, io_apic_range_select), + .propname = "fsps,io-apic-range-select", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, ish_enable), + .propname = "fsps,ish-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, bios_interface), + .propname = "fsps,bios-interface", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, bios_lock), + .propname = "fsps,bios-lock", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, spi_eiss), + .propname = "fsps,spi-eiss", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, bios_lock_sw_smi_number), + .propname = "fsps,bios-lock-sw-smi-number", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, lpss_s0ix_enable), + .propname = "fsps,lpss-s0ix-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c_clk_gate_cfg), + .propname = "fsps,i2c-clk-gate-cfg", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, i2c_clk_gate_cfg), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hsuart_clk_gate_cfg), + .propname = "fsps,hsuart-clk-gate-cfg", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, hsuart_clk_gate_cfg), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, spi_clk_gate_cfg), + .propname = "fsps,spi-clk-gate-cfg", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, spi_clk_gate_cfg), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c0_enable), + .propname = "fsps,i2c0-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c1_enable), + .propname = "fsps,i2c1-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c2_enable), + .propname = "fsps,i2c2-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c3_enable), + .propname = "fsps,i2c3-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c4_enable), + .propname = "fsps,i2c4-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c5_enable), + .propname = "fsps,i2c5-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c6_enable), + .propname = "fsps,i2c6-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, i2c7_enable), + .propname = "fsps,i2c7-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hsuart0_enable), + .propname = "fsps,hsuart0-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hsuart1_enable), + .propname = "fsps,hsuart1-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hsuart2_enable), + .propname = "fsps,hsuart2-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hsuart3_enable), + .propname = "fsps,hsuart3-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, spi0_enable), + .propname = "fsps,spi0-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, spi1_enable), + .propname = "fsps,spi1-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, spi2_enable), + .propname = "fsps,spi2-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, os_dbg_enable), + .propname = "fsps,os-dbg-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dci_en), + .propname = "fsps,dci-en", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, + uart2_kernel_debug_base_address), + .propname = "fsps,uart2-kernel-debug-base-address", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_clock_gating_disabled), + .propname = "fsps,pcie-clock-gating-disabled", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_root_port8xh_decode), + .propname = "fsps,pcie-root-port8xh-decode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie8xh_decode_port_index), + .propname = "fsps,pcie8xh-decode-port-index", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + pcie_root_port_peer_memory_write_enable), + .propname = "fsps,pcie-root-port-peer-memory-write-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_aspm_sw_smi_number), + .propname = "fsps,pcie-aspm-sw-smi-number", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_root_port_en), + .propname = "fsps,pcie-root-port-en", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_root_port_en), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_hide), + .propname = "fsps,pcie-rp-hide", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_rp_hide), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_slot_implemented), + .propname = "fsps,pcie-rp-slot-implemented", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_slot_implemented), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_hot_plug), + .propname = "fsps,pcie-rp-hot-plug", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_rp_hot_plug), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_pm_sci), + .propname = "fsps,pcie-rp-pm-sci", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_rp_pm_sci), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_ext_sync), + .propname = "fsps,pcie-rp-ext-sync", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_rp_ext_sync), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + pcie_rp_transmitter_half_swing), + .propname = "fsps,pcie-rp-transmitter-half-swing", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_transmitter_half_swing), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_acs_enabled), + .propname = "fsps,pcie-rp-acs-enabled", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_rp_acs_enabled), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_clk_req_supported), + .propname = "fsps,pcie-rp-clk-req-supported", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_clk_req_supported), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_clk_req_number), + .propname = "fsps,pcie-rp-clk-req-number", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_clk_req_number), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_clk_req_detect), + .propname = "fsps,pcie-rp-clk-req-detect", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_clk_req_detect), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, advanced_error_reporting), + .propname = "fsps,advanced-error-reporting", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + advanced_error_reporting), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pme_interrupt), + .propname = "fsps,pme-interrupt", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pme_interrupt), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, unsupported_request_report), + .propname = "fsps,unsupported-request-report", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + unsupported_request_report), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, fatal_error_report), + .propname = "fsps,fatal-error-report", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, fatal_error_report), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, no_fatal_error_report), + .propname = "fsps,no-fatal-error-report", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + no_fatal_error_report), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, correctable_error_report), + .propname = "fsps,correctable-error-report", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + correctable_error_report), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + system_error_on_fatal_error), + .propname = "fsps,system-error-on-fatal-error", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + system_error_on_fatal_error), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + system_error_on_non_fatal_error), + .propname = "fsps,system-error-on-non-fatal-error", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + system_error_on_non_fatal_error), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + system_error_on_correctable_error), + .propname = "fsps,system-error-on-correctable-error", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + system_error_on_correctable_error), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_speed), + .propname = "fsps,pcie-rp-speed", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_rp_speed), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, physical_slot_number), + .propname = "fsps,physical-slot-number", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + physical_slot_number), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_completion_timeout), + .propname = "fsps,pcie-rp-completion-timeout", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_completion_timeout), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, ptm_enable), + .propname = "fsps,ptm-enable", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, ptm_enable), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_aspm), + .propname = "fsps,pcie-rp-aspm", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_rp_aspm), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_l1_substates), + .propname = "fsps,pcie-rp-l1-substates", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_l1_substates), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_ltr_enable), + .propname = "fsps,pcie-rp-ltr-enable", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, pcie_rp_ltr_enable), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_ltr_config_lock), + .propname = "fsps,pcie-rp-ltr-config-lock", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_ltr_config_lock), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pme_b0_s5_dis), + .propname = "fsps,pme-b0-s5-dis", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pci_clock_run), + .propname = "fsps,pci-clock-run", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, timer8254_clk_setting), + .propname = "fsps,timer8254-clk-setting", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, enable_sata), + .propname = "fsps,enable-sata", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_mode), + .propname = "fsps,sata-mode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_salp_support), + .propname = "fsps,sata-salp-support", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_pwr_opt_enable), + .propname = "fsps,sata-pwr-opt-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, e_sata_speed_limit), + .propname = "fsps,e-sata-speed-limit", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, speed_limit), + .propname = "fsps,speed-limit", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_enable), + .propname = "fsps,sata-ports-enable", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, sata_ports_enable), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_dev_slp), + .propname = "fsps,sata-ports-dev-slp", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, sata_ports_dev_slp), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_hot_plug), + .propname = "fsps,sata-ports-hot-plug", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, sata_ports_hot_plug), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_interlock_sw), + .propname = "fsps,sata-ports-interlock-sw", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + sata_ports_interlock_sw), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_external), + .propname = "fsps,sata-ports-external", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, sata_ports_external), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_spin_up), + .propname = "fsps,sata-ports-spin-up", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, sata_ports_spin_up), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_solid_state_drive), + .propname = "fsps,sata-ports-solid-state-drive", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + sata_ports_solid_state_drive), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_enable_dito_config), + .propname = "fsps,sata-ports-enable-dito-config", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + sata_ports_enable_dito_config), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_dm_val), + .propname = "fsps,sata-ports-dm-val", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, sata_ports_dm_val), + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, sata_ports_dito_val), + .propname = "fsps,sata-ports-dito-val", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, sata_ports_dito_val), + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, sub_system_vendor_id), + .propname = "fsps,sub-system-vendor-id", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, sub_system_id), + .propname = "fsps,sub-system-id", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, crid_settings), + .propname = "fsps,crid-settings", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, reset_select), + .propname = "fsps,reset-select", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sdcard_enabled), + .propname = "fsps,sdcard-enabled", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, e_mmc_enabled), + .propname = "fsps,emmc-enabled", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, e_mmc_host_max_speed), + .propname = "fsps,emmc-host-max-speed", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, ufs_enabled), + .propname = "fsps,ufs-enabled", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sdio_enabled), + .propname = "fsps,sdio-enabled", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, gpp_lock), + .propname = "fsps,gpp-lock", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sirq_enable), + .propname = "fsps,sirq-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sirq_mode), + .propname = "fsps,sirq-mode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, start_frame_pulse), + .propname = "fsps,start-frame-pulse", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, smbus_enable), + .propname = "fsps,smbus-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, arp_enable), + .propname = "fsps,arp-enable", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, num_rsvd_smbus_addresses), + .propname = "fsps,num-rsvd-smbus-addresses", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, rsvd_smbus_address_table), + .propname = "fsps,rsvd-smbus-address-table", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + rsvd_smbus_address_table), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, disable_compliance_mode), + .propname = "fsps,disable-compliance-mode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, usb_per_port_ctl), + .propname = "fsps,usb-per-port-ctl", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, usb30_mode), + .propname = "fsps,usb30-mode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_usb20_enable), + .propname = "fsps,port-usb20-enable", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, port_usb20_enable), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_us20b_over_current_pin), + .propname = "fsps,port-us20b-over-current-pin", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_us20b_over_current_pin), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, usb_otg), + .propname = "fsps,usb-otg", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hsic_support_enable), + .propname = "fsps,hsic-support-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_usb30_enable), + .propname = "fsps,port-usb30-enable", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, port_usb30_enable), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_us30b_over_current_pin), + .propname = "fsps,port-us30b-over-current-pin", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_us30b_over_current_pin), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, ssic_port_enable), + .propname = "fsps,ssic-port-enable", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, ssic_port_enable), + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, dlane_pwr_gating), + .propname = "fsps,dlane-pwr-gating", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, vtd_enable), + .propname = "fsps,vtd-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, lock_down_global_smi), + .propname = "fsps,lock-down-global-smi", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, reset_wait_timer), + .propname = "fsps,reset-wait-timer", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, rtc_lock), + .propname = "fsps,rtc-lock", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_test_mode), + .propname = "fsps,sata-test-mode", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, ssic_rate), + .propname = "fsps,ssic-rate", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, ssic_rate), + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, dynamic_power_gating), + .propname = "fsps,dynamic-power-gating", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, pcie_rp_ltr_max_snoop_latency), + .propname = "fsps,pcie-rp-ltr-max-snoop-latency", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_ltr_max_snoop_latency), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + pcie_rp_snoop_latency_override_mode), + .propname = "fsps,pcie-rp-snoop-latency-override-mode", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_snoop_latency_override_mode), + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, + pcie_rp_snoop_latency_override_value), + .propname = "fsps,pcie-rp-snoop-latency-override-value", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_snoop_latency_override_value), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + pcie_rp_snoop_latency_override_multiplier), + .propname = "fsps,pcie-rp-snoop-latency-override-multiplier", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_snoop_latency_override_multiplier), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, skip_mp_init), + .propname = "fsps,skip-mp-init", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dci_auto_detect), + .propname = "fsps,dci-auto-detect", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, + pcie_rp_ltr_max_non_snoop_latency), + .propname = "fsps,pcie-rp-ltr-max-non-snoop-latency", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_ltr_max_non_snoop_latency), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + pcie_rp_non_snoop_latency_override_mode), + .propname = "fsps,pcie-rp-non-snoop-latency-override-mode", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_non_snoop_latency_override_mode), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, tco_timer_halt_lock), + .propname = "fsps,tco-timer-halt-lock", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pwr_btn_override_period), + .propname = "fsps,pwr-btn-override-period", + }, { + .type = FSP_UINT16, + .offset = offsetof(struct fsp_s_config, + pcie_rp_non_snoop_latency_override_value), + .propname = "fsps,pcie-rp-non-snoop-latency-override-value", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_non_snoop_latency_override_value), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + pcie_rp_non_snoop_latency_override_multiplier), + .propname = "fsps,pcie-rp-non-snoop-latency-override-multiplier", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_non_snoop_latency_override_multiplier), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_slot_power_limit_scale), + .propname = "fsps,pcie-rp-slot-power-limit-scale", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_slot_power_limit_scale), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_slot_power_limit_value), + .propname = "fsps,pcie-rp-slot-power-limit-value", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_slot_power_limit_value), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, disable_native_power_button), + .propname = "fsps,disable-native-power-button", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, power_butter_debounce_mode), + .propname = "fsps,power-butter-debounce-mode", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdio_tx_cmd_cntl), + .propname = "fsps,sdio-tx-cmd-cntl", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdio_tx_data_cntl1), + .propname = "fsps,sdio-tx-data-cntl1", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdio_tx_data_cntl2), + .propname = "fsps,sdio-tx-data-cntl2", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdio_rx_cmd_data_cntl1), + .propname = "fsps,sdio-rx-cmd-data-cntl1", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdio_rx_cmd_data_cntl2), + .propname = "fsps,sdio-rx-cmd-data-cntl2", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdcard_tx_cmd_cntl), + .propname = "fsps,sdcard-tx-cmd-cntl", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdcard_tx_data_cntl1), + .propname = "fsps,sdcard-tx-data-cntl1", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdcard_tx_data_cntl2), + .propname = "fsps,sdcard-tx-data-cntl2", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdcard_rx_cmd_data_cntl1), + .propname = "fsps,sdcard-rx-cmd-data-cntl1", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdcard_rx_strobe_cntl), + .propname = "fsps,sdcard-rx-strobe-cntl", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, sdcard_rx_cmd_data_cntl2), + .propname = "fsps,sdcard-rx-cmd-data-cntl2", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, emmc_tx_cmd_cntl), + .propname = "fsps,emmc-tx-cmd-cntl", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, emmc_tx_data_cntl1), + .propname = "fsps,emmc-tx-data-cntl1", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, emmc_tx_data_cntl2), + .propname = "fsps,emmc-tx-data-cntl2", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, emmc_rx_cmd_data_cntl1), + .propname = "fsps,emmc-rx-cmd-data-cntl1", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, emmc_rx_strobe_cntl), + .propname = "fsps,emmc-rx-strobe-cntl", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, emmc_rx_cmd_data_cntl2), + .propname = "fsps,emmc-rx-cmd-data-cntl2", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, emmc_master_sw_cntl), + .propname = "fsps,emmc-master-sw-cntl", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pcie_rp_selectable_deemphasis), + .propname = "fsps,pcie-rp-selectable-deemphasis", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + pcie_rp_selectable_deemphasis), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, monitor_mwait_enable), + .propname = "fsps,monitor-mwait-enable", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, hd_audio_dsp_uaa_compliance), + .propname = "fsps,hd-audio-dsp-uaa-compliance", + }, { + .type = FSP_UINT32, + .offset = offsetof(struct fsp_s_config, ipc), + .propname = "fsps,ipc", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, ipc), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, sata_ports_disable_dynamic_pg), + .propname = "fsps,sata-ports-disable-dynamic-pg", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + sata_ports_disable_dynamic_pg), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, init_s3_cpu), + .propname = "fsps,init-s3-cpu", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, skip_punit_init), + .propname = "fsps,skip-punit-init", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_usb20_per_port_tx_pe_half), + .propname = "fsps,port-usb20-per-port-tx-pe-half", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_usb20_per_port_tx_pe_half), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_usb20_per_port_pe_txi_set), + .propname = "fsps,port-usb20-per-port-pe-txi-set", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_usb20_per_port_pe_txi_set), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_usb20_per_port_txi_set), + .propname = "fsps,port-usb20-per-port-txi-set", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_usb20_per_port_txi_set), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_usb20_hs_skew_sel), + .propname = "fsps,port-usb20-hs-skew-sel", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_usb20_hs_skew_sel), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + port_usb20_i_usb_tx_emphasis_en), + .propname = "fsps,port-usb20-i-usb-tx-emphasis-en", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_usb20_i_usb_tx_emphasis_en), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, + port_usb20_per_port_rxi_set), + .propname = "fsps,port-usb20-per-port-rxi-set", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_usb20_per_port_rxi_set), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, port_usb20_hs_npre_drv_sel), + .propname = "fsps,port-usb20-hs-npre-drv-sel", + .count = ARRAY_SIZE_OF_MEMBER(struct fsp_s_config, + port_usb20_hs_npre_drv_sel), + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, os_selection), + .propname = "fsps,os-selection", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, dptf_enabled), + .propname = "fsps,dptf-enabled", + }, { + .type = FSP_UINT8, + .offset = offsetof(struct fsp_s_config, pwm_enabled), + .propname = "fsps,pwm-enabled", + }, { + .propname = NULL + } +}; + +int fsp_s_update_config_from_dtb(ofnode node, struct fsp_s_config *cfg) +{ + return fsp_update_config_from_dtb(node, (u8 *)cfg, fsp_s_bindings); +} +#endif diff --git a/roms/u-boot/arch/x86/cpu/apollolake/fsp_m.c b/roms/u-boot/arch/x86/cpu/apollolake/fsp_m.c new file mode 100644 index 000000000..c6be707e4 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/fsp_m.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <asm/arch/iomap.h> +#include <asm/arch/fsp_bindings.h> +#include <asm/fsp2/fsp_internal.h> +#include <dm/uclass-internal.h> + +int fspm_update_config(struct udevice *dev, struct fspm_upd *upd) +{ + struct fsp_m_config *cfg = &upd->config; + struct fspm_arch_upd *arch = &upd->arch; + int cache_ret = 0; + ofnode node; + int ret; + + arch->nvs_buffer_ptr = NULL; + cache_ret = prepare_mrc_cache(upd); + if (cache_ret && cache_ret != -ENOENT) + return log_msg_ret("mrc", cache_ret); + arch->stack_base = (void *)(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - + arch->stack_size); + arch->boot_loader_tolum_size = 0; + arch->boot_mode = cache_ret ? FSP_BOOT_WITH_FULL_CONFIGURATION : + FSP_BOOT_ASSUMING_NO_CONFIGURATION_CHANGES; + + node = dev_ofnode(dev); + if (!ofnode_valid(node)) + return log_msg_ret("node", -ENOENT); + node = ofnode_find_subnode(node, "fsp-m"); + if (!ofnode_valid(node)) + return log_msg_ret("fspm", -ENOENT); + + ret = fsp_m_update_config_from_dtb(node, cfg); + if (ret) + return log_msg_ret("dtb", cache_ret); + + return cache_ret; +} + +/* + * The FSP-M binary appears to break the SPI controller. It can be fixed by + * writing the BAR again, so do that here + */ +int fspm_done(struct udevice *dev) +{ + struct udevice *spi; + int ret; + + /* Don't probe the device, since that reads the BAR */ + ret = uclass_find_first_device(UCLASS_SPI, &spi); + if (ret) + return log_msg_ret("SPI", ret); + if (!spi) + return log_msg_ret("no SPI", -ENODEV); + + dm_pci_write_config32(spi, PCI_BASE_ADDRESS_0, + IOMAP_SPI_BASE | PCI_BASE_ADDRESS_SPACE_MEMORY); + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/apollolake/fsp_s.c b/roms/u-boot/arch/x86/cpu/apollolake/fsp_s.c new file mode 100644 index 000000000..a9b13c0c7 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/fsp_s.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <binman.h> +#include <bootstage.h> +#include <dm.h> +#include <init.h> +#include <irq.h> +#include <log.h> +#include <malloc.h> +#include <p2sb.h> +#include <acpi/acpi_s3.h> +#include <asm/global_data.h> +#include <asm/intel_pinctrl.h> +#include <asm/io.h> +#include <asm/intel_regs.h> +#include <asm/msr.h> +#include <asm/msr-index.h> +#include <asm/pci.h> +#include <asm/arch/cpu.h> +#include <asm/arch/systemagent.h> +#include <asm/arch/fsp_bindings.h> +#include <asm/arch/fsp/fsp_configs.h> +#include <asm/arch/fsp/fsp_s_upd.h> +#include <dm/uclass-internal.h> +#include <linux/bitops.h> + +#define PCH_P2SB_E0 0xe0 +#define HIDE_BIT BIT(0) + +int fsps_update_config(struct udevice *dev, ulong rom_offset, + struct fsps_upd *upd) +{ + struct fsp_s_config *cfg = &upd->config; + ofnode node; + + if (IS_ENABLED(CONFIG_HAVE_VBT)) { + void *buf; + int ret; + + ret = binman_entry_map(ofnode_null(), "intel-vbt", &buf, NULL); + if (ret) + return log_msg_ret("Cannot find VBT", ret); + if (*(u32 *)buf != VBT_SIGNATURE) + return log_msg_ret("VBT signature", -EINVAL); + + /* + * Load VBT before devicetree-specific config. This only + * supports memory-mapped SPI at present. + */ + cfg->graphics_config_ptr = (ulong)buf; + } + + node = dev_read_subnode(dev, "fsp-s"); + if (!ofnode_valid(node)) + return log_msg_ret("fsp-s settings", -ENOENT); + + return fsp_s_update_config_from_dtb(node, cfg); +} + +/* Configure package power limits */ +static int set_power_limits(struct udevice *dev) +{ + msr_t rapl_msr_reg, limit; + u32 power_unit; + u32 tdp, min_power, max_power; + u32 pl2_val; + u32 override_tdp[2]; + int ret; + + /* Get units */ + rapl_msr_reg = msr_read(MSR_PKG_POWER_SKU_UNIT); + power_unit = 1 << (rapl_msr_reg.lo & 0xf); + + /* Get power defaults for this SKU */ + rapl_msr_reg = msr_read(MSR_PKG_POWER_SKU); + tdp = rapl_msr_reg.lo & PKG_POWER_LIMIT_MASK; + pl2_val = rapl_msr_reg.hi & PKG_POWER_LIMIT_MASK; + min_power = (rapl_msr_reg.lo >> 16) & PKG_POWER_LIMIT_MASK; + max_power = rapl_msr_reg.hi & PKG_POWER_LIMIT_MASK; + + if (min_power > 0 && tdp < min_power) + tdp = min_power; + + if (max_power > 0 && tdp > max_power) + tdp = max_power; + + ret = dev_read_u32_array(dev, "tdp-pl-override-mw", override_tdp, + ARRAY_SIZE(override_tdp)); + if (ret) + return log_msg_ret("tdp-pl-override-mw", ret); + + /* Set PL1 override value */ + if (override_tdp[0]) + tdp = override_tdp[0] * power_unit / 1000; + + /* Set PL2 override value */ + if (override_tdp[1]) + pl2_val = override_tdp[1] * power_unit / 1000; + + /* Set long term power limit to TDP */ + limit.lo = tdp & PKG_POWER_LIMIT_MASK; + /* Set PL1 Pkg Power clamp bit */ + limit.lo |= PKG_POWER_LIMIT_CLAMP; + + limit.lo |= PKG_POWER_LIMIT_EN; + limit.lo |= (MB_POWER_LIMIT1_TIME_DEFAULT & + PKG_POWER_LIMIT_TIME_MASK) << PKG_POWER_LIMIT_TIME_SHIFT; + + /* Set short term power limit PL2 */ + limit.hi = pl2_val & PKG_POWER_LIMIT_MASK; + limit.hi |= PKG_POWER_LIMIT_EN; + + /* Program package power limits in RAPL MSR */ + msr_write(MSR_PKG_POWER_LIMIT, limit); + log_debug("RAPL PL1 %d.%dW\n", tdp / power_unit, + 100 * (tdp % power_unit) / power_unit); + log_debug("RAPL PL2 %d.%dW\n", pl2_val / power_unit, + 100 * (pl2_val % power_unit) / power_unit); + + /* + * Sett RAPL MMIO register for Power limits. RAPL driver is using MSR + * instead of MMIO, so disable LIMIT_EN bit for MMIO + */ + writel(limit.lo & ~PKG_POWER_LIMIT_EN, MCHBAR_REG(MCHBAR_RAPL_PPL)); + writel(limit.hi & ~PKG_POWER_LIMIT_EN, MCHBAR_REG(MCHBAR_RAPL_PPL + 4)); + + return 0; +} + +int p2sb_unhide(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_find_first_device(UCLASS_P2SB, &dev); + if (ret) + return log_msg_ret("p2sb", ret); + ret = p2sb_set_hide(dev, false); + if (ret) + return log_msg_ret("hide", ret); + + return 0; +} + +/* Overwrites the SCI IRQ if another IRQ number is given by device tree */ +static void set_sci_irq(void) +{ + /* Skip this for now */ +} + +int arch_fsps_preinit(void) +{ + struct udevice *itss; + int ret; + + if (!ll_boot_init()) + return 0; + ret = irq_first_device_type(X86_IRQT_ITSS, &itss); + if (ret) + return log_msg_ret("no itss", ret); + + /* + * Clear the GPI interrupt status and enable registers. These + * registers do not get reset to default state when booting from S5. + */ + ret = pinctrl_gpi_clear_int_cfg(); + if (ret) + return log_msg_ret("gpi_clear", ret); + + return 0; +} + +int arch_fsp_init_r(void) +{ + bool s3wake; + struct udevice *dev, *itss; + int ret; + + if (!ll_boot_init()) + return 0; + + s3wake = IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) && + gd->arch.prev_sleep_state == ACPI_S3; + + /* + * This must be called before any devices are probed. Put any probing + * into arch_fsps_preinit() above. + * + * We don't use CONFIG_APL_BOOT_FROM_FAST_SPI_FLASH here since it will + * force PCI to be probed. + */ + ret = fsp_silicon_init(s3wake, false); + if (ret) + return ret; + + ret = irq_first_device_type(X86_IRQT_ITSS, &itss); + if (ret) + return log_msg_ret("no itss", ret); + + /* + * Restore GPIO IRQ polarities back to previous settings. This was + * stored in reserve_arch() - see X86_IRQT_ITSS + */ + irq_restore_polarities(itss); + + /* soc_init() */ + ret = p2sb_unhide(); + if (ret) + return log_msg_ret("unhide p2sb", ret); + + /* Set RAPL MSR for Package power limits*/ + ret = uclass_first_device_err(UCLASS_NORTHBRIDGE, &dev); + if (ret) + return log_msg_ret("Cannot get northbridge", ret); + set_power_limits(dev); + + /* + * FSP-S routes SCI to IRQ 9. With the help of this function you can + * select another IRQ for SCI. + */ + set_sci_irq(); + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/apollolake/hostbridge.c b/roms/u-boot/arch/x86/cpu/apollolake/hostbridge.c new file mode 100644 index 000000000..9decab7aa --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/hostbridge.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + * Copyright (C) 2015 - 2017 Intel Corp. + * Copyright (C) 2017 - 2019 Siemens AG + * (Written by Alexandru Gagniuc <alexandrux.gagniuc@intel.com> for Intel Corp.) + * (Written by Andrey Petrov <andrey.petrov@intel.com> for Intel Corp.) + * + * Portions from coreboot soc/intel/apollolake/chip.c + */ + +#define LOG_CATEGORY UCLASS_NORTHBRIDGE + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> +#include <log.h> +#include <spl.h> +#include <tables_csum.h> +#include <acpi/acpi_table.h> +#include <asm/acpi_nhlt.h> +#include <asm/intel_pinctrl.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <asm/arch/acpi.h> +#include <asm/arch/hostbridge.h> +#include <asm/arch/systemagent.h> +#include <dt-bindings/sound/nhlt.h> +#include <dm/acpi.h> + +enum { + PCIEXBAR = 0x60, + PCIEXBAR_LENGTH_256MB = 0, + PCIEXBAR_LENGTH_128MB, + PCIEXBAR_LENGTH_64MB, + + PCIEXBAR_PCIEXBAREN = 1 << 0, + + BGSM = 0xb4, /* Base GTT Stolen Memory */ + TSEG = 0xb8, /* TSEG base */ + TOLUD = 0xbc, +}; + +#if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) +static const struct nhlt_format_config dmic_1ch_formats[] = { + /* 48 KHz 16-bits per sample. */ + { + .num_channels = 1, + .sample_freq_khz = 48, + .container_bits_per_sample = 16, + .valid_bits_per_sample = 16, + .settings_file = "dmic-1ch-48khz-16b.dat", + }, +}; + +static const struct nhlt_dmic_array_config dmic_1ch_mic_config = { + .tdm_config = { + .config_type = NHLT_TDM_MIC_ARRAY, + }, + .array_type = NHLT_MIC_ARRAY_VENDOR_DEFINED, +}; + +static const struct nhlt_endp_descriptor dmic_1ch_descriptors[] = { + { + .link = NHLT_LINK_PDM, + .device = NHLT_PDM_DEV, + .direction = NHLT_DIR_CAPTURE, + .vid = NHLT_VID, + .did = NHLT_DID_DMIC, + .cfg = &dmic_1ch_mic_config, + .cfg_size = sizeof(dmic_1ch_mic_config), + .formats = dmic_1ch_formats, + .num_formats = ARRAY_SIZE(dmic_1ch_formats), + }, +}; + +static const struct nhlt_format_config dmic_2ch_formats[] = { + /* 48 KHz 16-bits per sample. */ + { + .num_channels = 2, + .sample_freq_khz = 48, + .container_bits_per_sample = 16, + .valid_bits_per_sample = 16, + .settings_file = "dmic-2ch-48khz-16b.dat", + }, +}; + +static const struct nhlt_dmic_array_config dmic_2ch_mic_config = { + .tdm_config = { + .config_type = NHLT_TDM_MIC_ARRAY, + }, + .array_type = NHLT_MIC_ARRAY_2CH_SMALL, +}; + +static const struct nhlt_endp_descriptor dmic_2ch_descriptors[] = { + { + .link = NHLT_LINK_PDM, + .device = NHLT_PDM_DEV, + .direction = NHLT_DIR_CAPTURE, + .vid = NHLT_VID, + .did = NHLT_DID_DMIC, + .cfg = &dmic_2ch_mic_config, + .cfg_size = sizeof(dmic_2ch_mic_config), + .formats = dmic_2ch_formats, + .num_formats = ARRAY_SIZE(dmic_2ch_formats), + }, +}; + +static const struct nhlt_format_config dmic_4ch_formats[] = { + /* 48 KHz 16-bits per sample. */ + { + .num_channels = 4, + .sample_freq_khz = 48, + .container_bits_per_sample = 16, + .valid_bits_per_sample = 16, + .settings_file = "dmic-4ch-48khz-16b.dat", + }, +}; + +static const struct nhlt_dmic_array_config dmic_4ch_mic_config = { + .tdm_config = { + .config_type = NHLT_TDM_MIC_ARRAY, + }, + .array_type = NHLT_MIC_ARRAY_4CH_L_SHAPED, +}; + +static const struct nhlt_endp_descriptor dmic_4ch_descriptors[] = { + { + .link = NHLT_LINK_PDM, + .device = NHLT_PDM_DEV, + .direction = NHLT_DIR_CAPTURE, + .vid = NHLT_VID, + .did = NHLT_DID_DMIC, + .cfg = &dmic_4ch_mic_config, + .cfg_size = sizeof(dmic_4ch_mic_config), + .formats = dmic_4ch_formats, + .num_formats = ARRAY_SIZE(dmic_4ch_formats), + }, +}; +#endif + +static int apl_hostbridge_early_init_pinctrl(struct udevice *dev) +{ + struct apl_hostbridge_plat *plat = dev_get_plat(dev); + struct udevice *pinctrl; + int ret; + + ret = uclass_first_device_err(UCLASS_PINCTRL, &pinctrl); + if (ret) + return log_msg_ret("no hostbridge pinctrl", ret); + + return pinctrl_config_pads(pinctrl, plat->early_pads, + plat->early_pads_count); +} + +static int apl_hostbridge_early_init(struct udevice *dev) +{ + struct apl_hostbridge_plat *plat = dev_get_plat(dev); + u32 region_size; + ulong base; + u32 reg; + int ret; + + /* Set up the MCHBAR */ + pci_x86_read_config(plat->bdf, MCHBAR, &base, PCI_SIZE_32); + base = MCH_BASE_ADDRESS; + pci_x86_write_config(plat->bdf, MCHBAR, base | 1, PCI_SIZE_32); + + /* + * The PCIEXBAR is assumed to live in the memory mapped IO space under + * 4GiB + */ + pci_x86_write_config(plat->bdf, PCIEXBAR + 4, 0, PCI_SIZE_32); + + switch (plat->pciex_region_size >> 20) { + default: + case 256: + region_size = PCIEXBAR_LENGTH_256MB; + break; + case 128: + region_size = PCIEXBAR_LENGTH_128MB; + break; + case 64: + region_size = PCIEXBAR_LENGTH_64MB; + break; + } + + reg = CONFIG_MMCONF_BASE_ADDRESS | (region_size << 1) + | PCIEXBAR_PCIEXBAREN; + pci_x86_write_config(plat->bdf, PCIEXBAR, reg, PCI_SIZE_32); + + /* + * TSEG defines the base of SMM range. BIOS determines the base + * of TSEG memory which must be at or below Graphics base of GTT + * Stolen memory, hence its better to clear TSEG register early + * to avoid power on default non-zero value (if any). + */ + pci_x86_write_config(plat->bdf, TSEG, 0, PCI_SIZE_32); + + ret = apl_hostbridge_early_init_pinctrl(dev); + if (ret) + return log_msg_ret("pinctrl", ret); + + return 0; +} + +static int apl_hostbridge_of_to_plat(struct udevice *dev) +{ + struct apl_hostbridge_plat *plat = dev_get_plat(dev); + struct udevice *pinctrl; + int ret; + + /* + * The host bridge holds the early pad data needed to get through TPL. + * This is a small amount of data, enough to fit in TPL, so we keep it + * separate from the full pad data, stored in the fsp-s subnode. That + * subnode is not present in TPL, to save space. + */ + ret = uclass_first_device_err(UCLASS_PINCTRL, &pinctrl); + if (ret) + return log_msg_ret("no hostbridge PINCTRL", ret); +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + int root; + + /* Get length of PCI Express Region */ + plat->pciex_region_size = dev_read_u32_default(dev, "pciex-region-size", + 256 << 20); + + root = pci_get_devfn(dev); + if (root < 0) + return log_msg_ret("Cannot get host-bridge PCI address", root); + plat->bdf = root; + + ret = pinctrl_read_pads(pinctrl, dev_ofnode(dev), "early-pads", + &plat->early_pads, &plat->early_pads_count); + if (ret) + return log_msg_ret("early-pads", ret); +#else + struct dtd_intel_apl_hostbridge *dtplat = &plat->dtplat; + int size; + + plat->pciex_region_size = dtplat->pciex_region_size; + plat->bdf = pci_ofplat_get_devfn(dtplat->reg[0]); + + /* Assume that if everything is 0, it is empty */ + plat->early_pads = dtplat->early_pads; + size = ARRAY_SIZE(dtplat->early_pads); + plat->early_pads_count = pinctrl_count_pads(pinctrl, plat->early_pads, + size); + +#endif + + return 0; +} + +static int apl_hostbridge_probe(struct udevice *dev) +{ + if (spl_phase() == PHASE_TPL) + return apl_hostbridge_early_init(dev); + + return 0; +} + +static int apl_acpi_hb_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "RHUB"); +} + +#if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) +static int apl_acpi_hb_write_tables(const struct udevice *dev, + struct acpi_ctx *ctx) +{ + struct acpi_table_header *header; + struct acpi_dmar *dmar; + u32 val; + + /* + * Create DMAR table only if virtualization is enabled. Due to some + * constraints on Apollo Lake SoC (some stepping affected), VTD could + * not be enabled together with IPU. Doing so will override and disable + * VTD while leaving CAPID0_A still reporting that VTD is available. + * As in this case FSP will lock VTD to disabled state, we need to make + * sure that DMAR table generation only happens when at least DEFVTBAR + * is enabled. Otherwise the DMAR header will be generated while the + * content of the table will be missing. + */ + dm_pci_read_config32(dev, CAPID0_A, &val); + if ((val & VTD_DISABLE) || + !(readl(MCHBAR_REG(DEFVTBAR)) & VTBAR_ENABLED)) + return 0; + + log_debug("ACPI: * DMAR\n"); + dmar = (struct acpi_dmar *)ctx->current; + header = &dmar->header; + acpi_create_dmar(dmar, DMAR_INTR_REMAP); + ctx->current += sizeof(struct acpi_dmar); + apl_acpi_fill_dmar(ctx); + + /* (Re)calculate length and checksum */ + header->length = ctx->current - (void *)dmar; + header->checksum = table_compute_checksum((void *)dmar, header->length); + + acpi_align(ctx); + acpi_add_table(ctx, dmar); + + return 0; +} + +static int apl_acpi_setup_nhlt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + struct nhlt *nhlt = ctx->nhlt; + u32 channels; + ofnode node; + + node = ofnode_find_subnode(dev_ofnode(dev), "nhlt"); + if (ofnode_read_u32(node, "intel,dmic-channels", &channels)) + return log_msg_ret("channels", -EINVAL); + switch (channels) { + case 1: + return nhlt_add_endpoints(nhlt, dmic_1ch_descriptors, + ARRAY_SIZE(dmic_1ch_descriptors)); + case 2: + return nhlt_add_endpoints(nhlt, dmic_2ch_descriptors, + ARRAY_SIZE(dmic_2ch_descriptors)); + case 4: + return nhlt_add_endpoints(nhlt, dmic_4ch_descriptors, + ARRAY_SIZE(dmic_4ch_descriptors)); + } + + return log_msg_ret("channels", -EINVAL); +} +#endif + +static int apl_hostbridge_remove(struct udevice *dev) +{ + /* + * TODO(sjg@chromium.org): Consider adding code from coreboot's + * platform_fsp_notify_status() + */ + + return 0; +} + +static ulong sa_read_reg(struct udevice *dev, int reg) +{ + u32 val; + + /* All regions concerned for have 1 MiB alignment */ + dm_pci_read_config32(dev, BGSM, &val); + + return ALIGN_DOWN(val, 1 << 20); +} + +ulong sa_get_tolud_base(struct udevice *dev) +{ + return sa_read_reg(dev, TOLUD); +} + +ulong sa_get_gsm_base(struct udevice *dev) +{ + return sa_read_reg(dev, BGSM); +} + +ulong sa_get_tseg_base(struct udevice *dev) +{ + return sa_read_reg(dev, TSEG); +} + +struct acpi_ops apl_hostbridge_acpi_ops = { + .get_name = apl_acpi_hb_get_name, +#if CONFIG_IS_ENABLED(GENERATE_ACPI_TABLE) + .write_tables = apl_acpi_hb_write_tables, + .setup_nhlt = apl_acpi_setup_nhlt, +#endif +}; + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id apl_hostbridge_ids[] = { + { .compatible = "intel,apl-hostbridge" }, + { } +}; +#endif + +U_BOOT_DRIVER(intel_apl_hostbridge) = { + .name = "intel_apl_hostbridge", + .id = UCLASS_NORTHBRIDGE, + .of_match = of_match_ptr(apl_hostbridge_ids), + .of_to_plat = apl_hostbridge_of_to_plat, + .probe = apl_hostbridge_probe, + .remove = apl_hostbridge_remove, + .plat_auto = sizeof(struct apl_hostbridge_plat), + ACPI_OPS_PTR(&apl_hostbridge_acpi_ops) + .flags = DM_FLAG_OS_PREPARE, +}; diff --git a/roms/u-boot/arch/x86/cpu/apollolake/lpc.c b/roms/u-boot/arch/x86/cpu/apollolake/lpc.c new file mode 100644 index 000000000..e085890d6 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/lpc.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + * + * From coreboot Apollo Lake support lpc.c + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <spl.h> +#include <acpi/acpi_table.h> +#include <asm/cpu_common.h> +#include <asm/intel_acpi.h> +#include <asm/lpc_common.h> +#include <asm/pci.h> +#include <asm/arch/iomap.h> +#include <asm/arch/lpc.h> +#include <dm/acpi.h> +#include <linux/log2.h> + +void lpc_enable_fixed_io_ranges(uint io_enables) +{ + pci_x86_clrset_config(PCH_DEV_LPC, LPC_IO_ENABLES, 0, io_enables, + PCI_SIZE_16); +} + +/* + * Find the first unused IO window. + * Returns -1 if not found, 0 for reg 0x84, 1 for reg 0x88 ... + */ +static int find_unused_pmio_window(void) +{ + int i; + ulong lgir; + + for (i = 0; i < LPC_NUM_GENERIC_IO_RANGES; i++) { + pci_x86_read_config(PCH_DEV_LPC, LPC_GENERIC_IO_RANGE(i), + &lgir, PCI_SIZE_32); + + if (!(lgir & LPC_LGIR_EN)) + return i; + } + + return -1; +} + +int lpc_open_pmio_window(uint base, uint size) +{ + int i, lgir_reg_num; + u32 lgir_reg_offset, lgir, window_size, alignment; + ulong bridged_size, bridge_base; + ulong reg; + + log_debug("LPC: Trying to open IO window from %x size %x\n", base, + size); + + bridged_size = 0; + bridge_base = base; + + while (bridged_size < size) { + /* Each IO range register can only open a 256-byte window */ + window_size = min(size, (uint)LPC_LGIR_MAX_WINDOW_SIZE); + + /* Window size must be a power of two for the AMASK to work */ + alignment = 1UL << (order_base_2(window_size)); + window_size = ALIGN(window_size, alignment); + + /* Address[15:2] in LGIR[15:12] and Mask[7:2] in LGIR[23:18] */ + lgir = (bridge_base & LPC_LGIR_ADDR_MASK) | LPC_LGIR_EN; + lgir |= ((window_size - 1) << 16) & LPC_LGIR_AMASK_MASK; + + /* Skip programming if same range already programmed */ + for (i = 0; i < LPC_NUM_GENERIC_IO_RANGES; i++) { + pci_x86_read_config(PCH_DEV_LPC, + LPC_GENERIC_IO_RANGE(i), ®, + PCI_SIZE_32); + if (lgir == reg) + return -EALREADY; + } + + lgir_reg_num = find_unused_pmio_window(); + if (lgir_reg_num < 0) { + if (spl_phase() > PHASE_TPL) { + log_err("LPC: Cannot open IO window: %lx size %lx\n", + bridge_base, size - bridged_size); + log_err("No more IO windows\n"); + } + return -ENOSPC; + } + lgir_reg_offset = LPC_GENERIC_IO_RANGE(lgir_reg_num); + + pci_x86_write_config(PCH_DEV_LPC, lgir_reg_offset, lgir, + PCI_SIZE_32); + + log_debug("LPC: Opened IO window LGIR%d: base %lx size %x\n", + lgir_reg_num, bridge_base, window_size); + + bridged_size += window_size; + bridge_base += window_size; + } + + return 0; +} + +void lpc_io_setup_comm_a_b(void) +{ + /* ComA Range 3F8h-3FFh [2:0] */ + u16 com_ranges = LPC_IOD_COMA_RANGE; + u16 com_enable = LPC_IOE_COMA_EN; + + /* Setup I/O Decode Range Register for LPC */ + pci_write_config16(PCH_DEV_LPC, LPC_IO_DECODE, com_ranges); + /* Enable ComA and ComB Port */ + lpc_enable_fixed_io_ranges(com_enable); +} + +static int apl_acpi_lpc_get_name(const struct udevice *dev, char *out_name) +{ + return acpi_copy_name(out_name, "LPCB"); +} + +struct acpi_ops apl_lpc_acpi_ops = { + .get_name = apl_acpi_lpc_get_name, +#ifdef CONFIG_GENERATE_ACPI_TABLE + .write_tables = intel_southbridge_write_acpi_tables, +#endif + .inject_dsdt = southbridge_inject_dsdt, +}; + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id apl_lpc_ids[] = { + { .compatible = "intel,apl-lpc" }, + { } +}; +#endif + +/* All pads are LPC already configured by the hostbridge, so no probing here */ +U_BOOT_DRIVER(intel_apl_lpc) = { + .name = "intel_apl_lpc", + .id = UCLASS_LPC, + .of_match = of_match_ptr(apl_lpc_ids), + ACPI_OPS_PTR(&apl_lpc_acpi_ops) +}; diff --git a/roms/u-boot/arch/x86/cpu/apollolake/pch.c b/roms/u-boot/arch/x86/cpu/apollolake/pch.c new file mode 100644 index 000000000..39d6ad5ed --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/pch.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <dm.h> +#include <pch.h> +#include <spl.h> +#include <asm/lpc_common.h> + +#define BIOS_CTRL 0xdc + +static int apl_set_spi_protect(struct udevice *dev, bool protect) +{ + if (spl_phase() == PHASE_SPL) + return lpc_set_spi_protect(dev, BIOS_CTRL, protect); + + return 0; +} + +static const struct pch_ops apl_pch_ops = { + .set_spi_protect = apl_set_spi_protect, +}; + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id apl_pch_ids[] = { + { .compatible = "intel,apl-pch" }, + { } +}; +#endif + +U_BOOT_DRIVER(intel_apl_pch) = { + .name = "intel_apl_pch", + .id = UCLASS_PCH, + .of_match = of_match_ptr(apl_pch_ids), + .ops = &apl_pch_ops, +}; diff --git a/roms/u-boot/arch/x86/cpu/apollolake/pmc.c b/roms/u-boot/arch/x86/cpu/apollolake/pmc.c new file mode 100644 index 000000000..1d21187c9 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/pmc.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Intel Corporation. + * Copyright 2019 Google LLC + * + * Modified from coreboot pmclib.c, pmc.c and pmutil.c + */ + +#define LOG_CATEGORY UCLASS_ACPI_PMC + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> +#include <log.h> +#include <spl.h> +#include <acpi/acpi_s3.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <asm/arch/pmc.h> +#include <linux/bitops.h> +#include <power/acpi_pmc.h> + +#define GPIO_GPE_CFG 0x1050 + +/* Memory mapped IO registers behind PMC_BASE_ADDRESS */ +#define PRSTS 0x1000 +#define GEN_PMCON1 0x1020 +#define COLD_BOOT_STS BIT(27) +#define COLD_RESET_STS BIT(26) +#define WARM_RESET_STS BIT(25) +#define GLOBAL_RESET_STS BIT(24) +#define SRS BIT(20) +#define MS4V BIT(18) +#define RPS BIT(2) +#define GEN_PMCON1_CLR1_BITS (COLD_BOOT_STS | COLD_RESET_STS | \ + WARM_RESET_STS | GLOBAL_RESET_STS | \ + SRS | MS4V) +#define GEN_PMCON2 0x1024 +#define GEN_PMCON3 0x1028 + +/* Offset of TCO registers from ACPI base I/O address */ +#define TCO_REG_OFFSET 0x60 +#define TCO1_STS 0x64 +#define DMISCI_STS BIT(9) +#define BOOT_STS BIT(18) +#define TCO2_STS 0x66 +#define TCO1_CNT 0x68 +#define TCO_LOCK BIT(12) +#define TCO2_CNT 0x6a + +enum { + ETR = 0x1048, + CF9_LOCK = 1UL << 31, + CF9_GLB_RST = 1 << 20, +}; + +static int apl_pmc_fill_power_state(struct udevice *dev) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + upriv->tco1_sts = inw(upriv->acpi_base + TCO1_STS); + upriv->tco2_sts = inw(upriv->acpi_base + TCO2_STS); + + upriv->prsts = readl(upriv->pmc_bar0 + PRSTS); + upriv->gen_pmcon1 = readl(upriv->pmc_bar0 + GEN_PMCON1); + upriv->gen_pmcon2 = readl(upriv->pmc_bar0 + GEN_PMCON2); + upriv->gen_pmcon3 = readl(upriv->pmc_bar0 + GEN_PMCON3); + + return 0; +} + +static int apl_prev_sleep_state(struct udevice *dev, int prev_sleep_state) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + /* WAK_STS bit will not be set when waking from G3 state */ + if (!(upriv->pm1_sts & WAK_STS) && + (upriv->gen_pmcon1 & COLD_BOOT_STS)) + prev_sleep_state = ACPI_S5; + + return prev_sleep_state; +} + +static int apl_disable_tco(struct udevice *dev) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + pmc_disable_tco_base(upriv->acpi_base + TCO_REG_OFFSET); + + return 0; +} + +static int apl_global_reset_set_enable(struct udevice *dev, bool enable) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + if (enable) + setbits_le32(upriv->pmc_bar0 + ETR, CF9_GLB_RST); + else + clrbits_le32(upriv->pmc_bar0 + ETR, CF9_GLB_RST); + + return 0; +} + +int apl_pmc_ofdata_to_uc_plat(struct udevice *dev) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + struct apl_pmc_plat *plat = dev_get_plat(dev); + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + u32 base[6]; + int size; + int ret; + + ret = dev_read_u32_array(dev, "early-regs", base, + ARRAY_SIZE(base)); + if (ret) + return log_msg_ret("Missing/short early-regs", ret); + if (spl_phase() == PHASE_TPL) { + upriv->pmc_bar0 = (void *)base[0]; + upriv->pmc_bar2 = (void *)base[2]; + + /* Since PCI is not enabled, we must get the BDF manually */ + plat->bdf = pci_get_devfn(dev); + if (plat->bdf < 0) + return log_msg_ret("Cannot get PMC PCI address", + plat->bdf); + } + upriv->acpi_base = base[4]; + + /* Get the dwX values for pmc gpe settings */ + size = dev_read_size(dev, "gpe0-dw"); + if (size < 0) + return log_msg_ret("Cannot read gpe0-dm", size); + upriv->gpe0_count = size / sizeof(u32); + ret = dev_read_u32_array(dev, "gpe0-dw", upriv->gpe0_dw, + upriv->gpe0_count); + if (ret) + return log_msg_ret("Bad gpe0-dw", ret); + + return pmc_ofdata_to_uc_plat(dev); +#else + struct dtd_intel_apl_pmc *dtplat = &plat->dtplat; + + plat->bdf = pci_ofplat_get_devfn(dtplat->reg[0]); + upriv->pmc_bar0 = (void *)dtplat->early_regs[0]; + upriv->pmc_bar2 = (void *)dtplat->early_regs[2]; + upriv->acpi_base = dtplat->early_regs[4]; + upriv->gpe0_dwx_mask = dtplat->gpe0_dwx_mask; + upriv->gpe0_dwx_shift_base = dtplat->gpe0_dwx_shift_base; + upriv->gpe0_sts_reg = dtplat->gpe0_sts; + upriv->gpe0_sts_reg += upriv->acpi_base; + upriv->gpe0_en_reg = dtplat->gpe0_en; + upriv->gpe0_en_reg += upriv->acpi_base; + upriv->gpe0_count = min((int)ARRAY_SIZE(dtplat->gpe0_dw), GPE0_REG_MAX); + memcpy(upriv->gpe0_dw, dtplat->gpe0_dw, sizeof(dtplat->gpe0_dw)); +#endif + upriv->gpe_cfg = (u32 *)(upriv->pmc_bar0 + GPIO_GPE_CFG); + + return 0; +} + +static int enable_pmcbar(struct udevice *dev) +{ + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + struct apl_pmc_plat *priv = dev_get_plat(dev); + pci_dev_t pmc = priv->bdf; + + /* + * Set PMC base addresses and enable decoding. BARs 1 and 3 are 64-bit + * BARs. + */ + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_0, (ulong)upriv->pmc_bar0, + PCI_SIZE_32); + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_1, 0, PCI_SIZE_32); + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_2, (ulong)upriv->pmc_bar2, + PCI_SIZE_32); + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_3, 0, PCI_SIZE_32); + pci_x86_write_config(pmc, PCI_BASE_ADDRESS_4, upriv->acpi_base, + PCI_SIZE_16); + pci_x86_write_config(pmc, PCI_COMMAND, PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER, + PCI_SIZE_16); + + return 0; +} + +static int apl_pmc_probe(struct udevice *dev) +{ + if (spl_phase() == PHASE_TPL) { + return enable_pmcbar(dev); + } else { + struct acpi_pmc_upriv *upriv = dev_get_uclass_priv(dev); + + upriv->pmc_bar0 = (void *)dm_pci_read_bar32(dev, 0); + upriv->pmc_bar2 = (void *)dm_pci_read_bar32(dev, 2); + } + + return 0; +} + +static const struct acpi_pmc_ops apl_pmc_ops = { + .init = apl_pmc_fill_power_state, + .prev_sleep_state = apl_prev_sleep_state, + .disable_tco = apl_disable_tco, + .global_reset_set_enable = apl_global_reset_set_enable, +}; + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id apl_pmc_ids[] = { + { .compatible = "intel,apl-pmc" }, + { } +}; +#endif + +U_BOOT_DRIVER(intel_apl_pmc) = { + .name = "intel_apl_pmc", + .id = UCLASS_ACPI_PMC, + .of_match = of_match_ptr(apl_pmc_ids), + .of_to_plat = apl_pmc_ofdata_to_uc_plat, + .probe = apl_pmc_probe, + .ops = &apl_pmc_ops, + .plat_auto = sizeof(struct apl_pmc_plat), +}; diff --git a/roms/u-boot/arch/x86/cpu/apollolake/punit.c b/roms/u-boot/arch/x86/cpu/apollolake/punit.c new file mode 100644 index 000000000..5ed796357 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/punit.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <spl.h> +#include <asm/cpu.h> +#include <asm/cpu_common.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <asm/arch/systemagent.h> +#include <linux/delay.h> + +/* + * Punit Initialisation code. This all isn't documented, but + * this is the recipe. + */ +static int punit_init(struct udevice *dev) +{ + struct udevice *cpu; + u32 reg; + ulong start; + int ret; + + /* Thermal throttle activation offset */ + ret = uclass_first_device_err(UCLASS_CPU, &cpu); + if (ret) + return log_msg_ret("Cannot find CPU", ret); + cpu_configure_thermal_target(cpu); + + /* + * Software Core Disable Mask (P_CR_CORE_DISABLE_MASK_0_0_0_MCHBAR). + * Enable all cores here. + */ + writel(0, MCHBAR_REG(CORE_DISABLE_MASK)); + + /* P-Unit bring up */ + reg = readl(MCHBAR_REG(BIOS_RESET_CPL)); + if (reg == 0xffffffff) { + /* P-unit not found */ + debug("Punit MMIO not available\n"); + return -ENOENT; + } + + /* Set Punit interrupt pin IPIN offset 3D */ + dm_pci_write_config8(dev, PCI_INTERRUPT_PIN, 0x2); + + /* Set PUINT IRQ to 24 and INTPIN LOCK */ + writel(PUINT_THERMAL_DEVICE_IRQ_VEC_NUMBER | + PUINT_THERMAL_DEVICE_IRQ_LOCK, + MCHBAR_REG(PUNIT_THERMAL_DEVICE_IRQ)); + + /* Stage0 BIOS Reset Complete (RST_CPL) */ + enable_bios_reset_cpl(); + + /* + * Poll for bit 8 to check if PCODE has completed its action in response + * to BIOS Reset complete. We wait here till 1 ms for the bit to get + * set. + */ + start = get_timer(0); + while (!(readl(MCHBAR_REG(BIOS_RESET_CPL)) & PCODE_INIT_DONE)) { + if (get_timer(start) > 1) { + debug("PCODE Init Done timeout\n"); + return -ETIMEDOUT; + } + udelay(100); + } + debug("PUNIT init complete\n"); + + return 0; +} + +static int apl_punit_probe(struct udevice *dev) +{ + if (spl_phase() == PHASE_SPL) + return punit_init(dev); + + return 0; +} + +static const struct udevice_id apl_syscon_ids[] = { + { .compatible = "intel,apl-punit", .data = X86_SYSCON_PUNIT }, + { } +}; + +U_BOOT_DRIVER(intel_apl_punit) = { + .name = "intel_apl_punit", + .id = UCLASS_SYSCON, + .of_match = apl_syscon_ids, + .probe = apl_punit_probe, + DM_HEADER(<asm/cpu.h>) /* for X86_SYSCON_PUNIT */ +}; diff --git a/roms/u-boot/arch/x86/cpu/apollolake/spl.c b/roms/u-boot/arch/x86/cpu/apollolake/spl.c new file mode 100644 index 000000000..f2d25734c --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/spl.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#define LOG_CATEGORY LOGC_BOOT + +#include <common.h> +#include <binman_sym.h> +#include <bootstage.h> +#include <dm.h> +#include <image.h> +#include <log.h> +#include <malloc.h> +#include <spi.h> +#include <spl.h> +#include <spi_flash.h> +#include <asm/fast_spi.h> +#include <asm/spl.h> +#include <asm/arch/cpu.h> +#include <asm/arch/iomap.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> + +/* This reads the next phase from mapped SPI flash */ +static int rom_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + ulong spl_pos = spl_get_image_pos(); + ulong spl_size = spl_get_image_size(); + struct udevice *dev; + ulong map_base; + size_t map_size; + uint offset; + int ret; + + spl_image->size = CONFIG_SYS_MONITOR_LEN; /* We don't know SPL size */ + spl_image->entry_point = spl_get_image_text_base(); + spl_image->load_addr = spl_image->entry_point; + spl_image->os = IH_OS_U_BOOT; + spl_image->name = "U-Boot"; + log_debug("Reading from mapped SPI %lx, size %lx\n", spl_pos, spl_size); + + if (CONFIG_IS_ENABLED(SPI_FLASH_SUPPORT)) { + ret = uclass_find_first_device(UCLASS_SPI_FLASH, &dev); + if (ret) + return log_msg_ret("spi_flash", ret); + if (!dev) + return log_msg_ret("spi_flash dev", -ENODEV); + ret = dm_spi_get_mmap(dev, &map_base, &map_size, &offset); + if (ret) + return log_msg_ret("mmap", ret); + } else { + ret = fast_spi_get_bios_mmap(PCH_DEV_SPI, &map_base, &map_size, + &offset); + if (ret) + return ret; + } + spl_pos += map_base & ~0xff000000; + log_debug(", base %lx, pos %lx, load %lx\n", map_base, spl_pos, + spl_image->load_addr); + bootstage_start(BOOTSTAGE_ID_ACCUM_MMAP_SPI, "mmap_spi"); + memcpy((void *)spl_image->load_addr, (void *)spl_pos, spl_size); + cpu_flush_l1d_to_l2(); + bootstage_accum(BOOTSTAGE_ID_ACCUM_MMAP_SPI); + + return 0; +} +SPL_LOAD_IMAGE_METHOD("Mapped SPI", 2, BOOT_DEVICE_SPI_MMAP, rom_load_image); + +#if CONFIG_IS_ENABLED(SPI_FLASH_SUPPORT) + +static int apl_flash_std_read(struct udevice *dev, u32 offset, size_t len, + void *buf) +{ + struct spi_flash *flash = dev_get_uclass_priv(dev); + struct mtd_info *mtd = &flash->mtd; + size_t retlen; + + return log_ret(mtd->_read(mtd, offset, len, &retlen, buf)); +} + +static int apl_flash_probe(struct udevice *dev) +{ + return spi_flash_std_probe(dev); +} + +static const struct dm_spi_flash_ops apl_flash_ops = { + .read = apl_flash_std_read, +}; + +static const struct udevice_id apl_flash_ids[] = { + { .compatible = "jedec,spi-nor" }, + { } +}; + +U_BOOT_DRIVER(winbond_w25q128fw) = { + .name = "winbond_w25q128fw", + .id = UCLASS_SPI_FLASH, + .of_match = apl_flash_ids, + .probe = apl_flash_probe, + .priv_auto = sizeof(struct spi_nor), + .ops = &apl_flash_ops, +}; + +/* This uses a SPI flash device to read the next phase */ +static int spl_fast_spi_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + ulong spl_pos = spl_get_image_pos(); + ulong spl_size = spl_get_image_size(); + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_SPI_FLASH, &dev); + if (ret) + return ret; + + spl_image->size = CONFIG_SYS_MONITOR_LEN; /* We don't know SPL size */ + spl_image->entry_point = spl_phase() == PHASE_TPL ? + CONFIG_SPL_TEXT_BASE : CONFIG_SYS_TEXT_BASE; + spl_image->load_addr = spl_image->entry_point; + spl_image->os = IH_OS_U_BOOT; + spl_image->name = "U-Boot"; + spl_pos &= ~0xff000000; + log_debug("Reading from flash %lx, size %lx\n", spl_pos, spl_size); + ret = spi_flash_read_dm(dev, spl_pos, spl_size, + (void *)spl_image->load_addr); + cpu_flush_l1d_to_l2(); + if (ret) + return ret; + + return 0; +} +SPL_LOAD_IMAGE_METHOD("Fast SPI", 1, BOOT_DEVICE_FAST_SPI, + spl_fast_spi_load_image); + +void board_boot_order(u32 *spl_boot_list) +{ + bool use_spi_flash = IS_ENABLED(CONFIG_APL_BOOT_FROM_FAST_SPI_FLASH); + + if (use_spi_flash) { + spl_boot_list[0] = BOOT_DEVICE_FAST_SPI; + spl_boot_list[1] = BOOT_DEVICE_SPI_MMAP; + } else { + spl_boot_list[0] = BOOT_DEVICE_SPI_MMAP; + spl_boot_list[1] = BOOT_DEVICE_FAST_SPI; + } +} + +#else + +void board_boot_order(u32 *spl_boot_list) +{ + spl_boot_list[0] = BOOT_DEVICE_SPI_MMAP; +} +#endif diff --git a/roms/u-boot/arch/x86/cpu/apollolake/systemagent.c b/roms/u-boot/arch/x86/cpu/apollolake/systemagent.c new file mode 100644 index 000000000..b6bc2ba14 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/systemagent.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Intel Corporation. + * Take from coreboot project file of the same name + */ + +#include <common.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/arch/systemagent.h> + +void enable_bios_reset_cpl(void) +{ + /* + * Set bits 0+1 of BIOS_RESET_CPL to indicate to the CPU + * that BIOS has initialised memory and power management + * + * The FSP-S does not do this. If we leave this as zero then I believe + * the power-aware interrupts don't work in Linux, and CPU 0 always gets + * the interrupt. + */ + setbits_8(MCHBAR_REG(BIOS_RESET_CPL), 3); +} diff --git a/roms/u-boot/arch/x86/cpu/apollolake/uart.c b/roms/u-boot/arch/x86/cpu/apollolake/uart.c new file mode 100644 index 000000000..876fa592b --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/apollolake/uart.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Special driver to handle of-platdata + * + * Copyright 2019 Google LLC + * + * Some code from coreboot lpss.c + */ + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> +#include <malloc.h> +#include <ns16550.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <asm/lpss.h> +#include <dm/device-internal.h> +#include <asm/arch/uart.h> + +/* Low-power Subsystem (LPSS) clock register */ +enum { + LPSS_CLOCK_CTL_REG = 0x200, + LPSS_CNT_CLOCK_EN = 1, + LPSS_CNT_CLK_UPDATE = 1U << 31, + LPSS_CLOCK_DIV_N_SHIFT = 16, + LPSS_CLOCK_DIV_N_MASK = 0x7fff << LPSS_CLOCK_DIV_N_SHIFT, + LPSS_CLOCK_DIV_M_SHIFT = 1, + LPSS_CLOCK_DIV_M_MASK = 0x7fff << LPSS_CLOCK_DIV_M_SHIFT, + + /* These set the UART input clock speed */ + LPSS_UART_CLK_M_VAL = 0x25a, + LPSS_UART_CLK_N_VAL = 0x7fff, +}; + +static void lpss_clk_update(void *regs, u32 clk_m_val, u32 clk_n_val) +{ + u32 clk_sel; + + clk_sel = clk_n_val << LPSS_CLOCK_DIV_N_SHIFT | + clk_m_val << LPSS_CLOCK_DIV_M_SHIFT; + clk_sel |= LPSS_CNT_CLK_UPDATE | LPSS_CNT_CLOCK_EN; + + writel(clk_sel, regs + LPSS_CLOCK_CTL_REG); +} + +static void uart_lpss_init(void *regs) +{ + /* Take UART out of reset */ + lpss_reset_release(regs); + + /* Set M and N divisor inputs and enable clock */ + lpss_clk_update(regs, LPSS_UART_CLK_M_VAL, LPSS_UART_CLK_N_VAL); +} + +void apl_uart_init(pci_dev_t bdf, ulong base) +{ + /* Set UART base address */ + pci_x86_write_config(bdf, PCI_BASE_ADDRESS_0, base, PCI_SIZE_32); + + /* Enable memory access and bus master */ + pci_x86_write_config(bdf, PCI_COMMAND, PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER, PCI_SIZE_32); + + uart_lpss_init((void *)base); +} + +/* + * This driver uses its own compatible string but almost everything else from + * the standard ns16550 driver. This allows us to provide an of-platdata + * implementation, since the platdata produced by of-platdata does not match + * struct apl_ns16550_plat. + * + * When running with of-platdata (generally TPL), the platdata is converted to + * something that ns16550 expects. When running withoutof-platdata (SPL, U-Boot + * proper), we use ns16550's of_to_plat routine. + */ + +static int apl_ns16550_probe(struct udevice *dev) +{ + struct apl_ns16550_plat *plat = dev_get_plat(dev); + + if (!CONFIG_IS_ENABLED(PCI)) + apl_uart_init(plat->ns16550.bdf, plat->ns16550.base); + + return ns16550_serial_probe(dev); +} + +static int apl_ns16550_of_to_plat(struct udevice *dev) +{ +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct dtd_intel_apl_ns16550 *dtplat; + struct apl_ns16550_plat *plat = dev_get_plat(dev); + struct ns16550_plat ns; + + /* + * The device's plat uses struct apl_ns16550_plat which starts with the + * dtd struct, but the ns16550 driver expects it to be struct ns16550. + * Set up what that driver expects. Note that this means that the values + * cannot be read in this driver when using of-platdata. + * + * TODO(sjg@chromium.org): Consider having a separate plat pointer for + * of-platdata so that it is not necessary to overwrite this. + */ + dtplat = &plat->dtplat; + ns.base = dtplat->early_regs[0]; + ns.reg_width = 1; + ns.reg_shift = dtplat->reg_shift; + ns.reg_offset = 0; + ns.clock = dtplat->clock_frequency; + ns.fcr = UART_FCR_DEFVAL; + ns.bdf = pci_ofplat_get_devfn(dtplat->reg[0]); + memcpy(plat, &ns, sizeof(ns)); +#else + int ret; + + ret = ns16550_serial_of_to_plat(dev); + if (ret) + return ret; +#endif /* OF_PLATDATA */ + + return 0; +} + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id apl_ns16550_serial_ids[] = { + { .compatible = "intel,apl-ns16550" }, + { }, +}; +#endif + +U_BOOT_DRIVER(intel_apl_ns16550) = { + .name = "intel_apl_ns16550", + .id = UCLASS_SERIAL, + .of_match = of_match_ptr(apl_ns16550_serial_ids), + .plat_auto = sizeof(struct apl_ns16550_plat), + .priv_auto = sizeof(struct ns16550), + .ops = &ns16550_serial_ops, + .of_to_plat = apl_ns16550_of_to_plat, + .probe = apl_ns16550_probe, +}; diff --git a/roms/u-boot/arch/x86/cpu/baytrail/Kconfig b/roms/u-boot/arch/x86/cpu/baytrail/Kconfig new file mode 100644 index 000000000..d2c3473d6 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/baytrail/Kconfig @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/baytrail/Makefile b/roms/u-boot/arch/x86/cpu/baytrail/Makefile new file mode 100644 index 000000000..ce5d6178c --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/baytrail/Makefile @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/baytrail/acpi.c b/roms/u-boot/arch/x86/cpu/baytrail/acpi.c new file mode 100644 index 000000000..07757b88a --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/baytrail/acpi.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <cpu.h> +#include <dm.h> +#include <log.h> +#include <acpi/acpi_s3.h> +#include <acpi/acpi_table.h> +#include <asm/io.h> +#include <asm/tables.h> +#include <asm/arch/global_nvs.h> +#include <asm/arch/iomap.h> +#include <dm/uclass-internal.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); +} + +int 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; + + return 0; +} + +/* + * 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); +} diff --git a/roms/u-boot/arch/x86/cpu/baytrail/cpu.c b/roms/u-boot/arch/x86/cpu/baytrail/cpu.c new file mode 100644 index 000000000..309a50a11 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/baytrail/cpu.c @@ -0,0 +1,209 @@ +// 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 <init.h> +#include <log.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(const 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(const 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, +}; diff --git a/roms/u-boot/arch/x86/cpu/baytrail/early_uart.c b/roms/u-boot/arch/x86/cpu/baytrail/early_uart.c new file mode 100644 index 000000000..08dbd5538 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/baytrail/early_uart.c @@ -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); +} diff --git a/roms/u-boot/arch/x86/cpu/baytrail/fsp_configs.c b/roms/u-boot/arch/x86/cpu/baytrail/fsp_configs.c new file mode 100644 index 000000000..fb3f946c4 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/baytrail/fsp_configs.c @@ -0,0 +1,171 @@ +// 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 <log.h> +#include <asm/fsp1/fsp_support.h> +#include <asm/global_data.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); + } + } +} diff --git a/roms/u-boot/arch/x86/cpu/baytrail/valleyview.c b/roms/u-boot/arch/x86/cpu/baytrail/valleyview.c new file mode 100644 index 000000000..f73738ce5 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/baytrail/valleyview.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <init.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> +#include <linux/bitops.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; +} diff --git a/roms/u-boot/arch/x86/cpu/braswell/Kconfig b/roms/u-boot/arch/x86/cpu/braswell/Kconfig new file mode 100644 index 000000000..2676fe6e1 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/braswell/Kconfig @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/braswell/Makefile b/roms/u-boot/arch/x86/cpu/braswell/Makefile new file mode 100644 index 000000000..277f81e6d --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/braswell/Makefile @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/braswell/braswell.c b/roms/u-boot/arch/x86/cpu/braswell/braswell.c new file mode 100644 index 000000000..334504999 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/braswell/braswell.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <init.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; +} diff --git a/roms/u-boot/arch/x86/cpu/braswell/early_uart.c b/roms/u-boot/arch/x86/cpu/braswell/early_uart.c new file mode 100644 index 000000000..d78c6b0fe --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/braswell/early_uart.c @@ -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); +} diff --git a/roms/u-boot/arch/x86/cpu/braswell/fsp_configs.c b/roms/u-boot/arch/x86/cpu/braswell/fsp_configs.c new file mode 100644 index 000000000..243298fd5 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/braswell/fsp_configs.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <fdtdec.h> +#include <log.h> +#include <asm/fsp1/fsp_support.h> +#include <asm/global_data.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"); +} diff --git a/roms/u-boot/arch/x86/cpu/broadwell/Kconfig b/roms/u-boot/arch/x86/cpu/broadwell/Kconfig new file mode 100644 index 000000000..5b015c89d --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/Kconfig @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/broadwell/Makefile b/roms/u-boot/arch/x86/cpu/broadwell/Makefile new file mode 100644 index 000000000..52d56c65b --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/Makefile @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/broadwell/adsp.c b/roms/u-boot/arch/x86/cpu/broadwell/adsp.c new file mode 100644 index 000000000..1fa182378 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/adsp.c @@ -0,0 +1,157 @@ +// 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 <log.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_of_to_plat(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, + .of_to_plat = broadwell_adsp_of_to_plat, + .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); diff --git a/roms/u-boot/arch/x86/cpu/broadwell/cpu.c b/roms/u-boot/arch/x86/cpu/broadwell/cpu.c new file mode 100644 index 000000000..3832a97f2 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/cpu.c @@ -0,0 +1,110 @@ +// 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 <init.h> +#include <log.h> +#include <asm/cpu.h> +#include <asm/cpu_x86.h> +#include <asm/cpu_common.h> +#include <asm/global_data.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); +} diff --git a/roms/u-boot/arch/x86/cpu/broadwell/cpu_from_spl.c b/roms/u-boot/arch/x86/cpu/broadwell/cpu_from_spl.c new file mode 100644 index 000000000..e5f62e718 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/cpu_from_spl.c @@ -0,0 +1,65 @@ +// 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 <init.h> +#include <log.h> +#include <asm/global_data.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) +{ + 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); + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/broadwell/cpu_full.c b/roms/u-boot/arch/x86/cpu/broadwell/cpu_full.c new file mode 100644 index 000000000..2049dbfe2 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/cpu_full.c @@ -0,0 +1,671 @@ +// 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 <init.h> +#include <log.h> +#include <asm/cpu.h> +#include <asm/cpu_x86.h> +#include <asm/cpu_common.h> +#include <asm/global_data.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> +#include <linux/delay.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(const struct udevice *dev, struct cpu_info *info) +{ + return cpu_intel_get_info(info, INTEL_BCLK_MHZ); +} + +static int broadwell_get_count(const struct udevice *dev) +{ + return 4; +} + +static int cpu_x86_broadwell_probe(struct udevice *dev) +{ + if (dev_seq(dev) == 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 = sizeof(struct cpu_broadwell_priv), + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/roms/u-boot/arch/x86/cpu/broadwell/iobp.c b/roms/u-boot/arch/x86/cpu/broadwell/iobp.c new file mode 100644 index 000000000..cb5595c93 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/iobp.c @@ -0,0 +1,144 @@ +// 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> +#include <linux/delay.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; +} diff --git a/roms/u-boot/arch/x86/cpu/broadwell/lpc.c b/roms/u-boot/arch/x86/cpu/broadwell/lpc.c new file mode 100644 index 000000000..d2638a4e7 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/lpc.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 Google, Inc + * + * From coreboot broadwell support + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <pch.h> +#include <asm/global_data.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, +}; diff --git a/roms/u-boot/arch/x86/cpu/broadwell/me.c b/roms/u-boot/arch/x86/cpu/broadwell/me.c new file mode 100644 index 000000000..ae16ce264 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/me.c @@ -0,0 +1,58 @@ +// 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 <log.h> +#include <asm/arch/me.h> +#include <linux/delay.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; +} diff --git a/roms/u-boot/arch/x86/cpu/broadwell/northbridge.c b/roms/u-boot/arch/x86/cpu/broadwell/northbridge.c new file mode 100644 index 000000000..141babc51 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/northbridge.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2011 The Chromium Authors + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.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, +}; diff --git a/roms/u-boot/arch/x86/cpu/broadwell/pch.c b/roms/u-boot/arch/x86/cpu/broadwell/pch.c new file mode 100644 index 000000000..f012db9de --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/pch.c @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <pch.h> +#include <asm/cpu.h> +#include <asm/global_data.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> +#include <linux/delay.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, ®16); + 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, ®16); + 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, +}; diff --git a/roms/u-boot/arch/x86/cpu/broadwell/pinctrl_broadwell.c b/roms/u-boot/arch/x86/cpu/broadwell/pinctrl_broadwell.c new file mode 100644 index 000000000..85bd37101 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/pinctrl_broadwell.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <pch.h> +#include <pci.h> +#include <asm/cpu.h> +#include <asm/global_data.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, ®s->config[gpio].conf_a); + outl(pin->sense_disable << CONFB_SENSE_SHIFT, + ®s->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], ®s->own[set]); + outl(route_smi[set], ®s->gpi_route[set]); + outl(irq_enable[set], ®s->gpi_ie[set]); + outl(reset_rsmrst[set], ®s->rst_sel[set]); + } + + outl(pirq2apic, ®s->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, +}; diff --git a/roms/u-boot/arch/x86/cpu/broadwell/power_state.c b/roms/u-boot/arch/x86/cpu/broadwell/power_state.c new file mode 100644 index 000000000..62fd2e8d2 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/power_state.c @@ -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 <log.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) { + case SLP_TYP_S3: + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) + prev_sleep_state = SLEEP_STATE_S3; + break; + 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); +} diff --git a/roms/u-boot/arch/x86/cpu/broadwell/refcode.c b/roms/u-boot/arch/x86/cpu/broadwell/refcode.c new file mode 100644 index 000000000..6c04dde99 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/refcode.c @@ -0,0 +1,129 @@ +// 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 <init.h> +#include <log.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(); +} diff --git a/roms/u-boot/arch/x86/cpu/broadwell/sata.c b/roms/u-boot/arch/x86/cpu/broadwell/sata.c new file mode 100644 index 000000000..be3c9e764 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/sata.c @@ -0,0 +1,271 @@ +// 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 <log.h> +#include <asm/global_data.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> +#include <linux/delay.h> + +struct sata_plat { + 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_plat *plat = dev_get_plat(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, ®16); + 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, ®32); + 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, ®32); + 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, ®32); + 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, ®32); + reg32 |= 1 << 29; + dm_pci_write_config32(dev, 0x98, reg32); + + /* Register Lock */ + dm_pci_read_config32(dev, 0x9c, ®32); + reg32 |= 1 << 31; + dm_pci_write_config32(dev, 0x9c, reg32); +} + +static int broadwell_sata_enable(struct udevice *dev) +{ + struct sata_plat *plat = dev_get_plat(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_of_to_plat(struct udevice *dev) +{ + struct sata_plat *plat = dev_get_plat(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, + .of_to_plat = broadwell_sata_of_to_plat, + .probe = broadwell_sata_probe, + .plat_auto = sizeof(struct sata_plat), +}; diff --git a/roms/u-boot/arch/x86/cpu/broadwell/sdram.c b/roms/u-boot/arch/x86/cpu/broadwell/sdram.c new file mode 100644 index 000000000..c104a849a --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/broadwell/sdram.c @@ -0,0 +1,211 @@ +// 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 <log.h> +#include <pci.h> +#include <syscon.h> +#include <asm/cpu.h> +#include <asm/global_data.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(MRC_TYPE_NORMAL, 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) { + struct mrc_output *mrc = &gd->arch.mrc[MRC_TYPE_NORMAL]; + + /* + * This will be copied to SDRAM in reserve_arch(), then written + * to SPI flash in mrccache_save() + */ + mrc->buf = (char *)pei_data->data_to_save; + mrc->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, +}; diff --git a/roms/u-boot/arch/x86/cpu/call32.S b/roms/u-boot/arch/x86/cpu/call32.S new file mode 100644 index 000000000..b28d58a31 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/call32.S @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#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 + retfq +.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 */ + retfl diff --git a/roms/u-boot/arch/x86/cpu/config.mk b/roms/u-boot/arch/x86/cpu/config.mk new file mode 100644 index 000000000..d3033b416 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/config.mk @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2002 +# Daniel Engström, Omicron Ceti AB, daniel@omicron.se. + +# 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 diff --git a/roms/u-boot/arch/x86/cpu/coreboot/Kconfig b/roms/u-boot/arch/x86/cpu/coreboot/Kconfig new file mode 100644 index 000000000..497d6284a --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/coreboot/Kconfig @@ -0,0 +1,30 @@ +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 + imply X86_TSC_READ_BASE + select BINMAN if X86_64 + +endif diff --git a/roms/u-boot/arch/x86/cpu/coreboot/Makefile b/roms/u-boot/arch/x86/cpu/coreboot/Makefile new file mode 100644 index 000000000..a6cdb9a14 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/coreboot/Makefile @@ -0,0 +1,23 @@ +# 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. + +ifndef CONFIG_SPL +obj-y += car.o +endif +ifdef CONFIG_SPL_BUILD +obj-y += coreboot_spl.o +else +obj-y += sdram.o +endif +obj-y += coreboot.o +obj-y += timestamp.o diff --git a/roms/u-boot/arch/x86/cpu/coreboot/car.S b/roms/u-boot/arch/x86/cpu/coreboot/car.S new file mode 100644 index 000000000..7163b69a4 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/coreboot/car.S @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/coreboot/coreboot.c b/roms/u-boot/arch/x86/cpu/coreboot/coreboot.c new file mode 100644 index 000000000..69cf8f417 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/coreboot/coreboot.c @@ -0,0 +1,87 @@ +// 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 <init.h> +#include <usb.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/msr.h> +#include <asm/mtrr.h> +#include <asm/cb_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 IS_ENABLED(CONFIG_X86_RUN_64BIT) ? x86_cpu_reinit_f() : + x86_cpu_init_f(); +} + +int checkcpu(void) +{ + return 0; +} + +int print_cpuinfo(void) +{ + return default_print_cpuinfo(); +} + +static void board_final_init(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_init(); + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/coreboot/coreboot_spl.c b/roms/u-boot/arch/x86/cpu/coreboot/coreboot_spl.c new file mode 100644 index 000000000..36661871e --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/coreboot/coreboot_spl.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Google LLC + */ + +#include <common.h> +#include <init.h> + +int dram_init(void) +{ + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/coreboot/sdram.c b/roms/u-boot/arch/x86/cpu/coreboot/sdram.c new file mode 100644 index 000000000..4a256bad4 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/coreboot/sdram.c @@ -0,0 +1,103 @@ +// 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/cb_sysinfo.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +unsigned int install_e820_map(unsigned int max_entries, + struct e820_entry *entries) +{ + return cb_install_e820_map(max_entries, 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; +} diff --git a/roms/u-boot/arch/x86/cpu/coreboot/timestamp.c b/roms/u-boot/arch/x86/cpu/coreboot/timestamp.c new file mode 100644 index 000000000..3ad611a53 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/coreboot/timestamp.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * Modified from the coreboot version + */ + +#include <common.h> +#include <bootstage.h> +#include <asm/arch/timestamp.h> +#include <asm/cb_sysinfo.h> +#include <linux/compiler.h> + +static struct timestamp_table *ts_table __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; +} diff --git a/roms/u-boot/arch/x86/cpu/cpu.c b/roms/u-boot/arch/x86/cpu/cpu.c new file mode 100644 index 000000000..9c4edfcbf --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/cpu.c @@ -0,0 +1,358 @@ +// 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 + */ + +#define LOG_CATEGORY UCLASS_CPU + +#include <common.h> +#include <bootstage.h> +#include <command.h> +#include <cpu_func.h> +#include <dm.h> +#include <errno.h> +#include <init.h> +#include <irq.h> +#include <log.h> +#include <malloc.h> +#include <syscon.h> +#include <acpi/acpi_s3.h> +#include <acpi/acpi_table.h> +#include <asm/acpi.h> +#include <asm/control_regs.h> +#include <asm/coreboot_tables.h> +#include <asm/cpu.h> +#include <asm/global_data.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; + +#ifndef CONFIG_TPL_BUILD +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", +}; +#endif + +int __weak x86_cleanup_before_linux(void) +{ + int ret; + + ret = mp_park_aps(); + if (ret) + return log_msg_ret("park", ret); + bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR, + CONFIG_BOOTSTAGE_STASH_SIZE); + + 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; +} + +#ifndef CONFIG_TPL_BUILD +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; +} +#endif + +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); + + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) { + debug("ACPI previous sleep state: %s\n", + acpi_ss_string(gd->arch.prev_sleep_state)); + } + + 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 need to do some final init + * before the system is ready. + */ +__weak void board_final_init(void) +{ +} + +/* + * Implement a weak default function for boards that need to do some final + * processing before booting the OS. + */ +__weak void board_final_cleanup(void) +{ +} + +int last_stage_init(void) +{ + struct acpi_fadt __maybe_unused *fadt; + int ret; + + board_final_init(); + + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) { + fadt = acpi_find_fadt(); + + if (fadt && gd->arch.prev_sleep_state == ACPI_S3) + acpi_resume(fadt); + } + + ret = write_tables(); + if (ret) { + log_err("Failed to write tables\n"); + return log_msg_ret("table", ret); + } + + if (IS_ENABLED(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); + } + } + + /* + * TODO(sjg@chromium.org): Move this to bootm_announce_and_cleanup() + * once APL FSP-S at 0x200000 does not overlap with the bzimage at + * 0x100000. + */ + board_final_cleanup(); + + return 0; +} +#endif + +static int x86_init_cpus(void) +{ + if (IS_ENABLED(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); + } + + return 0; +} + +int cpu_init_r(void) +{ + struct udevice *dev; + int ret; + + if (!ll_boot_init()) { + uclass_first_device(UCLASS_PCI, &dev); + 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) +{ + struct udevice *itss; + int ret; + + if (IS_ENABLED(CONFIG_ENABLE_MRC_CACHE)) + mrccache_reserve(); + + if (IS_ENABLED(CONFIG_SEABIOS)) + high_table_reserve(); + + if (IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)) { + acpi_s3_reserve(); + + if (IS_ENABLED(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(); + } + } + ret = irq_first_device_type(X86_IRQT_ITSS, &itss); + if (!ret) { + /* + * Snapshot the current GPIO IRQ polarities. FSP-S is about to + * run and will set a default policy that doesn't honour boards' + * requirements + */ + irq_snapshot_polarities(itss); + } + + return 0; +} +#endif + +long detect_coreboot_table_at(ulong start, ulong size) +{ + u32 *ptr, *end; + + size /= 4; + for (ptr = (void *)start, end = ptr + size; ptr < end; ptr += 4) { + if (*ptr == 0x4f49424c) /* "LBIO" */ + return (long)ptr; + } + + return -ENOENT; +} + +long locate_coreboot_table(void) +{ + long addr; + + /* We look for LBIO in the first 4K of RAM and again at 960KB */ + addr = detect_coreboot_table_at(0x0, 0x1000); + if (addr < 0) + addr = detect_coreboot_table_at(0xf0000, 0x1000); + + return addr; +} diff --git a/roms/u-boot/arch/x86/cpu/cpu_x86.c b/roms/u-boot/arch/x86/cpu/cpu_x86.c new file mode 100644 index 000000000..59da41f38 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/cpu_x86.c @@ -0,0 +1,99 @@ +// 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> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +int cpu_x86_bind(struct udevice *dev) +{ + struct cpu_plat *plat = dev_get_parent_plat(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(const 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(const 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; +} + +int cpu_x86_get_count(const 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, +}; diff --git a/roms/u-boot/arch/x86/cpu/efi/Kconfig b/roms/u-boot/arch/x86/cpu/efi/Kconfig new file mode 100644 index 000000000..e0975d34d --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/efi/Kconfig @@ -0,0 +1,11 @@ +if EFI + +config SYS_CAR_ADDR + hex + default 0x100000 + +config SYS_CAR_SIZE + hex + default 0x20000 + +endif diff --git a/roms/u-boot/arch/x86/cpu/efi/Makefile b/roms/u-boot/arch/x86/cpu/efi/Makefile new file mode 100644 index 000000000..9716a4ebe --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/efi/Makefile @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/efi/app.c b/roms/u-boot/arch/x86/cpu/efi/app.c new file mode 100644 index 000000000..f75448978 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/efi/app.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + */ + +#include <common.h> +#include <cpu_func.h> +#include <fdtdec.h> +#include <init.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_init(void) +{ +} + +int misc_init_r(void) +{ + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/efi/car.S b/roms/u-boot/arch/x86/cpu/efi/car.S new file mode 100644 index 000000000..488dcde66 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/efi/car.S @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/efi/payload.c b/roms/u-boot/arch/x86/cpu/efi/payload.c new file mode 100644 index 000000000..9a73b768e --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/efi/payload.c @@ -0,0 +1,294 @@ +// 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 <log.h> +#include <usb.h> +#include <asm/bootparam.h> +#include <asm/e820.h> +#include <asm/global_data.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); +} diff --git a/roms/u-boot/arch/x86/cpu/efi/sdram.c b/roms/u-boot/arch/x86/cpu/efi/sdram.c new file mode 100644 index 000000000..af65982fd --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/efi/sdram.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + */ + +#include <common.h> +#include <efi.h> +#include <init.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/cpu/i386/Makefile b/roms/u-boot/arch/x86/cpu/i386/Makefile new file mode 100644 index 000000000..18e152074 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/i386/Makefile @@ -0,0 +1,11 @@ +# +# (C) Copyright 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +obj-y += call64.o +obj-y += cpu.o +ifndef CONFIG_TPL_BUILD +obj-y += interrupt.o +endif +obj-y += setjmp.o diff --git a/roms/u-boot/arch/x86/cpu/i386/call64.S b/roms/u-boot/arch/x86/cpu/i386/call64.S new file mode 100644 index 000000000..d81bcc6f8 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/i386/call64.S @@ -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/msr-index.h> +#include <asm/processor-flags.h> + +.code32 +.section .text_call64 +.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: diff --git a/roms/u-boot/arch/x86/cpu/i386/cpu.c b/roms/u-boot/arch/x86/cpu/i386/cpu.c new file mode 100644 index 000000000..e59215cc2 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/i386/cpu.c @@ -0,0 +1,669 @@ +// 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 <init.h> +#include <log.h> +#include <malloc.h> +#include <spl.h> +#include <asm/control_regs.h> +#include <asm/coreboot_tables.h> +#include <asm/cpu.h> +#include <asm/global_data.h> +#include <asm/mp.h> +#include <asm/msr.h> +#include <asm/mtrr.h> +#include <asm/processor-flags.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define CPUID_FEATURE_PAE BIT(6) +#define CPUID_FEATURE_PSE36 BIT(17) +#define CPUID_FEAURE_HTT BIT(28) + +/* + * 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; +}; + +/* gcc 7.3 does not wwant to drop x86_vendors, so use #ifdef */ +#ifndef CONFIG_TPL_BUILD +/* + * 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 ", }, +}; +#endif + +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, sizeof (Global Data Pointer), + * base (Global Data Pointer) + */ + new_gd->arch.gd_addr = new_gd; + gdt_addr[X86_GDT_ENTRY_32BIT_FS] = GDT_ENTRY(0x8093, + (ulong)&new_gd->arch.gd_addr, + sizeof(new_gd->arch.gd_addr) - 1); + + /* 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 segment 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; +} + +#ifndef CONFIG_TPL_BUILD +/* + * 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; +} +#endif + +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; +} + +#ifndef CONFIG_TPL_BUILD +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; +} +#endif + +static void identify_cpu(struct cpu_device_id *cpu) +{ + cpu->device = 0; /* fix gcc 4.4.4 warning */ + + /* + * Do a quick and dirty check to save space - Intel and AMD only and + * just the vendor. This is enough for most TPL code. + */ + if (spl_phase() == PHASE_TPL) { + struct cpuid_result result; + + result = cpuid(0x00000000); + switch (result.ecx >> 24) { + case 'l': /* GenuineIntel */ + cpu->vendor = X86_VENDOR_INTEL; + break; + case 'D': /* AuthenticAMD */ + cpu->vendor = X86_VENDOR_AMD; + break; + default: + cpu->vendor = X86_VENDOR_ANY; + break; + } + return; + } + +/* gcc 7.3 does not want to drop x86_vendors, so use #ifdef */ +#ifndef CONFIG_TPL_BUILD + char vendor_name[16]; + int i; + + vendor_name[0] = '\0'; /* Unset */ + + /* 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); + } else { + 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; + } + } +#endif +} + +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"); +} + +void cpu_reinit_fpu(void) +{ + asm ("fninit\n"); +} + +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(); + } +} + +static uint cpu_cpuid_extended_level(void) +{ + return cpuid_eax(0x80000000); +} + +int cpu_phys_address_size(void) +{ + if (!has_cpuid()) + return 32; + + if (cpu_cpuid_extended_level() >= 0x80000008) + return cpuid_eax(0x80000008) & 0xff; + + if (cpuid_edx(1) & (CPUID_FEATURE_PAE | CPUID_FEATURE_PSE36)) + return 36; + + return 32; +} + +/* 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) +{ + long addr; + + setup_identity(); + setup_pci_ram_top(); + addr = locate_coreboot_table(); + if (addr >= 0) { + gd->arch.coreboot_table = addr; + gd->flags |= GD_FLG_SKIP_LL_INIT; + } + + 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; + + /* Jump to U-Boot */ + func((ulong)pgtable, 0, (ulong)target); + + return -EFAULT; +} + +int x86_mp_init(void) +{ + int ret; + + ret = mp_init(); + if (ret) { + printf("Warning: MP init failure\n"); + return log_ret(ret); + } + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/i386/interrupt.c b/roms/u-boot/arch/x86/cpu/i386/interrupt.c new file mode 100644 index 000000000..fae2544c4 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/i386/interrupt.c @@ -0,0 +1,638 @@ +// 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 <hang.h> +#include <init.h> +#include <irq.h> +#include <irq_func.h> +#include <asm/control_regs.h> +#include <asm/global_data.h> +#include <asm/i8259.h> +#include <asm/interrupt.h> +#include <asm/io.h> +#include <asm/lapic.h> +#include <asm/processor-flags.h> +#include <asm/ptrace.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 idt_entry idt[256] __aligned(16); + +struct idt_ptr idt_ptr; + +static inline void load_idt(const struct idt_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 interrupt_read_idt(struct idt_ptr *ptr) +{ + asm volatile("sidt %0" : : "m" (*ptr)); +} + +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; + + if (!ll_boot_init()) + return 0; + + /* Try to set up the interrupt router, but don't require one */ + ret = irq_first_device_type(X86_IRQT_BASE, &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. + */ + 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)); diff --git a/roms/u-boot/arch/x86/cpu/i386/setjmp.S b/roms/u-boot/arch/x86/cpu/i386/setjmp.S new file mode 100644 index 000000000..40b10dc8d --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/i386/setjmp.S @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/intel_common/Makefile b/roms/u-boot/arch/x86/cpu/intel_common/Makefile new file mode 100644 index 000000000..8b9a810f6 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/Makefile @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2016 Google, Inc + +obj-$(CONFIG_INTEL_ACPIGEN) += acpi.o + +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 + +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_INTEL_GMA_ACPI) += intel_opregion.o +endif + +ifdef CONFIG_INTEL_CAR_CQOS +obj-$(CONFIG_TPL_BUILD) += car2.o +ifndef CONFIG_SPL_BUILD +obj-y += car2_uninit.o +endif +endif + +obj-y += cpu.o +obj-y += fast_spi.o +obj-y += lpc.o +obj-y += lpss.o +obj-$(CONFIG_$(SPL_)INTEL_GENERIC_WIFI) += generic_wifi.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 +obj-$(CONFIG_HAVE_ITSS) += itss.o +obj-$(CONFIG_HAVE_P2SB) += p2sb.o + +ifdef CONFIG_SPL +ifndef CONFIG_SPL_BUILD +ifndef CONFIG_SYS_COREBOOT +obj-y += cpu_from_spl.o +endif +endif +endif diff --git a/roms/u-boot/arch/x86/cpu/intel_common/acpi.c b/roms/u-boot/arch/x86/cpu/intel_common/acpi.c new file mode 100644 index 000000000..15f19da20 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/acpi.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generic Intel ACPI table generation + * + * Copyright (C) 2017 Intel Corp. + * Copyright 2019 Google LLC + * + * Modified from coreboot src/soc/intel/common/block/acpi.c + */ + +#include <common.h> +#include <bloblist.h> +#include <cpu.h> +#include <dm.h> +#include <acpi/acpigen.h> +#include <asm/acpigen.h> +#include <asm/acpi_table.h> +#include <asm/cpu.h> +#include <asm/cpu_common.h> +#include <asm/global_data.h> +#include <asm/intel_acpi.h> +#include <asm/ioapic.h> +#include <asm/mpspec.h> +#include <asm/smm.h> +#include <asm/turbo.h> +#include <asm/intel_gnvs.h> +#include <asm/arch/iomap.h> +#include <asm/arch/pm.h> +#include <asm/arch/systemagent.h> +#include <dm/acpi.h> +#include <linux/err.h> +#include <power/acpi_pmc.h> + +u32 acpi_fill_mcfg(u32 current) +{ + /* PCI Segment Group 0, Start Bus Number 0, End Bus Number is 255 */ + current += acpi_create_mcfg_mmconfig((void *)current, + CONFIG_MMCONF_BASE_ADDRESS, 0, 0, + (CONFIG_SA_PCIEX_LENGTH >> 20) + - 1); + return current; +} + +static int acpi_sci_irq(void) +{ + int sci_irq = 9; + uint scis; + int ret; + + ret = arch_read_sci_irq_select(); + if (IS_ERR_VALUE(ret)) + return log_msg_ret("sci_irq", ret); + scis = ret; + scis &= SCI_IRQ_MASK; + scis >>= SCI_IRQ_SHIFT; + + /* Determine how SCI is routed. */ + switch (scis) { + case SCIS_IRQ9: + case SCIS_IRQ10: + case SCIS_IRQ11: + sci_irq = scis - SCIS_IRQ9 + 9; + break; + case SCIS_IRQ20: + case SCIS_IRQ21: + case SCIS_IRQ22: + case SCIS_IRQ23: + sci_irq = scis - SCIS_IRQ20 + 20; + break; + default: + log_warning("Invalid SCI route! Defaulting to IRQ9\n"); + sci_irq = 9; + break; + } + + log_debug("SCI is IRQ%d\n", sci_irq); + + return sci_irq; +} + +static unsigned long acpi_madt_irq_overrides(unsigned long current) +{ + int sci = acpi_sci_irq(); + u16 flags = MP_IRQ_TRIGGER_LEVEL; + + if (sci < 0) + return log_msg_ret("sci irq", sci); + + /* INT_SRC_OVR */ + current += acpi_create_madt_irqoverride((void *)current, 0, 0, 2, 0); + + flags |= arch_madt_sci_irq_polarity(sci); + + /* SCI */ + current += + acpi_create_madt_irqoverride((void *)current, 0, sci, sci, flags); + + return current; +} + +u32 acpi_fill_madt(u32 current) +{ + /* Local APICs */ + current += acpi_create_madt_lapics(current); + + /* IOAPIC */ + current += acpi_create_madt_ioapic((void *)current, 2, IO_APIC_ADDR, 0); + + return acpi_madt_irq_overrides(current); +} + +void intel_acpi_fill_fadt(struct acpi_fadt *fadt) +{ + const u16 pmbase = IOMAP_ACPI_BASE; + + /* Use ACPI 3.0 revision. */ + fadt->header.revision = acpi_get_table_revision(ACPITAB_FADT); + + fadt->sci_int = acpi_sci_irq(); + fadt->smi_cmd = APM_CNT; + fadt->acpi_enable = APM_CNT_ACPI_ENABLE; + fadt->acpi_disable = APM_CNT_ACPI_DISABLE; + fadt->s4bios_req = 0x0; + fadt->pstate_cnt = 0; + + fadt->pm1a_evt_blk = pmbase + PM1_STS; + fadt->pm1b_evt_blk = 0x0; + fadt->pm1a_cnt_blk = pmbase + PM1_CNT; + fadt->pm1b_cnt_blk = 0x0; + + fadt->gpe0_blk = pmbase + GPE0_STS; + + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + + /* GPE0 STS/EN pairs each 32 bits wide. */ + fadt->gpe0_blk_len = 2 * GPE0_REG_MAX * sizeof(uint32_t); + + fadt->flush_size = 0x400; /* twice of cache size */ + fadt->flush_stride = 0x10; /* Cache line width */ + fadt->duty_offset = 1; + fadt->day_alrm = 0xd; + + fadt->flags = ACPI_FADT_WBINVD | ACPI_FADT_C1_SUPPORTED | + ACPI_FADT_C2_MP_SUPPORTED | ACPI_FADT_SLEEP_BUTTON | + ACPI_FADT_RESET_REGISTER | ACPI_FADT_SEALED_CASE | + ACPI_FADT_S4_RTC_WAKE | ACPI_FADT_PLATFORM_CLOCK; + + fadt->reset_reg.space_id = 1; + fadt->reset_reg.bit_width = 8; + fadt->reset_reg.addrl = IO_PORT_RESET; + fadt->reset_value = RST_CPU | SYS_RST; + + fadt->x_pm1a_evt_blk.space_id = 1; + fadt->x_pm1a_evt_blk.bit_width = fadt->pm1_evt_len * 8; + fadt->x_pm1a_evt_blk.addrl = pmbase + PM1_STS; + + fadt->x_pm1b_evt_blk.space_id = 1; + + fadt->x_pm1a_cnt_blk.space_id = 1; + fadt->x_pm1a_cnt_blk.bit_width = fadt->pm1_cnt_len * 8; + fadt->x_pm1a_cnt_blk.addrl = pmbase + PM1_CNT; + + fadt->x_pm1b_cnt_blk.space_id = 1; + + fadt->x_gpe1_blk.space_id = 1; +} + +int intel_southbridge_write_acpi_tables(const struct udevice *dev, + struct acpi_ctx *ctx) +{ + int ret; + + ret = acpi_write_dbg2_pci_uart(ctx, gd->cur_serial_dev, + ACPI_ACCESS_SIZE_DWORD_ACCESS); + if (ret) + return log_msg_ret("dbg2", ret); + + ret = acpi_write_hpet(ctx); + if (ret) + return log_msg_ret("hpet", ret); + + return 0; +} + +__weak u32 acpi_fill_soc_wake(u32 generic_pm1_en, + const struct chipset_power_state *ps) +{ + return generic_pm1_en; +} + +__weak int acpi_create_gnvs(struct acpi_global_nvs *gnvs) +{ + return 0; +} + +int southbridge_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + struct acpi_global_nvs *gnvs; + int ret; + + ret = bloblist_ensure_size(BLOBLISTT_ACPI_GNVS, sizeof(*gnvs), 0, + (void **)&gnvs); + if (ret) + return log_msg_ret("bloblist", ret); + + ret = acpi_create_gnvs(gnvs); + if (ret) + return log_msg_ret("gnvs", ret); + + /* + * TODO(sjg@chromum.org): tell SMI about it + * smm_setup_structures(gnvs, NULL, NULL); + */ + + /* Add it to DSDT */ + acpigen_write_scope(ctx, "\\"); + acpigen_write_name_dword(ctx, "NVSA", (uintptr_t)gnvs); + acpigen_pop_len(ctx); + + return 0; +} + +static int calculate_power(int tdp, int p1_ratio, int ratio) +{ + u32 m; + u32 power; + + /* + * M = ((1.1 - ((p1_ratio - ratio) * 0.00625)) / 1.1) ^ 2 + * + * Power = (ratio / p1_ratio) * m * tdp + */ + + m = (110000 - ((p1_ratio - ratio) * 625)) / 11; + m = (m * m) / 1000; + + power = ((ratio * 100000 / p1_ratio) / 100); + power *= (m / 100) * (tdp / 1000); + power /= 1000; + + return power; +} + +void generate_p_state_entries(struct acpi_ctx *ctx, int core, + int cores_per_package) +{ + int ratio_min, ratio_max, ratio_turbo, ratio_step; + int coord_type, power_max, num_entries; + int ratio, power, clock, clock_max; + bool turbo; + + coord_type = cpu_get_coord_type(); + ratio_min = cpu_get_min_ratio(); + ratio_max = cpu_get_max_ratio(); + clock_max = (ratio_max * cpu_get_bus_clock_khz()) / 1000; + turbo = (turbo_get_state() == TURBO_ENABLED); + + /* Calculate CPU TDP in mW */ + power_max = cpu_get_power_max(); + + /* Write _PCT indicating use of FFixedHW */ + acpigen_write_empty_pct(ctx); + + /* Write _PPC with no limit on supported P-state */ + acpigen_write_ppc_nvs(ctx); + /* Write PSD indicating configured coordination type */ + acpigen_write_psd_package(ctx, core, 1, coord_type); + + /* Add P-state entries in _PSS table */ + acpigen_write_name(ctx, "_PSS"); + + /* Determine ratio points */ + ratio_step = PSS_RATIO_STEP; + do { + num_entries = ((ratio_max - ratio_min) / ratio_step) + 1; + if (((ratio_max - ratio_min) % ratio_step) > 0) + num_entries += 1; + if (turbo) + num_entries += 1; + if (num_entries > PSS_MAX_ENTRIES) + ratio_step += 1; + } while (num_entries > PSS_MAX_ENTRIES); + + /* _PSS package count depends on Turbo */ + acpigen_write_package(ctx, num_entries); + + /* P[T] is Turbo state if enabled */ + if (turbo) { + ratio_turbo = cpu_get_max_turbo_ratio(); + + /* Add entry for Turbo ratio */ + acpigen_write_pss_package(ctx, clock_max + 1, /* MHz */ + power_max, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio_turbo << 8, /* control */ + ratio_turbo << 8); /* status */ + num_entries -= 1; + } + + /* First regular entry is max non-turbo ratio */ + acpigen_write_pss_package(ctx, clock_max, /* MHz */ + power_max, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio_max << 8, /* control */ + ratio_max << 8); /* status */ + num_entries -= 1; + + /* Generate the remaining entries */ + for (ratio = ratio_min + ((num_entries - 1) * ratio_step); + ratio >= ratio_min; ratio -= ratio_step) { + /* Calculate power at this ratio */ + power = calculate_power(power_max, ratio_max, ratio); + clock = (ratio * cpu_get_bus_clock_khz()) / 1000; + + acpigen_write_pss_package(ctx, clock, /* MHz */ + power, /* mW */ + PSS_LATENCY_TRANSITION,/* lat1 */ + PSS_LATENCY_BUSMASTER,/* lat2 */ + ratio << 8, /* control */ + ratio << 8); /* status */ + } + /* Fix package length */ + acpigen_pop_len(ctx); +} + +void generate_t_state_entries(struct acpi_ctx *ctx, int core, + int cores_per_package, struct acpi_tstate *entry, + int nentries) +{ + if (!nentries) + return; + + /* Indicate SW_ALL coordination for T-states */ + acpigen_write_tsd_package(ctx, core, cores_per_package, SW_ALL); + + /* Indicate FixedHW so OS will use MSR */ + acpigen_write_empty_ptc(ctx); + + /* Set NVS controlled T-state limit */ + acpigen_write_tpc(ctx, "\\TLVL"); + + /* Write TSS table for MSR access */ + acpigen_write_tss_package(ctx, entry, nentries); +} + +int acpi_generate_cpu_header(struct acpi_ctx *ctx, int core_id, + const struct acpi_cstate *c_state_map, + int num_cstates) +{ + bool is_first = !core_id; + + /* Generate processor \_PR.CPUx */ + acpigen_write_processor(ctx, core_id, is_first ? ACPI_BASE_ADDRESS : 0, + is_first ? 6 : 0); + + /* Generate C-state tables */ + acpigen_write_cst_package(ctx, c_state_map, num_cstates); + + return 0; +} + +int acpi_generate_cpu_package_final(struct acpi_ctx *ctx, int cores_per_package) +{ + /* + * PPKG is usually used for thermal management of the first and only + * package + */ + acpigen_write_processor_package(ctx, "PPKG", 0, cores_per_package); + + /* Add a method to notify processor nodes */ + acpigen_write_processor_cnot(ctx, cores_per_package); + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/car.S b/roms/u-boot/arch/x86/cpu/intel_common/car.S new file mode 100644 index 000000000..00308dbde --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/car.S @@ -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 */ diff --git a/roms/u-boot/arch/x86/cpu/intel_common/car2.S b/roms/u-boot/arch/x86/cpu/intel_common/car2.S new file mode 100644 index 000000000..086f98747 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/car2.S @@ -0,0 +1,448 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file was modified from the coreboot version. + * + * Copyright (C) 2015-2016 Intel Corp. + */ + +#include <config.h> +#include <asm/msr-index.h> +#include <asm/mtrr.h> +#include <asm/post.h> +#include <asm/processor.h> +#include <asm/processor-flags.h> + +#define KiB 1024 + +#define IS_POWER_OF_2(x) (!((x) & ((x) - 1))) + +.global car_init +car_init: + post_code(POST_CAR_START) + + /* + * Use the MTRR default type MSR as a proxy for detecting INIT#. + * Reset the system if any known bits are set in that MSR. That is + * an indication of the CPU not being properly reset. + */ +check_for_clean_reset: + mov $MTRR_DEF_TYPE_MSR, %ecx + rdmsr + and $(MTRR_DEF_TYPE_EN | MTRR_DEF_TYPE_FIX_EN), %eax + cmp $0, %eax + jz no_reset + /* perform warm reset */ + movw $IO_PORT_RESET, %dx + movb $(SYS_RST | RST_CPU), %al + outb %al, %dx + +no_reset: + post_code(POST_CAR_SIPI) + + /* Clear/disable fixed MTRRs */ + mov $fixed_mtrr_list_size, %ebx + xor %eax, %eax + xor %edx, %edx + +clear_fixed_mtrr: + add $-2, %ebx + movzwl fixed_mtrr_list(%ebx), %ecx + wrmsr + jnz clear_fixed_mtrr + + post_code(POST_CAR_MTRR) + + /* Figure put how many MTRRs we have, and clear them out */ + mov $MTRR_CAP_MSR, %ecx + rdmsr + movzb %al, %ebx /* Number of variable MTRRs */ + mov $MTRR_PHYS_BASE_MSR(0), %ecx + xor %eax, %eax + xor %edx, %edx + +clear_var_mtrr: + wrmsr + inc %ecx + wrmsr + inc %ecx + dec %ebx + jnz clear_var_mtrr + + post_code(POST_CAR_UNCACHEABLE) + + /* Configure default memory type to uncacheable (UC) */ + mov $MTRR_DEF_TYPE_MSR, %ecx + rdmsr + /* Clear enable bits and set default type to UC */ + and $~(MTRR_DEF_TYPE_MASK | MTRR_DEF_TYPE_EN | \ + MTRR_DEF_TYPE_FIX_EN), %eax + wrmsr + + /* + * Configure MTRR_PHYS_MASK_HIGH for proper addressing above 4GB + * based on the physical address size supported for this processor + * This is based on read from CPUID EAX = 080000008h, EAX bits [7:0] + * + * Examples: + * MTRR_PHYS_MASK_HIGH = 00000000Fh For 36 bit addressing + * MTRR_PHYS_MASK_HIGH = 0000000FFh For 40 bit addressing + */ + + movl $0x80000008, %eax /* Address sizes leaf */ + cpuid + sub $32, %al + movzx %al, %eax + xorl %esi, %esi + bts %eax, %esi + dec %esi /* esi <- MTRR_PHYS_MASK_HIGH */ + + post_code(POST_CAR_BASE_ADDRESS) + +#if IS_POWER_OF_2(CONFIG_DCACHE_RAM_SIZE) + /* Configure CAR region as write-back (WB) */ + mov $MTRR_PHYS_BASE_MSR(0), %ecx + mov $CONFIG_DCACHE_RAM_BASE, %eax + or $MTRR_TYPE_WRBACK, %eax + xor %edx,%edx + wrmsr + + /* Configure the MTRR mask for the size region */ + mov $MTRR_PHYS_MASK(0), %ecx + mov $CONFIG_DCACHE_RAM_SIZE, %eax /* size mask */ + dec %eax + not %eax + or $MTRR_PHYS_MASK_VALID, %eax + movl %esi, %edx /* edx <- MTRR_PHYS_MASK_HIGH */ + wrmsr +#elif (CONFIG_DCACHE_RAM_SIZE == 768 * KiB) /* 768 KiB */ + /* Configure CAR region as write-back (WB) */ + mov $MTRR_PHYS_BASE_MSR(0), %ecx + mov $CONFIG_DCACHE_RAM_BASE, %eax + or $MTRR_TYPE_WRBACK, %eax + xor %edx,%edx + wrmsr + + mov $MTRR_PHYS_MASK_MSR(0), %ecx + mov $(512 * KiB), %eax /* size mask */ + dec %eax + not %eax + or $MTRR_PHYS_MASK_VALID, %eax + movl %esi, %edx /* edx <- MTRR_PHYS_MASK_HIGH */ + wrmsr + + mov $MTRR_PHYS_BASE_MSR(1), %ecx + mov $(CONFIG_DCACHE_RAM_BASE + 512 * KiB), %eax + or $MTRR_TYPE_WRBACK, %eax + xor %edx,%edx + wrmsr + + mov $MTRR_PHYS_MASK_MSR(1), %ecx + mov $(256 * KiB), %eax /* size mask */ + dec %eax + not %eax + or $MTRR_PHYS_MASK_VALID, %eax + movl %esi, %edx /* edx <- MTRR_PHYS_MASK_HIGH */ + wrmsr +#else +#error "DCACHE_RAM_SIZE is not a power of 2 and setup code is missing" +#endif + post_code(POST_CAR_FILL) + + /* Enable variable MTRRs */ + mov $MTRR_DEF_TYPE_MSR, %ecx + rdmsr + or $MTRR_DEF_TYPE_EN, %eax + wrmsr + + /* Enable caching */ + mov %cr0, %eax + and $~(X86_CR0_CD | X86_CR0_NW), %eax + invd + mov %eax, %cr0 + +#if IS_ENABLED(CONFIG_INTEL_CAR_NEM) + jmp car_nem +#elif IS_ENABLED(CONFIG_INTEL_CAR_CQOS) + jmp car_cqos +#elif IS_ENABLED(CONFIG_INTEL_CAR_NEM_ENHANCED) + jmp car_nem_enhanced +#else +#error "No CAR mechanism selected: +#endif + jmp car_init_ret + +fixed_mtrr_list: + .word MTRR_FIX_64K_00000_MSR + .word MTRR_FIX_16K_80000_MSR + .word MTRR_FIX_16K_A0000_MSR + .word MTRR_FIX_4K_C0000_MSR + .word MTRR_FIX_4K_C8000_MSR + .word MTRR_FIX_4K_D0000_MSR + .word MTRR_FIX_4K_D8000_MSR + .word MTRR_FIX_4K_E0000_MSR + .word MTRR_FIX_4K_E8000_MSR + .word MTRR_FIX_4K_F0000_MSR + .word MTRR_FIX_4K_F8000_MSR +fixed_mtrr_list_size = . - fixed_mtrr_list + +#if IS_ENABLED(CONFIG_INTEL_CAR_NEM) +.global car_nem +car_nem: + /* Disable cache eviction (setup stage) */ + mov $MSR_EVICT_CTL, %ecx + rdmsr + or $0x1, %eax + wrmsr + + post_code(0x26) + + /* Clear the cache memory region. This will also fill up the cache */ + movl $CONFIG_DCACHE_RAM_BASE, %edi + movl $CONFIG_DCACHE_RAM_SIZE, %ecx + shr $0x02, %ecx + xor %eax, %eax + cld + rep stosl + + post_code(0x27) + + /* Disable cache eviction (run stage) */ + mov $MSR_EVICT_CTL, %ecx + rdmsr + or $0x2, %eax + wrmsr + + post_code(0x28) + + jmp car_init_ret + +#elif IS_ENABLED(CONFIG_INTEL_CAR_CQOS) +.global car_cqos +car_cqos: + /* + * Create CBM_LEN_MASK based on CBM_LEN + * Get CPUID.(EAX=10H, ECX=2H):EAX.CBM_LEN[bits 4:0] + */ + mov $0x10, %eax + mov $0x2, %ecx + cpuid + and $0x1f, %eax + add $1, %al + + mov $1, %ebx + mov %al, %cl + shl %cl, %ebx + sub $1, %ebx + + /* Store the CBM_LEN_MASK in mm3 for later use */ + movd %ebx, %mm3 + + /* + * Disable both L1 and L2 prefetcher. For yet-to-understood reason, + * prefetchers slow down filling cache with rep stos in CQOS mode. + */ + mov $MSR_PREFETCH_CTL, %ecx + rdmsr + or $(PREFETCH_L1_DISABLE | PREFETCH_L2_DISABLE), %eax + wrmsr + +#if (CONFIG_DCACHE_RAM_SIZE == CONFIG_L2_CACHE_SIZE) +/* + * If CAR size is set to full L2 size, mask is calculated as all-zeros. + * This is not supported by the CPU/uCode. + */ +#error "CQOS CAR may not use whole L2 cache area" +#endif + + /* Calculate how many bits to be used for CAR */ + xor %edx, %edx + mov $CONFIG_DCACHE_RAM_SIZE, %eax /* dividend */ + mov $CONFIG_CACHE_QOS_SIZE_PER_BIT, %ecx /* divisor */ + div %ecx /* result is in eax */ + mov %eax, %ecx /* save to ecx */ + mov $1, %ebx + shl %cl, %ebx + sub $1, %ebx /* resulting mask is is in ebx */ + + /* Set this mask for initial cache fill */ + mov $MSR_L2_QOS_MASK(0), %ecx + rdmsr + mov %ebx, %eax + wrmsr + + /* Set CLOS selector to 0 */ + mov $MSR_IA32_PQR_ASSOC, %ecx + rdmsr + and $~MSR_IA32_PQR_ASSOC_MASK, %edx /* select mask 0 */ + wrmsr + + /* We will need to block CAR region from evicts */ + mov $MSR_L2_QOS_MASK(1), %ecx + rdmsr + /* Invert bits that are to be used for cache */ + mov %ebx, %eax + xor $~0, %eax /* invert 32 bits */ + + /* + * Use CBM_LEN_MASK stored in mm3 to set bits based on Capacity Bit + * Mask Length. + */ + movd %mm3, %ebx + and %ebx, %eax + wrmsr + + post_code(0x26) + + /* Clear the cache memory region. This will also fill up the cache */ + movl $CONFIG_DCACHE_RAM_BASE, %edi + movl $CONFIG_DCACHE_RAM_SIZE, %ecx + shr $0x02, %ecx + xor %eax, %eax + cld + rep stosl + + post_code(0x27) + + /* Cache is populated. Use mask 1 that will block evicts */ + mov $MSR_IA32_PQR_ASSOC, %ecx + rdmsr + and $~MSR_IA32_PQR_ASSOC_MASK, %edx /* clear index bits first */ + or $1, %edx /* select mask 1 */ + wrmsr + + /* Enable prefetchers */ + mov $MSR_PREFETCH_CTL, %ecx + rdmsr + and $~(PREFETCH_L1_DISABLE | PREFETCH_L2_DISABLE), %eax + wrmsr + + post_code(0x28) + + jmp car_init_ret + +#elif IS_ENABLED(CONFIG_INTEL_CAR_NEM_ENHANCED) +.global car_nem_enhanced +car_nem_enhanced: + /* Disable cache eviction (setup stage) */ + mov $MSR_EVICT_CTL, %ecx + rdmsr + or $0x1, %eax + wrmsr + post_code(0x26) + + /* Create n-way set associativity of cache */ + xorl %edi, %edi +find_llc_subleaf: + movl %edi, %ecx + movl $0x04, %eax + cpuid + inc %edi + and $0xe0, %al /* EAX[7:5] = Cache Level */ + cmp $0x60, %al /* Check to see if it is LLC */ + jnz find_llc_subleaf + + /* + * Set MSR 0xC91 IA32_L3_MASK_! = 0xE/0xFE/0xFFE/0xFFFE + * for 4/8/16 way of LLC + */ + shr $22, %ebx + inc %ebx + /* Calculate n-way associativity of LLC */ + mov %bl, %cl + + /* + * Maximizing RO cacheability while locking in the CAR to a + * single way since that particular way won't be victim candidate + * for evictions. + * This has been done after programing LLC_WAY_MASK_1 MSR + * with desired LLC way as mentioned below. + * + * Hence create Code and Data Size as per request + * Code Size (RO) : Up to 16M + * Data Size (RW) : Up to 256K + */ + movl $0x01, %eax + /* + * LLC Ways -> LLC_WAY_MASK_1: + * 4: 0x000E + * 8: 0x00FE + * 12: 0x0FFE + * 16: 0xFFFE + * + * These MSRs contain one bit per each way of LLC + * - If this bit is '0' - the way is protected from eviction + * - If this bit is '1' - the way is not protected from eviction + */ + shl %cl, %eax + subl $0x02, %eax + movl $MSR_IA32_L3_MASK_1, %ecx + xorl %edx, %edx + wrmsr + /* + * Set MSR 0xC92 IA32_L3_MASK_2 = 0x1 + * + * For SKL SOC, data size remains 256K consistently. + * Hence, creating 1-way associative cache for Data + */ + mov $MSR_IA32_L3_MASK_2, %ecx + mov $0x01, %eax + xorl %edx, %edx + wrmsr + /* + * Set MSR_IA32_PQR_ASSOC = 0x02 + * + * Possible values: + * 0: Default value, no way mask should be applied + * 1: Apply way mask 1 to LLC + * 2: Apply way mask 2 to LLC + * 3: Shouldn't be use in NEM Mode + */ + movl $MSR_IA32_PQR_ASSOC, %ecx + movl $0x02, %eax + xorl %edx, %edx + wrmsr + + movl $CONFIG_DCACHE_RAM_BASE, %edi + movl $CONFIG_DCACHE_RAM_SIZE, %ecx + shr $0x02, %ecx + xor %eax, %eax + cld + rep stosl + /* + * Set MSR_IA32_PQR_ASSOC = 0x01 + * At this stage we apply LLC_WAY_MASK_1 to the cache. + * i.e. way 0 is protected from eviction. + */ + movl $MSR_IA32_PQR_ASSOC, %ecx + movl $0x01, %eax + xorl %edx, %edx + wrmsr + + post_code(0x27) + /* + * Enable No-Eviction Mode Run State by setting + * NO_EVICT_MODE MSR 2E0h bit [1] = '1'. + */ + + movl $MSR_EVICT_CTL, %ecx + rdmsr + orl $0x02, %eax + wrmsr + + post_code(0x28) + + jmp car_init_ret +#endif + +#if CONFIG_IS_ENABLED(X86_16BIT_INIT) +_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 */ + .long CONFIG_SYS_MONITOR_BASE /* code region base */ + .long CONFIG_SYS_MONITOR_LEN /* code region size */ +#endif diff --git a/roms/u-boot/arch/x86/cpu/intel_common/car2_uninit.S b/roms/u-boot/arch/x86/cpu/intel_common/car2_uninit.S new file mode 100644 index 000000000..aba3a5381 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/car2_uninit.S @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2017 Intel Corp. + * Copyright 2019 Google LLC + * Taken from coreboot file exit_car.S + */ + +#include <config.h> +#include <asm/msr-index.h> +#include <asm/mtrr.h> + +.text +.global car_uninit +car_uninit: + + /* + * Retrieve return address from stack as it will get trashed below if + * execution is utilizing the cache-as-ram stack. + */ + pop %ebx + + /* Disable MTRRs */ + mov $(MTRR_DEF_TYPE_MSR), %ecx + rdmsr + and $(~(MTRR_DEF_TYPE_EN | MTRR_DEF_TYPE_FIX_EN)), %eax + wrmsr + +#ifdef CONFIG_INTEL_CAR_NEM +.global car_nem_teardown +car_nem_teardown: + + /* invalidate cache contents */ + invd + + /* Knock down bit 1 then bit 0 of NEM control not combining steps */ + mov $(MSR_EVICT_CTL), %ecx + rdmsr + and $(~(1 << 1)), %eax + wrmsr + and $(~(1 << 0)), %eax + wrmsr + +#elif IS_ENABLED(CONFIG_INTEL_CAR_CQOS) +.global car_cqos_teardown +car_cqos_teardown: + + /* Go back to all-evicting mode, set both masks to all-1s */ + mov $MSR_L2_QOS_MASK(0), %ecx + rdmsr + mov $~0, %al + wrmsr + + mov $MSR_L2_QOS_MASK(1), %ecx + rdmsr + mov $~0, %al + wrmsr + + /* Reset CLOS selector to 0 */ + mov $MSR_IA32_PQR_ASSOC, %ecx + rdmsr + and $~MSR_IA32_PQR_ASSOC_MASK, %edx + wrmsr + +#elif IS_ENABLED(CONFIG_INTEL_CAR_NEM_ENHANCED) +.global car_nem_enhanced_teardown +car_nem_enhanced_teardown: + + /* invalidate cache contents */ + invd + + /* Knock down bit 1 then bit 0 of NEM control not combining steps */ + mov $(MSR_EVICT_CTL), %ecx + rdmsr + and $(~(1 << 1)), %eax + wrmsr + and $(~(1 << 0)), %eax + wrmsr + + /* Reset CLOS selector to 0 */ + mov $IA32_PQR_ASSOC, %ecx + rdmsr + and $~IA32_PQR_ASSOC_MASK, %edx + wrmsr +#endif + + /* Return to caller */ + jmp *%ebx diff --git a/roms/u-boot/arch/x86/cpu/intel_common/cpu.c b/roms/u-boot/arch/x86/cpu/intel_common/cpu.c new file mode 100644 index 000000000..96d05e2eb --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/cpu.c @@ -0,0 +1,328 @@ +// 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 <log.h> +#include <acpi/acpigen.h> +#include <asm/cpu.h> +#include <asm/cpu_common.h> +#include <asm/global_data.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; + info->address_width = cpu_phys_address_size(); + + 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); +} + +int cpu_get_coord_type(void) +{ + return HW_ALL; +} + +int cpu_get_min_ratio(void) +{ + msr_t msr; + + /* Get bus ratio limits and calculate clock speeds */ + msr = msr_read(MSR_PLATFORM_INFO); + + return (msr.hi >> 8) & 0xff; /* Max Efficiency Ratio */ +} + +int cpu_get_max_ratio(void) +{ + u32 ratio_max; + msr_t msr; + + if (cpu_config_tdp_levels()) { + /* Set max ratio to nominal TDP ratio */ + msr = msr_read(MSR_CONFIG_TDP_NOMINAL); + ratio_max = msr.lo & 0xff; + } else { + msr = msr_read(MSR_PLATFORM_INFO); + /* Max Non-Turbo Ratio */ + ratio_max = (msr.lo >> 8) & 0xff; + } + + return ratio_max; +} + +int cpu_get_bus_clock_khz(void) +{ + /* + * CPU bus clock is set by default here to 100MHz. This function returns + * the bus clock in KHz. + */ + return INTEL_BCLK_MHZ * 1000; +} + +int cpu_get_power_max(void) +{ + int power_unit; + msr_t msr; + + msr = msr_read(MSR_PKG_POWER_SKU_UNIT); + power_unit = 2 << ((msr.lo & 0xf) - 1); + msr = msr_read(MSR_PKG_POWER_SKU); + + return (msr.lo & 0x7fff) * 1000 / power_unit; +} + +int cpu_get_max_turbo_ratio(void) +{ + msr_t msr; + + msr = msr_read(MSR_TURBO_RATIO_LIMIT); + + return msr.lo & 0xff; +} + +int cpu_get_cores_per_package(void) +{ + struct cpuid_result result; + int cores = 1; + + if (gd->arch.x86_vendor != X86_VENDOR_INTEL) + return 1; + + result = cpuid_ext(0xb, 1); + cores = result.ebx & 0xff; + + return cores; +} + +void cpu_mca_configure(void) +{ + msr_t msr; + int i; + int num_banks; + + msr = msr_read(MSR_IA32_MCG_CAP); + num_banks = msr.lo & 0xff; + msr.lo = 0; + msr.hi = 0; + for (i = 0; i < num_banks; i++) { + /* Clear the machine check status */ + msr_write(MSR_IA32_MC0_STATUS + (i * 4), msr); + /* Initialise machine checks */ + msr_write(MSR_IA32_MC0_CTL + i * 4, + (msr_t) {.lo = 0xffffffff, .hi = 0xffffffff}); + } +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/cpu_from_spl.c b/roms/u-boot/arch/x86/cpu/intel_common/cpu_from_spl.c new file mode 100644 index 000000000..1c0dcedb5 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/cpu_from_spl.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <handoff.h> +#include <init.h> +#include <asm/cpu_common.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/fast_spi.c b/roms/u-boot/arch/x86/cpu/intel_common/fast_spi.c new file mode 100644 index 000000000..5d3944dee --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/fast_spi.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/cpu_common.h> +#include <asm/fast_spi.h> +#include <asm/pci.h> + +/* + * Returns bios_start and fills in size of the BIOS region. + */ +static ulong fast_spi_get_bios_region(struct fast_spi_regs *regs, + uint *bios_size) +{ + ulong bios_start, bios_end; + + /* + * BIOS_BFPREG provides info about BIOS-Flash Primary Region Base and + * Limit. Base and Limit fields are in units of 4K. + */ + u32 val = readl(®s->bfp); + + bios_start = (val & SPIBAR_BFPREG_PRB_MASK) << 12; + bios_end = (((val & SPIBAR_BFPREG_PRL_MASK) >> + SPIBAR_BFPREG_PRL_SHIFT) + 1) << 12; + *bios_size = bios_end - bios_start; + + return bios_start; +} + +int fast_spi_get_bios_mmap_regs(struct fast_spi_regs *regs, ulong *map_basep, + uint *map_sizep, uint *offsetp) +{ + ulong base; + + base = fast_spi_get_bios_region(regs, map_sizep); + *map_basep = (u32)-*map_sizep - base; + *offsetp = base; + + return 0; +} + +int fast_spi_get_bios_mmap(pci_dev_t pdev, ulong *map_basep, uint *map_sizep, + uint *offsetp) +{ + struct fast_spi_regs *regs; + ulong bar, mmio_base; + + /* Special case to find mapping without probing the device */ + pci_x86_read_config(pdev, PCI_BASE_ADDRESS_0, &bar, PCI_SIZE_32); + mmio_base = bar & PCI_BASE_ADDRESS_MEM_MASK; + regs = (struct fast_spi_regs *)mmio_base; + + return fast_spi_get_bios_mmap_regs(regs, map_basep, map_sizep, offsetp); +} + +int fast_spi_early_init(pci_dev_t pdev, ulong mmio_base) +{ + /* Program Temporary BAR for SPI */ + pci_x86_write_config(pdev, PCI_BASE_ADDRESS_0, + mmio_base | PCI_BASE_ADDRESS_SPACE_MEMORY, + PCI_SIZE_32); + + /* Enable Bus Master and MMIO Space */ + pci_x86_clrset_config(pdev, PCI_COMMAND, 0, PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY, PCI_SIZE_8); + + /* + * Disable the BIOS write protect so write commands are allowed. + * Enable Prefetching and caching. + */ + pci_x86_clrset_config(pdev, SPIBAR_BIOS_CONTROL, + SPIBAR_BIOS_CONTROL_EISS | + SPIBAR_BIOS_CONTROL_CACHE_DISABLE, + SPIBAR_BIOS_CONTROL_WPD | + SPIBAR_BIOS_CONTROL_PREFETCH_ENABLE, PCI_SIZE_8); + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/generic_wifi.c b/roms/u-boot/arch/x86/cpu/intel_common/generic_wifi.c new file mode 100644 index 000000000..61ec5391b --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/generic_wifi.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generic WiFi ACPI info + * + * Copyright 2019 Google LLC + * Modified from coreboot src/drivers/wifi/generic.c + */ + +#include <common.h> +#include <log.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_device.h> +#include <dm.h> +#include <dm/acpi.h> + +/* WRDS Spec Revision */ +#define WRDS_REVISION 0x0 + +/* EWRD Spec Revision */ +#define EWRD_REVISION 0x0 + +/* WRDS Domain type */ +#define WRDS_DOMAIN_TYPE_WIFI 0x7 + +/* EWRD Domain type */ +#define EWRD_DOMAIN_TYPE_WIFI 0x7 + +/* WGDS Domain type */ +#define WGDS_DOMAIN_TYPE_WIFI 0x7 + +/* + * WIFI ACPI NAME = "WF" + hex value of last 8 bits of dev_path_encode + '\0' + * The above representation returns unique and consistent name every time + * generate_wifi_acpi_name is invoked. The last 8 bits of dev_path_encode is + * chosen since it contains the bus address of the device. + */ +#define WIFI_ACPI_NAME_MAX_LEN 5 + +/** + * struct generic_wifi_config - Data structure to contain common wifi config + * @wake: Wake pin for ACPI _PRW + * @maxsleep: Maximum sleep state to wake from + */ +struct generic_wifi_config { + unsigned int wake; + unsigned int maxsleep; +}; + +static int generic_wifi_fill_ssdt(struct acpi_ctx *ctx, + const struct udevice *dev, + const struct generic_wifi_config *config) +{ + char name[ACPI_NAME_MAX]; + char path[ACPI_PATH_MAX]; + pci_dev_t bdf; + u32 address; + int ret; + + ret = acpi_device_path(dev_get_parent(dev), path, sizeof(path)); + if (ret) + return log_msg_ret("path", ret); + ret = acpi_get_name(dev, name); + if (ret) + return log_msg_ret("name", ret); + + /* Device */ + acpigen_write_scope(ctx, path); + acpigen_write_device(ctx, name); + acpigen_write_name_integer(ctx, "_UID", 0); + acpigen_write_name_string(ctx, "_DDN", + dev_read_string(dev, "acpi,ddn")); + + /* Address */ + bdf = dm_pci_get_bdf(dev); + address = (PCI_DEV(bdf) << 16) | PCI_FUNC(bdf); + acpigen_write_name_dword(ctx, "_ADR", address); + + /* Wake capabilities */ + if (config) + acpigen_write_prw(ctx, config->wake, config->maxsleep); + + acpigen_pop_len(ctx); /* Device */ + acpigen_pop_len(ctx); /* Scope */ + + return 0; +} + +static int intel_wifi_acpi_fill_ssdt(const struct udevice *dev, + struct acpi_ctx *ctx) +{ + struct generic_wifi_config config; + bool have_config; + int ret; + + ret = dev_read_u32(dev, "acpi,wake", &config.wake); + have_config = !ret; + /* By default, all intel wifi chips wake from S3 */ + config.maxsleep = 3; + ret = generic_wifi_fill_ssdt(ctx, dev, have_config ? &config : NULL); + if (ret) + return log_msg_ret("wifi", ret); + + return 0; +} + +struct acpi_ops wifi_acpi_ops = { + .fill_ssdt = intel_wifi_acpi_fill_ssdt, +}; + +static const struct udevice_id intel_wifi_ids[] = { + { .compatible = "intel,generic-wifi" }, + { } +}; + +U_BOOT_DRIVER(intel_wifi) = { + .name = "intel_wifi", + .id = UCLASS_MISC, + .of_match = intel_wifi_ids, + ACPI_OPS_PTR(&wifi_acpi_ops) +}; diff --git a/roms/u-boot/arch/x86/cpu/intel_common/intel_opregion.c b/roms/u-boot/arch/x86/cpu/intel_common/intel_opregion.c new file mode 100644 index 000000000..1eed21d8c --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/intel_opregion.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Writing IntelGraphicsMem table for ACPI + * + * Copyright 2019 Google LLC + * Modified from coreboot src/soc/intel/gma/opregion.c + */ + +#include <common.h> +#include <binman.h> +#include <bloblist.h> +#include <dm.h> +#include <spi_flash.h> +#include <asm/intel_opregion.h> + +static char vbt_data[8 << 10]; + +static int locate_vbt(char **vbtp, int *sizep) +{ + struct binman_entry vbt; + struct udevice *dev; + u32 vbtsig = 0; + int size; + int ret; + + ret = binman_entry_find("intel-vbt", &vbt); + if (ret) + return log_msg_ret("find VBT", ret); + ret = uclass_first_device_err(UCLASS_SPI_FLASH, &dev); + if (ret) + return log_msg_ret("find flash", ret); + size = vbt.size; + if (size > sizeof(vbt_data)) + return log_msg_ret("vbt", -E2BIG); + ret = spi_flash_read_dm(dev, vbt.image_pos, size, vbt_data); + if (ret) + return log_msg_ret("read", ret); + + memcpy(&vbtsig, vbt_data, sizeof(vbtsig)); + if (vbtsig != VBT_SIGNATURE) { + log_err("Missing/invalid signature in VBT data file!\n"); + return -EINVAL; + } + + log_debug("Found a VBT of %u bytes\n", size); + *sizep = size; + *vbtp = vbt_data; + + return 0; +} + +/* Write ASLS PCI register and prepare SWSCI register */ +static int intel_gma_opregion_register(struct udevice *dev, ulong opregion) +{ + int sci_reg; + + if (!device_active(dev)) + return -ENOENT; + + /* + * Intel BIOS Specification + * Chapter 5.3.7 "Initialise Hardware State" + */ + dm_pci_write_config32(dev, ASLS, opregion); + + /* + * Atom-based platforms use a combined SMI/SCI register, + * whereas non-Atom platforms use a separate SCI register + */ + if (IS_ENABLED(CONFIG_INTEL_GMA_SWSMISCI)) + sci_reg = SWSMISCI; + else + sci_reg = SWSCI; + + /* + * Intel's Windows driver relies on this: + * Intel BIOS Specification + * Chapter 5.4 "ASL Software SCI Handler" + */ + dm_pci_clrset_config16(dev, sci_reg, GSSCIE, SMISCISEL); + + return 0; +} + +int intel_gma_init_igd_opregion(struct udevice *dev, + struct igd_opregion *opregion) +{ + struct optionrom_vbt *vbt = NULL; + char *vbt_buf; + int vbt_size; + int ret; + + ret = locate_vbt(&vbt_buf, &vbt_size); + if (ret) { + log_err("GMA: VBT couldn't be found\n"); + return log_msg_ret("find vbt", ret); + } + vbt = (struct optionrom_vbt *)vbt_buf; + + memset(opregion, '\0', sizeof(struct igd_opregion)); + + memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE, + sizeof(opregion->header.signature)); + memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild, + ARRAY_SIZE(vbt->coreblock_biosbuild)); + /* Extended VBT support */ + if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) { + struct optionrom_vbt *ext_vbt; + + ret = bloblist_ensure_size(BLOBLISTT_INTEL_VBT, + vbt->hdr_vbt_size, 0, + (void **)&ext_vbt); + if (ret) { + log_err("GMA: Unable to add Ext VBT to bloblist\n"); + return log_msg_ret("blob", ret); + } + + memcpy(ext_vbt, vbt, vbt->hdr_vbt_size); + opregion->mailbox3.rvda = (uintptr_t)ext_vbt; + opregion->mailbox3.rvds = vbt->hdr_vbt_size; + } else { + /* Raw VBT size which can fit in gvd1 */ + printf("copy to %p\n", opregion->vbt.gvd1); + memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size); + } + + /* 8kb */ + opregion->header.size = sizeof(struct igd_opregion) / 1024; + + /* + * Left-shift version field to accommodate Intel Windows driver quirk + * when not using a VBIOS. + * Required for Legacy boot + NGI, UEFI + NGI, and UEFI + GOP driver. + * + * No adverse effects when using VBIOS or booting Linux. + */ + opregion->header.version = IGD_OPREGION_VERSION << 24; + + /* We just assume we're mobile for now */ + opregion->header.mailboxes = MAILBOXES_MOBILE; + + /* Initialise Mailbox 1 */ + opregion->mailbox1.clid = 1; + + /* Initialise Mailbox 3 */ + opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS; + opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH; + opregion->mailbox3.pcft = 0; /* should be (IMON << 1) & 0x3e */ + opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS; + opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000; + opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19; + opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433; + opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c; + opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866; + opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f; + opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99; + opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2; + opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc; + opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5; + opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff; + + /* Write ASLS PCI register and prepare SWSCI register */ + ret = intel_gma_opregion_register(dev, (ulong)opregion); + if (ret) + return log_msg_ret("write asls", ret); + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/itss.c b/roms/u-boot/arch/x86/cpu/intel_common/itss.c new file mode 100644 index 000000000..1eff03098 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/itss.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interrupt Timer Subsystem + * + * Copyright (C) 2017 Intel Corporation. + * Copyright (C) 2017 Siemens AG + * Copyright 2019 Google LLC + * + * Taken from coreboot itss.c + */ + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> +#include <irq.h> +#include <log.h> +#include <malloc.h> +#include <p2sb.h> +#include <spl.h> +#include <asm/global_data.h> +#include <asm/itss.h> + +static int set_polarity(struct udevice *dev, uint irq, bool active_low) +{ + u32 mask; + uint reg; + + if (irq > ITSS_MAX_IRQ) + return -EINVAL; + + reg = PCR_ITSS_IPC0_CONF + sizeof(u32) * (irq / IRQS_PER_IPC); + mask = 1 << (irq % IRQS_PER_IPC); + + pcr_clrsetbits32(dev, reg, mask, active_low ? mask : 0); + + return 0; +} + +#ifndef CONFIG_TPL_BUILD +static int snapshot_polarities(struct udevice *dev) +{ + struct itss_priv *priv = dev_get_priv(dev); + const int start = GPIO_IRQ_START; + const int end = GPIO_IRQ_END; + int reg_start; + int reg_end; + int i; + + reg_start = start / IRQS_PER_IPC; + reg_end = DIV_ROUND_UP(end, IRQS_PER_IPC); + + log_debug("ITSS IRQ Polarities snapshot %p\n", priv->irq_snapshot); + for (i = reg_start; i < reg_end; i++) { + uint reg = PCR_ITSS_IPC0_CONF + sizeof(u32) * i; + + priv->irq_snapshot[i] = pcr_read32(dev, reg); + log_debug(" - %d, reg %x: irq_snapshot[i] %x\n", i, reg, + priv->irq_snapshot[i]); + } + + /* Save the snapshot for use after relocation */ + gd->start_addr_sp -= sizeof(*priv); + gd->start_addr_sp &= ~0xf; + gd->arch.itss_priv = (void *)gd->start_addr_sp; + memcpy(gd->arch.itss_priv, priv, sizeof(*priv)); + + return 0; +} + +static void show_polarities(struct udevice *dev, const char *msg) +{ + int i; + + log_debug("ITSS IRQ Polarities %s:\n", msg); + for (i = 0; i < NUM_IPC_REGS; i++) { + uint reg = PCR_ITSS_IPC0_CONF + sizeof(u32) * i; + + log_debug("IPC%d: 0x%08x\n", i, pcr_read32(dev, reg)); + } +} + +static int restore_polarities(struct udevice *dev) +{ + struct itss_priv *priv = dev_get_priv(dev); + struct itss_priv *old_priv; + const int start = GPIO_IRQ_START; + const int end = GPIO_IRQ_END; + int reg_start; + int reg_end; + int i; + + /* Get the snapshot which was stored by the pre-reloc device */ + old_priv = gd->arch.itss_priv; + if (!old_priv) + return log_msg_ret("priv", -EFAULT); + memcpy(priv->irq_snapshot, old_priv->irq_snapshot, + sizeof(priv->irq_snapshot)); + + show_polarities(dev, "Before"); + log_debug("priv->irq_snapshot %p\n", priv->irq_snapshot); + + reg_start = start / IRQS_PER_IPC; + reg_end = DIV_ROUND_UP(end, IRQS_PER_IPC); + + + for (i = reg_start; i < reg_end; i++) { + u32 mask; + u16 reg; + int irq_start; + int irq_end; + + irq_start = i * IRQS_PER_IPC; + irq_end = min(irq_start + IRQS_PER_IPC - 1, ITSS_MAX_IRQ); + + if (start > irq_end) + continue; + if (end < irq_start) + break; + + /* Track bits within the bounds of of the register */ + irq_start = max(start, irq_start) % IRQS_PER_IPC; + irq_end = min(end, irq_end) % IRQS_PER_IPC; + + /* Create bitmask of the inclusive range of start and end */ + mask = (((1U << irq_end) - 1) | (1U << irq_end)); + mask &= ~((1U << irq_start) - 1); + + reg = PCR_ITSS_IPC0_CONF + sizeof(u32) * i; + log_debug(" - %d, reg %x: mask %x, irq_snapshot[i] %x\n", + i, reg, mask, priv->irq_snapshot[i]); + pcr_clrsetbits32(dev, reg, mask, mask & priv->irq_snapshot[i]); + } + + show_polarities(dev, "After"); + + return 0; +} +#endif + +static int route_pmc_gpio_gpe(struct udevice *dev, uint pmc_gpe_num) +{ + struct itss_priv *priv = dev_get_priv(dev); + struct pmc_route *route; + int i; + + for (i = 0, route = priv->route; i < priv->route_count; i++, route++) { + if (pmc_gpe_num == route->pmc) + return route->gpio; + } + + return -ENOENT; +} + +static int itss_bind(struct udevice *dev) +{ + /* This is not set with basic of-platdata, so set it manually */ + if (CONFIG_IS_ENABLED(OF_PLATDATA) && + !CONFIG_IS_ENABLED(OF_PLATDATA_INST)) + dev->driver_data = X86_IRQT_ITSS; + + return 0; +} + +static int itss_of_to_plat(struct udevice *dev) +{ + struct itss_priv *priv = dev_get_priv(dev); + int ret; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct itss_plat *plat = dev_get_plat(dev); + struct dtd_intel_itss *dtplat = &plat->dtplat; + + /* + * It would be nice to do this in the bind() method, but with + * of-platdata binding happens in the order that DM finds things in the + * linker list (i.e. alphabetical order by driver name). So the GPIO + * device may well be bound before its parent (p2sb), and this call + * will fail if p2sb is not bound yet. + * + * TODO(sjg@chromium.org): Add a parent pointer to child devices in dtoc + */ + ret = p2sb_set_port_id(dev, dtplat->intel_p2sb_port_id); + if (ret) + return log_msg_ret("Could not set port id", ret); + priv->route = (struct pmc_route *)dtplat->intel_pmc_routes; + priv->route_count = ARRAY_SIZE(dtplat->intel_pmc_routes) / + sizeof(struct pmc_route); +#else + int size; + + size = dev_read_size(dev, "intel,pmc-routes"); + if (size < 0) + return size; + priv->route = malloc(size); + if (!priv->route) + return -ENOMEM; + ret = dev_read_u32_array(dev, "intel,pmc-routes", (u32 *)priv->route, + size / sizeof(fdt32_t)); + if (ret) + return log_msg_ret("Cannot read pmc-routes", ret); + priv->route_count = size / sizeof(struct pmc_route); +#endif + + return 0; +} + +static const struct irq_ops itss_ops = { + .route_pmc_gpio_gpe = route_pmc_gpio_gpe, + .set_polarity = set_polarity, +#ifndef CONFIG_TPL_BUILD + .snapshot_polarities = snapshot_polarities, + .restore_polarities = restore_polarities, +#endif +}; + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id itss_ids[] = { + { .compatible = "intel,itss", .data = X86_IRQT_ITSS }, + { } +}; +#endif + +U_BOOT_DRIVER(intel_itss) = { + .name = "intel_itss", + .id = UCLASS_IRQ, + .of_match = of_match_ptr(itss_ids), + .ops = &itss_ops, + .bind = itss_bind, + .of_to_plat = itss_of_to_plat, + .plat_auto = sizeof(struct itss_plat), + .priv_auto = sizeof(struct itss_priv), +}; diff --git a/roms/u-boot/arch/x86/cpu/intel_common/lpc.c b/roms/u-boot/arch/x86/cpu/intel_common/lpc.c new file mode 100644 index 000000000..f31fddb6f --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/lpc.c @@ -0,0 +1,101 @@ +// 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/global_data.h> +#include <asm/intel_regs.h> +#include <asm/io.h> +#include <asm/lpc_common.h> +#include <linux/bitops.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, ®8); + 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; +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/lpss.c b/roms/u-boot/arch/x86/cpu/intel_common/lpss.c new file mode 100644 index 000000000..26a2d2d1e --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/lpss.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Special driver to handle of-platdata + * + * Copyright 2019 Google LLC + * + * Some code from coreboot lpss.c + */ + +#include <common.h> +#include <dm.h> +#include <pci.h> +#include <asm/io.h> +#include <asm/lpss.h> + +enum { + LPSS_RESET_CTL_REG = 0x204, + + /* + * Bit 1:0 controls LPSS controller reset. + * + * 00 ->LPSS Host Controller is in reset (Reset Asserted) + * 01/10 ->Reserved + * 11 ->LPSS Host Controller is NOT at reset (Reset Released) + */ + LPSS_CNT_RST_RELEASE = 3, + + /* Power management control and status register */ + PME_CTRL_STATUS = 0x84, + + /* Bit 1:0 Powerstate, controls D0 and D3 state */ + POWER_STATE_MASK = 3, +}; + +/* Take controller out of reset */ +void lpss_reset_release(void *regs) +{ + writel(LPSS_CNT_RST_RELEASE, regs + LPSS_RESET_CTL_REG); +} + +void lpss_set_power_state(struct udevice *dev, enum lpss_pwr_state state) +{ + dm_pci_clrset_config8(dev, PME_CTRL_STATUS, POWER_STATE_MASK, state); +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/me_status.c b/roms/u-boot/arch/x86/cpu/intel_common/me_status.c new file mode 100644 index 000000000..abc5f6fbc --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/me_status.c @@ -0,0 +1,213 @@ +// 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 <log.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); +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/microcode.c b/roms/u-boot/arch/x86/cpu/intel_common/microcode.c new file mode 100644 index 000000000..4d8e1d210 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/microcode.c @@ -0,0 +1,188 @@ +// 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 <log.h> +#include <asm/global_data.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); +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/mrc.c b/roms/u-boot/arch/x86/cpu/intel_common/mrc.c new file mode 100644 index 000000000..a97b0b7ce --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/mrc.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <init.h> +#include <log.h> +#include <syscon.h> +#include <asm/cpu.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/p2sb.c b/roms/u-boot/arch/x86/cpu/intel_common/p2sb.c new file mode 100644 index 000000000..d73ae438b --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/p2sb.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Primary-to-Sideband Bridge + * + * Copyright 2019 Google LLC + */ + +#define LOG_CATEGORY UCLASS_P2SB + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> +#include <log.h> +#include <p2sb.h> +#include <spl.h> +#include <asm/p2sb.h> +#include <asm/pci.h> +#include <linux/bitops.h> + +#define PCH_P2SB_E0 0xe0 +#define HIDE_BIT BIT(0) + +/* PCI config space registers */ +#define HPTC_OFFSET 0x60 +#define HPTC_ADDR_ENABLE_BIT BIT(7) + +/* High Performance Event Timer Configuration */ +#define P2SB_HPTC 0x60 +#define P2SB_HPTC_ADDRESS_ENABLE BIT(7) + +/* + * ADDRESS_SELECT ENCODING_RANGE + * 0 0xfed0 0000 - 0xfed0 03ff + * 1 0xfed0 1000 - 0xfed0 13ff + * 2 0xfed0 2000 - 0xfed0 23ff + * 3 0xfed0 3000 - 0xfed0 33ff + */ +#define P2SB_HPTC_ADDRESS_SELECT_0 (0 << 0) +#define P2SB_HPTC_ADDRESS_SELECT_1 (1 << 0) +#define P2SB_HPTC_ADDRESS_SELECT_2 (2 << 0) +#define P2SB_HPTC_ADDRESS_SELECT_3 (3 << 0) + +/* + * p2sb_early_init() - Enable decoding for HPET range + * + * This is needed by FSP-M which uses the High Precision Event Timer. + * + * @dev: P2SB device + * @return 0 if OK, -ve on error + */ +static int p2sb_early_init(struct udevice *dev) +{ + struct p2sb_plat *plat = dev_get_plat(dev); + pci_dev_t pdev = plat->bdf; + + /* + * Enable decoding for HPET memory address range. + * HPTC_OFFSET(0x60) bit 7, when set the P2SB will decode + * the High Performance Timer memory address range + * selected by bits 1:0 + */ + pci_x86_write_config(pdev, HPTC_OFFSET, HPTC_ADDR_ENABLE_BIT, + PCI_SIZE_8); + + /* Enable PCR Base address in PCH */ + pci_x86_write_config(pdev, PCI_BASE_ADDRESS_0, plat->mmio_base, + PCI_SIZE_32); + pci_x86_write_config(pdev, PCI_BASE_ADDRESS_1, 0, PCI_SIZE_32); + + /* Enable P2SB MSE */ + pci_x86_write_config(pdev, PCI_COMMAND, PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY, PCI_SIZE_8); + + return 0; +} + +static int p2sb_spl_init(struct udevice *dev) +{ + /* Enable decoding for HPET. Needed for FSP global pointer storage */ + dm_pci_write_config(dev, P2SB_HPTC, P2SB_HPTC_ADDRESS_SELECT_0 | + P2SB_HPTC_ADDRESS_ENABLE, PCI_SIZE_8); + + return 0; +} + +int p2sb_of_to_plat(struct udevice *dev) +{ + struct p2sb_uc_priv *upriv = dev_get_uclass_priv(dev); + struct p2sb_plat *plat = dev_get_plat(dev); + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + int ret; + u32 base[2]; + + ret = dev_read_u32_array(dev, "early-regs", base, ARRAY_SIZE(base)); + if (ret) + return log_msg_ret("Missing/short early-regs", ret); + plat->mmio_base = base[0]; + /* TPL sets up the initial BAR */ + if (spl_phase() == PHASE_TPL) { + plat->bdf = pci_get_devfn(dev); + if (plat->bdf < 0) + return log_msg_ret("Cannot get p2sb PCI address", + plat->bdf); + } + upriv->mmio_base = plat->mmio_base; +#else + plat->mmio_base = plat->dtplat.early_regs[0]; + plat->bdf = pci_ofplat_get_devfn(plat->dtplat.reg[0]); + upriv->mmio_base = plat->mmio_base; +#endif + + return 0; +} + +static int p2sb_probe(struct udevice *dev) +{ + if (spl_phase() == PHASE_TPL) + return p2sb_early_init(dev); + else if (spl_phase() == PHASE_SPL) + return p2sb_spl_init(dev); + + return 0; +} + +static void p2sb_set_hide_bit(struct udevice *dev, bool hide) +{ + dm_pci_clrset_config8(dev, PCH_P2SB_E0 + 1, HIDE_BIT, + hide ? HIDE_BIT : 0); +} + +static int intel_p2sb_set_hide(struct udevice *dev, bool hide) +{ + u16 vendor; + + if (!CONFIG_IS_ENABLED(PCI)) + return -EPERM; + p2sb_set_hide_bit(dev, hide); + + dm_pci_read_config16(dev, PCI_VENDOR_ID, &vendor); + if (hide && vendor != 0xffff) + return log_msg_ret("hide", -EEXIST); + else if (!hide && vendor != PCI_VENDOR_ID_INTEL) + return log_msg_ret("unhide", -ENOMEDIUM); + + return 0; +} + +static int p2sb_remove(struct udevice *dev) +{ + int ret; + + ret = intel_p2sb_set_hide(dev, true); + if (ret) + return log_msg_ret("hide", ret); + + return 0; +} + +static int p2sb_child_post_bind(struct udevice *dev) +{ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct p2sb_child_plat *pplat = dev_get_parent_plat(dev); + int ret; + u32 pid; + + ret = dev_read_u32(dev, "intel,p2sb-port-id", &pid); + if (ret) + return ret; + pplat->pid = pid; +#endif + + return 0; +} + +static const struct p2sb_ops p2sb_ops = { + .set_hide = intel_p2sb_set_hide, +}; + +#if !CONFIG_IS_ENABLED(OF_PLATDATA) +static const struct udevice_id p2sb_ids[] = { + { .compatible = "intel,p2sb" }, + { } +}; +#endif + +U_BOOT_DRIVER(intel_p2sb) = { + .name = "intel_p2sb", + .id = UCLASS_P2SB, + .of_match = of_match_ptr(p2sb_ids), + .probe = p2sb_probe, + .remove = p2sb_remove, + .ops = &p2sb_ops, + .of_to_plat = p2sb_of_to_plat, + .plat_auto = sizeof(struct p2sb_plat), + .per_child_plat_auto = sizeof(struct p2sb_child_plat), + .child_post_bind = p2sb_child_post_bind, + .flags = DM_FLAG_OS_PREPARE, +}; diff --git a/roms/u-boot/arch/x86/cpu/intel_common/pch.c b/roms/u-boot/arch/x86/cpu/intel_common/pch.c new file mode 100644 index 000000000..af82b64a1 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/pch.c @@ -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); +} diff --git a/roms/u-boot/arch/x86/cpu/intel_common/report_platform.c b/roms/u-boot/arch/x86/cpu/intel_common/report_platform.c new file mode 100644 index 000000000..a3612817c --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/intel_common/report_platform.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * From Coreboot src/northbridge/intel/sandybridge/report_platform.c + * + * Copyright (C) 2012 Google Inc. + */ + +#include <common.h> +#include <log.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); +} diff --git a/roms/u-boot/arch/x86/cpu/ioapic.c b/roms/u-boot/arch/x86/cpu/ioapic.c new file mode 100644 index 000000000..4f99de6ec --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ioapic.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <log.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)); + } +} diff --git a/roms/u-boot/arch/x86/cpu/irq.c b/roms/u-boot/arch/x86/cpu/irq.c new file mode 100644 index 000000000..766b2451a --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/irq.c @@ -0,0 +1,367 @@ +// 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 <irq.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.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; +} + +static const struct udevice_id irq_router_ids[] = { + { .compatible = "intel,irq-router", .data = X86_IRQT_BASE }, + { } +}; + +U_BOOT_DRIVER(irq_router_drv) = { + .name = "intel_irq", + .id = UCLASS_IRQ, + .of_match = irq_router_ids, + .probe = irq_router_probe, + .priv_auto = sizeof(struct irq_router), +}; diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/Kconfig b/roms/u-boot/arch/x86/cpu/ivybridge/Kconfig new file mode 100644 index 000000000..2f4239378 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/Kconfig @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/Makefile b/roms/u-boot/arch/x86/cpu/ivybridge/Makefile new file mode 100644 index 000000000..716134e9f --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/Makefile @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/bd82x6x.c b/roms/u-boot/arch/x86/cpu/ivybridge/bd82x6x.c new file mode 100644 index 000000000..0540b0216 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/bd82x6x.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014 Google, Inc + */ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <malloc.h> +#include <pch.h> +#include <asm/cpu.h> +#include <asm/global_data.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> +#include <linux/bitops.h> +#include <linux/delay.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, +}; diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/cpu.c b/roms/u-boot/arch/x86/cpu/ivybridge/cpu.c new file mode 100644 index 000000000..a02f4f960 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/cpu.c @@ -0,0 +1,207 @@ +// 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 <init.h> +#include <log.h> +#include <pch.h> +#include <asm/cpu.h> +#include <asm/cpu_common.h> +#include <asm/global_data.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(); + } + + 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); +} diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/early_me.c b/roms/u-boot/arch/x86/cpu/ivybridge/early_me.c new file mode 100644 index 000000000..bee1671ba --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/early_me.c @@ -0,0 +1,174 @@ +// 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 <log.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> +#include <linux/delay.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, +}; diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/fsp_configs.c b/roms/u-boot/arch/x86/cpu/ivybridge/fsp_configs.c new file mode 100644 index 000000000..3c4ea6c26 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/fsp_configs.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <fdtdec.h> +#include <log.h> +#include <asm/fsp1/fsp_support.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/ivybridge.c b/roms/u-boot/arch/x86/cpu/ivybridge/ivybridge.c new file mode 100644 index 000000000..eb3f362e4 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/ivybridge.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <init.h> +#include <asm/post.h> +#include <asm/processor.h> + +int arch_cpu_init(void) +{ + post_code(POST_CPU_INIT); + + return x86_cpu_init_f(); +} diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/lpc.c b/roms/u-boot/arch/x86/cpu/ivybridge/lpc.c new file mode 100644 index 000000000..f931d2be1 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/lpc.c @@ -0,0 +1,531 @@ +// 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 <log.h> +#include <rtc.h> +#include <pci.h> +#include <asm/global_data.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, ®16); + 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, ®16); + 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, ®8); + 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, ®16); + 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, ®8); /* 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, +}; diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/model_206ax.c b/roms/u-boot/arch/x86/cpu/ivybridge/model_206ax.c new file mode 100644 index 000000000..3906a6979 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/model_206ax.c @@ -0,0 +1,455 @@ +// 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 <log.h> +#include <malloc.h> +#include <asm/cpu.h> +#include <asm/cpu_common.h> +#include <asm/cpu_x86.h> +#include <asm/global_data.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(const struct udevice *dev, + struct cpu_info *info) +{ + return cpu_intel_get_info(info, INTEL_BCLK_MHZ); + + return 0; +} + +static int model_206ax_get_count(const struct udevice *dev) +{ + return 4; +} + +static int cpu_x86_model_206ax_probe(struct udevice *dev) +{ + if (dev_seq(dev) == 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, +}; diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/northbridge.c b/roms/u-boot/arch/x86/cpu/ivybridge/northbridge.c new file mode 100644 index 000000000..994f8a4ff --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/northbridge.c @@ -0,0 +1,264 @@ +// 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 <log.h> +#include <asm/global_data.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> +#include <linux/delay.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, ®8); + 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, +}; diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/sata.c b/roms/u-boot/arch/x86/cpu/ivybridge/sata.c new file mode 100644 index 000000000..f47ecdffa --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/sata.c @@ -0,0 +1,259 @@ +// 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 <log.h> +#include <asm/global_data.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, ®16); + 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, ®16); + 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, ®16); + 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, +}; diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/sdram.c b/roms/u-boot/arch/x86/cpu/ivybridge/sdram.c new file mode 100644 index 000000000..dd6b8753d --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/sdram.c @@ -0,0 +1,563 @@ +// 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 <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <init.h> +#include <log.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(MRC_TYPE_NORMAL, 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) { + struct mrc_output *mrc = &gd->arch.mrc[MRC_TYPE_NORMAL]; + + /* + * This will be copied to SDRAM in reserve_arch(), then written + * to SPI flash in mrccache_save() + */ + mrc->buf = (char *)pei_data->mrc_output; + mrc->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; +} diff --git a/roms/u-boot/arch/x86/cpu/ivybridge/sdram_nop.c b/roms/u-boot/arch/x86/cpu/ivybridge/sdram_nop.c new file mode 100644 index 000000000..51dfe23f9 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/ivybridge/sdram_nop.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 Google, Inc + */ + +#include <common.h> +#include <init.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/cpu/lapic.c b/roms/u-boot/arch/x86/cpu/lapic.c new file mode 100644 index 000000000..c0691454f --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/lapic.c @@ -0,0 +1,159 @@ +// 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 <log.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); +} diff --git a/roms/u-boot/arch/x86/cpu/mp_init.c b/roms/u-boot/arch/x86/cpu/mp_init.c new file mode 100644 index 000000000..c09762aee --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/mp_init.c @@ -0,0 +1,902 @@ +// 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 <log.h> +#include <malloc.h> +#include <qfw.h> +#include <asm/atomic.h> +#include <asm/cpu.h> +#include <asm/global_data.h> +#include <asm/interrupt.h> +#include <asm/io.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/delay.h> +#include <linux/linkage.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * Setting up multiprocessing + * + * See https://www.intel.com/content/www/us/en/intelligent-systems/intel-boot-loader-development-kit/minimal-intel-architecture-boot-loader-paper.html + * + * Note that this file refers to the boot CPU (the one U-Boot is running on) as + * the BSP (BootStrap Processor) and the others as APs (Application Processors). + * + * This module works by loading some setup code into RAM at AP_DEFAULT_BASE and + * telling each AP to execute it. The code that each AP runs is in + * sipi_vector.S (see ap_start16) which includes a struct sipi_params at the + * end of it. Those parameters are set up by the C code. + + * Setting up is handled by load_sipi_vector(). It inits the common block of + * parameters (sipi_params) which tell the APs what to do. This block includes + * microcode and the MTTRs (Memory-Type-Range Registers) from the main CPU. + * There is also an ap_count which each AP increments as it starts up, so the + * BSP can tell how many checked in. + * + * The APs are started with a SIPI (Startup Inter-Processor Interrupt) which + * tells an AP to start executing at a particular address, in this case + * AP_DEFAULT_BASE which contains the code copied from ap_start16. This protocol + * is handled by start_aps(). + * + * After being started, each AP runs the code in ap_start16, switches to 32-bit + * mode, runs the code at ap_start, then jumps to c_handler which is ap_init(). + * This runs a very simple 'flight plan' described in mp_steps(). This sets up + * the CPU and waits for further instructions by looking at its entry in + * ap_callbacks[]. Note that the flight plan is only actually run for each CPU + * in bsp_do_flight_plan(): once the BSP completes each flight record, it sets + * mp_flight_record->barrier to 1 to allow the APs to executed the record one + * by one. + * + * CPUS are numbered sequentially from 0 using the device tree: + * + * cpus { + * u-boot,dm-pre-reloc; + * #address-cells = <1>; + * #size-cells = <0>; + * + * cpu@0 { + * u-boot,dm-pre-reloc; + * device_type = "cpu"; + * compatible = "intel,apl-cpu"; + * reg = <0>; + * intel,apic-id = <0>; + * }; + * + * cpu@1 { + * device_type = "cpu"; + * compatible = "intel,apl-cpu"; + * reg = <1>; + * intel,apic-id = <2>; + * }; + * + * Here the 'reg' property is the CPU number and then is placed in dev_seq(cpu) + * so that we can index into ap_callbacks[] using that. The APIC ID is different + * and may not be sequential (it typically is if hyperthreading is supported). + * + * Once APs are inited they wait in ap_wait_for_instruction() for instructions. + * Instructions come in the form of a function to run. This logic is in + * mp_run_on_cpus() which supports running on any one AP, all APs, just the BSP + * or all CPUs. The BSP logic is handled directly in mp_run_on_cpus(), by + * calling the function. For the APs, callback information is stored in a + * single, common struct mp_callback and a pointer to this is written to each + * AP's slot in ap_callbacks[] by run_ap_work(). All APs get the message even + * if it is only for one of them. When an AP notices a message it checks whether + * it should call the function (see check in ap_wait_for_instruction()) and then + * does so if needed. After that it sets its slot to NULL to indicate it is + * done. + * + * While U-Boot is running it can use mp_run_on_cpus() to run code on the APs. + * An example of this is the 'mtrr' command which allows reading and changing + * the MTRRs on all CPUs. + * + * Before U-Boot exits it calls mp_park_aps() which tells all CPUs to halt by + * executing a 'hlt' instruction. That allows them to be used by Linux when it + * starts up. + */ + +/* This also needs to match the sipi.S assembly code for saved MSR encoding */ +struct __packed saved_msr { + uint32_t index; + uint32_t lo; + uint32_t hi; +}; + +/** + * struct mp_flight_plan - Holds the flight plan + * + * @num_records: Number of flight records + * @records: Pointer to each record + */ +struct mp_flight_plan { + int num_records; + struct mp_flight_record *records; +}; + +/** + * struct mp_callback - Callback information for APs + * + * @func: Function to run + * @arg: Argument to pass to the function + * @logical_cpu_number: Either a CPU number (i.e. dev_seq(cpu) or a special + * value like MP_SELECT_BSP. It tells the AP whether it should process this + * callback + */ +struct mp_callback { + mp_run_func func; + void *arg; + int logical_cpu_number; +}; + +/* Stores the flight plan so that APs can find it */ +static struct mp_flight_plan mp_info; + +/* + * ap_callbacks - Callback mailbox array + * + * Array of callback, one entry for each available CPU, indexed by the CPU + * number, which is dev_seq(cpu). The entry for the main CPU is never used. + * When this is NULL, there is no pending work for the CPU to run. When + * non-NULL it points to the mp_callback structure. This is shared between all + * CPUs, so should only be written by the main CPU. + */ +static struct mp_callback **ap_callbacks; + +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_plat *plat = dev_get_parent_plat(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, which only returns if CONFIG_SMP_AP_WORK is not + * enabled + */ + ap_do_flight_plan(dev); + +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 = ¶ms->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; +} + +/** + * start_aps() - Start up the APs and count how many we find + * + * This is called on the boot processor to start up all the other processors + * (here called APs). + * + * @num_aps: Number of APs we expect to find + * @ap_count: Initially zero. Incremented by this function for each AP found + * @return 0 if all APs were set up correctly or there are none to set up, + * -ENOSPC if the SIPI vector is too high in memory, + * -ETIMEDOUT if the ICR is busy or the second SIPI fails to complete + * -EIO if not all APs check in correctly + */ +static int start_aps(int num_aps, atomic_t *ap_count) +{ + int sipi_vector; + /* Max location is 4KiB below 1MiB */ + const int max_vector_loc = ((1 << 20) - (1 << 12)) >> 12; + + if (num_aps == 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", num_aps); + + 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(ap_count, num_aps, 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(ap_count, num_aps, 10000, 50)) { + debug("Not all APs checked in: %d/%d\n", + atomic_read(ap_count), num_aps); + return -EIO; + } + + return 0; +} + +/** + * bsp_do_flight_plan() - Do the flight plan on the BSP + * + * This runs the flight plan on the main CPU used to boot U-Boot + * + * @cpu: Device for the main CPU + * @plan: Flight plan to run + * @num_aps: Number of APs (CPUs other than the BSP) + * @returns 0 on success, -ETIMEDOUT if an AP failed to come up + */ +static int bsp_do_flight_plan(struct udevice *cpu, struct mp_flight_plan *plan, + int num_aps) +{ + int i; + int ret = 0; + const int timeout_us = 100000; + const int step_us = 100; + + for (i = 0; i < plan->num_records; i++) { + struct mp_flight_record *rec = &plan->records[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; +} + +/** + * get_bsp() - Get information about the bootstrap processor + * + * @devp: If non-NULL, returns CPU device corresponding to the BSP + * @cpu_countp: If non-NULL, returns the total number of CPUs + * @return CPU number of the BSP, or -ve on error. If multiprocessing is not + * enabled, returns 0 + */ +static int get_bsp(struct udevice **devp, int *cpu_countp) +{ + char processor_name[CPU_MAX_NAME_LEN]; + struct udevice *dev; + 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, &dev); + if (ret < 0) { + printf("Cannot find boot CPU, APIC ID %d\n", apic_id); + return ret; + } + ret = cpu_get_count(dev); + if (ret < 0) + return log_msg_ret("count", ret); + if (devp) + *devp = dev; + if (cpu_countp) + *cpu_countp = ret; + + return dev_seq(dev) >= 0 ? dev_seq(dev) : 0; +} + +/** + * read_callback() - Read the pointer in a callback slot + * + * This is called by APs to read their callback slot to see if there is a + * pointer to new instructions + * + * @slot: Pointer to the AP's callback slot + * @return value of that pointer + */ +static struct mp_callback *read_callback(struct mp_callback **slot) +{ + dmb(); + + return *slot; +} + +/** + * store_callback() - Store a pointer to the callback slot + * + * This is called by APs to write NULL into the callback slot when they have + * finished the work requested by the BSP. + * + * @slot: Pointer to the AP's callback slot + * @val: Value to write (e.g. NULL) + */ +static void store_callback(struct mp_callback **slot, struct mp_callback *val) +{ + *slot = val; + dmb(); +} + +/** + * run_ap_work() - Run a callback on selected APs + * + * This writes @callback to all APs and waits for them all to acknowledge it, + * Note that whether each AP actually calls the callback depends on the value + * of logical_cpu_number (see struct mp_callback). The logical CPU number is + * the CPU device's req->seq value. + * + * @callback: Callback information to pass to all APs + * @bsp: CPU device for the BSP + * @num_cpus: The number of CPUs in the system (= number of APs + 1) + * @expire_ms: Timeout to wait for all APs to finish, in milliseconds, or 0 for + * no timeout + * @return 0 if OK, -ETIMEDOUT if one or more APs failed to respond in time + */ +static int run_ap_work(struct mp_callback *callback, struct udevice *bsp, + int num_cpus, uint expire_ms) +{ + int cur_cpu = dev_seq(bsp); + int num_aps = num_cpus - 1; /* number of non-BSPs to get this message */ + int cpus_accepted; + ulong start; + int i; + + if (!IS_ENABLED(CONFIG_SMP_AP_WORK)) { + printf("APs already parked. CONFIG_SMP_AP_WORK not enabled\n"); + return -ENOTSUPP; + } + + /* Signal to all the APs to run the func. */ + for (i = 0; i < num_cpus; i++) { + if (cur_cpu != i) + store_callback(&ap_callbacks[i], callback); + } + mfence(); + + /* Wait for all the APs to signal back that call has been accepted. */ + start = get_timer(0); + + do { + mdelay(1); + cpus_accepted = 0; + + for (i = 0; i < num_cpus; i++) { + if (cur_cpu == i) + continue; + if (!read_callback(&ap_callbacks[i])) + cpus_accepted++; + } + + if (expire_ms && get_timer(start) >= expire_ms) { + log(UCLASS_CPU, LOGL_CRIT, + "AP call expired; %d/%d CPUs accepted\n", + cpus_accepted, num_aps); + return -ETIMEDOUT; + } + } while (cpus_accepted != num_aps); + + /* Make sure we can see any data written by the APs */ + mfence(); + + return 0; +} + +/** + * ap_wait_for_instruction() - Wait for and process requests from the main CPU + * + * This is called by APs (here, everything other than the main boot CPU) to + * await instructions. They arrive in the form of a function call and argument, + * which is then called. This uses a simple mailbox with atomic read/set + * + * @cpu: CPU that is waiting + * @unused: Optional argument provided by struct mp_flight_record, not used here + * @return Does not return + */ +static int ap_wait_for_instruction(struct udevice *cpu, void *unused) +{ + struct mp_callback lcb; + struct mp_callback **per_cpu_slot; + + if (!IS_ENABLED(CONFIG_SMP_AP_WORK)) + return 0; + + per_cpu_slot = &ap_callbacks[dev_seq(cpu)]; + + while (1) { + struct mp_callback *cb = read_callback(per_cpu_slot); + + if (!cb) { + asm ("pause"); + continue; + } + + /* Copy to local variable before using the value */ + memcpy(&lcb, cb, sizeof(lcb)); + mfence(); + if (lcb.logical_cpu_number == MP_SELECT_ALL || + lcb.logical_cpu_number == MP_SELECT_APS || + dev_seq(cpu) == lcb.logical_cpu_number) + lcb.func(lcb.arg); + + /* Indicate we are finished */ + store_callback(per_cpu_slot, NULL); + } + + return 0; +} + +static int mp_init_cpu(struct udevice *cpu, void *unused) +{ + struct cpu_plat *plat = dev_get_parent_plat(cpu); + + plat->ucode_version = microcode_read_rev(); + plat->device_id = gd->arch.x86_device; + + return device_probe(cpu); +} + +static struct mp_flight_record mp_steps[] = { + MP_FR_BLOCK_APS(mp_init_cpu, NULL, mp_init_cpu, NULL), + MP_FR_BLOCK_APS(ap_wait_for_instruction, NULL, NULL, NULL), +}; + +int mp_run_on_cpus(int cpu_select, mp_run_func func, void *arg) +{ + struct mp_callback lcb = { + .func = func, + .arg = arg, + .logical_cpu_number = cpu_select, + }; + struct udevice *dev; + int num_cpus; + int ret; + + ret = get_bsp(&dev, &num_cpus); + if (ret < 0) + return log_msg_ret("bsp", ret); + if (cpu_select == MP_SELECT_ALL || cpu_select == MP_SELECT_BSP || + cpu_select == ret) { + /* Run on BSP first */ + func(arg); + } + + if (!IS_ENABLED(CONFIG_SMP_AP_WORK) || + !(gd->flags & GD_FLG_SMP_READY)) { + /* Allow use of this function on the BSP only */ + if (cpu_select == MP_SELECT_BSP || !cpu_select) + return 0; + return -ENOTSUPP; + } + + /* Allow up to 1 second for all APs to finish */ + ret = run_ap_work(&lcb, dev, num_cpus, 1000 /* ms */); + if (ret) + return log_msg_ret("aps", ret); + + return 0; +} + +static void park_this_cpu(void *unused) +{ + stop_this_cpu(); +} + +int mp_park_aps(void) +{ + int ret; + + ret = mp_run_on_cpus(MP_SELECT_APS, park_this_cpu, NULL); + if (ret) + return log_ret(ret); + + return 0; +} + +int mp_first_cpu(int cpu_select) +{ + struct udevice *dev; + int num_cpus; + int ret; + + /* + * This assumes that CPUs are numbered from 0. This function tries to + * avoid assuming the CPU 0 is the boot CPU + */ + if (cpu_select == MP_SELECT_ALL) + return 0; /* start with the first one */ + + ret = get_bsp(&dev, &num_cpus); + if (ret < 0) + return log_msg_ret("bsp", ret); + + /* Return boot CPU if requested */ + if (cpu_select == MP_SELECT_BSP) + return ret; + + /* Return something other than the boot CPU, if APs requested */ + if (cpu_select == MP_SELECT_APS && num_cpus > 1) + return ret == 0 ? 1 : 0; + + /* Try to check for an invalid value */ + if (cpu_select < 0 || cpu_select >= num_cpus) + return -EINVAL; + + return cpu_select; /* return the only selected one */ +} + +int mp_next_cpu(int cpu_select, int prev_cpu) +{ + struct udevice *dev; + int num_cpus; + int ret; + int bsp; + + /* If we selected the BSP or a particular single CPU, we are done */ + if (!IS_ENABLED(CONFIG_SMP_AP_WORK) || cpu_select == MP_SELECT_BSP || + cpu_select >= 0) + return -EFBIG; + + /* Must be doing MP_SELECT_ALL or MP_SELECT_APS; return the next CPU */ + ret = get_bsp(&dev, &num_cpus); + if (ret < 0) + return log_msg_ret("bsp", ret); + bsp = ret; + + /* Move to the next CPU */ + assert(prev_cpu >= 0); + ret = prev_cpu + 1; + + /* Skip the BSP if needed */ + if (cpu_select == MP_SELECT_APS && ret == bsp) + ret++; + if (ret >= num_cpus) + return -EFBIG; + + return ret; +} + +int mp_init(void) +{ + int num_aps, num_cpus; + atomic_t *ap_count; + struct udevice *cpu; + int ret; + + if (IS_ENABLED(CONFIG_QFW)) { + ret = qemu_cpu_fixup(); + if (ret) + return ret; + } + + ret = get_bsp(&cpu, &num_cpus); + if (ret < 0) { + debug("Cannot init boot CPU: err=%d\n", ret); + return ret; + } + + if (num_cpus < 2) + debug("Warning: Only 1 CPU is detected\n"); + + ret = check_cpu_devices(num_cpus); + if (ret) + log_warning("Warning: Device tree does not describe all CPUs. Extra ones will not be started correctly\n"); + + ap_callbacks = calloc(num_cpus, sizeof(struct mp_callback *)); + if (!ap_callbacks) + return -ENOMEM; + + /* Copy needed parameters so that APs have a reference to the plan */ + mp_info.num_records = ARRAY_SIZE(mp_steps); + mp_info.records = mp_steps; + + /* 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, &mp_info, num_aps); + if (ret) { + debug("CPU init failed: err=%d\n", ret); + return ret; + } + gd->flags |= GD_FLG_SMP_READY; + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/mtrr.c b/roms/u-boot/arch/x86/cpu/mtrr.c new file mode 100644 index 000000000..166aff380 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/mtrr.c @@ -0,0 +1,319 @@ +// 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 <log.h> +#include <sort.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm/mp.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); +} + +void mtrr_read_all(struct mtrr_info *info) +{ + int reg_count = mtrr_get_var_count(); + int i; + + for (i = 0; i < reg_count; i++) { + info->mtrr[i].base = native_read_msr(MTRR_PHYS_BASE_MSR(i)); + info->mtrr[i].mask = native_read_msr(MTRR_PHYS_MASK_MSR(i)); + } +} + +void mtrr_write_all(struct mtrr_info *info) +{ + int reg_count = mtrr_get_var_count(); + struct mtrr_state state; + int i; + + for (i = 0; i < reg_count; i++) { + mtrr_open(&state, true); + wrmsrl(MTRR_PHYS_BASE_MSR(i), info->mtrr[i].base); + wrmsrl(MTRR_PHYS_MASK_MSR(i), info->mtrr[i].mask); + mtrr_close(&state, true); + } +} + +static void write_mtrrs(void *arg) +{ + struct mtrr_info *info = arg; + + mtrr_write_all(info); +} + +static void read_mtrrs(void *arg) +{ + struct mtrr_info *info = arg; + + mtrr_read_all(info); +} + +/** + * mtrr_copy_to_aps() - Copy the MTRRs from the boot CPU to other CPUs + * + * @return 0 on success, -ve on failure + */ +static int mtrr_copy_to_aps(void) +{ + struct mtrr_info info; + int ret; + + ret = mp_run_on_cpus(MP_SELECT_BSP, read_mtrrs, &info); + if (ret == -ENXIO) + return 0; + else if (ret) + return log_msg_ret("bsp", ret); + + ret = mp_run_on_cpus(MP_SELECT_APS, write_mtrrs, &info); + if (ret) + return log_msg_ret("bsp", ret); + + return 0; +} + +static int h_comp_mtrr(const void *p1, const void *p2) +{ + const struct mtrr_request *req1 = p1; + const struct mtrr_request *req2 = p2; + + s64 diff = req1->start - req2->start; + + return diff < 0 ? -1 : diff > 0 ? 1 : 0; +} + +int mtrr_commit(bool do_caches) +{ + struct mtrr_request *req = gd->arch.mtrr_req; + struct mtrr_state state; + int ret; + 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"); + qsort(req, gd->arch.mtrr_req_count, sizeof(*req), h_comp_mtrr); + 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_get_var_count(); i++) + wrmsrl(MTRR_PHYS_MASK_MSR(i), 0); + debug("close\n"); + mtrr_close(&state, do_caches); + debug("mtrr done\n"); + + if (gd->flags & GD_FLG_RELOC) { + ret = mtrr_copy_to_aps(); + if (ret) + return log_msg_ret("copy", ret); + } + + 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; +} + +int mtrr_get_var_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 = mtrr_get_var_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; +} + +/** enum mtrr_opcode - supported operations for mtrr_do_oper() */ +enum mtrr_opcode { + MTRR_OP_SET, + MTRR_OP_SET_VALID, +}; + +/** + * struct mtrr_oper - An MTRR operation to perform on a CPU + * + * @opcode: Indicates operation to perform + * @reg: MTRR reg number to select (0-7, -1 = all) + * @valid: Valid value to write for MTRR_OP_SET_VALID + * @base: Base value to write for MTRR_OP_SET + * @mask: Mask value to write for MTRR_OP_SET + */ +struct mtrr_oper { + enum mtrr_opcode opcode; + int reg; + bool valid; + u64 base; + u64 mask; +}; + +static void mtrr_do_oper(void *arg) +{ + struct mtrr_oper *oper = arg; + u64 mask; + + switch (oper->opcode) { + case MTRR_OP_SET_VALID: + mask = native_read_msr(MTRR_PHYS_MASK_MSR(oper->reg)); + if (oper->valid) + mask |= MTRR_PHYS_MASK_VALID; + else + mask &= ~MTRR_PHYS_MASK_VALID; + wrmsrl(MTRR_PHYS_MASK_MSR(oper->reg), mask); + break; + case MTRR_OP_SET: + wrmsrl(MTRR_PHYS_BASE_MSR(oper->reg), oper->base); + wrmsrl(MTRR_PHYS_MASK_MSR(oper->reg), oper->mask); + break; + } +} + +static int mtrr_start_op(int cpu_select, struct mtrr_oper *oper) +{ + struct mtrr_state state; + int ret; + + mtrr_open(&state, true); + ret = mp_run_on_cpus(cpu_select, mtrr_do_oper, oper); + mtrr_close(&state, true); + if (ret) + return log_msg_ret("run", ret); + + return 0; +} + +int mtrr_set_valid(int cpu_select, int reg, bool valid) +{ + struct mtrr_oper oper; + + oper.opcode = MTRR_OP_SET_VALID; + oper.reg = reg; + oper.valid = valid; + + return mtrr_start_op(cpu_select, &oper); +} + +int mtrr_set(int cpu_select, int reg, u64 base, u64 mask) +{ + struct mtrr_oper oper; + + oper.opcode = MTRR_OP_SET; + oper.reg = reg; + oper.base = base; + oper.mask = mask; + + return mtrr_start_op(cpu_select, &oper); +} diff --git a/roms/u-boot/arch/x86/cpu/pci.c b/roms/u-boot/arch/x86/cpu/pci.c new file mode 100644 index 000000000..d4f9290ca --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/pci.c @@ -0,0 +1,101 @@ +// 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 <log.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); + } +} diff --git a/roms/u-boot/arch/x86/cpu/qemu/Kconfig b/roms/u-boot/arch/x86/cpu/qemu/Kconfig new file mode 100644 index 000000000..f8f2f6473 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/qemu/Kconfig @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/qemu/Makefile b/roms/u-boot/arch/x86/cpu/qemu/Makefile new file mode 100644 index 000000000..b7dd5bd46 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/qemu/Makefile @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/qemu/car.S b/roms/u-boot/arch/x86/cpu/qemu/car.S new file mode 100644 index 000000000..466b4876e --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/qemu/car.S @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/qemu/cpu.c b/roms/u-boot/arch/x86/cpu/qemu/cpu.c new file mode 100644 index 000000000..735b65608 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/qemu/cpu.c @@ -0,0 +1,50 @@ +// 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(const 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(const struct udevice *dev) +{ + int ret; + struct udevice *qfw_dev; + + ret = qfw_get_dev(&qfw_dev); + if (ret) + return ret; + + return qfw_online_cpus(qfw_dev); +} + +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, +}; diff --git a/roms/u-boot/arch/x86/cpu/qemu/dram.c b/roms/u-boot/arch/x86/cpu/qemu/dram.c new file mode 100644 index 000000000..c17455012 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/qemu/dram.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <init.h> +#include <asm/global_data.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(); +} diff --git a/roms/u-boot/arch/x86/cpu/qemu/e820.c b/roms/u-boot/arch/x86/cpu/qemu/e820.c new file mode 100644 index 000000000..19e54c520 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/qemu/e820.c @@ -0,0 +1,69 @@ +// 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 <malloc.h> +#include <asm/e820.h> +#include <asm/arch/qemu.h> +#include <asm/global_data.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; +} diff --git a/roms/u-boot/arch/x86/cpu/qemu/qemu.c b/roms/u-boot/arch/x86/cpu/qemu/qemu.c new file mode 100644 index 000000000..e54082df7 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/qemu/qemu.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <init.h> +#include <pci.h> +#include <qfw.h> +#include <dm/platdata.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; + +#if CONFIG_IS_ENABLED(QFW_PIO) +U_BOOT_DRVINFO(x86_qfw_pio) = { + .name = "qfw_pio", +}; +#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(); + } +} + +#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 diff --git a/roms/u-boot/arch/x86/cpu/qfw_cpu.c b/roms/u-boot/arch/x86/cpu/qfw_cpu.c new file mode 100644 index 000000000..ee00b8fe7 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/qfw_cpu.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <cpu.h> +#include <dm.h> +#include <malloc.h> +#include <qfw.h> +#include <dm/lists.h> +#include <dm/uclass-internal.h> +#include <dm/root.h> + +int qemu_cpu_fixup(void) +{ + int ret; + int cpu_num; + int cpu_online; + struct uclass *uc; + struct udevice *dev, *pdev, *qfwdev; + struct cpu_plat *plat; + char *cpu; + + /* This will cause the CPUs devices to be bound */ + ret = uclass_get(UCLASS_CPU, &uc); + if (ret) + return ret; + + /* 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; + } + + /* get qfw dev */ + ret = qfw_get_dev(&qfwdev); + if (ret) { + printf("unable to find qfw device\n"); + return ret; + } + + /* 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 = qfw_online_cpus(qfwdev); + 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_plat(dev); + plat->cpu_id = cpu_num; + } + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/quark/Kconfig b/roms/u-boot/arch/x86/cpu/quark/Kconfig new file mode 100644 index 000000000..2fee38aed --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/Kconfig @@ -0,0 +1,138 @@ +# 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" + select ROM_NEEDS_BLOBS + 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 diff --git a/roms/u-boot/arch/x86/cpu/quark/Makefile b/roms/u-boot/arch/x86/cpu/quark/Makefile new file mode 100644 index 000000000..7039f8b9b --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/Makefile @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/quark/acpi.c b/roms/u-boot/arch/x86/cpu/quark/acpi.c new file mode 100644 index 000000000..82b776ff6 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/acpi.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <acpi/acpi_table.h> +#include <asm/processor.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); +} + +int acpi_create_gnvs(struct acpi_global_nvs *gnvs) +{ + /* quark is a uni-processor */ + gnvs->pcnt = 1; + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/quark/car.S b/roms/u-boot/arch/x86/cpu/quark/car.S new file mode 100644 index 000000000..48d5167c7 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/car.S @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/quark/dram.c b/roms/u-boot/arch/x86/cpu/quark/dram.c new file mode 100644 index 000000000..2287dce12 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/dram.c @@ -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 <errno.h> +#include <fdtdec.h> +#include <init.h> +#include <log.h> +#include <malloc.h> +#include <asm/cache.h> +#include <asm/global_data.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(MRC_TYPE_NORMAL, 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) { + struct mrc_output *mrc = &gd->arch.mrc[MRC_TYPE_NORMAL]; + + memcpy(cache, &mrc_params.timings, sizeof(struct mrc_timings)); + mrc->buf = cache; + mrc->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; +} diff --git a/roms/u-boot/arch/x86/cpu/quark/hte.c b/roms/u-boot/arch/x86/cpu/quark/hte.c new file mode 100644 index 000000000..df1477935 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/hte.c @@ -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(); +} diff --git a/roms/u-boot/arch/x86/cpu/quark/hte.h b/roms/u-boot/arch/x86/cpu/quark/hte.h new file mode 100644 index 000000000..b4ea488f3 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/hte.h @@ -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_ */ diff --git a/roms/u-boot/arch/x86/cpu/quark/mrc.c b/roms/u-boot/arch/x86/cpu/quark/mrc.c new file mode 100644 index 000000000..3e8c0bc28 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/mrc.c @@ -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(); +} diff --git a/roms/u-boot/arch/x86/cpu/quark/mrc_util.c b/roms/u-boot/arch/x86/cpu/quark/mrc_util.c new file mode 100644 index 000000000..b0bc59b71 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/mrc_util.c @@ -0,0 +1,1472 @@ +// 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 <hang.h> +#include <asm/arch/device.h> +#include <asm/arch/mrc.h> +#include <asm/arch/msg_port.h> +#include <asm/arch/quark.h> +#include "mrc_util.h" +#include "hte.h" +#include "smc.h" + +static const uint8_t vref_codes[64] = { + /* lowest to highest */ + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, + 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f +}; + +void mrc_write_mask(u32 unit, u32 addr, u32 data, u32 mask) +{ + msg_port_write(unit, addr, + (msg_port_read(unit, addr) & ~(mask)) | + ((data) & (mask))); +} + +void mrc_alt_write_mask(u32 unit, u32 addr, u32 data, u32 mask) +{ + msg_port_alt_write(unit, addr, + (msg_port_alt_read(unit, addr) & ~(mask)) | + ((data) & (mask))); +} + +void mrc_post_code(uint8_t major, uint8_t minor) +{ + /* send message to UART */ + DPF(D_INFO, "POST: 0x%01x%02x\n", major, minor); + + /* error check */ + if (major == 0xee) + hang(); +} + +/* Delay number of nanoseconds */ +void delay_n(uint32_t ns) +{ + /* 1000 MHz clock has 1ns period --> no conversion required */ + uint64_t final_tsc = rdtsc(); + + final_tsc += ((get_tbclk_mhz() * ns) / 1000); + + while (rdtsc() < final_tsc) + ; +} + +/* Delay number of microseconds */ +void delay_u(uint32_t ms) +{ + /* 64-bit math is not an option, just use loops */ + while (ms--) + delay_n(1000); +} + +/* Select Memory Manager as the source for PRI interface */ +void select_mem_mgr(void) +{ + u32 dco; + + ENTERFN(); + + dco = msg_port_read(MEM_CTLR, DCO); + dco &= ~DCO_PMICTL; + msg_port_write(MEM_CTLR, DCO, dco); + + LEAVEFN(); +} + +/* Select HTE as the source for PRI interface */ +void select_hte(void) +{ + u32 dco; + + ENTERFN(); + + dco = msg_port_read(MEM_CTLR, DCO); + dco |= DCO_PMICTL; + msg_port_write(MEM_CTLR, DCO, dco); + + LEAVEFN(); +} + +/* + * Send DRAM command + * data should be formated using DCMD_Xxxx macro or emrsXCommand structure + */ +void dram_init_command(uint32_t data) +{ + qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, data); + qrk_pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG, 0); + msg_port_setup(MSG_OP_DRAM_INIT, MEM_CTLR, 0); + + DPF(D_REGWR, "WR32 %03X %08X %08X\n", MEM_CTLR, 0, data); +} + +/* Send DRAM wake command using special MCU side-band WAKE opcode */ +void dram_wake_command(void) +{ + ENTERFN(); + + msg_port_setup(MSG_OP_DRAM_WAKE, MEM_CTLR, 0); + + LEAVEFN(); +} + +void training_message(uint8_t channel, uint8_t rank, uint8_t byte_lane) +{ + /* send message to UART */ + DPF(D_INFO, "CH%01X RK%01X BL%01X\n", channel, rank, byte_lane); +} + +/* + * This function will program the RCVEN delays + * + * (currently doesn't comprehend rank) + */ +void set_rcvn(uint8_t channel, uint8_t rank, + uint8_t byte_lane, uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t temp; + + ENTERFN(); + + DPF(D_TRN, "Rcvn ch%d rnk%d ln%d : pi=%03X\n", + channel, rank, byte_lane, pi_count); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * BL0 -> B01PTRCTL0[11:08] (0x0-0xF) + * BL1 -> B01PTRCTL0[23:20] (0x0-0xF) + */ + reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + msk = (byte_lane & 1) ? 0xf00000 : 0xf00; + temp = (byte_lane & 1) ? (pi_count / HALF_CLK) << 20 : + (pi_count / HALF_CLK) << 8; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* Adjust PI_COUNT */ + pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F) + * BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F) + */ + reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + msk = 0x3f000000; + temp = pi_count << 24; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* + * DEADBAND + * BL0/1 -> B01DBCTL1[08/11] (+1 select) + * BL0/1 -> B01DBCTL1[02/05] (enable) + */ + reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + msk = 0x00; + temp = 0x00; + + /* enable */ + msk |= (byte_lane & 1) ? (1 << 5) : (1 << 2); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (byte_lane & 1) ? (1 << 11) : (1 << 8); + if (pi_count < EARLY_DB) + temp |= msk; + + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* error check */ + if (pi_count > 0x3f) { + training_message(channel, rank, byte_lane); + mrc_post_code(0xee, 0xe0); + } + + LEAVEFN(); +} + +/* + * This function will return the current RCVEN delay on the given + * channel, rank, byte_lane as an absolute PI count. + * + * (currently doesn't comprehend rank) + */ +uint32_t get_rcvn(uint8_t channel, uint8_t rank, uint8_t byte_lane) +{ + uint32_t reg; + uint32_t temp; + uint32_t pi_count; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * BL0 -> B01PTRCTL0[11:08] (0x0-0xF) + * BL1 -> B01PTRCTL0[23:20] (0x0-0xF) + */ + reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= (byte_lane & 1) ? 20 : 8; + temp &= 0xf; + + /* Adjust PI_COUNT */ + pi_count = temp * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F) + * BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F) + */ + reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= 24; + temp &= 0x3f; + + /* Adjust PI_COUNT */ + pi_count += temp; + + LEAVEFN(); + + return pi_count; +} + +/* + * This function will program the RDQS delays based on an absolute + * amount of PIs. + * + * (currently doesn't comprehend rank) + */ +void set_rdqs(uint8_t channel, uint8_t rank, + uint8_t byte_lane, uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t temp; + + ENTERFN(); + DPF(D_TRN, "Rdqs ch%d rnk%d ln%d : pi=%03X\n", + channel, rank, byte_lane, pi_count); + + /* + * PI (1/128 MCLK) + * BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47) + * BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47) + */ + reg = (byte_lane & 1) ? B1RXDQSPICODE : B0RXDQSPICODE; + reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + msk = 0x7f; + temp = pi_count << 0; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* error check (shouldn't go above 0x3F) */ + if (pi_count > 0x47) { + training_message(channel, rank, byte_lane); + mrc_post_code(0xee, 0xe1); + } + + LEAVEFN(); +} + +/* + * This function will return the current RDQS delay on the given + * channel, rank, byte_lane as an absolute PI count. + * + * (currently doesn't comprehend rank) + */ +uint32_t get_rdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane) +{ + uint32_t reg; + uint32_t temp; + uint32_t pi_count; + + ENTERFN(); + + /* + * PI (1/128 MCLK) + * BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47) + * BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47) + */ + reg = (byte_lane & 1) ? B1RXDQSPICODE : B0RXDQSPICODE; + reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + temp = msg_port_alt_read(DDRPHY, reg); + + /* Adjust PI_COUNT */ + pi_count = temp & 0x7f; + + LEAVEFN(); + + return pi_count; +} + +/* + * This function will program the WDQS delays based on an absolute + * amount of PIs. + * + * (currently doesn't comprehend rank) + */ +void set_wdqs(uint8_t channel, uint8_t rank, + uint8_t byte_lane, uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t temp; + + ENTERFN(); + + DPF(D_TRN, "Wdqs ch%d rnk%d ln%d : pi=%03X\n", + channel, rank, byte_lane, pi_count); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * BL0 -> B01PTRCTL0[07:04] (0x0-0xF) + * BL1 -> B01PTRCTL0[19:16] (0x0-0xF) + */ + reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + msk = (byte_lane & 1) ? 0xf0000 : 0xf0; + temp = pi_count / HALF_CLK; + temp <<= (byte_lane & 1) ? 16 : 4; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* Adjust PI_COUNT */ + pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F) + * BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F) + */ + reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + msk = 0x3f0000; + temp = pi_count << 16; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* + * DEADBAND + * BL0/1 -> B01DBCTL1[07/10] (+1 select) + * BL0/1 -> B01DBCTL1[01/04] (enable) + */ + reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + msk = 0x00; + temp = 0x00; + + /* enable */ + msk |= (byte_lane & 1) ? (1 << 4) : (1 << 1); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (byte_lane & 1) ? (1 << 10) : (1 << 7); + if (pi_count < EARLY_DB) + temp |= msk; + + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* error check */ + if (pi_count > 0x3f) { + training_message(channel, rank, byte_lane); + mrc_post_code(0xee, 0xe2); + } + + LEAVEFN(); +} + +/* + * This function will return the amount of WDQS delay on the given + * channel, rank, byte_lane as an absolute PI count. + * + * (currently doesn't comprehend rank) + */ +uint32_t get_wdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane) +{ + uint32_t reg; + uint32_t temp; + uint32_t pi_count; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * BL0 -> B01PTRCTL0[07:04] (0x0-0xF) + * BL1 -> B01PTRCTL0[19:16] (0x0-0xF) + */ + reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= (byte_lane & 1) ? 16 : 4; + temp &= 0xf; + + /* Adjust PI_COUNT */ + pi_count = (temp * HALF_CLK); + + /* + * PI (1/64 MCLK, 1 PIs) + * BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F) + * BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F) + */ + reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= 16; + temp &= 0x3f; + + /* Adjust PI_COUNT */ + pi_count += temp; + + LEAVEFN(); + + return pi_count; +} + +/* + * This function will program the WDQ delays based on an absolute + * number of PIs. + * + * (currently doesn't comprehend rank) + */ +void set_wdq(uint8_t channel, uint8_t rank, + uint8_t byte_lane, uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t temp; + + ENTERFN(); + + DPF(D_TRN, "Wdq ch%d rnk%d ln%d : pi=%03X\n", + channel, rank, byte_lane, pi_count); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * BL0 -> B01PTRCTL0[03:00] (0x0-0xF) + * BL1 -> B01PTRCTL0[15:12] (0x0-0xF) + */ + reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + msk = (byte_lane & 1) ? 0xf000 : 0xf; + temp = pi_count / HALF_CLK; + temp <<= (byte_lane & 1) ? 12 : 0; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* Adjust PI_COUNT */ + pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F) + * BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F) + */ + reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + msk = 0x3f00; + temp = pi_count << 8; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* + * DEADBAND + * BL0/1 -> B01DBCTL1[06/09] (+1 select) + * BL0/1 -> B01DBCTL1[00/03] (enable) + */ + reg = B01DBCTL1 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + msk = 0x00; + temp = 0x00; + + /* enable */ + msk |= (byte_lane & 1) ? (1 << 3) : (1 << 0); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (byte_lane & 1) ? (1 << 9) : (1 << 6); + if (pi_count < EARLY_DB) + temp |= msk; + + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* error check */ + if (pi_count > 0x3f) { + training_message(channel, rank, byte_lane); + mrc_post_code(0xee, 0xe3); + } + + LEAVEFN(); +} + +/* + * This function will return the amount of WDQ delay on the given + * channel, rank, byte_lane as an absolute PI count. + * + * (currently doesn't comprehend rank) + */ +uint32_t get_wdq(uint8_t channel, uint8_t rank, uint8_t byte_lane) +{ + uint32_t reg; + uint32_t temp; + uint32_t pi_count; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * BL0 -> B01PTRCTL0[03:00] (0x0-0xF) + * BL1 -> B01PTRCTL0[15:12] (0x0-0xF) + */ + reg = B01PTRCTL0 + (byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET; + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= (byte_lane & 1) ? 12 : 0; + temp &= 0xf; + + /* Adjust PI_COUNT */ + pi_count = temp * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F) + * BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F) + */ + reg = (byte_lane & 1) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += ((byte_lane >> 1) * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= 8; + temp &= 0x3f; + + /* Adjust PI_COUNT */ + pi_count += temp; + + LEAVEFN(); + + return pi_count; +} + +/* + * This function will program the WCMD delays based on an absolute + * number of PIs. + */ +void set_wcmd(uint8_t channel, uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t temp; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * CMDPTRREG[11:08] (0x0-0xF) + */ + reg = CMDPTRREG + channel * DDRIOCCC_CH_OFFSET; + msk = 0xf00; + temp = pi_count / HALF_CLK; + temp <<= 8; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* Adjust PI_COUNT */ + pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused) + * CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused) + * CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused) + * CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused) + * CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused) + * CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F) + * CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused) + * CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused) + */ + reg = CMDDLLPICODER1 + channel * DDRIOCCC_CH_OFFSET; + msk = 0x3f3f3f3f; + temp = (pi_count << 24) | (pi_count << 16) | + (pi_count << 8) | (pi_count << 0); + + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + reg = CMDDLLPICODER0 + channel * DDRIOCCC_CH_OFFSET; /* PO */ + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* + * DEADBAND + * CMDCFGREG0[17] (+1 select) + * CMDCFGREG0[16] (enable) + */ + reg = CMDCFGREG0 + channel * DDRIOCCC_CH_OFFSET; + msk = 0x00; + temp = 0x00; + + /* enable */ + msk |= (1 << 16); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (1 << 17); + if (pi_count < EARLY_DB) + temp |= msk; + + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* error check */ + if (pi_count > 0x3f) + mrc_post_code(0xee, 0xe4); + + LEAVEFN(); +} + +/* + * This function will return the amount of WCMD delay on the given + * channel as an absolute PI count. + */ +uint32_t get_wcmd(uint8_t channel) +{ + uint32_t reg; + uint32_t temp; + uint32_t pi_count; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * CMDPTRREG[11:08] (0x0-0xF) + */ + reg = CMDPTRREG + channel * DDRIOCCC_CH_OFFSET; + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= 8; + temp &= 0xf; + + /* Adjust PI_COUNT */ + pi_count = temp * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused) + * CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused) + * CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused) + * CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused) + * CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused) + * CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F) + * CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused) + * CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused) + */ + reg = CMDDLLPICODER1 + channel * DDRIOCCC_CH_OFFSET; + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= 16; + temp &= 0x3f; + + /* Adjust PI_COUNT */ + pi_count += temp; + + LEAVEFN(); + + return pi_count; +} + +/* + * This function will program the WCLK delays based on an absolute + * number of PIs. + */ +void set_wclk(uint8_t channel, uint8_t rank, uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t temp; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * CCPTRREG[15:12] -> CLK1 (0x0-0xF) + * CCPTRREG[11:08] -> CLK0 (0x0-0xF) + */ + reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET; + msk = 0xff00; + temp = ((pi_count / HALF_CLK) << 12) | ((pi_count / HALF_CLK) << 8); + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* Adjust PI_COUNT */ + pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F) + * ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F) + */ + reg = rank ? ECCB1DLLPICODER0 : ECCB1DLLPICODER0; + reg += (channel * DDRIOCCC_CH_OFFSET); + msk = 0x3f3f00; + temp = (pi_count << 16) | (pi_count << 8); + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + reg = rank ? ECCB1DLLPICODER1 : ECCB1DLLPICODER1; + reg += (channel * DDRIOCCC_CH_OFFSET); + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + reg = rank ? ECCB1DLLPICODER2 : ECCB1DLLPICODER2; + reg += (channel * DDRIOCCC_CH_OFFSET); + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + reg = rank ? ECCB1DLLPICODER3 : ECCB1DLLPICODER3; + reg += (channel * DDRIOCCC_CH_OFFSET); + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* + * DEADBAND + * CCCFGREG1[11:08] (+1 select) + * CCCFGREG1[03:00] (enable) + */ + reg = CCCFGREG1 + channel * DDRIOCCC_CH_OFFSET; + msk = 0x00; + temp = 0x00; + + /* enable */ + msk |= 0xf; + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= 0xf00; + if (pi_count < EARLY_DB) + temp |= msk; + + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* error check */ + if (pi_count > 0x3f) + mrc_post_code(0xee, 0xe5); + + LEAVEFN(); +} + +/* + * This function will return the amout of WCLK delay on the given + * channel, rank as an absolute PI count. + */ +uint32_t get_wclk(uint8_t channel, uint8_t rank) +{ + uint32_t reg; + uint32_t temp; + uint32_t pi_count; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * CCPTRREG[15:12] -> CLK1 (0x0-0xF) + * CCPTRREG[11:08] -> CLK0 (0x0-0xF) + */ + reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET; + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= rank ? 12 : 8; + temp &= 0xf; + + /* Adjust PI_COUNT */ + pi_count = temp * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F) + * ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F) + */ + reg = rank ? ECCB1DLLPICODER0 : ECCB1DLLPICODER0; + reg += (channel * DDRIOCCC_CH_OFFSET); + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= rank ? 16 : 8; + temp &= 0x3f; + + pi_count += temp; + + LEAVEFN(); + + return pi_count; +} + +/* + * This function will program the WCTL delays based on an absolute + * number of PIs. + * + * (currently doesn't comprehend rank) + */ +void set_wctl(uint8_t channel, uint8_t rank, uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t temp; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * CCPTRREG[31:28] (0x0-0xF) + * CCPTRREG[27:24] (0x0-0xF) + */ + reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET; + msk = 0xff000000; + temp = ((pi_count / HALF_CLK) << 28) | ((pi_count / HALF_CLK) << 24); + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* Adjust PI_COUNT */ + pi_count -= ((pi_count / HALF_CLK) & 0xf) * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * ECCB1DLLPICODER?[29:24] (0x00-0x3F) + * ECCB1DLLPICODER?[29:24] (0x00-0x3F) + */ + reg = ECCB1DLLPICODER0 + channel * DDRIOCCC_CH_OFFSET; + msk = 0x3f000000; + temp = (pi_count << 24); + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + reg = ECCB1DLLPICODER1 + channel * DDRIOCCC_CH_OFFSET; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + reg = ECCB1DLLPICODER2 + channel * DDRIOCCC_CH_OFFSET; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + reg = ECCB1DLLPICODER3 + channel * DDRIOCCC_CH_OFFSET; + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* + * DEADBAND + * CCCFGREG1[13:12] (+1 select) + * CCCFGREG1[05:04] (enable) + */ + reg = CCCFGREG1 + channel * DDRIOCCC_CH_OFFSET; + msk = 0x00; + temp = 0x00; + + /* enable */ + msk |= 0x30; + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= 0x3000; + if (pi_count < EARLY_DB) + temp |= msk; + + mrc_alt_write_mask(DDRPHY, reg, temp, msk); + + /* error check */ + if (pi_count > 0x3f) + mrc_post_code(0xee, 0xe6); + + LEAVEFN(); +} + +/* + * This function will return the amount of WCTL delay on the given + * channel, rank as an absolute PI count. + * + * (currently doesn't comprehend rank) + */ +uint32_t get_wctl(uint8_t channel, uint8_t rank) +{ + uint32_t reg; + uint32_t temp; + uint32_t pi_count; + + ENTERFN(); + + /* + * RDPTR (1/2 MCLK, 64 PIs) + * CCPTRREG[31:28] (0x0-0xF) + * CCPTRREG[27:24] (0x0-0xF) + */ + reg = CCPTRREG + channel * DDRIOCCC_CH_OFFSET; + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= 24; + temp &= 0xf; + + /* Adjust PI_COUNT */ + pi_count = temp * HALF_CLK; + + /* + * PI (1/64 MCLK, 1 PIs) + * ECCB1DLLPICODER?[29:24] (0x00-0x3F) + * ECCB1DLLPICODER?[29:24] (0x00-0x3F) + */ + reg = ECCB1DLLPICODER0 + channel * DDRIOCCC_CH_OFFSET; + temp = msg_port_alt_read(DDRPHY, reg); + temp >>= 24; + temp &= 0x3f; + + /* Adjust PI_COUNT */ + pi_count += temp; + + LEAVEFN(); + + return pi_count; +} + +/* + * This function will program the internal Vref setting in a given + * byte lane in a given channel. + */ +void set_vref(uint8_t channel, uint8_t byte_lane, uint32_t setting) +{ + uint32_t reg = (byte_lane & 0x1) ? B1VREFCTL : B0VREFCTL; + + ENTERFN(); + + DPF(D_TRN, "Vref ch%d ln%d : val=%03X\n", + channel, byte_lane, setting); + + mrc_alt_write_mask(DDRPHY, reg + channel * DDRIODQ_CH_OFFSET + + (byte_lane >> 1) * DDRIODQ_BL_OFFSET, + vref_codes[setting] << 2, 0xfc); + + /* + * need to wait ~300ns for Vref to settle + * (check that this is necessary) + */ + delay_n(300); + + /* ??? may need to clear pointers ??? */ + + LEAVEFN(); +} + +/* + * This function will return the internal Vref setting for the given + * channel, byte_lane. + */ +uint32_t get_vref(uint8_t channel, uint8_t byte_lane) +{ + uint8_t j; + uint32_t ret_val = sizeof(vref_codes) / 2; + uint32_t reg = (byte_lane & 0x1) ? B1VREFCTL : B0VREFCTL; + uint32_t temp; + + ENTERFN(); + + temp = msg_port_alt_read(DDRPHY, reg + channel * DDRIODQ_CH_OFFSET + + (byte_lane >> 1) * DDRIODQ_BL_OFFSET); + temp >>= 2; + temp &= 0x3f; + + for (j = 0; j < sizeof(vref_codes); j++) { + if (vref_codes[j] == temp) { + ret_val = j; + break; + } + } + + LEAVEFN(); + + return ret_val; +} + +/* + * This function will return a 32-bit address in the desired + * channel and rank. + */ +uint32_t get_addr(uint8_t channel, uint8_t rank) +{ + uint32_t offset = 32 * 1024 * 1024; /* 32MB */ + + /* Begin product specific code */ + if (channel > 0) { + DPF(D_ERROR, "ILLEGAL CHANNEL\n"); + DEAD_LOOP(); + } + + if (rank > 1) { + DPF(D_ERROR, "ILLEGAL RANK\n"); + DEAD_LOOP(); + } + + /* use 256MB lowest density as per DRP == 0x0003 */ + offset += rank * (256 * 1024 * 1024); + + return offset; +} + +/* + * This function will sample the DQTRAINSTS registers in the given + * channel/rank SAMPLE_SIZE times looking for a valid '0' or '1'. + * + * It will return an encoded 32-bit date in which each bit corresponds to + * the sampled value on the byte lane. + */ +uint32_t sample_dqs(struct mrc_params *mrc_params, uint8_t channel, + uint8_t rank, bool rcvn) +{ + uint8_t j; /* just a counter */ + uint8_t bl; /* which BL in the module (always 2 per module) */ + uint8_t bl_grp; /* which BL module */ + /* byte lane divisor */ + uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; + uint32_t msk[2]; /* BLx in module */ + /* DQTRAINSTS register contents for each sample */ + uint32_t sampled_val[SAMPLE_SIZE]; + uint32_t num_0s; /* tracks the number of '0' samples */ + uint32_t num_1s; /* tracks the number of '1' samples */ + uint32_t ret_val = 0x00; /* assume all '0' samples */ + uint32_t address = get_addr(channel, rank); + + /* initialise msk[] */ + msk[0] = rcvn ? (1 << 1) : (1 << 9); /* BL0 */ + msk[1] = rcvn ? (1 << 0) : (1 << 8); /* BL1 */ + + /* cycle through each byte lane group */ + for (bl_grp = 0; bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; bl_grp++) { + /* take SAMPLE_SIZE samples */ + for (j = 0; j < SAMPLE_SIZE; j++) { + hte_mem_op(address, mrc_params->first_run, + rcvn ? 0 : 1); + mrc_params->first_run = 0; + + /* + * record the contents of the proper + * DQTRAINSTS register + */ + sampled_val[j] = msg_port_alt_read(DDRPHY, + DQTRAINSTS + + bl_grp * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET); + } + + /* + * look for a majority value (SAMPLE_SIZE / 2) + 1 + * on the byte lane and set that value in the corresponding + * ret_val bit + */ + for (bl = 0; bl < 2; bl++) { + num_0s = 0x00; /* reset '0' tracker for byte lane */ + num_1s = 0x00; /* reset '1' tracker for byte lane */ + for (j = 0; j < SAMPLE_SIZE; j++) { + if (sampled_val[j] & msk[bl]) + num_1s++; + else + num_0s++; + } + if (num_1s > num_0s) + ret_val |= (1 << (bl + bl_grp * 2)); + } + } + + /* + * "ret_val.0" contains the status of BL0 + * "ret_val.1" contains the status of BL1 + * "ret_val.2" contains the status of BL2 + * etc. + */ + return ret_val; +} + +/* This function will find the rising edge transition on RCVN or WDQS */ +void find_rising_edge(struct mrc_params *mrc_params, uint32_t delay[], + uint8_t channel, uint8_t rank, bool rcvn) +{ + bool all_edges_found; /* determines stop condition */ + bool direction[NUM_BYTE_LANES]; /* direction indicator */ + uint8_t sample; /* sample counter */ + uint8_t bl; /* byte lane counter */ + /* byte lane divisor */ + uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; + uint32_t sample_result[SAMPLE_CNT]; /* results of sample_dqs() */ + uint32_t temp; + uint32_t transition_pattern; + + ENTERFN(); + + /* select hte and request initial configuration */ + select_hte(); + mrc_params->first_run = 1; + + /* Take 3 sample points (T1,T2,T3) to obtain a transition pattern */ + for (sample = 0; sample < SAMPLE_CNT; sample++) { + /* program the desired delays for sample */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + /* increase sample delay by 26 PI (0.2 CLK) */ + if (rcvn) { + set_rcvn(channel, rank, bl, + delay[bl] + sample * SAMPLE_DLY); + } else { + set_wdqs(channel, rank, bl, + delay[bl] + sample * SAMPLE_DLY); + } + } + + /* take samples (Tsample_i) */ + sample_result[sample] = sample_dqs(mrc_params, + channel, rank, rcvn); + + DPF(D_TRN, + "Find rising edge %s ch%d rnk%d: #%d dly=%d dqs=%02X\n", + rcvn ? "RCVN" : "WDQS", channel, rank, sample, + sample * SAMPLE_DLY, sample_result[sample]); + } + + /* + * This pattern will help determine where we landed and ultimately + * how to place RCVEN/WDQS. + */ + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + /* build transition_pattern (MSB is 1st sample) */ + transition_pattern = 0; + for (sample = 0; sample < SAMPLE_CNT; sample++) { + transition_pattern |= + ((sample_result[sample] & (1 << bl)) >> bl) << + (SAMPLE_CNT - 1 - sample); + } + + DPF(D_TRN, "=== transition pattern %d\n", transition_pattern); + + /* + * set up to look for rising edge based on + * transition_pattern + */ + switch (transition_pattern) { + case 0: /* sampled 0->0->0 */ + /* move forward from T3 looking for 0->1 */ + delay[bl] += 2 * SAMPLE_DLY; + direction[bl] = FORWARD; + break; + case 1: /* sampled 0->0->1 */ + case 5: /* sampled 1->0->1 (bad duty cycle) *HSD#237503* */ + /* move forward from T2 looking for 0->1 */ + delay[bl] += 1 * SAMPLE_DLY; + direction[bl] = FORWARD; + break; + case 2: /* sampled 0->1->0 (bad duty cycle) *HSD#237503* */ + case 3: /* sampled 0->1->1 */ + /* move forward from T1 looking for 0->1 */ + delay[bl] += 0 * SAMPLE_DLY; + direction[bl] = FORWARD; + break; + case 4: /* sampled 1->0->0 (assumes BL8, HSD#234975) */ + /* move forward from T3 looking for 0->1 */ + delay[bl] += 2 * SAMPLE_DLY; + direction[bl] = FORWARD; + break; + case 6: /* sampled 1->1->0 */ + case 7: /* sampled 1->1->1 */ + /* move backward from T1 looking for 1->0 */ + delay[bl] += 0 * SAMPLE_DLY; + direction[bl] = BACKWARD; + break; + default: + mrc_post_code(0xee, 0xee); + break; + } + + /* program delays */ + if (rcvn) + set_rcvn(channel, rank, bl, delay[bl]); + else + set_wdqs(channel, rank, bl, delay[bl]); + } + + /* + * Based on the observed transition pattern on the byte lane, + * begin looking for a rising edge with single PI granularity. + */ + do { + all_edges_found = true; /* assume all byte lanes passed */ + /* take a sample */ + temp = sample_dqs(mrc_params, channel, rank, rcvn); + /* check all each byte lane for proper edge */ + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + if (temp & (1 << bl)) { + /* sampled "1" */ + if (direction[bl] == BACKWARD) { + /* + * keep looking for edge + * on this byte lane + */ + all_edges_found = false; + delay[bl] -= 1; + if (rcvn) { + set_rcvn(channel, rank, + bl, delay[bl]); + } else { + set_wdqs(channel, rank, + bl, delay[bl]); + } + } + } else { + /* sampled "0" */ + if (direction[bl] == FORWARD) { + /* + * keep looking for edge + * on this byte lane + */ + all_edges_found = false; + delay[bl] += 1; + if (rcvn) { + set_rcvn(channel, rank, + bl, delay[bl]); + } else { + set_wdqs(channel, rank, + bl, delay[bl]); + } + } + } + } + } while (!all_edges_found); + + /* restore DDR idle state */ + dram_init_command(DCMD_PREA(rank)); + + DPF(D_TRN, "Delay %03X %03X %03X %03X\n", + delay[0], delay[1], delay[2], delay[3]); + + LEAVEFN(); +} + +/* + * This function will return a 32 bit mask that will be used to + * check for byte lane failures. + */ +uint32_t byte_lane_mask(struct mrc_params *mrc_params) +{ + uint32_t j; + uint32_t ret_val = 0x00; + + /* + * set ret_val based on NUM_BYTE_LANES such that you will check + * only BL0 in result + * + * (each bit in result represents a byte lane) + */ + for (j = 0; j < MAX_BYTE_LANES; j += NUM_BYTE_LANES) + ret_val |= (1 << ((j / NUM_BYTE_LANES) * NUM_BYTE_LANES)); + + /* + * HSD#235037 + * need to adjust the mask for 16-bit mode + */ + if (mrc_params->channel_width == X16) + ret_val |= (ret_val << 2); + + return ret_val; +} + +/* + * Check memory executing simple write/read/verify at the specified address. + * + * Bits in the result indicate failure on specific byte lane. + */ +uint32_t check_rw_coarse(struct mrc_params *mrc_params, uint32_t address) +{ + uint32_t result = 0; + uint8_t first_run = 0; + + if (mrc_params->hte_setup) { + mrc_params->hte_setup = 0; + first_run = 1; + select_hte(); + } + + result = hte_basic_write_read(mrc_params, address, first_run, + WRITE_TRAIN); + + DPF(D_TRN, "check_rw_coarse result is %x\n", result); + + return result; +} + +/* + * Check memory executing write/read/verify of many data patterns + * at the specified address. Bits in the result indicate failure + * on specific byte lane. + */ +uint32_t check_bls_ex(struct mrc_params *mrc_params, uint32_t address) +{ + uint32_t result; + uint8_t first_run = 0; + + if (mrc_params->hte_setup) { + mrc_params->hte_setup = 0; + first_run = 1; + select_hte(); + } + + result = hte_write_stress_bit_lanes(mrc_params, address, first_run); + + DPF(D_TRN, "check_bls_ex result is %x\n", result); + + return result; +} + +/* + * 32-bit LFSR with characteristic polynomial: X^32 + X^22 +X^2 + X^1 + * + * The function takes pointer to previous 32 bit value and + * modifies it to next value. + */ +void lfsr32(uint32_t *lfsr_ptr) +{ + uint32_t bit; + uint32_t lfsr; + int i; + + lfsr = *lfsr_ptr; + + for (i = 0; i < 32; i++) { + bit = 1 ^ (lfsr & 1); + bit = bit ^ ((lfsr & 2) >> 1); + bit = bit ^ ((lfsr & 4) >> 2); + bit = bit ^ ((lfsr & 0x400000) >> 22); + + lfsr = ((lfsr >> 1) | (bit << 31)); + } + + *lfsr_ptr = lfsr; +} + +/* Clear the pointers in a given byte lane in a given channel */ +void clear_pointers(void) +{ + uint8_t channel; + uint8_t bl; + + ENTERFN(); + + for (channel = 0; channel < NUM_CHANNELS; channel++) { + for (bl = 0; bl < NUM_BYTE_LANES; bl++) { + mrc_alt_write_mask(DDRPHY, + B01PTRCTL1 + + channel * DDRIODQ_CH_OFFSET + + (bl >> 1) * DDRIODQ_BL_OFFSET, + ~(1 << 8), (1 << 8)); + + mrc_alt_write_mask(DDRPHY, + B01PTRCTL1 + + channel * DDRIODQ_CH_OFFSET + + (bl >> 1) * DDRIODQ_BL_OFFSET, + (1 << 8), (1 << 8)); + } + } + + LEAVEFN(); +} + +static void print_timings_internal(uint8_t algo, uint8_t channel, uint8_t rank, + uint8_t bl_divisor) +{ + uint8_t bl; + + switch (algo) { + case RCVN: + DPF(D_INFO, "\nRCVN[%02d:%02d]", channel, rank); + break; + case WDQS: + DPF(D_INFO, "\nWDQS[%02d:%02d]", channel, rank); + break; + case WDQX: + DPF(D_INFO, "\nWDQx[%02d:%02d]", channel, rank); + break; + case RDQS: + DPF(D_INFO, "\nRDQS[%02d:%02d]", channel, rank); + break; + case VREF: + DPF(D_INFO, "\nVREF[%02d:%02d]", channel, rank); + break; + case WCMD: + DPF(D_INFO, "\nWCMD[%02d:%02d]", channel, rank); + break; + case WCTL: + DPF(D_INFO, "\nWCTL[%02d:%02d]", channel, rank); + break; + case WCLK: + DPF(D_INFO, "\nWCLK[%02d:%02d]", channel, rank); + break; + default: + break; + } + + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + switch (algo) { + case RCVN: + DPF(D_INFO, " %03d", get_rcvn(channel, rank, bl)); + break; + case WDQS: + DPF(D_INFO, " %03d", get_wdqs(channel, rank, bl)); + break; + case WDQX: + DPF(D_INFO, " %03d", get_wdq(channel, rank, bl)); + break; + case RDQS: + DPF(D_INFO, " %03d", get_rdqs(channel, rank, bl)); + break; + case VREF: + DPF(D_INFO, " %03d", get_vref(channel, bl)); + break; + case WCMD: + DPF(D_INFO, " %03d", get_wcmd(channel)); + break; + case WCTL: + DPF(D_INFO, " %03d", get_wctl(channel, rank)); + break; + case WCLK: + DPF(D_INFO, " %03d", get_wclk(channel, rank)); + break; + default: + break; + } + } +} + +void print_timings(struct mrc_params *mrc_params) +{ + uint8_t algo; + uint8_t channel; + uint8_t rank; + uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; + + DPF(D_INFO, "\n---------------------------"); + DPF(D_INFO, "\nALGO[CH:RK] BL0 BL1 BL2 BL3"); + DPF(D_INFO, "\n==========================="); + + for (algo = 0; algo < MAX_ALGOS; algo++) { + for (channel = 0; channel < NUM_CHANNELS; channel++) { + if (mrc_params->channel_enables & (1 << channel)) { + for (rank = 0; rank < NUM_RANKS; rank++) { + if (mrc_params->rank_enables & + (1 << rank)) { + print_timings_internal(algo, + channel, rank, + bl_divisor); + } + } + } + } + } + + DPF(D_INFO, "\n---------------------------"); + DPF(D_INFO, "\n"); +} diff --git a/roms/u-boot/arch/x86/cpu/quark/mrc_util.h b/roms/u-boot/arch/x86/cpu/quark/mrc_util.h new file mode 100644 index 000000000..e6e69c49d --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/mrc_util.h @@ -0,0 +1,120 @@ +/* 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_ + +#include <log.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_ */ diff --git a/roms/u-boot/arch/x86/cpu/quark/msg_port.c b/roms/u-boot/arch/x86/cpu/quark/msg_port.c new file mode 100644 index 000000000..d4f8c082f --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/msg_port.c @@ -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); +} diff --git a/roms/u-boot/arch/x86/cpu/quark/quark.c b/roms/u-boot/arch/x86/cpu/quark/quark.c new file mode 100644 index 000000000..30b4711b9 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/quark.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <init.h> +#include <mmc.h> +#include <asm/cache.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> +#include <linux/delay.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_init(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; +} diff --git a/roms/u-boot/arch/x86/cpu/quark/smc.c b/roms/u-boot/arch/x86/cpu/quark/smc.c new file mode 100644 index 000000000..b4b3e1204 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/smc.c @@ -0,0 +1,2615 @@ +// 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 <pci.h> +#include <asm/arch/device.h> +#include <asm/arch/mrc.h> +#include <asm/arch/msg_port.h> +#include "mrc_util.h" +#include "hte.h" +#include "smc.h" + +/* t_ck clock period in picoseconds per speed index 800, 1066, 1333 */ +static const uint32_t t_ck[3] = { + 2500, + 1875, + 1500 +}; + +/* Global variables */ +static const uint16_t ddr_wclk[] = {193, 158}; +#ifdef BACKUP_WCTL +static const uint16_t ddr_wctl[] = {1, 217}; +#endif +#ifdef BACKUP_WCMD +static const uint16_t ddr_wcmd[] = {1, 220}; +#endif + +#ifdef BACKUP_RCVN +static const uint16_t ddr_rcvn[] = {129, 498}; +#endif + +#ifdef BACKUP_WDQS +static const uint16_t ddr_wdqs[] = {65, 289}; +#endif + +#ifdef BACKUP_RDQS +static const uint8_t ddr_rdqs[] = {32, 24}; +#endif + +#ifdef BACKUP_WDQ +static const uint16_t ddr_wdq[] = {32, 257}; +#endif + +/* Stop self refresh driven by MCU */ +void clear_self_refresh(struct mrc_params *mrc_params) +{ + ENTERFN(); + + /* clear the PMSTS Channel Self Refresh bits */ + mrc_write_mask(MEM_CTLR, PMSTS, PMSTS_DISR, PMSTS_DISR); + + LEAVEFN(); +} + +/* It will initialize timing registers in the MCU (DTR0..DTR4) */ +void prog_ddr_timing_control(struct mrc_params *mrc_params) +{ + uint8_t tcl, wl; + uint8_t trp, trcd, tras, twr, twtr, trrd, trtp, tfaw; + uint32_t tck; + u32 dtr0, dtr1, dtr2, dtr3, dtr4; + u32 tmp1, tmp2; + + ENTERFN(); + + /* mcu_init starts */ + mrc_post_code(0x02, 0x00); + + dtr0 = msg_port_read(MEM_CTLR, DTR0); + dtr1 = msg_port_read(MEM_CTLR, DTR1); + dtr2 = msg_port_read(MEM_CTLR, DTR2); + dtr3 = msg_port_read(MEM_CTLR, DTR3); + dtr4 = msg_port_read(MEM_CTLR, DTR4); + + tck = t_ck[mrc_params->ddr_speed]; /* Clock in picoseconds */ + tcl = mrc_params->params.cl; /* CAS latency in clocks */ + trp = tcl; /* Per CAT MRC */ + trcd = tcl; /* Per CAT MRC */ + tras = MCEIL(mrc_params->params.ras, tck); + + /* Per JEDEC: tWR=15000ps DDR2/3 from 800-1600 */ + twr = MCEIL(15000, tck); + + twtr = MCEIL(mrc_params->params.wtr, tck); + trrd = MCEIL(mrc_params->params.rrd, tck); + trtp = 4; /* Valid for 800 and 1066, use 5 for 1333 */ + tfaw = MCEIL(mrc_params->params.faw, tck); + + wl = 5 + mrc_params->ddr_speed; + + dtr0 &= ~DTR0_DFREQ_MASK; + dtr0 |= mrc_params->ddr_speed; + dtr0 &= ~DTR0_TCL_MASK; + tmp1 = tcl - 5; + dtr0 |= ((tcl - 5) << 12); + dtr0 &= ~DTR0_TRP_MASK; + dtr0 |= ((trp - 5) << 4); /* 5 bit DRAM Clock */ + dtr0 &= ~DTR0_TRCD_MASK; + dtr0 |= ((trcd - 5) << 8); /* 5 bit DRAM Clock */ + + dtr1 &= ~DTR1_TWCL_MASK; + tmp2 = wl - 3; + dtr1 |= (wl - 3); + dtr1 &= ~DTR1_TWTP_MASK; + dtr1 |= ((wl + 4 + twr - 14) << 8); /* Change to tWTP */ + dtr1 &= ~DTR1_TRTP_MASK; + dtr1 |= ((MMAX(trtp, 4) - 3) << 28); /* 4 bit DRAM Clock */ + dtr1 &= ~DTR1_TRRD_MASK; + dtr1 |= ((trrd - 4) << 24); /* 4 bit DRAM Clock */ + dtr1 &= ~DTR1_TCMD_MASK; + dtr1 |= (1 << 4); + dtr1 &= ~DTR1_TRAS_MASK; + dtr1 |= ((tras - 14) << 20); /* 6 bit DRAM Clock */ + dtr1 &= ~DTR1_TFAW_MASK; + dtr1 |= ((((tfaw + 1) >> 1) - 5) << 16);/* 4 bit DRAM Clock */ + /* Set 4 Clock CAS to CAS delay (multi-burst) */ + dtr1 &= ~DTR1_TCCD_MASK; + + dtr2 &= ~DTR2_TRRDR_MASK; + dtr2 |= 1; + dtr2 &= ~DTR2_TWWDR_MASK; + dtr2 |= (2 << 8); + dtr2 &= ~DTR2_TRWDR_MASK; + dtr2 |= (2 << 16); + + dtr3 &= ~DTR3_TWRDR_MASK; + dtr3 |= 2; + dtr3 &= ~DTR3_TXXXX_MASK; + dtr3 |= (2 << 4); + + dtr3 &= ~DTR3_TRWSR_MASK; + if (mrc_params->ddr_speed == DDRFREQ_800) { + /* Extended RW delay (+1) */ + dtr3 |= ((tcl - 5 + 1) << 8); + } else if (mrc_params->ddr_speed == DDRFREQ_1066) { + /* Extended RW delay (+1) */ + dtr3 |= ((tcl - 5 + 1) << 8); + } + + dtr3 &= ~DTR3_TWRSR_MASK; + dtr3 |= ((4 + wl + twtr - 11) << 13); + + dtr3 &= ~DTR3_TXP_MASK; + if (mrc_params->ddr_speed == DDRFREQ_800) + dtr3 |= ((MMAX(0, 1 - 1)) << 22); + else + dtr3 |= ((MMAX(0, 2 - 1)) << 22); + + dtr4 &= ~DTR4_WRODTSTRT_MASK; + dtr4 |= 1; + dtr4 &= ~DTR4_WRODTSTOP_MASK; + dtr4 |= (1 << 4); + dtr4 &= ~DTR4_XXXX1_MASK; + dtr4 |= ((1 + tmp1 - tmp2 + 2) << 8); + dtr4 &= ~DTR4_XXXX2_MASK; + dtr4 |= ((1 + tmp1 - tmp2 + 2) << 12); + dtr4 &= ~(DTR4_ODTDIS | DTR4_TRGSTRDIS); + + msg_port_write(MEM_CTLR, DTR0, dtr0); + msg_port_write(MEM_CTLR, DTR1, dtr1); + msg_port_write(MEM_CTLR, DTR2, dtr2); + msg_port_write(MEM_CTLR, DTR3, dtr3); + msg_port_write(MEM_CTLR, DTR4, dtr4); + + LEAVEFN(); +} + +/* Configure MCU before jedec init sequence */ +void prog_decode_before_jedec(struct mrc_params *mrc_params) +{ + u32 drp; + u32 drfc; + u32 dcal; + u32 dsch; + u32 dpmc0; + + ENTERFN(); + + /* Disable power saving features */ + dpmc0 = msg_port_read(MEM_CTLR, DPMC0); + dpmc0 |= (DPMC0_CLKGTDIS | DPMC0_DISPWRDN); + dpmc0 &= ~DPMC0_PCLSTO_MASK; + dpmc0 &= ~DPMC0_DYNSREN; + msg_port_write(MEM_CTLR, DPMC0, dpmc0); + + /* Disable out of order transactions */ + dsch = msg_port_read(MEM_CTLR, DSCH); + dsch |= (DSCH_OOODIS | DSCH_NEWBYPDIS); + msg_port_write(MEM_CTLR, DSCH, dsch); + + /* Disable issuing the REF command */ + drfc = msg_port_read(MEM_CTLR, DRFC); + drfc &= ~DRFC_TREFI_MASK; + msg_port_write(MEM_CTLR, DRFC, drfc); + + /* Disable ZQ calibration short */ + dcal = msg_port_read(MEM_CTLR, DCAL); + dcal &= ~DCAL_ZQCINT_MASK; + dcal &= ~DCAL_SRXZQCL_MASK; + msg_port_write(MEM_CTLR, DCAL, dcal); + + /* + * Training performed in address mode 0, rank population has limited + * impact, however simulator complains if enabled non-existing rank. + */ + drp = 0; + if (mrc_params->rank_enables & 1) + drp |= DRP_RKEN0; + if (mrc_params->rank_enables & 2) + drp |= DRP_RKEN1; + msg_port_write(MEM_CTLR, DRP, drp); + + LEAVEFN(); +} + +/* + * After Cold Reset, BIOS should set COLDWAKE bit to 1 before + * sending the WAKE message to the Dunit. + * + * For Standby Exit, or any other mode in which the DRAM is in + * SR, this bit must be set to 0. + */ +void perform_ddr_reset(struct mrc_params *mrc_params) +{ + ENTERFN(); + + /* Set COLDWAKE bit before sending the WAKE message */ + mrc_write_mask(MEM_CTLR, DRMC, DRMC_COLDWAKE, DRMC_COLDWAKE); + + /* Send wake command to DUNIT (MUST be done before JEDEC) */ + dram_wake_command(); + + /* Set default value */ + msg_port_write(MEM_CTLR, DRMC, + mrc_params->rd_odt_value == 0 ? DRMC_ODTMODE : 0); + + LEAVEFN(); +} + + +/* + * This function performs some initialization on the DDRIO unit. + * This function is dependent on BOARD_ID, DDR_SPEED, and CHANNEL_ENABLES. + */ +void ddrphy_init(struct mrc_params *mrc_params) +{ + uint32_t temp; + uint8_t ch; /* channel counter */ + uint8_t rk; /* rank counter */ + uint8_t bl_grp; /* byte lane group counter (2 BLs per module) */ + uint8_t bl_divisor = 1; /* byte lane divisor */ + /* For DDR3 --> 0 == 800, 1 == 1066, 2 == 1333 */ + uint8_t speed = mrc_params->ddr_speed & 3; + uint8_t cas; + uint8_t cwl; + + ENTERFN(); + + cas = mrc_params->params.cl; + cwl = 5 + mrc_params->ddr_speed; + + /* ddrphy_init starts */ + mrc_post_code(0x03, 0x00); + + /* + * HSD#231531 + * Make sure IOBUFACT is deasserted before initializing the DDR PHY + * + * HSD#234845 + * Make sure WRPTRENABLE is deasserted before initializing the DDR PHY + */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + /* Deassert DDRPHY Initialization Complete */ + mrc_alt_write_mask(DDRPHY, + CMDPMCONFIG0 + ch * DDRIOCCC_CH_OFFSET, + ~(1 << 20), 1 << 20); /* SPID_INIT_COMPLETE=0 */ + /* Deassert IOBUFACT */ + mrc_alt_write_mask(DDRPHY, + CMDCFGREG0 + ch * DDRIOCCC_CH_OFFSET, + ~(1 << 2), 1 << 2); /* IOBUFACTRST_N=0 */ + /* Disable WRPTR */ + mrc_alt_write_mask(DDRPHY, + CMDPTRREG + ch * DDRIOCCC_CH_OFFSET, + ~(1 << 0), 1 << 0); /* WRPTRENABLE=0 */ + } + } + + /* Put PHY in reset */ + mrc_alt_write_mask(DDRPHY, MASTERRSTN, 0, 1); + + /* Initialize DQ01, DQ23, CMD, CLK-CTL, COMP modules */ + + /* STEP0 */ + mrc_post_code(0x03, 0x10); + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + /* DQ01-DQ23 */ + for (bl_grp = 0; + bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; + bl_grp++) { + /* Analog MUX select - IO2xCLKSEL */ + mrc_alt_write_mask(DDRPHY, + DQOBSCKEBBCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + bl_grp ? 0 : (1 << 22), 1 << 22); + + /* ODT Strength */ + switch (mrc_params->rd_odt_value) { + case 1: + temp = 0x3; + break; /* 60 ohm */ + case 2: + temp = 0x3; + break; /* 120 ohm */ + case 3: + temp = 0x3; + break; /* 180 ohm */ + default: + temp = 0x3; + break; /* 120 ohm */ + } + + /* ODT strength */ + mrc_alt_write_mask(DDRPHY, + B0RXIOBUFCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + temp << 5, 0x60); + /* ODT strength */ + mrc_alt_write_mask(DDRPHY, + B1RXIOBUFCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + temp << 5, 0x60); + + /* Dynamic ODT/DIFFAMP */ + temp = (cas << 24) | (cas << 16) | + (cas << 8) | (cas << 0); + switch (speed) { + case 0: + temp -= 0x01010101; + break; /* 800 */ + case 1: + temp -= 0x02020202; + break; /* 1066 */ + case 2: + temp -= 0x03030303; + break; /* 1333 */ + case 3: + temp -= 0x04040404; + break; /* 1600 */ + } + + /* Launch Time: ODT, DIFFAMP, ODT, DIFFAMP */ + mrc_alt_write_mask(DDRPHY, + B01LATCTL1 + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + temp, 0x1f1f1f1f); + switch (speed) { + /* HSD#234715 */ + case 0: + temp = (0x06 << 16) | (0x07 << 8); + break; /* 800 */ + case 1: + temp = (0x07 << 16) | (0x08 << 8); + break; /* 1066 */ + case 2: + temp = (0x09 << 16) | (0x0a << 8); + break; /* 1333 */ + case 3: + temp = (0x0a << 16) | (0x0b << 8); + break; /* 1600 */ + } + + /* On Duration: ODT, DIFFAMP */ + mrc_alt_write_mask(DDRPHY, + B0ONDURCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + temp, 0x003f3f00); + /* On Duration: ODT, DIFFAMP */ + mrc_alt_write_mask(DDRPHY, + B1ONDURCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + temp, 0x003f3f00); + + switch (mrc_params->rd_odt_value) { + case 0: + /* override DIFFAMP=on, ODT=off */ + temp = (0x3f << 16) | (0x3f << 10); + break; + default: + /* override DIFFAMP=on, ODT=on */ + temp = (0x3f << 16) | (0x2a << 10); + break; + } + + /* Override: DIFFAMP, ODT */ + mrc_alt_write_mask(DDRPHY, + B0OVRCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + temp, 0x003ffc00); + /* Override: DIFFAMP, ODT */ + mrc_alt_write_mask(DDRPHY, + B1OVRCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + temp, 0x003ffc00); + + /* DLL Setup */ + + /* 1xCLK Domain Timings: tEDP,RCVEN,WDQS (PO) */ + mrc_alt_write_mask(DDRPHY, + B0LATCTL0 + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + ((cas + 7) << 16) | ((cas - 4) << 8) | + ((cwl - 2) << 0), 0x003f1f1f); + mrc_alt_write_mask(DDRPHY, + B1LATCTL0 + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + ((cas + 7) << 16) | ((cas - 4) << 8) | + ((cwl - 2) << 0), 0x003f1f1f); + + /* RCVEN Bypass (PO) */ + mrc_alt_write_mask(DDRPHY, + B0RXIOBUFCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 0, 0x81); + mrc_alt_write_mask(DDRPHY, + B1RXIOBUFCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 0, 0x81); + + /* TX */ + mrc_alt_write_mask(DDRPHY, + DQCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 1 << 16, 1 << 16); + mrc_alt_write_mask(DDRPHY, + B01PTRCTL1 + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 1 << 8, 1 << 8); + + /* RX (PO) */ + /* Internal Vref Code, Enable#, Ext_or_Int (1=Ext) */ + mrc_alt_write_mask(DDRPHY, + B0VREFCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + (0x03 << 2) | (0x0 << 1) | (0x0 << 0), + 0xff); + /* Internal Vref Code, Enable#, Ext_or_Int (1=Ext) */ + mrc_alt_write_mask(DDRPHY, + B1VREFCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + (0x03 << 2) | (0x0 << 1) | (0x0 << 0), + 0xff); + /* Per-Bit De-Skew Enable */ + mrc_alt_write_mask(DDRPHY, + B0RXIOBUFCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 0, 0x10); + /* Per-Bit De-Skew Enable */ + mrc_alt_write_mask(DDRPHY, + B1RXIOBUFCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 0, 0x10); + } + + /* CLKEBB */ + mrc_alt_write_mask(DDRPHY, + CMDOBSCKEBBCTL + ch * DDRIOCCC_CH_OFFSET, + 0, 1 << 23); + + /* Enable tristate control of cmd/address bus */ + mrc_alt_write_mask(DDRPHY, + CMDCFGREG0 + ch * DDRIOCCC_CH_OFFSET, + 0, 0x03); + + /* ODT RCOMP */ + mrc_alt_write_mask(DDRPHY, + CMDRCOMPODT + ch * DDRIOCCC_CH_OFFSET, + (0x03 << 5) | (0x03 << 0), 0x3ff); + + /* CMDPM* registers must be programmed in this order */ + + /* Turn On Delays: SFR (regulator), MPLL */ + mrc_alt_write_mask(DDRPHY, + CMDPMDLYREG4 + ch * DDRIOCCC_CH_OFFSET, + 0xffffffff, 0xffffffff); + /* + * Delays: ASSERT_IOBUFACT_to_ALLON0_for_PM_MSG_3, + * VREG (MDLL) Turn On, ALLON0_to_DEASSERT_IOBUFACT + * for_PM_MSG_gt0, MDLL Turn On + */ + mrc_alt_write_mask(DDRPHY, + CMDPMDLYREG3 + ch * DDRIOCCC_CH_OFFSET, + 0xfffff616, 0xffffffff); + /* MPLL Divider Reset Delays */ + mrc_alt_write_mask(DDRPHY, + CMDPMDLYREG2 + ch * DDRIOCCC_CH_OFFSET, + 0xffffffff, 0xffffffff); + /* Turn Off Delays: VREG, Staggered MDLL, MDLL, PI */ + mrc_alt_write_mask(DDRPHY, + CMDPMDLYREG1 + ch * DDRIOCCC_CH_OFFSET, + 0xffffffff, 0xffffffff); + /* Turn On Delays: MPLL, Staggered MDLL, PI, IOBUFACT */ + mrc_alt_write_mask(DDRPHY, + CMDPMDLYREG0 + ch * DDRIOCCC_CH_OFFSET, + 0xffffffff, 0xffffffff); + /* Allow PUnit signals */ + mrc_alt_write_mask(DDRPHY, + CMDPMCONFIG0 + ch * DDRIOCCC_CH_OFFSET, + (0x6 << 8) | (0x1 << 6) | (0x4 << 0), + 0xffe00f4f); + /* DLL_VREG Bias Trim, VREF Tuning for DLL_VREG */ + mrc_alt_write_mask(DDRPHY, + CMDMDLLCTL + ch * DDRIOCCC_CH_OFFSET, + (0x3 << 4) | (0x7 << 0), 0x7f); + + /* CLK-CTL */ + mrc_alt_write_mask(DDRPHY, + CCOBSCKEBBCTL + ch * DDRIOCCC_CH_OFFSET, + 0, 1 << 24); /* CLKEBB */ + /* Buffer Enable: CS,CKE,ODT,CLK */ + mrc_alt_write_mask(DDRPHY, + CCCFGREG0 + ch * DDRIOCCC_CH_OFFSET, + 0x1f, 0x000ffff1); + /* ODT RCOMP */ + mrc_alt_write_mask(DDRPHY, + CCRCOMPODT + ch * DDRIOCCC_CH_OFFSET, + (0x03 << 8) | (0x03 << 0), 0x00001f1f); + /* DLL_VREG Bias Trim, VREF Tuning for DLL_VREG */ + mrc_alt_write_mask(DDRPHY, + CCMDLLCTL + ch * DDRIOCCC_CH_OFFSET, + (0x3 << 4) | (0x7 << 0), 0x7f); + + /* + * COMP (RON channel specific) + * - DQ/DQS/DM RON: 32 Ohm + * - CTRL/CMD RON: 27 Ohm + * - CLK RON: 26 Ohm + */ + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + DQVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x08 << 24) | (0x03 << 16), 0x3f3f0000); + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + CMDVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x0C << 24) | (0x03 << 16), 0x3f3f0000); + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + CLKVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x0F << 24) | (0x03 << 16), 0x3f3f0000); + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + DQSVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x08 << 24) | (0x03 << 16), 0x3f3f0000); + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + CTLVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x0C << 24) | (0x03 << 16), 0x3f3f0000); + + /* DQS Swapped Input Enable */ + mrc_alt_write_mask(DDRPHY, + COMPEN1CH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 19) | (1 << 17), 0xc00ac000); + + /* ODT VREF = 1.5 x 274/360+274 = 0.65V (code of ~50) */ + /* ODT Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + DQVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x32 << 8) | (0x03 << 0), 0x00003f3f); + /* ODT Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + DQSVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x32 << 8) | (0x03 << 0), 0x00003f3f); + /* ODT Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + CLKVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x0E << 8) | (0x05 << 0), 0x00003f3f); + + /* + * Slew rate settings are frequency specific, + * numbers below are for 800Mhz (speed == 0) + * - DQ/DQS/DM/CLK SR: 4V/ns, + * - CTRL/CMD SR: 1.5V/ns + */ + temp = (0x0e << 16) | (0x0e << 12) | (0x08 << 8) | + (0x0b << 4) | (0x0b << 0); + /* DCOMP Delay Select: CTL,CMD,CLK,DQS,DQ */ + mrc_alt_write_mask(DDRPHY, + DLYSELCH0 + ch * DDRCOMP_CH_OFFSET, + temp, 0x000fffff); + /* TCO Vref CLK,DQS,DQ */ + mrc_alt_write_mask(DDRPHY, + TCOVREFCH0 + ch * DDRCOMP_CH_OFFSET, + (0x05 << 16) | (0x05 << 8) | (0x05 << 0), + 0x003f3f3f); + /* ODTCOMP CMD/CTL PU/PD */ + mrc_alt_write_mask(DDRPHY, + CCBUFODTCH0 + ch * DDRCOMP_CH_OFFSET, + (0x03 << 8) | (0x03 << 0), + 0x00001f1f); + /* COMP */ + mrc_alt_write_mask(DDRPHY, + COMPEN0CH0 + ch * DDRCOMP_CH_OFFSET, + 0, 0xc0000100); + +#ifdef BACKUP_COMPS + /* DQ COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0a << 16), + 0x801f0000); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0a << 16), + 0x801f0000); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x10 << 16), + 0x801f0000); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x10 << 16), + 0x801f0000); + /* ODTCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQODTPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0b << 16), + 0x801f0000); + /* ODTCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQODTPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0b << 16), + 0x801f0000); + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + 1 << 31, 1 << 31); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + 1 << 31, 1 << 31); + + /* DQS COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQSDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0a << 16), + 0x801f0000); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQSDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0a << 16), + 0x801f0000); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQSDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x10 << 16), + 0x801f0000); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQSDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x10 << 16), + 0x801f0000); + /* ODTCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQSODTPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0b << 16), + 0x801f0000); + /* ODTCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQSODTPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0b << 16), + 0x801f0000); + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQSTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + 1 << 31, 1 << 31); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQSTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + 1 << 31, 1 << 31); + + /* CLK COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CLKDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0c << 16), + 0x801f0000); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CLKDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0c << 16), + 0x801f0000); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CLKDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x07 << 16), + 0x801f0000); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CLKDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x07 << 16), + 0x801f0000); + /* ODTCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CLKODTPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0b << 16), + 0x801f0000); + /* ODTCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CLKODTPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0b << 16), + 0x801f0000); + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CLKTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + 1 << 31, 1 << 31); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CLKTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + 1 << 31, 1 << 31); + + /* CMD COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CMDDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0d << 16), + 0x803f0000); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CMDDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0d << 16), + 0x803f0000); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CMDDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0a << 16), + 0x801f0000); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CMDDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0a << 16), + 0x801f0000); + + /* CTL COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CTLDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0d << 16), + 0x803f0000); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CTLDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0d << 16), + 0x803f0000); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CTLDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0a << 16), + 0x801f0000); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CTLDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x0a << 16), + 0x801f0000); +#else + /* DQ TCOCOMP Overrides */ + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x1f << 16), + 0x801f0000); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x1f << 16), + 0x801f0000); + + /* DQS TCOCOMP Overrides */ + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + DQSTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x1f << 16), + 0x801f0000); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + DQSTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x1f << 16), + 0x801f0000); + + /* CLK TCOCOMP Overrides */ + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + CLKTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x1f << 16), + 0x801f0000); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + CLKTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET, + (1 << 31) | (0x1f << 16), + 0x801f0000); +#endif + + /* program STATIC delays */ +#ifdef BACKUP_WCMD + set_wcmd(ch, ddr_wcmd[PLATFORM_ID]); +#else + set_wcmd(ch, ddr_wclk[PLATFORM_ID] + HALF_CLK); +#endif + + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + set_wclk(ch, rk, ddr_wclk[PLATFORM_ID]); +#ifdef BACKUP_WCTL + set_wctl(ch, rk, ddr_wctl[PLATFORM_ID]); +#else + set_wctl(ch, rk, ddr_wclk[PLATFORM_ID] + HALF_CLK); +#endif + } + } + } + } + + /* COMP (non channel specific) */ + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, DQANADRVPUCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, DQANADRVPDCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, CMDANADRVPUCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, CMDANADRVPDCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, CLKANADRVPUCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, CLKANADRVPDCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, DQSANADRVPUCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, DQSANADRVPDCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, CTLANADRVPUCTL, 1 << 30, 1 << 30); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, CTLANADRVPDCTL, 1 << 30, 1 << 30); + /* ODT: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, DQANAODTPUCTL, 1 << 30, 1 << 30); + /* ODT: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, DQANAODTPDCTL, 1 << 30, 1 << 30); + /* ODT: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, CLKANAODTPUCTL, 1 << 30, 1 << 30); + /* ODT: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, CLKANAODTPDCTL, 1 << 30, 1 << 30); + /* ODT: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, DQSANAODTPUCTL, 1 << 30, 1 << 30); + /* ODT: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, DQSANAODTPDCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, DQANADLYPUCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, DQANADLYPDCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, CMDANADLYPUCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, CMDANADLYPDCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, CLKANADLYPUCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, CLKANADLYPDCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, DQSANADLYPUCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, DQSANADLYPDCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, CTLANADLYPUCTL, 1 << 30, 1 << 30); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, CTLANADLYPDCTL, 1 << 30, 1 << 30); + /* TCO: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, DQANATCOPUCTL, 1 << 30, 1 << 30); + /* TCO: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, DQANATCOPDCTL, 1 << 30, 1 << 30); + /* TCO: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, CLKANATCOPUCTL, 1 << 30, 1 << 30); + /* TCO: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, CLKANATCOPDCTL, 1 << 30, 1 << 30); + /* TCO: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, DQSANATCOPUCTL, 1 << 30, 1 << 30); + /* TCO: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, DQSANATCOPDCTL, 1 << 30, 1 << 30); + /* TCOCOMP: Pulse Count */ + mrc_alt_write_mask(DDRPHY, TCOCNTCTRL, 1, 3); + /* ODT: CMD/CTL PD/PU */ + mrc_alt_write_mask(DDRPHY, CHNLBUFSTATIC, + (0x03 << 24) | (0x03 << 16), 0x1f1f0000); + /* Set 1us counter */ + mrc_alt_write_mask(DDRPHY, MSCNTR, 0x64, 0xff); + mrc_alt_write_mask(DDRPHY, LATCH1CTL, 0x1 << 28, 0x70000000); + + /* Release PHY from reset */ + mrc_alt_write_mask(DDRPHY, MASTERRSTN, 1, 1); + + /* STEP1 */ + mrc_post_code(0x03, 0x11); + + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + /* DQ01-DQ23 */ + for (bl_grp = 0; + bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; + bl_grp++) { + mrc_alt_write_mask(DDRPHY, + DQMDLLCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 1 << 13, + 1 << 13); /* Enable VREG */ + delay_n(3); + } + + /* ECC */ + mrc_alt_write_mask(DDRPHY, ECCMDLLCTL, + 1 << 13, 1 << 13); /* Enable VREG */ + delay_n(3); + /* CMD */ + mrc_alt_write_mask(DDRPHY, + CMDMDLLCTL + ch * DDRIOCCC_CH_OFFSET, + 1 << 13, 1 << 13); /* Enable VREG */ + delay_n(3); + /* CLK-CTL */ + mrc_alt_write_mask(DDRPHY, + CCMDLLCTL + ch * DDRIOCCC_CH_OFFSET, + 1 << 13, 1 << 13); /* Enable VREG */ + delay_n(3); + } + } + + /* STEP2 */ + mrc_post_code(0x03, 0x12); + delay_n(200); + + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + /* DQ01-DQ23 */ + for (bl_grp = 0; + bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; + bl_grp++) { + mrc_alt_write_mask(DDRPHY, + DQMDLLCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 1 << 17, + 1 << 17); /* Enable MCDLL */ + delay_n(50); + } + + /* ECC */ + mrc_alt_write_mask(DDRPHY, ECCMDLLCTL, + 1 << 17, 1 << 17); /* Enable MCDLL */ + delay_n(50); + /* CMD */ + mrc_alt_write_mask(DDRPHY, + CMDMDLLCTL + ch * DDRIOCCC_CH_OFFSET, + 1 << 18, 1 << 18); /* Enable MCDLL */ + delay_n(50); + /* CLK-CTL */ + mrc_alt_write_mask(DDRPHY, + CCMDLLCTL + ch * DDRIOCCC_CH_OFFSET, + 1 << 18, 1 << 18); /* Enable MCDLL */ + delay_n(50); + } + } + + /* STEP3: */ + mrc_post_code(0x03, 0x13); + delay_n(100); + + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + /* DQ01-DQ23 */ + for (bl_grp = 0; + bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; + bl_grp++) { +#ifdef FORCE_16BIT_DDRIO + temp = (bl_grp && + (mrc_params->channel_width == X16)) ? + 0x11ff : 0xffff; +#else + temp = 0xffff; +#endif + /* Enable TXDLL */ + mrc_alt_write_mask(DDRPHY, + DQDLLTXCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + temp, 0xffff); + delay_n(3); + /* Enable RXDLL */ + mrc_alt_write_mask(DDRPHY, + DQDLLRXCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 0xf, 0xf); + delay_n(3); + /* Enable RXDLL Overrides BL0 */ + mrc_alt_write_mask(DDRPHY, + B0OVRCTL + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 0xf, 0xf); + } + + /* ECC */ + temp = 0xffff; + mrc_alt_write_mask(DDRPHY, ECCDLLTXCTL, + temp, 0xffff); + delay_n(3); + + /* CMD (PO) */ + mrc_alt_write_mask(DDRPHY, + CMDDLLTXCTL + ch * DDRIOCCC_CH_OFFSET, + temp, 0xffff); + delay_n(3); + } + } + + /* STEP4 */ + mrc_post_code(0x03, 0x14); + + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + /* Host To Memory Clock Alignment (HMC) for 800/1066 */ + for (bl_grp = 0; + bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; + bl_grp++) { + /* CLK_ALIGN_MOD_ID */ + mrc_alt_write_mask(DDRPHY, + DQCLKALIGNREG2 + + bl_grp * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + bl_grp ? 3 : 1, + 0xf); + } + + mrc_alt_write_mask(DDRPHY, + ECCCLKALIGNREG2 + ch * DDRIODQ_CH_OFFSET, + 0x2, 0xf); + mrc_alt_write_mask(DDRPHY, + CMDCLKALIGNREG2 + ch * DDRIODQ_CH_OFFSET, + 0x0, 0xf); + mrc_alt_write_mask(DDRPHY, + CCCLKALIGNREG2 + ch * DDRIODQ_CH_OFFSET, + 0x2, 0xf); + mrc_alt_write_mask(DDRPHY, + CMDCLKALIGNREG0 + ch * DDRIOCCC_CH_OFFSET, + 0x20, 0x30); + /* + * NUM_SAMPLES, MAX_SAMPLES, + * MACRO_PI_STEP, MICRO_PI_STEP + */ + mrc_alt_write_mask(DDRPHY, + CMDCLKALIGNREG1 + ch * DDRIOCCC_CH_OFFSET, + (0x18 << 16) | (0x10 << 8) | + (0x8 << 2) | (0x1 << 0), + 0x007f7fff); + /* TOTAL_NUM_MODULES, FIRST_U_PARTITION */ + mrc_alt_write_mask(DDRPHY, + CMDCLKALIGNREG2 + ch * DDRIOCCC_CH_OFFSET, + (0x10 << 16) | (0x4 << 8) | (0x2 << 4), + 0x001f0ff0); +#ifdef HMC_TEST + /* START_CLK_ALIGN=1 */ + mrc_alt_write_mask(DDRPHY, + CMDCLKALIGNREG0 + ch * DDRIOCCC_CH_OFFSET, + 1 << 24, 1 << 24); + while (msg_port_alt_read(DDRPHY, + CMDCLKALIGNREG0 + ch * DDRIOCCC_CH_OFFSET) & + (1 << 24)) + ; /* wait for START_CLK_ALIGN=0 */ +#endif + + /* Set RD/WR Pointer Seperation & COUNTEN & FIFOPTREN */ + mrc_alt_write_mask(DDRPHY, + CMDPTRREG + ch * DDRIOCCC_CH_OFFSET, + 1, 1); /* WRPTRENABLE=1 */ + + /* COMP initial */ + /* enable bypass for CLK buffer (PO) */ + mrc_alt_write_mask(DDRPHY, + COMPEN0CH0 + ch * DDRCOMP_CH_OFFSET, + 1 << 5, 1 << 5); + /* Initial COMP Enable */ + mrc_alt_write_mask(DDRPHY, CMPCTRL, 1, 1); + /* wait for Initial COMP Enable = 0 */ + while (msg_port_alt_read(DDRPHY, CMPCTRL) & 1) + ; + /* disable bypass for CLK buffer (PO) */ + mrc_alt_write_mask(DDRPHY, + COMPEN0CH0 + ch * DDRCOMP_CH_OFFSET, + ~(1 << 5), 1 << 5); + + /* IOBUFACT */ + + /* STEP4a */ + mrc_alt_write_mask(DDRPHY, + CMDCFGREG0 + ch * DDRIOCCC_CH_OFFSET, + 1 << 2, 1 << 2); /* IOBUFACTRST_N=1 */ + + /* DDRPHY initialization complete */ + mrc_alt_write_mask(DDRPHY, + CMDPMCONFIG0 + ch * DDRIOCCC_CH_OFFSET, + 1 << 20, 1 << 20); /* SPID_INIT_COMPLETE=1 */ + } + } + + LEAVEFN(); +} + +/* This function performs JEDEC initialization on all enabled channels */ +void perform_jedec_init(struct mrc_params *mrc_params) +{ + uint8_t twr, wl, rank; + uint32_t tck; + u32 dtr0; + u32 drp; + u32 drmc; + u32 mrs0_cmd = 0; + u32 emrs1_cmd = 0; + u32 emrs2_cmd = 0; + u32 emrs3_cmd = 0; + + ENTERFN(); + + /* jedec_init starts */ + mrc_post_code(0x04, 0x00); + + /* DDR3_RESET_SET=0, DDR3_RESET_RESET=1 */ + mrc_alt_write_mask(DDRPHY, CCDDR3RESETCTL, 2, 0x102); + + /* Assert RESET# for 200us */ + delay_u(200); + + /* DDR3_RESET_SET=1, DDR3_RESET_RESET=0 */ + mrc_alt_write_mask(DDRPHY, CCDDR3RESETCTL, 0x100, 0x102); + + dtr0 = msg_port_read(MEM_CTLR, DTR0); + + /* + * Set CKEVAL for populated ranks + * then send NOP to each rank (#4550197) + */ + + drp = msg_port_read(MEM_CTLR, DRP); + drp &= 0x3; + + drmc = msg_port_read(MEM_CTLR, DRMC); + drmc &= 0xfffffffc; + drmc |= (DRMC_CKEMODE | drp); + + msg_port_write(MEM_CTLR, DRMC, drmc); + + for (rank = 0; rank < NUM_RANKS; rank++) { + /* Skip to next populated rank */ + if ((mrc_params->rank_enables & (1 << rank)) == 0) + continue; + + dram_init_command(DCMD_NOP(rank)); + } + + msg_port_write(MEM_CTLR, DRMC, + (mrc_params->rd_odt_value == 0 ? DRMC_ODTMODE : 0)); + + /* + * setup for emrs 2 + * BIT[15:11] --> Always "0" + * BIT[10:09] --> Rtt_WR: want "Dynamic ODT Off" (0) + * BIT[08] --> Always "0" + * BIT[07] --> SRT: use sr_temp_range + * BIT[06] --> ASR: want "Manual SR Reference" (0) + * BIT[05:03] --> CWL: use oem_tCWL + * BIT[02:00] --> PASR: want "Full Array" (0) + */ + emrs2_cmd |= (2 << 3); + wl = 5 + mrc_params->ddr_speed; + emrs2_cmd |= ((wl - 5) << 9); + emrs2_cmd |= (mrc_params->sr_temp_range << 13); + + /* + * setup for emrs 3 + * BIT[15:03] --> Always "0" + * BIT[02] --> MPR: want "Normal Operation" (0) + * BIT[01:00] --> MPR_Loc: want "Predefined Pattern" (0) + */ + emrs3_cmd |= (3 << 3); + + /* + * setup for emrs 1 + * BIT[15:13] --> Always "0" + * BIT[12:12] --> Qoff: want "Output Buffer Enabled" (0) + * BIT[11:11] --> TDQS: want "Disabled" (0) + * BIT[10:10] --> Always "0" + * BIT[09,06,02] --> Rtt_nom: use rtt_nom_value + * BIT[08] --> Always "0" + * BIT[07] --> WR_LVL: want "Disabled" (0) + * BIT[05,01] --> DIC: use ron_value + * BIT[04:03] --> AL: additive latency want "0" (0) + * BIT[00] --> DLL: want "Enable" (0) + * + * (BIT5|BIT1) set Ron value + * 00 --> RZQ/6 (40ohm) + * 01 --> RZQ/7 (34ohm) + * 1* --> RESERVED + * + * (BIT9|BIT6|BIT2) set Rtt_nom value + * 000 --> Disabled + * 001 --> RZQ/4 ( 60ohm) + * 010 --> RZQ/2 (120ohm) + * 011 --> RZQ/6 ( 40ohm) + * 1** --> RESERVED + */ + emrs1_cmd |= (1 << 3); + emrs1_cmd &= ~(1 << 6); + + if (mrc_params->ron_value == 0) + emrs1_cmd |= (1 << 7); + else + emrs1_cmd &= ~(1 << 7); + + if (mrc_params->rtt_nom_value == 0) + emrs1_cmd |= (DDR3_EMRS1_RTTNOM_40 << 6); + else if (mrc_params->rtt_nom_value == 1) + emrs1_cmd |= (DDR3_EMRS1_RTTNOM_60 << 6); + else if (mrc_params->rtt_nom_value == 2) + emrs1_cmd |= (DDR3_EMRS1_RTTNOM_120 << 6); + + /* save MRS1 value (excluding control fields) */ + mrc_params->mrs1 = emrs1_cmd >> 6; + + /* + * setup for mrs 0 + * BIT[15:13] --> Always "0" + * BIT[12] --> PPD: for Quark (1) + * BIT[11:09] --> WR: use oem_tWR + * BIT[08] --> DLL: want "Reset" (1, self clearing) + * BIT[07] --> MODE: want "Normal" (0) + * BIT[06:04,02] --> CL: use oem_tCAS + * BIT[03] --> RD_BURST_TYPE: want "Interleave" (1) + * BIT[01:00] --> BL: want "8 Fixed" (0) + * WR: + * 0 --> 16 + * 1 --> 5 + * 2 --> 6 + * 3 --> 7 + * 4 --> 8 + * 5 --> 10 + * 6 --> 12 + * 7 --> 14 + * CL: + * BIT[02:02] "0" if oem_tCAS <= 11 (1866?) + * BIT[06:04] use oem_tCAS-4 + */ + mrs0_cmd |= (1 << 14); + mrs0_cmd |= (1 << 18); + mrs0_cmd |= ((((dtr0 >> 12) & 7) + 1) << 10); + + tck = t_ck[mrc_params->ddr_speed]; + /* Per JEDEC: tWR=15000ps DDR2/3 from 800-1600 */ + twr = MCEIL(15000, tck); + mrs0_cmd |= ((twr - 4) << 15); + + for (rank = 0; rank < NUM_RANKS; rank++) { + /* Skip to next populated rank */ + if ((mrc_params->rank_enables & (1 << rank)) == 0) + continue; + + emrs2_cmd |= (rank << 22); + dram_init_command(emrs2_cmd); + + emrs3_cmd |= (rank << 22); + dram_init_command(emrs3_cmd); + + emrs1_cmd |= (rank << 22); + dram_init_command(emrs1_cmd); + + mrs0_cmd |= (rank << 22); + dram_init_command(mrs0_cmd); + + dram_init_command(DCMD_ZQCL(rank)); + } + + LEAVEFN(); +} + +/* + * Dunit Initialization Complete + * + * Indicates that initialization of the Dunit has completed. + * + * Memory accesses are permitted and maintenance operation begins. + * Until this bit is set to a 1, the memory controller will not accept + * DRAM requests from the MEMORY_MANAGER or HTE. + */ +void set_ddr_init_complete(struct mrc_params *mrc_params) +{ + u32 dco; + + ENTERFN(); + + dco = msg_port_read(MEM_CTLR, DCO); + dco &= ~DCO_PMICTL; + dco |= DCO_IC; + msg_port_write(MEM_CTLR, DCO, dco); + + LEAVEFN(); +} + +/* + * This function will retrieve relevant timing data + * + * This data will be used on subsequent boots to speed up boot times + * and is required for Suspend To RAM capabilities. + */ +void restore_timings(struct mrc_params *mrc_params) +{ + uint8_t ch, rk, bl; + const struct mrc_timings *mt = &mrc_params->timings; + + for (ch = 0; ch < NUM_CHANNELS; ch++) { + for (rk = 0; rk < NUM_RANKS; rk++) { + for (bl = 0; bl < NUM_BYTE_LANES; bl++) { + set_rcvn(ch, rk, bl, mt->rcvn[ch][rk][bl]); + set_rdqs(ch, rk, bl, mt->rdqs[ch][rk][bl]); + set_wdqs(ch, rk, bl, mt->wdqs[ch][rk][bl]); + set_wdq(ch, rk, bl, mt->wdq[ch][rk][bl]); + if (rk == 0) { + /* VREF (RANK0 only) */ + set_vref(ch, bl, mt->vref[ch][bl]); + } + } + set_wctl(ch, rk, mt->wctl[ch][rk]); + } + set_wcmd(ch, mt->wcmd[ch]); + } +} + +/* + * Configure default settings normally set as part of read training + * + * Some defaults have to be set earlier as they may affect earlier + * training steps. + */ +void default_timings(struct mrc_params *mrc_params) +{ + uint8_t ch, rk, bl; + + for (ch = 0; ch < NUM_CHANNELS; ch++) { + for (rk = 0; rk < NUM_RANKS; rk++) { + for (bl = 0; bl < NUM_BYTE_LANES; bl++) { + set_rdqs(ch, rk, bl, 24); + if (rk == 0) { + /* VREF (RANK0 only) */ + set_vref(ch, bl, 32); + } + } + } + } +} + +/* + * This function will perform our RCVEN Calibration Algorithm. + * We will only use the 2xCLK domain timings to perform RCVEN Calibration. + * All byte lanes will be calibrated "simultaneously" per channel per rank. + */ +void rcvn_cal(struct mrc_params *mrc_params) +{ + uint8_t ch; /* channel counter */ + uint8_t rk; /* rank counter */ + uint8_t bl; /* byte lane counter */ + uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; + +#ifdef R2R_SHARING + /* used to find placement for rank2rank sharing configs */ + uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; +#ifndef BACKUP_RCVN + /* used to find placement for rank2rank sharing configs */ + uint32_t num_ranks_enabled = 0; +#endif +#endif + +#ifdef BACKUP_RCVN +#else + uint32_t temp; + /* absolute PI value to be programmed on the byte lane */ + uint32_t delay[NUM_BYTE_LANES]; + u32 dtr1, dtr1_save; +#endif + + ENTERFN(); + + /* rcvn_cal starts */ + mrc_post_code(0x05, 0x00); + +#ifndef BACKUP_RCVN + /* need separate burst to sample DQS preamble */ + dtr1 = msg_port_read(MEM_CTLR, DTR1); + dtr1_save = dtr1; + dtr1 |= DTR1_TCCD_12CLK; + msg_port_write(MEM_CTLR, DTR1, dtr1); +#endif + +#ifdef R2R_SHARING + /* need to set "final_delay[][]" elements to "0" */ + memset((void *)(final_delay), 0x00, (size_t)sizeof(final_delay)); +#endif + + /* loop through each enabled channel */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + /* perform RCVEN Calibration on a per rank basis */ + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + /* + * POST_CODE here indicates the current + * channel and rank being calibrated + */ + mrc_post_code(0x05, 0x10 + ((ch << 4) | rk)); + +#ifdef BACKUP_RCVN + /* et hard-coded timing values */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) + set_rcvn(ch, rk, bl, ddr_rcvn[PLATFORM_ID]); +#else + /* enable FIFORST */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl += 2) { + mrc_alt_write_mask(DDRPHY, + B01PTRCTL1 + + (bl >> 1) * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 0, 1 << 8); + } + /* initialize the starting delay to 128 PI (cas +1 CLK) */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + /* 1x CLK domain timing is cas-4 */ + delay[bl] = (4 + 1) * FULL_CLK; + + set_rcvn(ch, rk, bl, delay[bl]); + } + + /* now find the rising edge */ + find_rising_edge(mrc_params, delay, ch, rk, true); + + /* Now increase delay by 32 PI (1/4 CLK) to place in center of high pulse */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + delay[bl] += QRTR_CLK; + set_rcvn(ch, rk, bl, delay[bl]); + } + /* Now decrement delay by 128 PI (1 CLK) until we sample a "0" */ + do { + temp = sample_dqs(mrc_params, ch, rk, true); + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + if (temp & (1 << bl)) { + if (delay[bl] >= FULL_CLK) { + delay[bl] -= FULL_CLK; + set_rcvn(ch, rk, bl, delay[bl]); + } else { + /* not enough delay */ + training_message(ch, rk, bl); + mrc_post_code(0xee, 0x50); + } + } + } + } while (temp & 0xff); + +#ifdef R2R_SHARING + /* increment "num_ranks_enabled" */ + num_ranks_enabled++; + /* Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + delay[bl] += QRTR_CLK; + /* add "delay[]" values to "final_delay[][]" for rolling average */ + final_delay[ch][bl] += delay[bl]; + /* set timing based on rolling average values */ + set_rcvn(ch, rk, bl, final_delay[ch][bl] / num_ranks_enabled); + } +#else + /* Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + delay[bl] += QRTR_CLK; + set_rcvn(ch, rk, bl, delay[bl]); + } +#endif + + /* disable FIFORST */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl += 2) { + mrc_alt_write_mask(DDRPHY, + B01PTRCTL1 + + (bl >> 1) * DDRIODQ_BL_OFFSET + + ch * DDRIODQ_CH_OFFSET, + 1 << 8, 1 << 8); + } +#endif + } + } + } + } + +#ifndef BACKUP_RCVN + /* restore original */ + msg_port_write(MEM_CTLR, DTR1, dtr1_save); +#endif + + LEAVEFN(); +} + +/* + * This function will perform the Write Levelling algorithm + * (align WCLK and WDQS). + * + * This algorithm will act on each rank in each channel separately. + */ +void wr_level(struct mrc_params *mrc_params) +{ + uint8_t ch; /* channel counter */ + uint8_t rk; /* rank counter */ + uint8_t bl; /* byte lane counter */ + uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; + +#ifdef R2R_SHARING + /* used to find placement for rank2rank sharing configs */ + uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; +#ifndef BACKUP_WDQS + /* used to find placement for rank2rank sharing configs */ + uint32_t num_ranks_enabled = 0; +#endif +#endif + +#ifdef BACKUP_WDQS +#else + /* determines stop condition for CRS_WR_LVL */ + bool all_edges_found; + /* absolute PI value to be programmed on the byte lane */ + uint32_t delay[NUM_BYTE_LANES]; + /* + * static makes it so the data is loaded in the heap once by shadow(), + * where non-static copies the data onto the stack every time this + * function is called + */ + uint32_t address; /* address to be checked during COARSE_WR_LVL */ + u32 dtr4, dtr4_save; +#endif + + ENTERFN(); + + /* wr_level starts */ + mrc_post_code(0x06, 0x00); + +#ifdef R2R_SHARING + /* need to set "final_delay[][]" elements to "0" */ + memset((void *)(final_delay), 0x00, (size_t)sizeof(final_delay)); +#endif + + /* loop through each enabled channel */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + /* perform WRITE LEVELING algorithm on a per rank basis */ + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + /* + * POST_CODE here indicates the current + * rank and channel being calibrated + */ + mrc_post_code(0x06, 0x10 + ((ch << 4) | rk)); + +#ifdef BACKUP_WDQS + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + set_wdqs(ch, rk, bl, ddr_wdqs[PLATFORM_ID]); + set_wdq(ch, rk, bl, ddr_wdqs[PLATFORM_ID] - QRTR_CLK); + } +#else + /* + * perform a single PRECHARGE_ALL command to + * make DRAM state machine go to IDLE state + */ + dram_init_command(DCMD_PREA(rk)); + + /* + * enable Write Levelling Mode + * (EMRS1 w/ Write Levelling Mode Enable) + */ + dram_init_command(DCMD_MRS1(rk, 0x82)); + + /* + * set ODT DRAM Full Time Termination + * disable in MCU + */ + + dtr4 = msg_port_read(MEM_CTLR, DTR4); + dtr4_save = dtr4; + dtr4 |= DTR4_ODTDIS; + msg_port_write(MEM_CTLR, DTR4, dtr4); + + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor) / 2; bl++) { + /* + * Enable Sandy Bridge Mode (WDQ Tri-State) & + * Ensure 5 WDQS pulses during Write Leveling + */ + mrc_alt_write_mask(DDRPHY, + DQCTL + DDRIODQ_BL_OFFSET * bl + DDRIODQ_CH_OFFSET * ch, + 0x10000154, + 0x100003fc); + } + + /* Write Leveling Mode enabled in IO */ + mrc_alt_write_mask(DDRPHY, + CCDDR3RESETCTL + DDRIOCCC_CH_OFFSET * ch, + 1 << 16, 1 << 16); + + /* Initialize the starting delay to WCLK */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + /* + * CLK0 --> RK0 + * CLK1 --> RK1 + */ + delay[bl] = get_wclk(ch, rk); + + set_wdqs(ch, rk, bl, delay[bl]); + } + + /* now find the rising edge */ + find_rising_edge(mrc_params, delay, ch, rk, false); + + /* disable Write Levelling Mode */ + mrc_alt_write_mask(DDRPHY, + CCDDR3RESETCTL + DDRIOCCC_CH_OFFSET * ch, + 0, 1 << 16); + + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor) / 2; bl++) { + /* Disable Sandy Bridge Mode & Ensure 4 WDQS pulses during normal operation */ + mrc_alt_write_mask(DDRPHY, + DQCTL + DDRIODQ_BL_OFFSET * bl + DDRIODQ_CH_OFFSET * ch, + 0x00000154, + 0x100003fc); + } + + /* restore original DTR4 */ + msg_port_write(MEM_CTLR, DTR4, dtr4_save); + + /* + * restore original value + * (Write Levelling Mode Disable) + */ + dram_init_command(DCMD_MRS1(rk, mrc_params->mrs1)); + + /* + * perform a single PRECHARGE_ALL command to + * make DRAM state machine go to IDLE state + */ + dram_init_command(DCMD_PREA(rk)); + + mrc_post_code(0x06, 0x30 + ((ch << 4) | rk)); + + /* + * COARSE WRITE LEVEL: + * check that we're on the correct clock edge + */ + + /* hte reconfiguration request */ + mrc_params->hte_setup = 1; + + /* start CRS_WR_LVL with WDQS = WDQS + 128 PI */ + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + delay[bl] = get_wdqs(ch, rk, bl) + FULL_CLK; + set_wdqs(ch, rk, bl, delay[bl]); + /* + * program WDQ timings based on WDQS + * (WDQ = WDQS - 32 PI) + */ + set_wdq(ch, rk, bl, (delay[bl] - QRTR_CLK)); + } + + /* get an address in the targeted channel/rank */ + address = get_addr(ch, rk); + do { + uint32_t coarse_result = 0x00; + uint32_t coarse_result_mask = byte_lane_mask(mrc_params); + /* assume pass */ + all_edges_found = true; + + mrc_params->hte_setup = 1; + coarse_result = check_rw_coarse(mrc_params, address); + + /* check for failures and margin the byte lane back 128 PI (1 CLK) */ + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + if (coarse_result & (coarse_result_mask << bl)) { + all_edges_found = false; + delay[bl] -= FULL_CLK; + set_wdqs(ch, rk, bl, delay[bl]); + /* program WDQ timings based on WDQS (WDQ = WDQS - 32 PI) */ + set_wdq(ch, rk, bl, delay[bl] - QRTR_CLK); + } + } + } while (!all_edges_found); + +#ifdef R2R_SHARING + /* increment "num_ranks_enabled" */ + num_ranks_enabled++; + /* accumulate "final_delay[][]" values from "delay[]" values for rolling average */ + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + final_delay[ch][bl] += delay[bl]; + set_wdqs(ch, rk, bl, final_delay[ch][bl] / num_ranks_enabled); + /* program WDQ timings based on WDQS (WDQ = WDQS - 32 PI) */ + set_wdq(ch, rk, bl, final_delay[ch][bl] / num_ranks_enabled - QRTR_CLK); + } +#endif +#endif + } + } + } + } + + LEAVEFN(); +} + +void prog_page_ctrl(struct mrc_params *mrc_params) +{ + u32 dpmc0; + + ENTERFN(); + + dpmc0 = msg_port_read(MEM_CTLR, DPMC0); + dpmc0 &= ~DPMC0_PCLSTO_MASK; + dpmc0 |= (4 << 16); + dpmc0 |= DPMC0_PREAPWDEN; + msg_port_write(MEM_CTLR, DPMC0, dpmc0); +} + +/* + * This function will perform the READ TRAINING Algorithm on all + * channels/ranks/byte_lanes simultaneously to minimize execution time. + * + * The idea here is to train the VREF and RDQS (and eventually RDQ) values + * to achieve maximum READ margins. The algorithm will first determine the + * X coordinate (RDQS setting). This is done by collapsing the VREF eye + * until we find a minimum required RDQS eye for VREF_MIN and VREF_MAX. + * Then we take the averages of the RDQS eye at VREF_MIN and VREF_MAX, + * then average those; this will be the final X coordinate. The algorithm + * will then determine the Y coordinate (VREF setting). This is done by + * collapsing the RDQS eye until we find a minimum required VREF eye for + * RDQS_MIN and RDQS_MAX. Then we take the averages of the VREF eye at + * RDQS_MIN and RDQS_MAX, then average those; this will be the final Y + * coordinate. + * + * NOTE: this algorithm assumes the eye curves have a one-to-one relationship, + * meaning for each X the curve has only one Y and vice-a-versa. + */ +void rd_train(struct mrc_params *mrc_params) +{ + uint8_t ch; /* channel counter */ + uint8_t rk; /* rank counter */ + uint8_t bl; /* byte lane counter */ + uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; +#ifdef BACKUP_RDQS +#else + uint8_t side_x; /* tracks LEFT/RIGHT approach vectors */ + uint8_t side_y; /* tracks BOTTOM/TOP approach vectors */ + /* X coordinate data (passing RDQS values) for approach vectors */ + uint8_t x_coordinate[2][2][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; + /* Y coordinate data (passing VREF values) for approach vectors */ + uint8_t y_coordinate[2][2][NUM_CHANNELS][NUM_BYTE_LANES]; + /* centered X (RDQS) */ + uint8_t x_center[NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; + /* centered Y (VREF) */ + uint8_t y_center[NUM_CHANNELS][NUM_BYTE_LANES]; + uint32_t address; /* target address for check_bls_ex() */ + uint32_t result; /* result of check_bls_ex() */ + uint32_t bl_mask; /* byte lane mask for result checking */ +#ifdef R2R_SHARING + /* used to find placement for rank2rank sharing configs */ + uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; + /* used to find placement for rank2rank sharing configs */ + uint32_t num_ranks_enabled = 0; +#endif +#endif + + /* rd_train starts */ + mrc_post_code(0x07, 0x00); + + ENTERFN(); + +#ifdef BACKUP_RDQS + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + for (bl = 0; + bl < NUM_BYTE_LANES / bl_divisor; + bl++) { + set_rdqs(ch, rk, bl, ddr_rdqs[PLATFORM_ID]); + } + } + } + } + } +#else + /* initialize x/y_coordinate arrays */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + for (bl = 0; + bl < NUM_BYTE_LANES / bl_divisor; + bl++) { + /* x_coordinate */ + x_coordinate[L][B][ch][rk][bl] = RDQS_MIN; + x_coordinate[R][B][ch][rk][bl] = RDQS_MAX; + x_coordinate[L][T][ch][rk][bl] = RDQS_MIN; + x_coordinate[R][T][ch][rk][bl] = RDQS_MAX; + /* y_coordinate */ + y_coordinate[L][B][ch][bl] = VREF_MIN; + y_coordinate[R][B][ch][bl] = VREF_MIN; + y_coordinate[L][T][ch][bl] = VREF_MAX; + y_coordinate[R][T][ch][bl] = VREF_MAX; + } + } + } + } + } + + /* initialize other variables */ + bl_mask = byte_lane_mask(mrc_params); + address = get_addr(0, 0); + +#ifdef R2R_SHARING + /* need to set "final_delay[][]" elements to "0" */ + memset((void *)(final_delay), 0x00, (size_t)sizeof(final_delay)); +#endif + + /* look for passing coordinates */ + for (side_y = B; side_y <= T; side_y++) { + for (side_x = L; side_x <= R; side_x++) { + mrc_post_code(0x07, 0x10 + side_y * 2 + side_x); + + /* find passing values */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (0x1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & + (0x1 << rk)) { + /* set x/y_coordinate search starting settings */ + for (bl = 0; + bl < NUM_BYTE_LANES / bl_divisor; + bl++) { + set_rdqs(ch, rk, bl, + x_coordinate[side_x][side_y][ch][rk][bl]); + set_vref(ch, bl, + y_coordinate[side_x][side_y][ch][bl]); + } + + /* get an address in the target channel/rank */ + address = get_addr(ch, rk); + + /* request HTE reconfiguration */ + mrc_params->hte_setup = 1; + + /* test the settings */ + do { + /* result[07:00] == failing byte lane (MAX 8) */ + result = check_bls_ex(mrc_params, address); + + /* check for failures */ + if (result & 0xff) { + /* at least 1 byte lane failed */ + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + if (result & + (bl_mask << bl)) { + /* adjust the RDQS values accordingly */ + if (side_x == L) + x_coordinate[L][side_y][ch][rk][bl] += RDQS_STEP; + else + x_coordinate[R][side_y][ch][rk][bl] -= RDQS_STEP; + + /* check that we haven't closed the RDQS_EYE too much */ + if ((x_coordinate[L][side_y][ch][rk][bl] > (RDQS_MAX - MIN_RDQS_EYE)) || + (x_coordinate[R][side_y][ch][rk][bl] < (RDQS_MIN + MIN_RDQS_EYE)) || + (x_coordinate[L][side_y][ch][rk][bl] == + x_coordinate[R][side_y][ch][rk][bl])) { + /* + * not enough RDQS margin available at this VREF + * update VREF values accordingly + */ + if (side_y == B) + y_coordinate[side_x][B][ch][bl] += VREF_STEP; + else + y_coordinate[side_x][T][ch][bl] -= VREF_STEP; + + /* check that we haven't closed the VREF_EYE too much */ + if ((y_coordinate[side_x][B][ch][bl] > (VREF_MAX - MIN_VREF_EYE)) || + (y_coordinate[side_x][T][ch][bl] < (VREF_MIN + MIN_VREF_EYE)) || + (y_coordinate[side_x][B][ch][bl] == y_coordinate[side_x][T][ch][bl])) { + /* VREF_EYE collapsed below MIN_VREF_EYE */ + training_message(ch, rk, bl); + mrc_post_code(0xEE, 0x70 + side_y * 2 + side_x); + } else { + /* update the VREF setting */ + set_vref(ch, bl, y_coordinate[side_x][side_y][ch][bl]); + /* reset the X coordinate to begin the search at the new VREF */ + x_coordinate[side_x][side_y][ch][rk][bl] = + (side_x == L) ? RDQS_MIN : RDQS_MAX; + } + } + + /* update the RDQS setting */ + set_rdqs(ch, rk, bl, x_coordinate[side_x][side_y][ch][rk][bl]); + } + } + } + } while (result & 0xff); + } + } + } + } + } + } + + mrc_post_code(0x07, 0x20); + + /* find final RDQS (X coordinate) & final VREF (Y coordinate) */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + uint32_t temp1; + uint32_t temp2; + + /* x_coordinate */ + DPF(D_INFO, + "RDQS T/B eye rank%d lane%d : %d-%d %d-%d\n", + rk, bl, + x_coordinate[L][T][ch][rk][bl], + x_coordinate[R][T][ch][rk][bl], + x_coordinate[L][B][ch][rk][bl], + x_coordinate[R][B][ch][rk][bl]); + + /* average the TOP side LEFT & RIGHT values */ + temp1 = (x_coordinate[R][T][ch][rk][bl] + x_coordinate[L][T][ch][rk][bl]) / 2; + /* average the BOTTOM side LEFT & RIGHT values */ + temp2 = (x_coordinate[R][B][ch][rk][bl] + x_coordinate[L][B][ch][rk][bl]) / 2; + /* average the above averages */ + x_center[ch][rk][bl] = (uint8_t) ((temp1 + temp2) / 2); + + /* y_coordinate */ + DPF(D_INFO, + "VREF R/L eye lane%d : %d-%d %d-%d\n", + bl, + y_coordinate[R][B][ch][bl], + y_coordinate[R][T][ch][bl], + y_coordinate[L][B][ch][bl], + y_coordinate[L][T][ch][bl]); + + /* average the RIGHT side TOP & BOTTOM values */ + temp1 = (y_coordinate[R][T][ch][bl] + y_coordinate[R][B][ch][bl]) / 2; + /* average the LEFT side TOP & BOTTOM values */ + temp2 = (y_coordinate[L][T][ch][bl] + y_coordinate[L][B][ch][bl]) / 2; + /* average the above averages */ + y_center[ch][bl] = (uint8_t) ((temp1 + temp2) / 2); + } + } + } + } + } + +#ifdef RX_EYE_CHECK + /* perform an eye check */ + for (side_y = B; side_y <= T; side_y++) { + for (side_x = L; side_x <= R; side_x++) { + mrc_post_code(0x07, 0x30 + side_y * 2 + side_x); + + /* update the settings for the eye check */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + if (side_x == L) + set_rdqs(ch, rk, bl, x_center[ch][rk][bl] - (MIN_RDQS_EYE / 2)); + else + set_rdqs(ch, rk, bl, x_center[ch][rk][bl] + (MIN_RDQS_EYE / 2)); + + if (side_y == B) + set_vref(ch, bl, y_center[ch][bl] - (MIN_VREF_EYE / 2)); + else + set_vref(ch, bl, y_center[ch][bl] + (MIN_VREF_EYE / 2)); + } + } + } + } + } + + /* request HTE reconfiguration */ + mrc_params->hte_setup = 1; + + /* check the eye */ + if (check_bls_ex(mrc_params, address) & 0xff) { + /* one or more byte lanes failed */ + mrc_post_code(0xee, 0x74 + side_x * 2 + side_y); + } + } + } +#endif + + mrc_post_code(0x07, 0x40); + + /* set final placements */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { +#ifdef R2R_SHARING + /* increment "num_ranks_enabled" */ + num_ranks_enabled++; +#endif + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) { + /* x_coordinate */ +#ifdef R2R_SHARING + final_delay[ch][bl] += x_center[ch][rk][bl]; + set_rdqs(ch, rk, bl, final_delay[ch][bl] / num_ranks_enabled); +#else + set_rdqs(ch, rk, bl, x_center[ch][rk][bl]); +#endif + /* y_coordinate */ + set_vref(ch, bl, y_center[ch][bl]); + } + } + } + } + } +#endif + + LEAVEFN(); +} + +/* + * This function will perform the WRITE TRAINING Algorithm on all + * channels/ranks/byte_lanes simultaneously to minimize execution time. + * + * The idea here is to train the WDQ timings to achieve maximum WRITE margins. + * The algorithm will start with WDQ at the current WDQ setting (tracks WDQS + * in WR_LVL) +/- 32 PIs (+/- 1/4 CLK) and collapse the eye until all data + * patterns pass. This is because WDQS will be aligned to WCLK by the + * Write Leveling algorithm and WDQ will only ever have a 1/2 CLK window + * of validity. + */ +void wr_train(struct mrc_params *mrc_params) +{ + uint8_t ch; /* channel counter */ + uint8_t rk; /* rank counter */ + uint8_t bl; /* byte lane counter */ + uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1; +#ifdef BACKUP_WDQ +#else + uint8_t side; /* LEFT/RIGHT side indicator (0=L, 1=R) */ + uint32_t temp; /* temporary DWORD */ + /* 2 arrays, for L & R side passing delays */ + uint32_t delay[2][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; + uint32_t address; /* target address for check_bls_ex() */ + uint32_t result; /* result of check_bls_ex() */ + uint32_t bl_mask; /* byte lane mask for result checking */ +#ifdef R2R_SHARING + /* used to find placement for rank2rank sharing configs */ + uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; + /* used to find placement for rank2rank sharing configs */ + uint32_t num_ranks_enabled = 0; +#endif +#endif + + /* wr_train starts */ + mrc_post_code(0x08, 0x00); + + ENTERFN(); + +#ifdef BACKUP_WDQ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + for (bl = 0; + bl < NUM_BYTE_LANES / bl_divisor; + bl++) { + set_wdq(ch, rk, bl, ddr_wdq[PLATFORM_ID]); + } + } + } + } + } +#else + /* initialize "delay" */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { + for (bl = 0; + bl < NUM_BYTE_LANES / bl_divisor; + bl++) { + /* + * want to start with + * WDQ = (WDQS - QRTR_CLK) + * +/- QRTR_CLK + */ + temp = get_wdqs(ch, rk, bl) - QRTR_CLK; + delay[L][ch][rk][bl] = temp - QRTR_CLK; + delay[R][ch][rk][bl] = temp + QRTR_CLK; + } + } + } + } + } + + /* initialize other variables */ + bl_mask = byte_lane_mask(mrc_params); + address = get_addr(0, 0); + +#ifdef R2R_SHARING + /* need to set "final_delay[][]" elements to "0" */ + memset((void *)(final_delay), 0x00, (size_t)sizeof(final_delay)); +#endif + + /* + * start algorithm on the LEFT side and train each channel/bl + * until no failures are observed, then repeat for the RIGHT side. + */ + for (side = L; side <= R; side++) { + mrc_post_code(0x08, 0x10 + side); + + /* set starting values */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & + (1 << rk)) { + for (bl = 0; + bl < NUM_BYTE_LANES / bl_divisor; + bl++) { + set_wdq(ch, rk, bl, delay[side][ch][rk][bl]); + } + } + } + } + } + + /* find passing values */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & + (1 << rk)) { + /* get an address in the target channel/rank */ + address = get_addr(ch, rk); + + /* request HTE reconfiguration */ + mrc_params->hte_setup = 1; + + /* check the settings */ + do { + /* result[07:00] == failing byte lane (MAX 8) */ + result = check_bls_ex(mrc_params, address); + /* check for failures */ + if (result & 0xff) { + /* at least 1 byte lane failed */ + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + if (result & + (bl_mask << bl)) { + if (side == L) + delay[L][ch][rk][bl] += WDQ_STEP; + else + delay[R][ch][rk][bl] -= WDQ_STEP; + + /* check for algorithm failure */ + if (delay[L][ch][rk][bl] != delay[R][ch][rk][bl]) { + /* + * margin available + * update delay setting + */ + set_wdq(ch, rk, bl, + delay[side][ch][rk][bl]); + } else { + /* + * no margin available + * notify the user and halt + */ + training_message(ch, rk, bl); + mrc_post_code(0xee, 0x80 + side); + } + } + } + } + /* stop when all byte lanes pass */ + } while (result & 0xff); + } + } + } + } + } + + /* program WDQ to the middle of passing window */ + for (ch = 0; ch < NUM_CHANNELS; ch++) { + if (mrc_params->channel_enables & (1 << ch)) { + for (rk = 0; rk < NUM_RANKS; rk++) { + if (mrc_params->rank_enables & (1 << rk)) { +#ifdef R2R_SHARING + /* increment "num_ranks_enabled" */ + num_ranks_enabled++; +#endif + for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) { + DPF(D_INFO, + "WDQ eye rank%d lane%d : %d-%d\n", + rk, bl, + delay[L][ch][rk][bl], + delay[R][ch][rk][bl]); + + temp = (delay[R][ch][rk][bl] + delay[L][ch][rk][bl]) / 2; + +#ifdef R2R_SHARING + final_delay[ch][bl] += temp; + set_wdq(ch, rk, bl, + final_delay[ch][bl] / num_ranks_enabled); +#else + set_wdq(ch, rk, bl, temp); +#endif + } + } + } + } + } +#endif + + LEAVEFN(); +} + +/* + * This function will store relevant timing data + * + * This data will be used on subsequent boots to speed up boot times + * and is required for Suspend To RAM capabilities. + */ +void store_timings(struct mrc_params *mrc_params) +{ + uint8_t ch, rk, bl; + struct mrc_timings *mt = &mrc_params->timings; + + for (ch = 0; ch < NUM_CHANNELS; ch++) { + for (rk = 0; rk < NUM_RANKS; rk++) { + for (bl = 0; bl < NUM_BYTE_LANES; bl++) { + mt->rcvn[ch][rk][bl] = get_rcvn(ch, rk, bl); + mt->rdqs[ch][rk][bl] = get_rdqs(ch, rk, bl); + mt->wdqs[ch][rk][bl] = get_wdqs(ch, rk, bl); + mt->wdq[ch][rk][bl] = get_wdq(ch, rk, bl); + + if (rk == 0) + mt->vref[ch][bl] = get_vref(ch, bl); + } + + mt->wctl[ch][rk] = get_wctl(ch, rk); + } + + mt->wcmd[ch] = get_wcmd(ch); + } + + /* need to save for a case of changing frequency after warm reset */ + mt->ddr_speed = mrc_params->ddr_speed; +} + +/* + * The purpose of this function is to ensure the SEC comes out of reset + * and IA initiates the SEC enabling Memory Scrambling. + */ +void enable_scrambling(struct mrc_params *mrc_params) +{ + uint32_t lfsr = 0; + uint8_t i; + + if (mrc_params->scrambling_enables == 0) + return; + + ENTERFN(); + + /* 32 bit seed is always stored in BIOS NVM */ + lfsr = mrc_params->timings.scrambler_seed; + + if (mrc_params->boot_mode == BM_COLD) { + /* + * factory value is 0 and in first boot, + * a clock based seed is loaded. + */ + if (lfsr == 0) { + /* + * get seed from system clock + * and make sure it is not all 1's + */ + lfsr = rdtsc() & 0x0fffffff; + } else { + /* + * Need to replace scrambler + * + * get next 32bit LFSR 16 times which is the last + * part of the previous scrambler vector + */ + for (i = 0; i < 16; i++) + lfsr32(&lfsr); + } + + /* save new seed */ + mrc_params->timings.scrambler_seed = lfsr; + } + + /* + * In warm boot or S3 exit, we have the previous seed. + * In cold boot, we have the last 32bit LFSR which is the new seed. + */ + lfsr32(&lfsr); /* shift to next value */ + msg_port_write(MEM_CTLR, SCRMSEED, (lfsr & 0x0003ffff)); + + for (i = 0; i < 2; i++) + msg_port_write(MEM_CTLR, SCRMLO + i, (lfsr & 0xaaaaaaaa)); + + LEAVEFN(); +} + +/* + * Configure MCU Power Management Control Register + * and Scheduler Control Register + */ +void prog_ddr_control(struct mrc_params *mrc_params) +{ + u32 dsch; + u32 dpmc0; + + ENTERFN(); + + dsch = msg_port_read(MEM_CTLR, DSCH); + dsch &= ~(DSCH_OOODIS | DSCH_OOOST3DIS | DSCH_NEWBYPDIS); + msg_port_write(MEM_CTLR, DSCH, dsch); + + dpmc0 = msg_port_read(MEM_CTLR, DPMC0); + dpmc0 &= ~DPMC0_DISPWRDN; + dpmc0 |= (mrc_params->power_down_disable << 25); + dpmc0 &= ~DPMC0_CLKGTDIS; + dpmc0 &= ~DPMC0_PCLSTO_MASK; + dpmc0 |= (4 << 16); + dpmc0 |= DPMC0_PREAPWDEN; + msg_port_write(MEM_CTLR, DPMC0, dpmc0); + + /* CMDTRIST = 2h - CMD/ADDR are tristated when no valid command */ + mrc_write_mask(MEM_CTLR, DPMC1, 0x20, 0x30); + + LEAVEFN(); +} + +/* + * After training complete configure MCU Rank Population Register + * specifying: ranks enabled, device width, density, address mode + */ +void prog_dra_drb(struct mrc_params *mrc_params) +{ + u32 drp; + u32 dco; + u8 density = mrc_params->params.density; + + ENTERFN(); + + dco = msg_port_read(MEM_CTLR, DCO); + dco &= ~DCO_IC; + msg_port_write(MEM_CTLR, DCO, dco); + + drp = 0; + if (mrc_params->rank_enables & 1) + drp |= DRP_RKEN0; + if (mrc_params->rank_enables & 2) + drp |= DRP_RKEN1; + if (mrc_params->dram_width == X16) { + drp |= (1 << 4); + drp |= (1 << 9); + } + + /* + * Density encoding in struct dram_params: 0=512Mb, 1=Gb, 2=2Gb, 3=4Gb + * has to be mapped RANKDENSx encoding (0=1Gb) + */ + if (density == 0) + density = 4; + + drp |= ((density - 1) << 6); + drp |= ((density - 1) << 11); + + /* Address mode can be overwritten if ECC enabled */ + drp |= (mrc_params->address_mode << 14); + + msg_port_write(MEM_CTLR, DRP, drp); + + dco &= ~DCO_PMICTL; + dco |= DCO_IC; + msg_port_write(MEM_CTLR, DCO, dco); + + LEAVEFN(); +} + +/* Send DRAM wake command */ +void perform_wake(struct mrc_params *mrc_params) +{ + ENTERFN(); + + dram_wake_command(); + + LEAVEFN(); +} + +/* + * Configure refresh rate and short ZQ calibration interval + * Activate dynamic self refresh + */ +void change_refresh_period(struct mrc_params *mrc_params) +{ + u32 drfc; + u32 dcal; + u32 dpmc0; + + ENTERFN(); + + drfc = msg_port_read(MEM_CTLR, DRFC); + drfc &= ~DRFC_TREFI_MASK; + drfc |= (mrc_params->refresh_rate << 12); + drfc |= DRFC_REFDBTCLR; + msg_port_write(MEM_CTLR, DRFC, drfc); + + dcal = msg_port_read(MEM_CTLR, DCAL); + dcal &= ~DCAL_ZQCINT_MASK; + dcal |= (3 << 8); /* 63ms */ + msg_port_write(MEM_CTLR, DCAL, dcal); + + dpmc0 = msg_port_read(MEM_CTLR, DPMC0); + dpmc0 |= (DPMC0_DYNSREN | DPMC0_ENPHYCLKGATE); + msg_port_write(MEM_CTLR, DPMC0, dpmc0); + + LEAVEFN(); +} + +/* + * Configure DDRPHY for Auto-Refresh, Periodic Compensations, + * Dynamic Diff-Amp, ZQSPERIOD, Auto-Precharge, CKE Power-Down + */ +void set_auto_refresh(struct mrc_params *mrc_params) +{ + uint32_t channel; + uint32_t rank; + uint32_t bl; + uint32_t bl_divisor = 1; + uint32_t temp; + + ENTERFN(); + + /* + * Enable Auto-Refresh, Periodic Compensations, Dynamic Diff-Amp, + * ZQSPERIOD, Auto-Precharge, CKE Power-Down + */ + for (channel = 0; channel < NUM_CHANNELS; channel++) { + if (mrc_params->channel_enables & (1 << channel)) { + /* Enable Periodic RCOMPS */ + mrc_alt_write_mask(DDRPHY, CMPCTRL, 2, 2); + + /* Enable Dynamic DiffAmp & Set Read ODT Value */ + switch (mrc_params->rd_odt_value) { + case 0: + temp = 0x3f; /* OFF */ + break; + default: + temp = 0x00; /* Auto */ + break; + } + + for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor) / 2; bl++) { + /* Override: DIFFAMP, ODT */ + mrc_alt_write_mask(DDRPHY, + B0OVRCTL + bl * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET, + temp << 10, + 0x003ffc00); + + /* Override: DIFFAMP, ODT */ + mrc_alt_write_mask(DDRPHY, + B1OVRCTL + bl * DDRIODQ_BL_OFFSET + + channel * DDRIODQ_CH_OFFSET, + temp << 10, + 0x003ffc00); + } + + /* Issue ZQCS command */ + for (rank = 0; rank < NUM_RANKS; rank++) { + if (mrc_params->rank_enables & (1 << rank)) + dram_init_command(DCMD_ZQCS(rank)); + } + } + } + + clear_pointers(); + + LEAVEFN(); +} + +/* + * Depending on configuration enables ECC support + * + * Available memory size is decreased, and updated with 0s + * in order to clear error status. Address mode 2 forced. + */ +void ecc_enable(struct mrc_params *mrc_params) +{ + u32 drp; + u32 dsch; + u32 ecc_ctrl; + + if (mrc_params->ecc_enables == 0) + return; + + ENTERFN(); + + /* Configuration required in ECC mode */ + drp = msg_port_read(MEM_CTLR, DRP); + drp &= ~DRP_ADDRMAP_MASK; + drp |= DRP_ADDRMAP_MAP1; + drp |= DRP_PRI64BSPLITEN; + msg_port_write(MEM_CTLR, DRP, drp); + + /* Disable new request bypass */ + dsch = msg_port_read(MEM_CTLR, DSCH); + dsch |= DSCH_NEWBYPDIS; + msg_port_write(MEM_CTLR, DSCH, dsch); + + /* Enable ECC */ + ecc_ctrl = (DECCCTRL_SBEEN | DECCCTRL_DBEEN | DECCCTRL_ENCBGEN); + msg_port_write(MEM_CTLR, DECCCTRL, ecc_ctrl); + + /* Assume 8 bank memory, one bank is gone for ECC */ + mrc_params->mem_size -= mrc_params->mem_size / 8; + + /* For S3 resume memory content has to be preserved */ + if (mrc_params->boot_mode != BM_S3) { + select_hte(); + hte_mem_init(mrc_params, MRC_MEM_INIT); + select_mem_mgr(); + } + + LEAVEFN(); +} + +/* + * Execute memory test + * if error detected it is indicated in mrc_params->status + */ +void memory_test(struct mrc_params *mrc_params) +{ + uint32_t result = 0; + + ENTERFN(); + + select_hte(); + result = hte_mem_init(mrc_params, MRC_MEM_TEST); + select_mem_mgr(); + + DPF(D_INFO, "Memory test result %x\n", result); + mrc_params->status = ((result == 0) ? MRC_SUCCESS : MRC_E_MEMTEST); + LEAVEFN(); +} + +/* Lock MCU registers at the end of initialization sequence */ +void lock_registers(struct mrc_params *mrc_params) +{ + u32 dco; + + ENTERFN(); + + dco = msg_port_read(MEM_CTLR, DCO); + dco &= ~(DCO_PMICTL | DCO_PMIDIS); + dco |= (DCO_DRPLOCK | DCO_CPGCLOCK); + msg_port_write(MEM_CTLR, DCO, dco); + + LEAVEFN(); +} diff --git a/roms/u-boot/arch/x86/cpu/quark/smc.h b/roms/u-boot/arch/x86/cpu/quark/smc.h new file mode 100644 index 000000000..eee27564c --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/quark/smc.h @@ -0,0 +1,532 @@ +/* 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 _SMC_H_ +#define _SMC_H_ + +/* System Memory Controller Register Defines */ + +/* Memory Controller Message Bus Registers Offsets */ +#define DRP 0x00 +#define DTR0 0x01 +#define DTR1 0x02 +#define DTR2 0x03 +#define DTR3 0x04 +#define DTR4 0x05 +#define DPMC0 0x06 +#define DPMC1 0x07 +#define DRFC 0x08 +#define DSCH 0x09 +#define DCAL 0x0a +#define DRMC 0x0b +#define PMSTS 0x0c +#define DCO 0x0f +#define DSTAT 0x20 +#define SSKPD0 0x4a +#define SSKPD1 0x4b +#define DECCCTRL 0x60 +#define DECCSTAT 0x61 +#define DECCSBECNT 0x62 +#define DECCSBECA 0x68 +#define DECCSBECS 0x69 +#define DECCDBECA 0x6a +#define DECCDBECS 0x6b +#define DFUSESTAT 0x70 +#define SCRMSEED 0x80 +#define SCRMLO 0x81 +#define SCRMHI 0x82 + +/* DRP register defines */ +#define DRP_RKEN0 (1 << 0) +#define DRP_RKEN1 (1 << 1) +#define DRP_PRI64BSPLITEN (1 << 13) +#define DRP_ADDRMAP_MAP0 (1 << 14) +#define DRP_ADDRMAP_MAP1 (1 << 15) +#define DRP_ADDRMAP_MASK 0x0000c000 + +/* DTR0 register defines */ +#define DTR0_DFREQ_MASK 0x00000003 +#define DTR0_TRP_MASK 0x000000f0 +#define DTR0_TRCD_MASK 0x00000f00 +#define DTR0_TCL_MASK 0x00007000 + +/* DTR1 register defines */ +#define DTR1_TWCL_MASK 0x00000007 +#define DTR1_TCMD_MASK 0x00000030 +#define DTR1_TWTP_MASK 0x00000f00 +#define DTR1_TCCD_12CLK (1 << 12) +#define DTR1_TCCD_18CLK (1 << 13) +#define DTR1_TCCD_MASK 0x00003000 +#define DTR1_TFAW_MASK 0x000f0000 +#define DTR1_TRAS_MASK 0x00f00000 +#define DTR1_TRRD_MASK 0x03000000 +#define DTR1_TRTP_MASK 0x70000000 + +/* DTR2 register defines */ +#define DTR2_TRRDR_MASK 0x00000007 +#define DTR2_TWWDR_MASK 0x00000700 +#define DTR2_TRWDR_MASK 0x000f0000 + +/* DTR3 register defines */ +#define DTR3_TWRDR_MASK 0x00000007 +#define DTR3_TXXXX_MASK 0x00000070 +#define DTR3_TRWSR_MASK 0x00000f00 +#define DTR3_TWRSR_MASK 0x0001e000 +#define DTR3_TXP_MASK 0x00c00000 + +/* DTR4 register defines */ +#define DTR4_WRODTSTRT_MASK 0x00000003 +#define DTR4_WRODTSTOP_MASK 0x00000070 +#define DTR4_XXXX1_MASK 0x00000700 +#define DTR4_XXXX2_MASK 0x00007000 +#define DTR4_ODTDIS (1 << 15) +#define DTR4_TRGSTRDIS (1 << 16) + +/* DPMC0 register defines */ +#define DPMC0_PCLSTO_MASK 0x00070000 +#define DPMC0_PREAPWDEN (1 << 21) +#define DPMC0_DYNSREN (1 << 23) +#define DPMC0_CLKGTDIS (1 << 24) +#define DPMC0_DISPWRDN (1 << 25) +#define DPMC0_ENPHYCLKGATE (1 << 29) + +/* DRFC register defines */ +#define DRFC_TREFI_MASK 0x00007000 +#define DRFC_REFDBTCLR (1 << 21) + +/* DSCH register defines */ +#define DSCH_OOODIS (1 << 8) +#define DSCH_OOOST3DIS (1 << 9) +#define DSCH_NEWBYPDIS (1 << 12) + +/* DCAL register defines */ +#define DCAL_ZQCINT_MASK 0x00000700 +#define DCAL_SRXZQCL_MASK 0x00003000 + +/* DRMC register defines */ +#define DRMC_CKEMODE (1 << 4) +#define DRMC_ODTMODE (1 << 12) +#define DRMC_COLDWAKE (1 << 16) + +/* PMSTS register defines */ +#define PMSTS_DISR (1 << 0) + +/* DCO register defines */ +#define DCO_DRPLOCK (1 << 0) +#define DCO_CPGCLOCK (1 << 8) +#define DCO_PMICTL (1 << 28) +#define DCO_PMIDIS (1 << 29) +#define DCO_IC (1 << 31) + +/* DECCCTRL register defines */ +#define DECCCTRL_SBEEN (1 << 0) +#define DECCCTRL_DBEEN (1 << 1) +#define DECCCTRL_ENCBGEN (1 << 17) + +/* DRAM init command */ +#define DCMD_MRS1(rnk, dat) (0 | ((rnk) << 22) | (1 << 3) | ((dat) << 6)) +#define DCMD_REF(rnk) (1 | ((rnk) << 22)) +#define DCMD_PRE(rnk) (2 | ((rnk) << 22)) +#define DCMD_PREA(rnk) (2 | ((rnk) << 22) | (0x400 << 6)) +#define DCMD_ACT(rnk, row) (3 | ((rnk) << 22) | ((row) << 6)) +#define DCMD_WR(rnk, col) (4 | ((rnk) << 22) | ((col) << 6)) +#define DCMD_RD(rnk, col) (5 | ((rnk) << 22) | ((col) << 6)) +#define DCMD_ZQCS(rnk) (6 | ((rnk) << 22)) +#define DCMD_ZQCL(rnk) (6 | ((rnk) << 22) | (0x400 << 6)) +#define DCMD_NOP(rnk) (7 | ((rnk) << 22)) + +#define DDR3_EMRS1_DIC_40 0 +#define DDR3_EMRS1_DIC_34 1 + +#define DDR3_EMRS1_RTTNOM_0 0 +#define DDR3_EMRS1_RTTNOM_60 0x04 +#define DDR3_EMRS1_RTTNOM_120 0x40 +#define DDR3_EMRS1_RTTNOM_40 0x44 +#define DDR3_EMRS1_RTTNOM_20 0x200 +#define DDR3_EMRS1_RTTNOM_30 0x204 + +#define DDR3_EMRS2_RTTWR_60 (1 << 9) +#define DDR3_EMRS2_RTTWR_120 (1 << 10) + +/* BEGIN DDRIO Registers */ + +/* DDR IOs & COMPs */ +#define DDRIODQ_BL_OFFSET 0x0800 +#define DDRIODQ_CH_OFFSET ((NUM_BYTE_LANES / 2) * DDRIODQ_BL_OFFSET) +#define DDRIOCCC_CH_OFFSET 0x0800 +#define DDRCOMP_CH_OFFSET 0x0100 + +/* CH0-BL01-DQ */ +#define DQOBSCKEBBCTL 0x0000 +#define DQDLLTXCTL 0x0004 +#define DQDLLRXCTL 0x0008 +#define DQMDLLCTL 0x000c +#define B0RXIOBUFCTL 0x0010 +#define B0VREFCTL 0x0014 +#define B0RXOFFSET1 0x0018 +#define B0RXOFFSET0 0x001c +#define B1RXIOBUFCTL 0x0020 +#define B1VREFCTL 0x0024 +#define B1RXOFFSET1 0x0028 +#define B1RXOFFSET0 0x002c +#define DQDFTCTL 0x0030 +#define DQTRAINSTS 0x0034 +#define B1DLLPICODER0 0x0038 +#define B0DLLPICODER0 0x003c +#define B1DLLPICODER1 0x0040 +#define B0DLLPICODER1 0x0044 +#define B1DLLPICODER2 0x0048 +#define B0DLLPICODER2 0x004c +#define B1DLLPICODER3 0x0050 +#define B0DLLPICODER3 0x0054 +#define B1RXDQSPICODE 0x0058 +#define B0RXDQSPICODE 0x005c +#define B1RXDQPICODER32 0x0060 +#define B1RXDQPICODER10 0x0064 +#define B0RXDQPICODER32 0x0068 +#define B0RXDQPICODER10 0x006c +#define B01PTRCTL0 0x0070 +#define B01PTRCTL1 0x0074 +#define B01DBCTL0 0x0078 +#define B01DBCTL1 0x007c +#define B0LATCTL0 0x0080 +#define B1LATCTL0 0x0084 +#define B01LATCTL1 0x0088 +#define B0ONDURCTL 0x008c +#define B1ONDURCTL 0x0090 +#define B0OVRCTL 0x0094 +#define B1OVRCTL 0x0098 +#define DQCTL 0x009c +#define B0RK2RKCHGPTRCTRL 0x00a0 +#define B1RK2RKCHGPTRCTRL 0x00a4 +#define DQRK2RKCTL 0x00a8 +#define DQRK2RKPTRCTL 0x00ac +#define B0RK2RKLAT 0x00b0 +#define B1RK2RKLAT 0x00b4 +#define DQCLKALIGNREG0 0x00b8 +#define DQCLKALIGNREG1 0x00bc +#define DQCLKALIGNREG2 0x00c0 +#define DQCLKALIGNSTS0 0x00c4 +#define DQCLKALIGNSTS1 0x00c8 +#define DQCLKGATE 0x00cc +#define B0COMPSLV1 0x00d0 +#define B1COMPSLV1 0x00d4 +#define B0COMPSLV2 0x00d8 +#define B1COMPSLV2 0x00dc +#define B0COMPSLV3 0x00e0 +#define B1COMPSLV3 0x00e4 +#define DQVISALANECR0TOP 0x00e8 +#define DQVISALANECR1TOP 0x00ec +#define DQVISACONTROLCRTOP 0x00f0 +#define DQVISALANECR0BL 0x00f4 +#define DQVISALANECR1BL 0x00f8 +#define DQVISACONTROLCRBL 0x00fc +#define DQTIMINGCTRL 0x010c + +/* CH0-ECC */ +#define ECCDLLTXCTL 0x2004 +#define ECCDLLRXCTL 0x2008 +#define ECCMDLLCTL 0x200c +#define ECCB1DLLPICODER0 0x2038 +#define ECCB1DLLPICODER1 0x2040 +#define ECCB1DLLPICODER2 0x2048 +#define ECCB1DLLPICODER3 0x2050 +#define ECCB01DBCTL0 0x2078 +#define ECCB01DBCTL1 0x207c +#define ECCCLKALIGNREG0 0x20b8 +#define ECCCLKALIGNREG1 0x20bc +#define ECCCLKALIGNREG2 0x20c0 + +/* CH0-CMD */ +#define CMDOBSCKEBBCTL 0x4800 +#define CMDDLLTXCTL 0x4808 +#define CMDDLLRXCTL 0x480c +#define CMDMDLLCTL 0x4810 +#define CMDRCOMPODT 0x4814 +#define CMDDLLPICODER0 0x4820 +#define CMDDLLPICODER1 0x4824 +#define CMDCFGREG0 0x4840 +#define CMDPTRREG 0x4844 +#define CMDCLKALIGNREG0 0x4850 +#define CMDCLKALIGNREG1 0x4854 +#define CMDCLKALIGNREG2 0x4858 +#define CMDPMCONFIG0 0x485c +#define CMDPMDLYREG0 0x4860 +#define CMDPMDLYREG1 0x4864 +#define CMDPMDLYREG2 0x4868 +#define CMDPMDLYREG3 0x486c +#define CMDPMDLYREG4 0x4870 +#define CMDCLKALIGNSTS0 0x4874 +#define CMDCLKALIGNSTS1 0x4878 +#define CMDPMSTS0 0x487c +#define CMDPMSTS1 0x4880 +#define CMDCOMPSLV 0x4884 +#define CMDBONUS0 0x488c +#define CMDBONUS1 0x4890 +#define CMDVISALANECR0 0x4894 +#define CMDVISALANECR1 0x4898 +#define CMDVISACONTROLCR 0x489c +#define CMDCLKGATE 0x48a0 +#define CMDTIMINGCTRL 0x48a4 + +/* CH0-CLK-CTL */ +#define CCOBSCKEBBCTL 0x5800 +#define CCRCOMPIO 0x5804 +#define CCDLLTXCTL 0x5808 +#define CCDLLRXCTL 0x580c +#define CCMDLLCTL 0x5810 +#define CCRCOMPODT 0x5814 +#define CCDLLPICODER0 0x5820 +#define CCDLLPICODER1 0x5824 +#define CCDDR3RESETCTL 0x5830 +#define CCCFGREG0 0x5838 +#define CCCFGREG1 0x5840 +#define CCPTRREG 0x5844 +#define CCCLKALIGNREG0 0x5850 +#define CCCLKALIGNREG1 0x5854 +#define CCCLKALIGNREG2 0x5858 +#define CCPMCONFIG0 0x585c +#define CCPMDLYREG0 0x5860 +#define CCPMDLYREG1 0x5864 +#define CCPMDLYREG2 0x5868 +#define CCPMDLYREG3 0x586c +#define CCPMDLYREG4 0x5870 +#define CCCLKALIGNSTS0 0x5874 +#define CCCLKALIGNSTS1 0x5878 +#define CCPMSTS0 0x587c +#define CCPMSTS1 0x5880 +#define CCCOMPSLV1 0x5884 +#define CCCOMPSLV2 0x5888 +#define CCCOMPSLV3 0x588c +#define CCBONUS0 0x5894 +#define CCBONUS1 0x5898 +#define CCVISALANECR0 0x589c +#define CCVISALANECR1 0x58a0 +#define CCVISACONTROLCR 0x58a4 +#define CCCLKGATE 0x58a8 +#define CCTIMINGCTL 0x58ac + +/* COMP */ +#define CMPCTRL 0x6800 +#define SOFTRSTCNTL 0x6804 +#define MSCNTR 0x6808 +#define NMSCNTRL 0x680c +#define LATCH1CTL 0x6814 +#define COMPVISALANECR0 0x681c +#define COMPVISALANECR1 0x6820 +#define COMPVISACONTROLCR 0x6824 +#define COMPBONUS0 0x6830 +#define TCOCNTCTRL 0x683c +#define DQANAODTPUCTL 0x6840 +#define DQANAODTPDCTL 0x6844 +#define DQANADRVPUCTL 0x6848 +#define DQANADRVPDCTL 0x684c +#define DQANADLYPUCTL 0x6850 +#define DQANADLYPDCTL 0x6854 +#define DQANATCOPUCTL 0x6858 +#define DQANATCOPDCTL 0x685c +#define CMDANADRVPUCTL 0x6868 +#define CMDANADRVPDCTL 0x686c +#define CMDANADLYPUCTL 0x6870 +#define CMDANADLYPDCTL 0x6874 +#define CLKANAODTPUCTL 0x6880 +#define CLKANAODTPDCTL 0x6884 +#define CLKANADRVPUCTL 0x6888 +#define CLKANADRVPDCTL 0x688c +#define CLKANADLYPUCTL 0x6890 +#define CLKANADLYPDCTL 0x6894 +#define CLKANATCOPUCTL 0x6898 +#define CLKANATCOPDCTL 0x689c +#define DQSANAODTPUCTL 0x68a0 +#define DQSANAODTPDCTL 0x68a4 +#define DQSANADRVPUCTL 0x68a8 +#define DQSANADRVPDCTL 0x68ac +#define DQSANADLYPUCTL 0x68b0 +#define DQSANADLYPDCTL 0x68b4 +#define DQSANATCOPUCTL 0x68b8 +#define DQSANATCOPDCTL 0x68bc +#define CTLANADRVPUCTL 0x68c8 +#define CTLANADRVPDCTL 0x68cc +#define CTLANADLYPUCTL 0x68d0 +#define CTLANADLYPDCTL 0x68d4 +#define CHNLBUFSTATIC 0x68f0 +#define COMPOBSCNTRL 0x68f4 +#define COMPBUFFDBG0 0x68f8 +#define COMPBUFFDBG1 0x68fc +#define CFGMISCCH0 0x6900 +#define COMPEN0CH0 0x6904 +#define COMPEN1CH0 0x6908 +#define COMPEN2CH0 0x690c +#define STATLEGEN0CH0 0x6910 +#define STATLEGEN1CH0 0x6914 +#define DQVREFCH0 0x6918 +#define CMDVREFCH0 0x691c +#define CLKVREFCH0 0x6920 +#define DQSVREFCH0 0x6924 +#define CTLVREFCH0 0x6928 +#define TCOVREFCH0 0x692c +#define DLYSELCH0 0x6930 +#define TCODRAMBUFODTCH0 0x6934 +#define CCBUFODTCH0 0x6938 +#define RXOFFSETCH0 0x693c +#define DQODTPUCTLCH0 0x6940 +#define DQODTPDCTLCH0 0x6944 +#define DQDRVPUCTLCH0 0x6948 +#define DQDRVPDCTLCH0 0x694c +#define DQDLYPUCTLCH0 0x6950 +#define DQDLYPDCTLCH0 0x6954 +#define DQTCOPUCTLCH0 0x6958 +#define DQTCOPDCTLCH0 0x695c +#define CMDDRVPUCTLCH0 0x6968 +#define CMDDRVPDCTLCH0 0x696c +#define CMDDLYPUCTLCH0 0x6970 +#define CMDDLYPDCTLCH0 0x6974 +#define CLKODTPUCTLCH0 0x6980 +#define CLKODTPDCTLCH0 0x6984 +#define CLKDRVPUCTLCH0 0x6988 +#define CLKDRVPDCTLCH0 0x698c +#define CLKDLYPUCTLCH0 0x6990 +#define CLKDLYPDCTLCH0 0x6994 +#define CLKTCOPUCTLCH0 0x6998 +#define CLKTCOPDCTLCH0 0x699c +#define DQSODTPUCTLCH0 0x69a0 +#define DQSODTPDCTLCH0 0x69a4 +#define DQSDRVPUCTLCH0 0x69a8 +#define DQSDRVPDCTLCH0 0x69ac +#define DQSDLYPUCTLCH0 0x69b0 +#define DQSDLYPDCTLCH0 0x69b4 +#define DQSTCOPUCTLCH0 0x69b8 +#define DQSTCOPDCTLCH0 0x69bc +#define CTLDRVPUCTLCH0 0x69c8 +#define CTLDRVPDCTLCH0 0x69cc +#define CTLDLYPUCTLCH0 0x69d0 +#define CTLDLYPDCTLCH0 0x69d4 +#define FNLUPDTCTLCH0 0x69f0 + +/* PLL */ +#define MPLLCTRL0 0x7800 +#define MPLLCTRL1 0x7808 +#define MPLLCSR0 0x7810 +#define MPLLCSR1 0x7814 +#define MPLLCSR2 0x7820 +#define MPLLDFT 0x7828 +#define MPLLMON0CTL 0x7830 +#define MPLLMON1CTL 0x7838 +#define MPLLMON2CTL 0x783c +#define SFRTRIM 0x7850 +#define MPLLDFTOUT0 0x7858 +#define MPLLDFTOUT1 0x785c +#define MASTERRSTN 0x7880 +#define PLLLOCKDEL 0x7884 +#define SFRDEL 0x7888 +#define CRUVISALANECR0 0x78f0 +#define CRUVISALANECR1 0x78f4 +#define CRUVISACONTROLCR 0x78f8 +#define IOSFVISALANECR0 0x78fc +#define IOSFVISALANECR1 0x7900 +#define IOSFVISACONTROLCR 0x7904 + +/* END DDRIO Registers */ + +/* DRAM Specific Message Bus OpCodes */ +#define MSG_OP_DRAM_INIT 0x68 +#define MSG_OP_DRAM_WAKE 0xca + +#define SAMPLE_SIZE 6 + +/* must be less than this number to enable early deadband */ +#define EARLY_DB 0x12 +/* must be greater than this number to enable late deadband */ +#define LATE_DB 0x34 + +#define CHX_REGS (11 * 4) +#define FULL_CLK 128 +#define HALF_CLK 64 +#define QRTR_CLK 32 + +#define MCEIL(num, den) ((uint8_t)((num + den - 1) / den)) +#define MMAX(a, b) ((a) > (b) ? (a) : (b)) +#define DEAD_LOOP() for (;;); + +#define MIN_RDQS_EYE 10 /* in PI Codes */ +#define MIN_VREF_EYE 10 /* in VREF Codes */ +/* how many RDQS codes to jump while margining */ +#define RDQS_STEP 1 +/* how many VREF codes to jump while margining */ +#define VREF_STEP 1 +/* offset into "vref_codes[]" for minimum allowed VREF setting */ +#define VREF_MIN 0x00 +/* offset into "vref_codes[]" for maximum allowed VREF setting */ +#define VREF_MAX 0x3f +#define RDQS_MIN 0x00 /* minimum RDQS delay value */ +#define RDQS_MAX 0x3f /* maximum RDQS delay value */ + +/* how many WDQ codes to jump while margining */ +#define WDQ_STEP 1 + +enum { + B, /* BOTTOM VREF */ + T /* TOP VREF */ +}; + +enum { + L, /* LEFT RDQS */ + R /* RIGHT RDQS */ +}; + +/* Memory Options */ + +/* enable STATIC timing settings for RCVN (BACKUP_MODE) */ +#undef BACKUP_RCVN +/* enable STATIC timing settings for WDQS (BACKUP_MODE) */ +#undef BACKUP_WDQS +/* enable STATIC timing settings for RDQS (BACKUP_MODE) */ +#undef BACKUP_RDQS +/* enable STATIC timing settings for WDQ (BACKUP_MODE) */ +#undef BACKUP_WDQ +/* enable *COMP overrides (BACKUP_MODE) */ +#undef BACKUP_COMPS +/* enable the RD_TRAIN eye check */ +#undef RX_EYE_CHECK + +/* enable Host to Memory Clock Alignment */ +#define HMC_TEST +/* enable multi-rank support via rank2rank sharing */ +#define R2R_SHARING +/* disable signals not used in 16bit mode of DDRIO */ +#define FORCE_16BIT_DDRIO + +#define PLATFORM_ID 1 + +void clear_self_refresh(struct mrc_params *mrc_params); +void prog_ddr_timing_control(struct mrc_params *mrc_params); +void prog_decode_before_jedec(struct mrc_params *mrc_params); +void perform_ddr_reset(struct mrc_params *mrc_params); +void ddrphy_init(struct mrc_params *mrc_params); +void perform_jedec_init(struct mrc_params *mrc_params); +void set_ddr_init_complete(struct mrc_params *mrc_params); +void restore_timings(struct mrc_params *mrc_params); +void default_timings(struct mrc_params *mrc_params); +void rcvn_cal(struct mrc_params *mrc_params); +void wr_level(struct mrc_params *mrc_params); +void prog_page_ctrl(struct mrc_params *mrc_params); +void rd_train(struct mrc_params *mrc_params); +void wr_train(struct mrc_params *mrc_params); +void store_timings(struct mrc_params *mrc_params); +void enable_scrambling(struct mrc_params *mrc_params); +void prog_ddr_control(struct mrc_params *mrc_params); +void prog_dra_drb(struct mrc_params *mrc_params); +void perform_wake(struct mrc_params *mrc_params); +void change_refresh_period(struct mrc_params *mrc_params); +void set_auto_refresh(struct mrc_params *mrc_params); +void ecc_enable(struct mrc_params *mrc_params); +void memory_test(struct mrc_params *mrc_params); +void lock_registers(struct mrc_params *mrc_params); + +#endif /* _SMC_H_ */ diff --git a/roms/u-boot/arch/x86/cpu/queensbay/Kconfig b/roms/u-boot/arch/x86/cpu/queensbay/Kconfig new file mode 100644 index 000000000..c9e0fda03 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/queensbay/Kconfig @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + +config INTEL_QUEENSBAY + bool + select HAVE_FSP + select HAVE_CMC + select ARCH_EARLY_INIT_R + imply AHCI_PCI + imply ICH_SPI + imply INTEL_ICH6_GPIO + imply MMC + imply MMC_PCI + imply MMC_SDHCI + imply MMC_SDHCI_SDMA + imply PCH_GBE + imply SCSI + imply SCSI_AHCI + imply SPI_FLASH + imply SYS_NS16550 + imply USB + imply USB_EHCI_HCD + imply VIDEO_VESA + +if INTEL_QUEENSBAY + +config HAVE_CMC + bool "Add a Chipset Micro Code state machine binary" + help + Select this option to add a Chipset Micro Code state machine binary + to the resulting U-Boot image. It is a 64K data block of machine + specific code which must be put in the flash for the processor to + access when powered up before system BIOS is executed. + +config CMC_FILE + string "Chipset Micro Code state machine filename" + depends on HAVE_CMC + default "cmc.bin" + help + The filename of the file to use as Chipset Micro Code state machine + binary in the board directory. + +config CMC_ADDR + hex "Chipset Micro Code state machine binary location" + depends on HAVE_CMC + default 0xfffb0000 + help + The location of the CMC binary is determined by a strap. It must be + put in flash at a location matching the strap-determined base address. + + The default base address of 0xfffb0000 indicates that the binary must + be located at offset 0xb0000 from the beginning of a 1MB flash device. + +config CPU_ADDR_BITS + int + default 32 + +config DISABLE_IGD + bool "Disable Integrated Graphics Device (IGD)" + help + Disable the Integrated Graphics Device (IGD) so that it does not + show in the PCI configuration space as a VGA disaplay controller. + This gives a chance for U-Boot to run PCI/PCIe based graphics + card's VGA BIOS and use that card for the graphics console. + +endif diff --git a/roms/u-boot/arch/x86/cpu/queensbay/Makefile b/roms/u-boot/arch/x86/cpu/queensbay/Makefile new file mode 100644 index 000000000..ac2961356 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/queensbay/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + +obj-y += fsp_configs.o +obj-y += tnc.o diff --git a/roms/u-boot/arch/x86/cpu/queensbay/fsp_configs.c b/roms/u-boot/arch/x86/cpu/queensbay/fsp_configs.c new file mode 100644 index 000000000..381edd076 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/queensbay/fsp_configs.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Intel +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <asm/fsp1/fsp_support.h> + +void fsp_update_configs(struct fsp_config_data *config, + struct fspinit_rtbuf *rt_buf) +{ + /* Initialize runtime buffer for fsp_init() */ + rt_buf->common.stack_top = config->common.stack_top - 32; + rt_buf->common.boot_mode = config->common.boot_mode; + rt_buf->common.upd_data = &config->fsp_upd; + + /* Override any UPD setting if required */ +} diff --git a/roms/u-boot/arch/x86/cpu/queensbay/tnc.c b/roms/u-boot/arch/x86/cpu/queensbay/tnc.c new file mode 100644 index 000000000..782ed863f --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/queensbay/tnc.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <init.h> +#include <dm/device-internal.h> +#include <pci.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/post.h> +#include <asm/arch/device.h> +#include <asm/arch/tnc.h> +#include <asm/fsp1/fsp_support.h> +#include <asm/processor.h> + +static int __maybe_unused disable_igd(void) +{ + struct udevice *igd, *sdvo; + int ret; + + ret = dm_pci_bus_find_bdf(TNC_IGD, &igd); + if (ret) + return ret; + if (!igd) + return 0; + + ret = dm_pci_bus_find_bdf(TNC_SDVO, &sdvo); + if (ret) + return ret; + if (!sdvo) + return 0; + + /* + * According to Atom E6xx datasheet, setting VGA Disable (bit17) + * of Graphics Controller register (offset 0x50) prevents IGD + * (D2:F0) from reporting itself as a VGA display controller + * class in the PCI configuration space, and should also prevent + * it from responding to VGA legacy memory range and I/O addresses. + * + * However test result shows that with just VGA Disable bit set and + * a PCIe graphics card connected to one of the PCIe controllers on + * the E6xx, accessing the VGA legacy space still causes system hang. + * After a number of attempts, it turns out besides VGA Disable bit, + * the SDVO (D3:F0) device should be disabled to make it work. + * + * To simplify, use the Function Disable register (offset 0xc4) + * to disable both IGD (D2:F0) and SDVO (D3:F0) devices. Now these + * two devices will be completely disabled (invisible in the PCI + * configuration space) unless a system reset is performed. + */ + dm_pci_write_config32(igd, IGD_FD, FUNC_DISABLE); + dm_pci_write_config32(sdvo, IGD_FD, FUNC_DISABLE); + + /* + * After setting the function disable bit, IGD and SDVO devices will + * disappear in the PCI configuration space. This however creates an + * inconsistent state from a driver model PCI controller point of view, + * as these two PCI devices are still attached to its parent's child + * device list as maintained by the driver model. Some driver model PCI + * APIs like dm_pci_find_class(), are referring to the list to speed up + * the finding process instead of re-enumerating the whole PCI bus, so + * it gets the stale cached data which is wrong. + * + * Note x86 PCI enueration normally happens twice, in pre-relocation + * phase and post-relocation. One option might be to call disable_igd() + * in one of the pre-relocation initialization hooks so that it gets + * disabled in the first round, and when it comes to the second round + * driver model PCI will construct a correct list. Unfortunately this + * does not work as Intel FSP is used on this platform to perform low + * level initialization, and fsp_init_phase_pci() is called only once + * in the post-relocation phase. If we disable IGD and SDVO devices, + * fsp_init_phase_pci() simply hangs and never returns. + * + * So the only option we have is to manually remove these two devices. + */ + ret = device_remove(igd, DM_REMOVE_NORMAL); + if (ret) + return ret; + ret = device_unbind(igd); + if (ret) + return ret; + ret = device_remove(sdvo, DM_REMOVE_NORMAL); + if (ret) + return ret; + ret = device_unbind(sdvo); + if (ret) + return ret; + + return 0; +} + +int arch_cpu_init(void) +{ + post_code(POST_CPU_INIT); + + return x86_cpu_init_f(); +} + +static void tnc_irq_init(void) +{ + struct tnc_rcba *rcba; + u32 base; + + pci_read_config32(TNC_LPC, LPC_RCBA, &base); + base &= ~MEM_BAR_EN; + rcba = (struct tnc_rcba *)base; + + /* Make sure all internal PCI devices are using INTA */ + writel(INTA, &rcba->d02ip); + writel(INTA, &rcba->d03ip); + writel(INTA, &rcba->d27ip); + writel(INTA, &rcba->d31ip); + writel(INTA, &rcba->d23ip); + writel(INTA, &rcba->d24ip); + writel(INTA, &rcba->d25ip); + writel(INTA, &rcba->d26ip); + + /* + * Route TunnelCreek PCI device interrupt pin to PIRQ + * + * Since PCIe downstream ports received INTx are routed to PIRQ + * A/B/C/D directly and not configurable, we have to route PCIe + * root ports' INTx to PIRQ A/B/C/D as well. For other devices + * on TunneCreek, route them to PIRQ E/F/G/H. + */ + writew(PIRQE, &rcba->d02ir); + writew(PIRQF, &rcba->d03ir); + writew(PIRQG, &rcba->d27ir); + writew(PIRQH, &rcba->d31ir); + writew(PIRQA, &rcba->d23ir); + writew(PIRQB, &rcba->d24ir); + writew(PIRQC, &rcba->d25ir); + writew(PIRQD, &rcba->d26ir); +} + +int arch_early_init_r(void) +{ + int ret = 0; + +#ifdef CONFIG_DISABLE_IGD + ret = disable_igd(); +#endif + + tnc_irq_init(); + + return ret; +} diff --git a/roms/u-boot/arch/x86/cpu/resetvec.S b/roms/u-boot/arch/x86/cpu/resetvec.S new file mode 100644 index 000000000..cf972738b --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/resetvec.S @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * U-Boot - x86 Startup Code + * + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + */ + +/* Reset vector, jumps to start16.S */ + +.extern start16 + +.section .resetvec, "ax" +.code16 +reset_vector: + cli + cld + jmp start16 diff --git a/roms/u-boot/arch/x86/cpu/sipi_vector.S b/roms/u-boot/arch/x86/cpu/sipi_vector.S new file mode 100644 index 000000000..6d2da963b --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/sipi_vector.S @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015 Google, Inc + * + * Taken from coreboot file of the same name + */ + +/* + * The SIPI vector is responsible for initializing the APs in the sytem. It + * loads microcode, sets up MSRs, and enables caching before calling into + * C code + */ + +#include <asm/msr-index.h> +#include <asm/processor.h> +#include <asm/processor-flags.h> +#include <asm/sipi.h> + +#define CODE_SEG (X86_GDT_ENTRY_32BIT_CS * X86_GDT_ENTRY_SIZE) +#define DATA_SEG (X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE) + +/* + * First we have the 16-bit section. Every AP process starts here. + * The simple task is to load U-Boot's Global Descriptor Table (GDT) to allow + * U-Boot's 32-bit code to become visible, then jump to ap_start. + * + * Note that this code is copied to RAM below 1MB in mp_init.c, and runs from + * there, but the 32-bit code (ap_start and onwards) is part of U-Boot and + * is therefore relocated to the top of RAM with other U-Boot code. This + * means that for the 16-bit code we must write relocatable code, but for the + * rest, we can do what we like. + */ +.text +.code16 +.globl ap_start16 +ap_start16: + cli + xorl %eax, %eax + movl %eax, %cr3 /* Invalidate TLB */ + + /* setup the data segment */ + movw %cs, %ax + movw %ax, %ds + + /* Use an address relative to the data segment for the GDT */ + movl $gdtaddr, %ebx + subl $ap_start16, %ebx + + data32 lgdt (%ebx) + + movl %cr0, %eax + andl $(~(X86_CR0_PG | X86_CR0_AM | X86_CR0_WP | X86_CR0_NE | \ + X86_CR0_TS | X86_CR0_EM | X86_CR0_MP)), %eax + orl $(X86_CR0_NW | X86_CR0_CD | X86_CR0_PE), %eax + movl %eax, %cr0 + + movl $ap_start_jmp, %eax + subl $ap_start16, %eax + movw %ax, %bp + + /* Jump to ap_start within U-Boot */ +data32 cs ljmp *(%bp) + + .align 4 +.globl sipi_params_16bit +sipi_params_16bit: + /* 48-bit far pointer */ +ap_start_jmp: + .long 0 /* offset set to ap_start by U-Boot */ + .word CODE_SEG /* segment */ + + .word 0 /* padding */ +gdtaddr: + .word 0 /* limit */ + .long 0 /* table */ + .word 0 /* unused */ + +.globl ap_start16_code_end +ap_start16_code_end: + +/* + * Set up the special 'fs' segment for global_data. Then jump to ap_continue + * to set up the AP. + */ +.globl ap_start +ap_start: + .code32 + movw $DATA_SEG, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %gs + + movw $(X86_GDT_ENTRY_32BIT_FS * X86_GDT_ENTRY_SIZE), %ax + movw %ax, %fs + + /* Load the Interrupt descriptor table */ + mov idt_ptr, %ebx + lidt (%ebx) + + /* Obtain cpu number */ + movl ap_count, %eax +1: + movl %eax, %ecx + inc %ecx + lock cmpxchg %ecx, ap_count + jnz 1b + + /* Setup stacks for each CPU */ + movl stack_size, %eax + mul %ecx + movl stack_top, %edx + subl %eax, %edx + mov %edx, %esp + /* Save cpu number */ + mov %ecx, %esi + + /* Determine if one should check microcode versions */ + mov microcode_ptr, %edi + test %edi, %edi + jz microcode_done /* Bypass if no microde exists */ + + /* Get the Microcode version */ + mov $1, %eax + cpuid + mov $MSR_IA32_UCODE_REV, %ecx + rdmsr + /* If something already loaded skip loading again */ + test %edx, %edx + jnz microcode_done + + /* Determine if parallel microcode loading is allowed */ + cmpl $0xffffffff, microcode_lock + je load_microcode + + /* Protect microcode loading */ +lock_microcode: + lock btsl $0, microcode_lock + jc lock_microcode + +load_microcode: + /* Load new microcode */ + mov $MSR_IA32_UCODE_WRITE, %ecx + xor %edx, %edx + mov %edi, %eax + /* + * The microcode pointer is passed in pointing to the header. Adjust + * pointer to reflect the payload (header size is 48 bytes) + */ + add $UCODE_HEADER_LEN, %eax + pusha + wrmsr + popa + + /* Unconditionally unlock microcode loading */ + cmpl $0xffffffff, microcode_lock + je microcode_done + + xor %eax, %eax + mov %eax, microcode_lock + +microcode_done: + /* + * Load MSRs. Each entry in the table consists of: + * 0: index, + * 4: value[31:0] + * 8: value[63:32] + * See struct saved_msr in mp_init.c. + */ + mov msr_table_ptr, %edi + mov msr_count, %ebx + test %ebx, %ebx + jz 1f +load_msr: + mov (%edi), %ecx + mov 4(%edi), %eax + mov 8(%edi), %edx + wrmsr + add $12, %edi + dec %ebx + jnz load_msr + +1: + /* Enable caching */ + mov %cr0, %eax + andl $(~(X86_CR0_CD | X86_CR0_NW)), %eax + mov %eax, %cr0 + + /* c_handler(cpu_num) */ + movl %esi, %eax /* cpu_num */ + mov c_handler, %esi + call *%esi + + /* This matches struct sipi_param */ + .align 4 +.globl sipi_params +sipi_params: +idt_ptr: + .long 0 +stack_top: + .long 0 +stack_size: + .long 0 +microcode_lock: + .long 0 +microcode_ptr: + .long 0 +msr_table_ptr: + .long 0 +msr_count: + .long 0 +c_handler: + .long 0 +ap_count: + .long 0 diff --git a/roms/u-boot/arch/x86/cpu/slimbootloader/Kconfig b/roms/u-boot/arch/x86/cpu/slimbootloader/Kconfig new file mode 100644 index 000000000..58a9ca01a --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/slimbootloader/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2019 Intel Corporation <www.intel.com> + +config SYS_SLIMBOOTLOADER + bool + select USE_HOB + imply SYS_NS16550 + imply AHCI_PCI + imply SCSI + imply SCSI_AHCI + imply MMC + imply MMC_PCI + imply MMC_SDHCI + imply MMC_SDHCI_SDMA + imply USB + imply USB_EHCI_HCD + imply USB_XHCI_HCD + imply E1000 + imply X86_TSC_READ_BASE diff --git a/roms/u-boot/arch/x86/cpu/slimbootloader/Makefile b/roms/u-boot/arch/x86/cpu/slimbootloader/Makefile new file mode 100644 index 000000000..aac9fa3db --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/slimbootloader/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2019 Intel Corporation <www.intel.com> + +obj-y += car.o slimbootloader.o sdram.o serial.o diff --git a/roms/u-boot/arch/x86/cpu/slimbootloader/car.S b/roms/u-boot/arch/x86/cpu/slimbootloader/car.S new file mode 100644 index 000000000..6e0304333 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/slimbootloader/car.S @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + */ + +#include <generated/asm-offsets.h> + +.section .text + +.globl car_init +car_init: + /* Get hob pointer parameter from previous stage's stack */ + mov 0x4(%esp), %esi + jmp car_init_ret diff --git a/roms/u-boot/arch/x86/cpu/slimbootloader/sdram.c b/roms/u-boot/arch/x86/cpu/slimbootloader/sdram.c new file mode 100644 index 000000000..36d0d4da5 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/slimbootloader/sdram.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + */ + +#include <common.h> +#include <init.h> +#include <asm/global_data.h> +#include <linux/sizes.h> +#include <asm/e820.h> +#include <asm/arch/slimbootloader.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * This returns a data pointer of memory map info from the guid hob. + * + * @return: A data pointer of memory map info hob + */ +static struct sbl_memory_map_info *get_memory_map_info(void) +{ + struct sbl_memory_map_info *data; + const efi_guid_t guid = SBL_MEMORY_MAP_INFO_GUID; + + if (!gd->arch.hob_list) + return NULL; + + data = hob_get_guid_hob_data(gd->arch.hob_list, NULL, &guid); + if (!data) + panic("memory map info hob not found\n"); + if (!data->count) + panic("invalid number of memory map entries\n"); + + return data; +} + +#define for_each_if(condition) if (!(condition)) {} else + +#define for_each_memory_map_entry_reversed(iter, entries) \ + for (iter = entries->count - 1; iter >= 0; iter--) \ + for_each_if(entries->entry[iter].type == E820_RAM) + +/** + * This is to give usable memory region information for u-boot relocation. + * so search usable memory region lower than 4GB. + * The memory map entries from Slim Bootloader hob are already sorted. + * + * @total_size: The memory size that u-boot occupies + * @return : The top available memory address lower than 4GB + */ +ulong board_get_usable_ram_top(ulong total_size) +{ + struct sbl_memory_map_info *data; + int i; + u64 addr_start; + u64 addr_end; + ulong ram_top; + + data = get_memory_map_info(); + + /** + * sorted memory map entries from Slim Bootloader based on physical + * start memory address, from low to high. So do reversed search to + * get highest usable, suitable size, 4KB aligned available memory + * under 4GB. + */ + ram_top = 0; + for_each_memory_map_entry_reversed(i, data) { + addr_start = data->entry[i].addr; + addr_end = addr_start + data->entry[i].size; + + if (addr_start > SZ_4G) + continue; + + if (addr_end > SZ_4G) + addr_end = SZ_4G; + + if (addr_end < total_size) + continue; + + /* to relocate u-boot at 4K aligned memory */ + addr_end = rounddown(addr_end - total_size, SZ_4K); + if (addr_end >= addr_start) { + ram_top = (ulong)addr_end + total_size; + break; + } + } + + if (!ram_top) + panic("failed to find available memory for relocation!"); + + return ram_top; +} + +/** + * The memory initialization has already been done in previous Slim Bootloader + * stage thru FSP-M. Instead, this sets the ram_size from the memory map info + * hob. + */ +int dram_init(void) +{ + struct sbl_memory_map_info *data; + int i; + u64 ram_size; + + data = get_memory_map_info(); + + /** + * sorted memory map entries from Slim Bootloader based on physical + * start memory address, from low to high. So do reversed search to + * simply get highest usable memory address as RAM size + */ + ram_size = 0; + for_each_memory_map_entry_reversed(i, data) { + /* simply use the highest usable memory address as RAM size */ + ram_size = data->entry[i].addr + data->entry[i].size; + break; + } + + if (!ram_size) + panic("failed to detect memory size"); + + gd->ram_size = ram_size; + return 0; +} + +int dram_init_banksize(void) +{ + if (!CONFIG_NR_DRAM_BANKS) + return 0; + + /* simply use a single bank to have whole size for now */ + gd->bd->bi_dram[0].start = 0; + gd->bd->bi_dram[0].size = gd->ram_size; + return 0; +} + +unsigned int install_e820_map(unsigned int max_entries, + struct e820_entry *entries) +{ + struct sbl_memory_map_info *data; + unsigned int i; + + data = get_memory_map_info(); + + for (i = 0; i < data->count; i++) { + entries[i].addr = data->entry[i].addr; + entries[i].size = data->entry[i].size; + entries[i].type = data->entry[i].type; + } + + return i; +} diff --git a/roms/u-boot/arch/x86/cpu/slimbootloader/serial.c b/roms/u-boot/arch/x86/cpu/slimbootloader/serial.c new file mode 100644 index 000000000..d28b28089 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/slimbootloader/serial.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <ns16550.h> +#include <serial.h> +#include <asm/arch/slimbootloader.h> +#include <asm/global_data.h> + +/** + * The serial port info hob is generated by Slim Bootloader, so eligible for + * Slim Bootloader based boards only. + */ +static int slimbootloader_serial_of_to_plat(struct udevice *dev) +{ + const efi_guid_t guid = SBL_SERIAL_PORT_INFO_GUID; + struct sbl_serial_port_info *data; + struct ns16550_plat *plat = dev_get_plat(dev); + + if (!gd->arch.hob_list) + panic("hob list not found!"); + + data = hob_get_guid_hob_data(gd->arch.hob_list, NULL, &guid); + if (!data) { + debug("failed to get serial port information\n"); + return -ENOENT; + } + debug("type:%d base=0x%08x baudrate=%d stride=%d clk=%d\n", + data->type, + data->base, + data->baud, + data->stride, + data->clk); + + plat->base = data->base; + /* ns16550 uses reg_shift, then covert stride to shift */ + plat->reg_shift = data->stride >> 1; + plat->reg_width = data->stride; + plat->clock = data->clk; + plat->fcr = UART_FCR_DEFVAL; + plat->flags = 0; + if (data->type == 1) + plat->flags |= NS16550_FLAG_IO; + + return 0; +} + +static const struct udevice_id slimbootloader_serial_ids[] = { + { .compatible = "intel,slimbootloader-uart" }, + {} +}; + +U_BOOT_DRIVER(serial_slimbootloader) = { + .name = "serial_slimbootloader", + .id = UCLASS_SERIAL, + .of_match = slimbootloader_serial_ids, + .of_to_plat = slimbootloader_serial_of_to_plat, + .plat_auto = sizeof(struct ns16550_plat), + .priv_auto = sizeof(struct ns16550), + .probe = ns16550_serial_probe, + .ops = &ns16550_serial_ops, +}; diff --git a/roms/u-boot/arch/x86/cpu/slimbootloader/slimbootloader.c b/roms/u-boot/arch/x86/cpu/slimbootloader/slimbootloader.c new file mode 100644 index 000000000..ec5b87cfd --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/slimbootloader/slimbootloader.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + */ + +#include <common.h> +#include <cpu_func.h> +#include <init.h> +#include <log.h> +#include <asm/arch/slimbootloader.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * This sets tsc_base and clock_rate for early_timer and tsc_timer. + * The performance info guid hob has all performance timestamp data, but + * the only tsc frequency info is used for the timer driver for now. + * + * Slim Bootloader already calibrated TSC and provides it to U-Boot. + * Therefore, U-Boot does not have to re-calibrate TSC. + * Configuring tsc_base and clock_rate here makes x86 tsc_timer driver + * bypass TSC calibration and use the provided TSC frequency. + */ +static void tsc_init(void) +{ + struct sbl_performance_info *data; + const efi_guid_t guid = SBL_PERFORMANCE_INFO_GUID; + + if (!gd->arch.hob_list) + panic("hob list not found!"); + + gd->arch.tsc_base = rdtsc(); + debug("tsc_base=0x%llx\n", gd->arch.tsc_base); + + data = hob_get_guid_hob_data(gd->arch.hob_list, NULL, &guid); + if (!data) { + debug("performance info hob not found\n"); + return; + } + + /* frequency is in KHz, so to Hz */ + gd->arch.clock_rate = data->frequency * 1000; + debug("freq=0x%lx\n", gd->arch.clock_rate); +} + +int arch_cpu_init(void) +{ + tsc_init(); + + return x86_cpu_init_f(); +} + +int checkcpu(void) +{ + return 0; +} + +int print_cpuinfo(void) +{ + return default_print_cpuinfo(); +} diff --git a/roms/u-boot/arch/x86/cpu/start.S b/roms/u-boot/arch/x86/cpu/start.S new file mode 100644 index 000000000..897fd926c --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/start.S @@ -0,0 +1,316 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * U-Boot - x86 Startup Code + * + * This is always the first code to run from the U-Boot source. To spell it out: + * + * 1. When TPL (Tertiary Program Loader) is enabled, the boot flow is + * TPL->SPL->U-Boot and this file is used for TPL. Then start_from_tpl.S is used + * for SPL and start_from_spl.S is used for U-Boot proper. + * + * 2. When SPL (Secondary Program Loader) is enabled, but not TPL, the boot + * flow is SPL->U-Boot and this file is used for SPL. Then start_from_spl.S is + * used for U-Boot proper. + * + * 3. When neither TPL nor SPL is used, this file is used for U-Boot proper. + * + * (C) Copyright 2008-2011 + * Graeme Russ, <graeme.russ@gmail.com> + * + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + */ + +#include <config.h> +#include <asm/post.h> +#include <asm/processor.h> +#include <asm/processor-flags.h> +#include <generated/generic-asm-offsets.h> +#include <generated/asm-offsets.h> +#include <linux/linkage.h> + +.section .text.start +.code32 +.globl _start +.type _start, @function +.globl _x86boot_start +_x86boot_start: + /* + * This is the fail-safe 32-bit bootstrap entry point. + * + * This code is used when booting from another boot loader like + * coreboot or EFI. So we repeat some of the same init found in + * start16. + */ + cli + cld + + /* Turn off cache (this might require a 486-class CPU) */ + movl %cr0, %eax + orl $(X86_CR0_NW | X86_CR0_CD), %eax + movl %eax, %cr0 + wbinvd + + /* + * Zero the BIST (Built-In Self Test) value since we don't have it. + * It must be 0 or the previous loader would have reported an error. + */ + movl $0, %ebp + + jmp 1f + + /* Add a way for tools to discover the _start entry point */ + .align 4 + .long 0x12345678 +_start: + /* This is the 32-bit cold-reset entry point, coming from start16 */ + + /* Save BIST */ + movl %eax, %ebp +1: + + /* Save table pointer */ + movl %ecx, %esi + +#ifdef CONFIG_X86_LOAD_FROM_32_BIT + lgdt gdt_ptr2 +#endif + + /* Load the segment registers to match the GDT loaded in start16.S */ + movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax + movw %ax, %fs + movw %ax, %ds + movw %ax, %gs + movw %ax, %es + movw %ax, %ss + + /* Clear the interrupt vectors */ + lidt blank_idt_ptr + +#ifdef CONFIG_USE_EARLY_BOARD_INIT + /* + * Critical early platform init - generally not used, we prefer init + * to happen later when we have a console, in case something goes + * wrong. + */ + jmp early_board_init +.globl early_board_init_ret +early_board_init_ret: +#endif + + post_code(POST_START) + + /* Initialise Cache-As-RAM */ + jmp car_init +.globl car_init_ret +car_init_ret: +#ifdef CONFIG_USE_CAR + /* + * We now have CONFIG_SYS_CAR_SIZE bytes of Cache-As-RAM (or SRAM, + * or fully initialised SDRAM - we really don't care which) + * starting at CONFIG_SYS_CAR_ADDR to be used as a temporary stack + * and early malloc() area. The MRC requires some space at the top. + * + * Stack grows down from top of CAR. We have: + * + * top-> CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE + * MRC area + * global_data with x86 global descriptor table + * early malloc area + * stack + * bottom-> CONFIG_SYS_CAR_ADDR + */ + movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %esp +#ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE + subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %esp +#endif +#else + /* + * Instructions for FSP1, but not FSP2: + * U-Boot enters here twice. For the first time it comes from + * car_init_done() with esp points to a temporary stack and esi + * set to zero. For the second time it comes from fsp_init_done() + * with esi holding the HOB list address returned by the FSP. + */ +#endif + /* Set up global data */ + mov %esp, %eax + call board_init_f_alloc_reserve + mov %eax, %esp + call board_init_f_init_reserve + +#ifdef CONFIG_DEBUG_UART + call debug_uart_init +#endif + + /* Get address of global_data */ + mov %fs:0, %edx +#if defined(CONFIG_USE_HOB) && !defined(CONFIG_USE_CAR) + /* Store the HOB list if we have one */ + test %esi, %esi + jz skip_hob + movl %esi, GD_HOB_LIST(%edx) + +#ifdef CONFIG_HAVE_FSP + /* + * After fsp_init() returns, the stack has already been switched to a + * place within system memory as defined by CONFIG_FSP_TEMP_RAM_ADDR. + * Enlarge the size of malloc() pool before relocation since we have + * plenty of memory now. + */ + subl $CONFIG_FSP_SYS_MALLOC_F_LEN, %esp + movl %esp, GD_MALLOC_BASE(%edx) +#endif +skip_hob: +#else + /* Store table pointer */ + movl %esi, GD_TABLE(%edx) +#endif + /* Store BIST */ + movl %ebp, GD_BIST(%edx) + + /* Set parameter to board_init_f() to boot flags */ + post_code(POST_START_DONE) + xorl %eax, %eax + + /* Enter, U-Boot! */ + call board_init_f + + /* indicate (lack of) progress */ + movw $0x85, %ax + jmp die + +.globl board_init_f_r_trampoline +.type board_init_f_r_trampoline, @function +board_init_f_r_trampoline: + /* + * SDRAM has been initialised, U-Boot code has been copied into + * RAM, BSS has been cleared and relocation adjustments have been + * made. It is now time to jump into the in-RAM copy of U-Boot + * + * %eax = Address of top of new stack + */ + + /* Stack grows down from top of SDRAM */ + movl %eax, %esp + + /* See if we need to disable CAR */ + call car_uninit + + /* Re-enter U-Boot by calling board_init_f_r() */ + call board_init_f_r + +#ifdef CONFIG_TPL +.globl jump_to_spl +.type jump_to_spl, @function +jump_to_spl: + /* Reset stack to the top of CAR space */ + movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %esp +#ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE + subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %esp +#endif + + jmp *%eax +#endif + +die: + hlt + jmp die + hlt + +WEAK(car_uninit) + ret +ENDPROC(car_uninit) + +blank_idt_ptr: + .word 0 /* limit */ + .long 0 /* base */ + + .p2align 2 /* force 4-byte alignment */ + + /* Add a multiboot header so U-Boot can be loaded by GRUB2 */ +multiboot_header: + /* magic */ + .long 0x1badb002 + /* flags */ + .long (1 << 16) + /* checksum */ + .long -0x1BADB002 - (1 << 16) + /* header addr */ + .long multiboot_header - _x86boot_start + CONFIG_SYS_TEXT_BASE + /* load addr */ + .long CONFIG_SYS_TEXT_BASE + /* load end addr */ + .long 0 + /* bss end addr */ + .long 0 + /* entry addr */ + .long CONFIG_SYS_TEXT_BASE + +#ifdef CONFIG_X86_LOAD_FROM_32_BIT + /* + * The following Global Descriptor Table is just enough to get us into + * 'Flat Protected Mode' - It will be discarded as soon as the final + * GDT is setup in a safe location in RAM + */ +gdt_ptr2: + .word 0x1f /* limit (31 bytes = 4 GDT entries - 1) */ + .long gdt_rom2 /* base */ + + /* Some CPUs are picky about GDT alignment... */ + .align 16 +.globl gdt_rom2 +gdt_rom2: + /* + * The GDT table ... + * + * Selector Type + * 0x00 NULL + * 0x08 Unused + * 0x10 32bit code + * 0x18 32bit data/stack + */ + /* The NULL Desciptor - Mandatory */ + .word 0x0000 /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x00 /* access */ + .byte 0x00 /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* Unused Desciptor - (matches Linux) */ + .word 0x0000 /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x00 /* access */ + .byte 0x00 /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* + * The Code Segment Descriptor: + * - Base = 0x00000000 + * - Size = 4GB + * - Access = Present, Ring 0, Exec (Code), Readable + * - Flags = 4kB Granularity, 32-bit + */ + .word 0xffff /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x9b /* access */ + .byte 0xcf /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* + * The Data Segment Descriptor: + * - Base = 0x00000000 + * - Size = 4GB + * - Access = Present, Ring 0, Non-Exec (Data), Writable + * - Flags = 4kB Granularity, 32-bit + */ + .word 0xffff /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x93 /* access */ + .byte 0xcf /* flags + limit_high */ + .byte 0x00 /* base_high */ +#endif diff --git a/roms/u-boot/arch/x86/cpu/start16.S b/roms/u-boot/arch/x86/cpu/start16.S new file mode 100644 index 000000000..865a49731 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/start16.S @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * U-Boot - x86 Startup Code + * + * (C) Copyright 2008-2011 + * Graeme Russ, <graeme.russ@gmail.com> + * + * (C) Copyright 2002,2003 + * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se> + */ + +#include <asm/processor-flags.h> + +#define BOOT_SEG 0xffff0000 /* linear segment of boot code */ + +.section .start16, "ax" +.code16 +.globl start16 +start16: + /* Save BIST */ + movl %eax, %ecx + + xorl %eax, %eax + movl %eax, %cr3 /* Invalidate TLB */ + + /* Turn off cache (this might require a 486-class CPU) */ + movl %cr0, %eax + orl $(X86_CR0_NW | X86_CR0_CD), %eax + movl %eax, %cr0 + wbinvd + + /* load the temporary Global Descriptor Table */ +data32 cs lidt idt_ptr +data32 cs lgdt gdt_ptr + + /* Now, we enter protected mode */ + movl %cr0, %eax + orl $X86_CR0_PE, %eax + movl %eax, %cr0 + + /* Flush the prefetch queue */ + jmp ff +ff: + + /* Finally restore BIST and jump to the 32-bit initialization code */ + movl %ecx, %eax +data32 cs ljmp *code32start + + /* 48-bit far pointer */ +code32start: + .long _start /* offset */ + .word 0x10 /* segment */ + +idt_ptr: + .word 0 /* limit */ + .long 0 /* base */ + + /* + * The following Global Descriptor Table is just enough to get us into + * 'Flat Protected Mode' - It will be discarded as soon as the final + * GDT is setup in a safe location in RAM + */ +gdt_ptr: + .word 0x1f /* limit (31 bytes = 4 GDT entries - 1) */ + .long BOOT_SEG + gdt_rom /* base */ + + /* Some CPUs are picky about GDT alignment... */ + .align 16 +.globl gdt_rom +gdt_rom: + /* + * The GDT table ... + * + * Selector Type + * 0x00 NULL + * 0x08 Unused + * 0x10 32bit code + * 0x18 32bit data/stack + */ + /* The NULL Desciptor - Mandatory */ + .word 0x0000 /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x00 /* access */ + .byte 0x00 /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* Unused Desciptor - (matches Linux) */ + .word 0x0000 /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x00 /* access */ + .byte 0x00 /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* + * The Code Segment Descriptor: + * - Base = 0x00000000 + * - Size = 4GB + * - Access = Present, Ring 0, Exec (Code), Readable + * - Flags = 4kB Granularity, 32-bit + */ + .word 0xffff /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x9b /* access */ + .byte 0xcf /* flags + limit_high */ + .byte 0x00 /* base_high */ + + /* + * The Data Segment Descriptor: + * - Base = 0x00000000 + * - Size = 4GB + * - Access = Present, Ring 0, Non-Exec (Data), Writable + * - Flags = 4kB Granularity, 32-bit + */ + .word 0xffff /* limit_low */ + .word 0x0000 /* base_low */ + .byte 0x00 /* base_middle */ + .byte 0x93 /* access */ + .byte 0xcf /* flags + limit_high */ + .byte 0x00 /* base_high */ diff --git a/roms/u-boot/arch/x86/cpu/start64.S b/roms/u-boot/arch/x86/cpu/start64.S new file mode 100644 index 000000000..7be834788 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/start64.S @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * 64-bit x86 Startup Code + * + * Copyright 2019 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <config.h> + +.section .text.start +.code64 +.globl _start +.type _start, @function +_start: + /* Set up memory using the existing stack */ + mov %rsp, %rdi + call board_init_f_alloc_reserve + mov %rax, %rsp + + call board_init_f_init_reserve + + xor %rdi, %rdi + call board_init_f + call board_init_f_r + + /* Should not return here */ + jmp . diff --git a/roms/u-boot/arch/x86/cpu/start_from_spl.S b/roms/u-boot/arch/x86/cpu/start_from_spl.S new file mode 100644 index 000000000..abfd4abb6 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/start_from_spl.S @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * 32-bit x86 Startup Code when running from SPL. This is the startup code in + * U-Boot proper, when SPL is used. + + * Copyright 2018 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <config.h> + +.section .text.start +.code32 +.globl _start +.type _start, @function +_start: + /* + * If running from coreboot, CAR is no-longer available. Use the + * existing stack, which is large enough. + */ + call locate_coreboot_table + cmp $0, %eax + jge use_existing_stack + + movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %eax +#ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE + subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %eax +#endif + jmp 2f + /* + * We don't subtract CONFIG_DCACHE_RAM_MRC_VAR_SIZE since memory is + * already set up. This has the happy side-effect of putting gd in a + * new place separate from SPL, so the memset() in + * board_init_f_init_reserve() does not cause any problems (otherwise + * it would zero out the gd and crash) + */ + /* Set up memory using the existing stack */ +use_existing_stack: + mov %esp, %eax +2: + call board_init_f_alloc_reserve + mov %eax, %esp + + call board_init_f_init_reserve + +#ifdef CONFIG_DEBUG_UART + call debug_uart_init +#endif + + call x86_cpu_reinit_f + xorl %eax, %eax + call board_init_f + call board_init_f_r + + /* Should not return here */ + jmp . + +.globl board_init_f_r_trampoline +.type board_init_f_r_trampoline, @function +board_init_f_r_trampoline: + /* + * SPL has been executed and SDRAM has been initialised, U-Boot code + * has been copied into RAM, BSS has been cleared and relocation + * adjustments have been made. It is now time to jump into the in-RAM + * copy of U-Boot + * + * %eax = Address of top of new stack + */ + + /* Stack grows down from top of SDRAM */ + movl %eax, %esp + + /* Re-enter U-Boot by calling board_init_f_r() */ + call board_init_f_r + +die: + hlt + jmp die + hlt + + .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 */ diff --git a/roms/u-boot/arch/x86/cpu/start_from_tpl.S b/roms/u-boot/arch/x86/cpu/start_from_tpl.S new file mode 100644 index 000000000..9a4974a5f --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/start_from_tpl.S @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * 32-bit x86 Startup Code when running from TPL. This is the startup code in + * SPL, when TPL is used. + * + * Copyright 2018 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <config.h> + +.section .text.start +.code32 +.globl _start +.type _start, @function +_start: + /* Set up memory using the existing stack */ + mov %esp, %eax + call board_init_f_alloc_reserve + mov %eax, %esp + + call board_init_f_init_reserve + + xorl %eax, %eax + call board_init_f + call board_init_f_r + + /* Should not return here */ + jmp . + +.globl board_init_f_r_trampoline +.type board_init_f_r_trampoline, @function +board_init_f_r_trampoline: + /* + * TPL has been executed: SDRAM has been initialised, BSS has been + * cleared. + * + * %eax = Address of top of new stack + */ + + /* Stack grows down from top of SDRAM */ + movl %eax, %esp + + /* Re-enter SPL by calling board_init_f_r() */ + call board_init_f_r + +die: + hlt + jmp die + hlt diff --git a/roms/u-boot/arch/x86/cpu/tangier/Kconfig b/roms/u-boot/arch/x86/cpu/tangier/Kconfig new file mode 100644 index 000000000..19aaf165d --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/tangier/Kconfig @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2017 Intel Corporation + +config INTEL_TANGIER + bool + depends on INTEL_MID + imply INTEL_MID_SERIAL + imply MMC + imply MMC_SDHCI + imply MMC_SDHCI_SDMA + imply MMC_SDHCI_TANGIER + imply MISC + imply USB + imply USB_XHCI_HCD + imply USB_DWC3 + imply USB_DWC3_GENERIC + +if INTEL_TANGIER + +config SYS_CAR_ADDR + hex + default 0x19200000 + +config SYS_CAR_SIZE + hex + default 0x4000 + help + Space in bytes in eSRAM used as Cache-As-RAM (CAR). + Note this size must not exceed eSRAM's total size. + +endif diff --git a/roms/u-boot/arch/x86/cpu/tangier/Makefile b/roms/u-boot/arch/x86/cpu/tangier/Makefile new file mode 100644 index 000000000..68f4a326e --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/tangier/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2017 Intel Corporation + +obj-y += car.o tangier.o sdram.o sysreset.o pinmux.o +obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o diff --git a/roms/u-boot/arch/x86/cpu/tangier/acpi.c b/roms/u-boot/arch/x86/cpu/tangier/acpi.c new file mode 100644 index 000000000..41bd177e0 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/tangier/acpi.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Intel Corporation + * + * Partially based on acpi.c for other x86 platforms + */ + +#include <common.h> +#include <cpu.h> +#include <dm.h> +#include <acpi/acpi_table.h> +#include <asm/ioapic.h> +#include <asm/mpspec.h> +#include <asm/tables.h> +#include <asm/arch/global_nvs.h> +#include <asm/arch/iomap.h> +#include <dm/uclass-internal.h> + +void acpi_create_fadt(struct acpi_fadt *fadt, struct acpi_facs *facs, + void *dsdt) +{ + struct acpi_table_header *header = &(fadt->header); + + memset((void *)fadt, 0, sizeof(struct acpi_fadt)); + + acpi_fill_header(header, "FACP"); + header->length = sizeof(struct acpi_fadt); + header->revision = 6; + + fadt->firmware_ctrl = (u32)facs; + fadt->dsdt = (u32)dsdt; + fadt->preferred_pm_profile = ACPI_PM_UNSPECIFIED; + + fadt->iapc_boot_arch = ACPI_FADT_VGA_NOT_PRESENT | + ACPI_FADT_NO_PCIE_ASPM_CONTROL; + fadt->flags = + ACPI_FADT_WBINVD | + ACPI_FADT_POWER_BUTTON | ACPI_FADT_SLEEP_BUTTON | + ACPI_FADT_SEALED_CASE | ACPI_FADT_HEADLESS | + ACPI_FADT_HW_REDUCED_ACPI; + + fadt->minor_revision = 2; + + fadt->x_firmware_ctl_l = (u32)facs; + fadt->x_firmware_ctl_h = 0; + fadt->x_dsdt_l = (u32)dsdt; + fadt->x_dsdt_h = 0; + + header->checksum = table_compute_checksum(fadt, header->length); +} + +u32 acpi_fill_madt(u32 current) +{ + current += acpi_create_madt_lapics(current); + + current += acpi_create_madt_ioapic((struct acpi_madt_ioapic *)current, + io_apic_read(IO_APIC_ID) >> 24, IO_APIC_ADDR, 0); + + return current; +} + +u32 acpi_fill_mcfg(u32 current) +{ + /* TODO: Derive parameters from SFI MCFG table */ + current += acpi_create_mcfg_mmconfig + ((struct acpi_mcfg_mmconfig *)current, + MCFG_BASE_ADDRESS, 0x0, 0x0, 0x0); + + return current; +} + +static u32 acpi_fill_csrt_dma(struct acpi_csrt_group *grp) +{ + struct acpi_csrt_shared_info *si = (struct acpi_csrt_shared_info *)&grp[1]; + + /* Fill the Resource Group with Shared Information attached */ + memset(grp, 0, sizeof(*grp)); + grp->shared_info_length = sizeof(struct acpi_csrt_shared_info); + grp->length = sizeof(struct acpi_csrt_group) + grp->shared_info_length; + /* TODO: All values below should come from U-Boot DT somehow */ + sprintf((char *)&grp->vendor_id, "%04X", 0x8086); + grp->device_id = 0x11a2; + + /* Fill the Resource Group Shared Information */ + memset(si, 0, sizeof(*si)); + si->major_version = 1; + si->minor_version = 0; + /* TODO: All values below should come from U-Boot DT somehow */ + si->mmio_base_low = 0xff192000; + si->mmio_base_high = 0; + si->gsi_interrupt = 32; + si->interrupt_polarity = 1; + si->interrupt_mode = 0; + si->num_channels = 8; + si->dma_address_width = 32; + si->base_request_line = 0; + si->num_handshake_signals = 16; + si->max_block_size = 0x1ffff; + + return grp->length; +} + +u32 acpi_fill_csrt(u32 current) +{ + current += acpi_fill_csrt_dma((struct acpi_csrt_group *)current); + + return current; +} + +int 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; + } + + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/tangier/car.S b/roms/u-boot/arch/x86/cpu/tangier/car.S new file mode 100644 index 000000000..7163b69a4 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/tangier/car.S @@ -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 diff --git a/roms/u-boot/arch/x86/cpu/tangier/pinmux.c b/roms/u-boot/arch/x86/cpu/tangier/pinmux.c new file mode 100644 index 000000000..acf97e3af --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/tangier/pinmux.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018 Emlid Limited + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <dm/pinctrl.h> +#include <dm/read.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/cpu.h> +#include <asm/scu.h> +#include <linux/io.h> + +#define BUFCFG_OFFSET 0x100 + +#define MRFLD_FAMILY_LEN 0x400 + +/* These are taken from Linux kernel */ +#define MRFLD_PINMODE_MASK 0x07 + +#define pin_to_bufno(f, p) ((p) - (f)->pin_base) + +struct mrfld_family { + unsigned int family_number; + unsigned int pin_base; + size_t npins; + void __iomem *regs; +}; + +#define MRFLD_FAMILY(b, s, e) \ + { \ + .family_number = (b), \ + .pin_base = (s), \ + .npins = (e) - (s) + 1, \ + } + +/* Now we only support I2C family of pins */ +static struct mrfld_family mrfld_families[] = { + MRFLD_FAMILY(7, 101, 114), +}; + +struct mrfld_pinctrl { + const struct mrfld_family *families; + size_t nfamilies; +}; + +static const struct mrfld_family * +mrfld_get_family(struct mrfld_pinctrl *mp, unsigned int pin) +{ + const struct mrfld_family *family; + unsigned int i; + + for (i = 0; i < mp->nfamilies; i++) { + family = &mp->families[i]; + if (pin >= family->pin_base && + pin < family->pin_base + family->npins) + return family; + } + + pr_err("failed to find family for pin %u\n", pin); + return NULL; +} + +static void __iomem * +mrfld_get_bufcfg(struct mrfld_pinctrl *pinctrl, unsigned int pin) +{ + const struct mrfld_family *family; + unsigned int bufno; + + family = mrfld_get_family(pinctrl, pin); + if (!family) + return NULL; + + bufno = pin_to_bufno(family, pin); + + return family->regs + BUFCFG_OFFSET + bufno * 4; +} + +static void +mrfld_setup_families(void *base_addr, + struct mrfld_family *families, unsigned int nfam) +{ + for (int i = 0; i < nfam; i++) { + struct mrfld_family *family = &families[i]; + + family->regs = base_addr + + family->family_number * MRFLD_FAMILY_LEN; + } +} + +static int mrfld_pinconfig_protected(unsigned int pin, u32 mask, u32 bits) +{ + struct mrfld_pinctrl *pinctrl; + struct udevice *dev; + void __iomem *bufcfg; + u32 v, value; + int ret; + + ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev); + if (ret) + return ret; + + pinctrl = dev_get_priv(dev); + + bufcfg = mrfld_get_bufcfg(pinctrl, pin); + if (!bufcfg) + return -EINVAL; + + value = readl(bufcfg); + + v = (value & ~mask) | (bits & mask); + + debug("scu: v: 0x%x p: 0x%x bits: %d, mask: %d bufcfg: 0x%p\n", + v, (u32)bufcfg, bits, mask, bufcfg); + + ret = scu_ipc_raw_command(IPCMSG_INDIRECT_WRITE, 0, &v, 4, + NULL, 0, (u32)bufcfg, 0); + if (ret) + pr_err("Failed to set mode via SCU for pin %u (%d)\n", + pin, ret); + + return ret; +} + +static int mrfld_pinctrl_cfg_pin(ofnode pin_node) +{ + bool is_protected; + int pad_offset; + int mode; + u32 mask; + int ret; + + /* For now we only support just protected Family of pins */ + is_protected = ofnode_read_bool(pin_node, "protected"); + if (!is_protected) + return -ENOTSUPP; + + pad_offset = ofnode_read_s32_default(pin_node, "pad-offset", -1); + if (pad_offset == -1) + return -EINVAL; + + mode = ofnode_read_s32_default(pin_node, "mode-func", -1); + if (mode == -1) + return -EINVAL; + + mask = MRFLD_PINMODE_MASK; + + /* We don't support modes not in range 0..7 */ + if (mode & ~mask) + return -ENOTSUPP; + + ret = mrfld_pinconfig_protected(pad_offset, mask, mode); + + return ret; +} + +static int tangier_pinctrl_probe(struct udevice *dev) +{ + void *base_addr = syscon_get_first_range(X86_SYSCON_PINCONF); + struct mrfld_pinctrl *pinctrl = dev_get_priv(dev); + ofnode pin_node; + int ret; + + mrfld_setup_families(base_addr, mrfld_families, + ARRAY_SIZE(mrfld_families)); + + pinctrl->families = mrfld_families; + pinctrl->nfamilies = ARRAY_SIZE(mrfld_families); + + ofnode_for_each_subnode(pin_node, dev_ofnode(dev)) { + ret = mrfld_pinctrl_cfg_pin(pin_node); + if (ret) { + pr_err("%s: invalid configuration for the pin %ld\n", + __func__, pin_node.of_offset); + } + } + + return 0; +} + +static const struct udevice_id tangier_pinctrl_match[] = { + { .compatible = "intel,pinctrl-tangier", .data = X86_SYSCON_PINCONF }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(tangier_pinctrl) = { + .name = "tangier_pinctrl", + .id = UCLASS_SYSCON, + .of_match = tangier_pinctrl_match, + .probe = tangier_pinctrl_probe, + .priv_auto = sizeof(struct mrfld_pinctrl), +}; diff --git a/roms/u-boot/arch/x86/cpu/tangier/sdram.c b/roms/u-boot/arch/x86/cpu/tangier/sdram.c new file mode 100644 index 000000000..afb08476e --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/tangier/sdram.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Intel Corporation + */ + +#include <common.h> +#include <init.h> +#include <log.h> +#include <asm/e820.h> +#include <asm/global_data.h> +#include <asm/sfi.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* + * SFI tables are part of the first stage bootloader. + * + * U-Boot finds the System Table by searching 16-byte boundaries between + * physical address 0x000E0000 and 0x000FFFFF. U-Boot shall search this region + * starting at the low address and shall stop searching when the 1st valid SFI + * System Table is found. + */ +#define SFI_BASE_ADDR 0x000E0000 +#define SFI_LENGTH 0x00020000 +#define SFI_TABLE_LENGTH 16 + +static int sfi_table_check(struct sfi_table_header *sbh) +{ + char chksum = 0; + char *pos = (char *)sbh; + u32 i; + + if (sbh->len < SFI_TABLE_LENGTH) + return -ENXIO; + + if (sbh->len > SFI_LENGTH) + return -ENXIO; + + for (i = 0; i < sbh->len; i++) + chksum += *pos++; + + if (chksum) + pr_err("sfi: Invalid checksum\n"); + + /* Checksum is OK if zero */ + return chksum ? -EILSEQ : 0; +} + +static int sfi_table_is_type(struct sfi_table_header *sbh, const char *signature) +{ + return !strncmp(sbh->sig, signature, SFI_SIGNATURE_SIZE) && + !sfi_table_check(sbh); +} + +static struct sfi_table_simple *sfi_get_table_by_sig(unsigned long addr, + const char *signature) +{ + struct sfi_table_simple *sb; + u32 i; + + for (i = 0; i < SFI_LENGTH; i += SFI_TABLE_LENGTH) { + sb = (struct sfi_table_simple *)(addr + i); + if (sfi_table_is_type(&sb->header, signature)) + return sb; + } + + return NULL; +} + +static struct sfi_table_simple *sfi_search_mmap(void) +{ + struct sfi_table_header *sbh; + struct sfi_table_simple *sb; + u32 sys_entry_cnt; + u32 i; + + /* Find SYST table */ + sb = sfi_get_table_by_sig(SFI_BASE_ADDR, SFI_SIG_SYST); + if (!sb) { + pr_err("sfi: failed to locate SYST table\n"); + return NULL; + } + + sys_entry_cnt = (sb->header.len - sizeof(*sbh)) / 8; + + /* Search through each SYST entry for MMAP table */ + for (i = 0; i < sys_entry_cnt; i++) { + sbh = (struct sfi_table_header *)(unsigned long)sb->pentry[i]; + + if (sfi_table_is_type(sbh, SFI_SIG_MMAP)) + return (struct sfi_table_simple *)sbh; + } + + pr_err("sfi: failed to locate SFI MMAP table\n"); + return NULL; +} + +#define sfi_for_each_mentry(i, sb, mentry) \ + for (i = 0, mentry = (struct sfi_mem_entry *)sb->pentry; \ + i < SFI_GET_NUM_ENTRIES(sb, struct sfi_mem_entry); \ + i++, mentry++) \ + +static unsigned int sfi_setup_e820(unsigned int max_entries, + struct e820_entry *entries) +{ + struct sfi_table_simple *sb; + struct sfi_mem_entry *mentry; + unsigned long long start, end, size; + int type, total = 0; + u32 i; + + sb = sfi_search_mmap(); + if (!sb) + return 0; + + sfi_for_each_mentry(i, sb, mentry) { + start = mentry->phys_start; + size = mentry->pages << 12; + end = start + size; + + if (start > end) + continue; + + /* translate SFI mmap type to E820 map type */ + switch (mentry->type) { + case SFI_MEM_CONV: + type = E820_RAM; + break; + case SFI_MEM_UNUSABLE: + case SFI_RUNTIME_SERVICE_DATA: + continue; + default: + type = E820_RESERVED; + } + + if (total == E820MAX) + break; + entries[total].addr = start; + entries[total].size = size; + entries[total].type = type; + + total++; + } + + return total; +} + +static int sfi_get_bank_size(void) +{ + struct sfi_table_simple *sb; + struct sfi_mem_entry *mentry; + int bank = 0; + u32 i; + + sb = sfi_search_mmap(); + if (!sb) + return 0; + + sfi_for_each_mentry(i, sb, mentry) { + if (mentry->type != SFI_MEM_CONV) + continue; + + gd->bd->bi_dram[bank].start = mentry->phys_start; + gd->bd->bi_dram[bank].size = mentry->pages << 12; + bank++; + } + + return bank; +} + +static phys_size_t sfi_get_ram_size(void) +{ + struct sfi_table_simple *sb; + struct sfi_mem_entry *mentry; + phys_size_t ram = 0; + u32 i; + + sb = sfi_search_mmap(); + if (!sb) + return 0; + + sfi_for_each_mentry(i, sb, mentry) { + if (mentry->type != SFI_MEM_CONV) + continue; + + ram += mentry->pages << 12; + } + + debug("sfi: RAM size %llu\n", ram); + return ram; +} + +unsigned int install_e820_map(unsigned int max_entries, + struct e820_entry *entries) +{ + return sfi_setup_e820(max_entries, entries); +} + +/* + * This function looks for the highest region of memory lower than 2GB 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 sfi_table_simple *sb; + struct sfi_mem_entry *mentry; + ulong dest_addr = 0; + u32 i; + + sb = sfi_search_mmap(); + if (!sb) + panic("No available memory found for relocation"); + + sfi_for_each_mentry(i, sb, mentry) { + unsigned long long start, end; + + if (mentry->type != SFI_MEM_CONV) + continue; + + start = mentry->phys_start; + end = start + (mentry->pages << 12); + + /* Filter memory over 2GB. */ + if (end > 0x7fffffffULL) + end = 0x80000000ULL; + /* 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; + } + + return dest_addr; +} + +int dram_init_banksize(void) +{ + sfi_get_bank_size(); + return 0; +} + +int dram_init(void) +{ + gd->ram_size = sfi_get_ram_size(); + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/tangier/sysreset.c b/roms/u-boot/arch/x86/cpu/tangier/sysreset.c new file mode 100644 index 000000000..b03bc28f9 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/tangier/sysreset.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> + * + * Reset driver for tangier processor + */ + +#include <common.h> +#include <dm.h> +#include <sysreset.h> +#include <asm/scu.h> + +static int tangier_sysreset_request(struct udevice *dev, enum sysreset_t type) +{ + int value; + + switch (type) { + case SYSRESET_WARM: + value = IPCMSG_WARM_RESET; + break; + case SYSRESET_COLD: + value = IPCMSG_COLD_RESET; + break; + default: + return -ENOSYS; + } + + scu_ipc_simple_command(value, 0); + + return -EINPROGRESS; +} + +static const struct udevice_id tangier_sysreset_ids[] = { + { .compatible = "intel,reset-tangier" }, + { } +}; + +static struct sysreset_ops tangier_sysreset_ops = { + .request = tangier_sysreset_request, +}; + +U_BOOT_DRIVER(tangier_sysreset) = { + .name = "tangier-sysreset", + .id = UCLASS_SYSRESET, + .of_match = tangier_sysreset_ids, + .ops = &tangier_sysreset_ops, +}; diff --git a/roms/u-boot/arch/x86/cpu/tangier/tangier.c b/roms/u-boot/arch/x86/cpu/tangier/tangier.c new file mode 100644 index 000000000..1e2f6cc8b --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/tangier/tangier.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2017 Intel Corporation + */ + +#include <common.h> +#include <cpu_func.h> +#include <init.h> +#include <asm/u-boot-x86.h> + +/* + * Miscellaneous platform dependent initializations + */ +int arch_cpu_init(void) +{ + return x86_cpu_init_f(); +} + +int checkcpu(void) +{ + return 0; +} + +int print_cpuinfo(void) +{ + return default_print_cpuinfo(); +} diff --git a/roms/u-boot/arch/x86/cpu/turbo.c b/roms/u-boot/arch/x86/cpu/turbo.c new file mode 100644 index 000000000..e2c84cdde --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/turbo.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * From Coreboot file of the same name + * + * Copyright (C) 2011 The Chromium Authors. + */ + +#include <common.h> +#include <log.h> +#include <asm/cpu.h> +#include <asm/global_data.h> +#include <asm/msr.h> +#include <asm/processor.h> +#include <asm/turbo.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_CPU_INTEL_TURBO_NOT_PACKAGE_SCOPED +static inline int get_global_turbo_state(void) +{ + return TURBO_UNKNOWN; +} + +static inline void set_global_turbo_state(int state) +{ +} +#else +static inline int get_global_turbo_state(void) +{ + return gd->arch.turbo_state; +} + +static inline void set_global_turbo_state(int state) +{ + gd->arch.turbo_state = state; +} +#endif + +/* gcc 7.3 does not wwant to drop strings, so use #ifdef */ +#ifndef CONFIG_TPL_BUILD +static const char *const turbo_state_desc[] = { + [TURBO_UNKNOWN] = "unknown", + [TURBO_UNAVAILABLE] = "unavailable", + [TURBO_DISABLED] = "available but hidden", + [TURBO_ENABLED] = "available and visible" +}; +#endif + +/* + * Determine the current state of Turbo and cache it for later. + * Turbo is a package level config so it does not need to be + * enabled on every core. + */ +int turbo_get_state(void) +{ + struct cpuid_result cpuid_regs; + int turbo_en, turbo_cap; + msr_t msr; + int turbo_state = get_global_turbo_state(); + + /* Return cached state if available */ + if (turbo_state != TURBO_UNKNOWN) + return turbo_state; + + cpuid_regs = cpuid(CPUID_LEAF_PM); + turbo_cap = !!(cpuid_regs.eax & PM_CAP_TURBO_MODE); + + msr = msr_read(MSR_IA32_MISC_ENABLE); + turbo_en = !(msr.hi & MISC_DISABLE_TURBO); + + if (!turbo_cap && turbo_en) { + /* Unavailable */ + turbo_state = TURBO_UNAVAILABLE; + } else if (!turbo_cap && !turbo_en) { + /* Available but disabled */ + turbo_state = TURBO_DISABLED; + } else if (turbo_cap && turbo_en) { + /* Available */ + turbo_state = TURBO_ENABLED; + } + + set_global_turbo_state(turbo_state); +#ifndef CONFIG_TPL_BUILD + debug("Turbo is %s\n", turbo_state_desc[turbo_state]); +#endif + return turbo_state; +} + +void turbo_enable(void) +{ + msr_t msr; + + /* Only possible if turbo is available but hidden */ + if (turbo_get_state() == TURBO_DISABLED) { + /* Clear Turbo Disable bit in Misc Enables */ + msr = msr_read(MSR_IA32_MISC_ENABLE); + msr.hi &= ~MISC_DISABLE_TURBO; + msr_write(MSR_IA32_MISC_ENABLE, msr); + + /* Update cached turbo state */ + set_global_turbo_state(TURBO_ENABLED); + debug("Turbo has been enabled\n"); + } +} diff --git a/roms/u-boot/arch/x86/cpu/u-boot-64.lds b/roms/u-boot/arch/x86/cpu/u-boot-64.lds new file mode 100644 index 000000000..ee0812aef --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/u-boot-64.lds @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se. + */ + +#include <config.h> +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) + +SECTIONS +{ +#ifndef CONFIG_CMDLINE + /DISCARD/ : { *(.u_boot_list_2_cmd_*) } +#endif + + . = CONFIG_SYS_TEXT_BASE; /* Location of bootcode in flash */ + __text_start = .; + + .text.start : { *(.text.start); } + + .__efi_runtime_start : { + *(.__efi_runtime_start) + } + + .efi_runtime : { + *(.text.efi_runtime*) + *(.rodata.efi_runtime*) + *(.data.efi_runtime*) + } + + .__efi_runtime_stop : { + *(.__efi_runtime_stop) + } + + .text : { *(.text*); } + + . = ALIGN(4); + + . = ALIGN(4); + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*))); + } + + . = ALIGN(4); + .rodata : { + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + KEEP(*(.rodata.efi.init)); + } + + . = ALIGN(4); + .data : { *(.data*) } + + . = ALIGN(4); + .hash : { *(.hash*) } + + . = ALIGN(4); + .got : { *(.got*) } + + .efi_runtime_rel_start : + { + *(.__efi_runtime_rel_start) + } + + .efi_runtime_rel : { + *(.rel*.efi_runtime) + *(.rel*.efi_runtime.*) + } + + .efi_runtime_rel_stop : + { + *(.__efi_runtime_rel_stop) + } + + . = ALIGN(4); + __data_end = .; + __init_end = .; + + . = ALIGN(4); + .dynsym : { *(.dynsym*) } + + . = ALIGN(4); + __rel_dyn_start = .; + .rela.dyn : { + *(.rela*) + } + __rel_dyn_end = .; + . = ALIGN(4); + + .dynamic : { *(.dynamic) } + + . = ALIGN(4); + _end = .; + + .bss __rel_dyn_start (OVERLAY) : { + __bss_start = .; + *(.bss*) + *(COM*) + . = ALIGN(4); + __bss_end = .; + } + + /DISCARD/ : { *(.dynsym) } + /DISCARD/ : { *(.dynstr*) } + /DISCARD/ : { *(.dynamic*) } + /DISCARD/ : { *(.plt*) } + /DISCARD/ : { *(.interp*) } + /DISCARD/ : { *(.gnu*) } + /DISCARD/ : { *(.note.gnu.property) } +} diff --git a/roms/u-boot/arch/x86/cpu/u-boot-spl.lds b/roms/u-boot/arch/x86/cpu/u-boot-spl.lds new file mode 100644 index 000000000..346f60bda --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/u-boot-spl.lds @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se. + */ + +#include <config.h> +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ +#ifndef CONFIG_CMDLINE + /DISCARD/ : { *(.u_boot_list_2_cmd_*) } +#endif + + . = IMAGE_TEXT_BASE; /* Location of bootcode in flash */ + __text_start = .; + .text : { + __image_copy_start = .; + *(.text*); + } + + . = ALIGN(4); + + . = ALIGN(4); + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*))); + } + + . = ALIGN(4); + .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } + + . = ALIGN(4); + + .priv_data : { + __priv_data_start = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.priv_data*))) + __priv_data_end = .; + } + + . = ALIGN(4); + .data : { *(.data*) } + + . = ALIGN(4); + __data_end = .; + __init_end = .; + . = ALIGN(4); + .binman_sym_table : { + __binman_sym_start = .; + KEEP(*(SORT(.binman_sym*))); + __binman_sym_end = .; + + /* + * Force 32-byte alignment so that it lines up with the start of + * bss, which may have up to 32-byte alignment. This ensures + * that the end of the .bin file matches up with + * _image_binary_end or __bss_end - see board_fdt_blob_setup(). + * The alignment of BSS depends on what is in it, so can range + * from 4 to 32 bytes. + */ + . = ALIGN(32); + } + + _image_binary_end = .; + +#if CONFIG_IS_ENABLED(SEPARATE_BSS) + . = 0x120000; +#endif + .bss (OVERLAY) : { + __bss_start = .; + *(.bss*) + *(COM*) + . = ALIGN(4); + __bss_end = .; + } + __bss_size = __bss_end - __bss_start; + + /DISCARD/ : { *(.dynstr*) } + /DISCARD/ : { *(.dynamic*) } + /DISCARD/ : { *(.plt*) } + /DISCARD/ : { *(.interp*) } + /DISCARD/ : { *(.gnu*) } + /DISCARD/ : { *(.note.gnu.property) } + +#if defined(CONFIG_SPL_X86_16BIT_INIT) || defined(CONFIG_TPL_X86_16BIT_INIT) + /* + * The following expressions place the 16-bit Real-Mode code and + * Reset Vector at the end of the Flash ROM + */ + . = START_16 - RESET_SEG_START; + .start16 : AT (START_16) { + KEEP(*(.start16)); + } + + . = RESET_VEC_LOC - RESET_SEG_START; + .resetvec : AT (RESET_VEC_LOC) { + KEEP(*(.resetvec)); + } +#endif + +} diff --git a/roms/u-boot/arch/x86/cpu/u-boot.lds b/roms/u-boot/arch/x86/cpu/u-boot.lds new file mode 100644 index 000000000..22fde01e7 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/u-boot.lds @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2002 + * Daniel Engström, Omicron Ceti AB, daniel@omicron.se. + */ + +#include <config.h> +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ +#ifndef CONFIG_CMDLINE + /DISCARD/ : { *(.u_boot_list_2_cmd_*) } +#endif + + . = CONFIG_SYS_TEXT_BASE; /* Location of bootcode in flash */ + __text_start = .; + + .text.start : { *(.text.start); } + + .__efi_runtime_start : { + *(.__efi_runtime_start) + } + + .efi_runtime : { + *(.text.efi_runtime*) + *(.rodata.efi_runtime*) + *(.data.efi_runtime*) + } + + .__efi_runtime_stop : { + *(.__efi_runtime_stop) + } + + .text : { *(.text*); } + + . = ALIGN(4); + + . = ALIGN(4); + .u_boot_list : { + KEEP(*(SORT(.u_boot_list*))); + } + + . = ALIGN(4); + .rodata : { + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) + KEEP(*(.rodata.efi.init)); + } + + . = ALIGN(4); + .data : { *(.data*) } + + . = ALIGN(4); + .hash : { *(.hash*) } + + . = ALIGN(4); + .got : { *(.got*) } + + . = ALIGN(4); + + .efi_runtime_rel_start : + { + *(.__efi_runtime_rel_start) + } + + .efi_runtime_rel : { + *(.rel*.efi_runtime) + *(.rel*.efi_runtime.*) + } + + .efi_runtime_rel_stop : + { + *(.__efi_runtime_rel_stop) + } + + . = ALIGN(4); + + __data_end = .; + __init_end = .; + + . = ALIGN(4); + .dynsym : { *(.dynsym*) } + + . = ALIGN(4); + __rel_dyn_start = .; + .rel.dyn : { + *(.rel*) + } + __rel_dyn_end = .; + . = ALIGN(4); + _end = .; + + .bss __rel_dyn_start (OVERLAY) : { + __bss_start = .; + *(.bss*) + *(COM*) + . = ALIGN(4); + __bss_end = .; + } + + /DISCARD/ : { *(.dynstr*) } + /DISCARD/ : { *(.dynamic*) } + /DISCARD/ : { *(.plt*) } + /DISCARD/ : { *(.interp*) } + /DISCARD/ : { *(.gnu*) } + /DISCARD/ : { *(.note.gnu.property) } + +#ifdef CONFIG_X86_16BIT_INIT + /* + * The following expressions place the 16-bit Real-Mode code and + * Reset Vector at the end of the Flash ROM + */ + . = START_16 - RESET_SEG_START; + .start16 : AT (START_16) { + KEEP(*(.start16)); + } + + . = RESET_VEC_LOC - RESET_SEG_START; + .resetvec : AT (RESET_VEC_LOC) { + KEEP(*(.resetvec)); + } +#endif + +} diff --git a/roms/u-boot/arch/x86/cpu/wakeup.S b/roms/u-boot/arch/x86/cpu/wakeup.S new file mode 100644 index 000000000..093bf3bcc --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/wakeup.S @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2017, Bin Meng <bmeng.cn@gmail.com> + * + * From coreboot src/arch/x86/wakeup.S + */ + +#include <acpi/acpi_s3.h> +#include <asm/processor.h> +#include <asm/processor-flags.h> + +#define RELOCATED(x) ((x) - __wakeup + WAKEUP_BASE) + +#define CODE_SEG (X86_GDT_ENTRY_16BIT_CS * X86_GDT_ENTRY_SIZE) +#define DATA_SEG (X86_GDT_ENTRY_16BIT_DS * X86_GDT_ENTRY_SIZE) + + .code32 + .globl __wakeup +__wakeup: + /* First prepare the jmp to the resume vector */ + mov 0x4(%esp), %eax /* vector */ + /* last 4 bits of linear addr are taken as offset */ + andw $0x0f, %ax + movw %ax, (__wakeup_offset) + mov 0x4(%esp), %eax + /* the rest is taken as segment */ + shr $4, %eax + movw %ax, (__wakeup_segment) + + /* Activate the right segment descriptor real mode */ + ljmp $CODE_SEG, $RELOCATED(1f) +1: + /* 16 bit code from here on... */ + .code16 + + /* + * Load the segment registers w/ properly configured segment + * descriptors. They will retain these configurations (limits, + * writability, etc.) once protected mode is turned off. + */ + mov $DATA_SEG, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + /* Turn off protection */ + movl %cr0, %eax + andl $~X86_CR0_PE, %eax + movl %eax, %cr0 + + /* Now really going into real mode */ + ljmp $0, $RELOCATED(1f) +1: + movw $0x0, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %ss + movw %ax, %fs + movw %ax, %gs + + /* + * This is a FAR JMP to the OS waking vector. + * The C code changes the address to be correct. + */ + .byte 0xea + +__wakeup_offset = RELOCATED(.) + .word 0x0000 + +__wakeup_segment = RELOCATED(.) + .word 0x0000 + + .globl __wakeup_size +__wakeup_size: + .long . - __wakeup diff --git a/roms/u-boot/arch/x86/cpu/x86_64/Makefile b/roms/u-boot/arch/x86/cpu/x86_64/Makefile new file mode 100644 index 000000000..400f0ffe3 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/x86_64/Makefile @@ -0,0 +1,6 @@ +# +# (C) Copyright 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# + +obj-y += cpu.o interrupts.o setjmp.o diff --git a/roms/u-boot/arch/x86/cpu/x86_64/cpu.c b/roms/u-boot/arch/x86/cpu/x86_64/cpu.c new file mode 100644 index 000000000..90a766c3c --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/x86_64/cpu.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <cpu_func.h> +#include <debug_uart.h> +#include <init.h> + +/* + * Global declaration of gd. + * + * As we write to it before relocation we have to make sure it is not put into + * a .bss section which may overlap a .rela section. Initialization forces it + * into a .data section which cannot overlap any .rela section. + */ +struct global_data *global_data_ptr = (struct global_data *)~0; + +void arch_setup_gd(gd_t *new_gd) +{ + global_data_ptr = new_gd; +} + +int cpu_has_64bit(void) +{ + return true; +} + +void enable_caches(void) +{ + /* Not implemented */ +} + +void disable_caches(void) +{ + /* Not implemented */ +} + +int dcache_status(void) +{ + return true; +} + +int x86_mp_init(void) +{ + /* Not implemented */ + return 0; +} + +int misc_init_r(void) +{ + return 0; +} + +#ifndef CONFIG_SYS_COREBOOT +int checkcpu(void) +{ + return 0; +} + +int print_cpuinfo(void) +{ + return 0; +} +#endif + +int x86_cpu_reinit_f(void) +{ + return 0; +} + +int cpu_phys_address_size(void) +{ + return CONFIG_CPU_ADDR_BITS; +} diff --git a/roms/u-boot/arch/x86/cpu/x86_64/interrupts.c b/roms/u-boot/arch/x86/cpu/x86_64/interrupts.c new file mode 100644 index 000000000..634f7660c --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/x86_64/interrupts.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2016 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <irq_func.h> +#include <asm/processor-flags.h> + +void enable_interrupts(void) +{ + asm("sti\n"); +} + +int disable_interrupts(void) +{ + long flags; + + asm volatile ("pushfq ; popq %0 ; cli\n" : "=g" (flags) : ); + + return flags & X86_EFLAGS_IF; +} + +int interrupt_init(void) +{ + /* Nothing to do - this was already done in SPL */ + return 0; +} diff --git a/roms/u-boot/arch/x86/cpu/x86_64/setjmp.S b/roms/u-boot/arch/x86/cpu/x86_64/setjmp.S new file mode 100644 index 000000000..97b812854 --- /dev/null +++ b/roms/u-boot/arch/x86/cpu/x86_64/setjmp.S @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Intel Corporation + * + * See arch/x86/include/asm/setjmp.h for jmp_buf format + */ + +#include <linux/linkage.h> + +.text +.align 8 + +ENTRY(setjmp) + + pop %rcx + movq %rcx, (%rdi) /* Return address */ + movq %rsp, 8(%rdi) + movq %rbp, 16(%rdi) + movq %rbx, 24(%rdi) + movq %r12, 32(%rdi) + movq %r13, 40(%rdi) + movq %r14, 48(%rdi) + movq %r15, 56(%rdi) + xorq %rax, %rax /* Direct invocation returns 0 */ + jmpq *%rcx + +ENDPROC(setjmp) + +.align 8 + +ENTRY(longjmp) + + movq (%rdi), %rcx /* Return address */ + movq 8(%rdi), %rsp + movq 16(%rdi), %rbp + movq 24(%rdi), %rbx + movq 32(%rdi), %r12 + movq 40(%rdi), %r13 + movq 48(%rdi), %r14 + movq 56(%rdi), %r15 + + movq %rsi, %rax /* Value to be returned by setjmp() */ + testq %rax, %rax /* cannot be 0 in this case */ + jnz 1f + incq %rax /* Return 1 instead */ +1: + jmpq *%rcx + +ENDPROC(longjmp) |