diff options
author | Tom Rini <trini@ti.com> | 2015-02-10 10:42:56 -0500 |
---|---|---|
committer | Tom Rini <trini@ti.com> | 2015-02-10 10:42:56 -0500 |
commit | db7a7dee6878fe7539d5967de1caff83246254e0 (patch) | |
tree | 27a94cac552d2015cc53cd29658f15f834475a5c /arch/x86/cpu | |
parent | c956662cc3e2475b451afa9a8b639c0ccc49d432 (diff) | |
parent | ba877efb802edb7080703e4dd99e51a437e44f26 (diff) |
Merge branch 'master' of git://git.denx.de/u-boot-x86
Diffstat (limited to 'arch/x86/cpu')
32 files changed, 6470 insertions, 730 deletions
diff --git a/arch/x86/cpu/Makefile b/arch/x86/cpu/Makefile index 62e43c04e5..6ded0a7f0f 100644 --- a/arch/x86/cpu/Makefile +++ b/arch/x86/cpu/Makefile @@ -12,9 +12,11 @@ extra-y = start.o obj-$(CONFIG_X86_RESET_VECTOR) += resetvec.o start16.o obj-y += interrupts.o cpu.o call64.o +obj-$(CONFIG_INTEL_BAYTRAIL) += baytrail/ obj-$(CONFIG_SYS_COREBOOT) += coreboot/ obj-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += ivybridge/ obj-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += ivybridge/ +obj-$(CONFIG_INTEL_QUARK) += quark/ obj-$(CONFIG_INTEL_QUEENSBAY) += queensbay/ obj-y += lapic.o obj-y += mtrr.o diff --git a/arch/x86/cpu/baytrail/Kconfig b/arch/x86/cpu/baytrail/Kconfig new file mode 100644 index 0000000000..e86cc01115 --- /dev/null +++ b/arch/x86/cpu/baytrail/Kconfig @@ -0,0 +1,9 @@ +# +# Copyright (C) 2015 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +config INTEL_BAYTRAIL + bool + select HAVE_FSP diff --git a/arch/x86/cpu/baytrail/Makefile b/arch/x86/cpu/baytrail/Makefile new file mode 100644 index 0000000000..8914e8b6d5 --- /dev/null +++ b/arch/x86/cpu/baytrail/Makefile @@ -0,0 +1,10 @@ +# +# Copyright (C) 2015 Google, Inc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += early_uart.o +obj-y += fsp_configs.o +obj-y += pci.o +obj-y += valleyview.o diff --git a/arch/x86/cpu/baytrail/early_uart.c b/arch/x86/cpu/baytrail/early_uart.c new file mode 100644 index 0000000000..41992105fe --- /dev/null +++ b/arch/x86/cpu/baytrail/early_uart.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <asm/io.h> + +#define PCI_DEV_CONFIG(segbus, dev, fn) ( \ + (((segbus) & 0xfff) << 20) | \ + (((dev) & 0x1f) << 15) | \ + (((fn) & 0x07) << 12)) + +/* Platform Controller Unit */ +#define LPC_DEV 0x1f +#define LPC_FUNC 0 + +/* Enable UART */ +#define UART_CONT 0x80 + +/* SCORE Pad definitions */ +#define UART_RXD_PAD 82 +#define UART_TXD_PAD 83 + +/* Pad base: PAD_CONF0[n]= PAD_BASE + 16 * n */ +#define GPSCORE_PAD_BASE (IO_BASE_ADDRESS + IO_BASE_OFFSET_GPSCORE) + +/* IO Memory */ +#define IO_BASE_ADDRESS 0xfed0c000 +#define IO_BASE_OFFSET_GPSCORE 0x0000 +#define IO_BASE_OFFSET_GPNCORE 0x1000 +#define IO_BASE_OFFSET_GPSSUS 0x2000 +#define IO_BASE_SIZE 0x4000 + +static inline unsigned int score_pconf0(int pad_num) +{ + return GPSCORE_PAD_BASE + pad_num * 16; +} + +static void score_select_func(int pad, int func) +{ + uint32_t reg; + uint32_t pconf0_addr = score_pconf0(pad); + + reg = readl(pconf0_addr); + reg &= ~0x7; + reg |= func & 0x7; + writel(reg, pconf0_addr); +} + +static void 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_early_uart(void) +{ + /* Enable the legacy UART hardware. */ + pci_write_config32(PCI_DEV_CONFIG(0, LPC_DEV, LPC_FUNC), UART_CONT, 1); + + /* + * 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; +} diff --git a/arch/x86/cpu/baytrail/fsp_configs.c b/arch/x86/cpu/baytrail/fsp_configs.c new file mode 100644 index 0000000000..86b6926272 --- /dev/null +++ b/arch/x86/cpu/baytrail/fsp_configs.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: Intel + */ + +#include <common.h> +#include <asm/arch/fsp/azalia.h> +#include <asm/fsp/fsp_support.h> + +/* ALC262 Verb Table - 10EC0262 */ +static const uint32_t verb_table_data13[] = { + /* Pin Complex (NID 0x11) */ + 0x01171cf0, + 0x01171d11, + 0x01171e11, + 0x01171f41, + /* Pin Complex (NID 0x12) */ + 0x01271cf0, + 0x01271d11, + 0x01271e11, + 0x01271f41, + /* Pin Complex (NID 0x14) */ + 0x01471c10, + 0x01471d40, + 0x01471e01, + 0x01471f01, + /* Pin Complex (NID 0x15) */ + 0x01571cf0, + 0x01571d11, + 0x01571e11, + 0x01571f41, + /* Pin Complex (NID 0x16) */ + 0x01671cf0, + 0x01671d11, + 0x01671e11, + 0x01671f41, + /* Pin Complex (NID 0x18) */ + 0x01871c20, + 0x01871d98, + 0x01871ea1, + 0x01871f01, + /* Pin Complex (NID 0x19) */ + 0x01971c21, + 0x01971d98, + 0x01971ea1, + 0x01971f02, + /* Pin Complex (NID 0x1A) */ + 0x01a71c2f, + 0x01a71d30, + 0x01a71e81, + 0x01a71f01, + /* Pin Complex */ + 0x01b71c1f, + 0x01b71d40, + 0x01b71e21, + 0x01b71f02, + /* Pin Complex */ + 0x01c71cf0, + 0x01c71d11, + 0x01c71e11, + 0x01c71f41, + /* Pin Complex */ + 0x01d71c01, + 0x01d71dc6, + 0x01d71e14, + 0x01d71f40, + /* Pin Complex */ + 0x01e71cf0, + 0x01e71d11, + 0x01e71e11, + 0x01e71f41, + /* Pin Complex */ + 0x01f71cf0, + 0x01f71d11, + 0x01f71e11, + 0x01f71f41, +}; + +/* + * This needs to be in ROM since if we put it in CAR, FSP init loses it when + * it drops CAR. + * + * TODO(sjg@chromium.org): Move to device tree when FSP allows it + * + * VerbTable: (RealTek ALC262) + * Revision ID = 0xFF, support all steps + * Codec Verb Table For AZALIA + * Codec Address: CAd value (0/1/2) + * Codec Vendor: 0x10EC0262 + */ +static const struct pch_azalia_verb_table azalia_verb_table[] = { + { + { + 0x10ec0262, + 0x0000, + 0xff, + 0x01, + 0x000b, + 0x0002, + }, + verb_table_data13 + } +}; + +const struct pch_azalia_config azalia_config = { + .pme_enable = 1, + .docking_supported = 1, + .docking_attached = 0, + .hdmi_codec_enable = 1, + .azalia_v_ci_enable = 1, + .rsvdbits = 0, + .azalia_verb_table_num = 1, + .azalia_verb_table = azalia_verb_table, + .reset_wait_timer_us = 300 +}; + +void update_fsp_upd(struct upd_region *fsp_upd) +{ + struct memory_down_data *mem; + + /* + * Configure everything here to avoid the poor hard-pressed user + * needing to run Intel's binary configuration tool. It may also allow + * us to support the 1GB single core variant easily. + * + * TODO(sjg@chromium.org): Move to device tree + */ + fsp_upd->mrc_init_tseg_size = 8; + fsp_upd->mrc_init_mmio_size = 0x800; + fsp_upd->emmc_boot_mode = 0xff; + fsp_upd->enable_sdio = 1; + fsp_upd->enable_sdcard = 1; + fsp_upd->enable_hsuart0 = 1; + fsp_upd->azalia_config_ptr = (uint32_t)&azalia_config; + fsp_upd->enable_i2_c0 = 0; + fsp_upd->enable_i2_c2 = 0; + fsp_upd->enable_i2_c3 = 0; + fsp_upd->enable_i2_c4 = 0; + fsp_upd->enable_xhci = 0; + fsp_upd->igd_render_standby = 1; + + mem = &fsp_upd->memory_params; + mem->enable_memory_down = 1; + mem->dram_speed = 1; + mem->dimm_width = 1; + mem->dimm_density = 2; + mem->dimm_tcl = 0xb; + mem->dimm_trpt_rcd = 0xb; + mem->dimm_twr = 0xc; + mem->dimm_twtr = 6; + mem->dimm_trrd = 6; + mem->dimm_trtp = 6; + mem->dimm_tfaw = 0x14; +} diff --git a/arch/x86/cpu/baytrail/pci.c b/arch/x86/cpu/baytrail/pci.c new file mode 100644 index 0000000000..6c291f9ee9 --- /dev/null +++ b/arch/x86/cpu/baytrail/pci.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <pci.h> +#include <asm/pci.h> +#include <asm/fsp/fsp_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +void board_pci_setup_hose(struct pci_controller *hose) +{ + hose->first_busno = 0; + hose->last_busno = 0; + + /* PCI memory space */ + pci_set_region(hose->regions + 0, + CONFIG_PCI_MEM_BUS, + CONFIG_PCI_MEM_PHYS, + CONFIG_PCI_MEM_SIZE, + PCI_REGION_MEM); + + /* PCI IO space */ + pci_set_region(hose->regions + 1, + CONFIG_PCI_IO_BUS, + CONFIG_PCI_IO_PHYS, + CONFIG_PCI_IO_SIZE, + PCI_REGION_IO); + + pci_set_region(hose->regions + 2, + CONFIG_PCI_PREF_BUS, + CONFIG_PCI_PREF_PHYS, + CONFIG_PCI_PREF_SIZE, + PCI_REGION_PREFETCH); + + pci_set_region(hose->regions + 3, + 0, + 0, + gd->ram_size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + hose->region_count = 4; +} diff --git a/arch/x86/cpu/baytrail/valleyview.c b/arch/x86/cpu/baytrail/valleyview.c new file mode 100644 index 0000000000..a3e837d43e --- /dev/null +++ b/arch/x86/cpu/baytrail/valleyview.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <mmc.h> +#include <pci_ids.h> +#include <asm/post.h> + +static struct pci_device_id mmc_supported[] = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VALLEYVIEW_SDIO }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VALLEYVIEW_SDCARD }, +}; + +int cpu_mmc_init(bd_t *bis) +{ + printf("mmc init\n"); + return pci_mmc_init("ValleyView SDHCI", mmc_supported, + ARRAY_SIZE(mmc_supported)); +} + +int arch_cpu_init(void) +{ + int ret; + + post_code(POST_CPU_INIT); +#ifdef CONFIG_SYS_X86_TSC_TIMER + timer_set_base(rdtsc()); +#endif + + ret = x86_cpu_init_f(); + if (ret) + return ret; + + return 0; +} diff --git a/arch/x86/cpu/ivybridge/gma.c b/arch/x86/cpu/ivybridge/gma.c index 6cf9654e02..821ea25019 100644 --- a/arch/x86/cpu/ivybridge/gma.c +++ b/arch/x86/cpu/ivybridge/gma.c @@ -758,7 +758,8 @@ int gma_func0_init(pci_dev_t dev, struct pci_controller *hose, #ifdef CONFIG_VIDEO start = get_timer(0); - ret = pci_run_vga_bios(dev, int15_handler, false); + ret = pci_run_vga_bios(dev, int15_handler, PCI_ROM_USE_NATIVE | + PCI_ROM_ALLOW_FALLBACK); debug("BIOS ran in %lums\n", get_timer(start)); #endif /* Post VBIOS init */ diff --git a/arch/x86/cpu/ivybridge/sdram.c b/arch/x86/cpu/ivybridge/sdram.c index 49634485f3..766b385c25 100644 --- a/arch/x86/cpu/ivybridge/sdram.c +++ b/arch/x86/cpu/ivybridge/sdram.c @@ -757,7 +757,7 @@ int dram_init(void) .mchbar = DEFAULT_MCHBAR, .dmibar = DEFAULT_DMIBAR, .epbar = DEFAULT_EPBAR, - .pciexbar = CONFIG_MMCONF_BASE_ADDRESS, + .pciexbar = CONFIG_PCIE_ECAM_BASE, .smbusbar = SMBUS_IO_BASE, .wdbbar = 0x4000000, .wdbsize = 0x1000, diff --git a/arch/x86/cpu/quark/Kconfig b/arch/x86/cpu/quark/Kconfig new file mode 100644 index 0000000000..bc961ef07c --- /dev/null +++ b/arch/x86/cpu/quark/Kconfig @@ -0,0 +1,126 @@ +# +# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +config INTEL_QUARK + bool + select HAVE_RMU + select TSC_CALIBRATION_BYPASS + +if INTEL_QUARK + +config HAVE_RMU + bool "Add a Remote Management Unit (RMU) binary" + help + Select this option to add a Remote Management Unit (RMU) binary + to the resulting U-Boot image. It is a data block (up to 64K) of + machine-specific code which must be put in the flash for the RMU + within the Quark SoC processor to access when powered up before + system BIOS is executed. + +config RMU_FILE + string "Remote Management Unit (RMU) binary filename" + depends on HAVE_RMU + default "rmu.bin" + help + The filename of the file to use as Remote Management Unit (RMU) + binary in the board directory. + +config RMU_ADDR + hex "Remote Management Unit (RMU) binary location" + depends on HAVE_RMU + default 0xfff00000 + help + The location of the RMU binary is determined by a strap. It must be + put in flash at a location matching the strap-determined base address. + + The default base address of 0xfff00000 indicates that the binary must + be located at offset 0 from the beginning of a 1MB flash device. + +config HAVE_CMC + bool + default HAVE_RMU + +config CMC_FILE + string + depends on HAVE_CMC + default RMU_FILE + +config CMC_ADDR + hex + depends on HAVE_CMC + default RMU_ADDR + +config ESRAM_BASE + hex + default 0x80000000 + help + Embedded SRAM (eSRAM) memory-mapped base address. + +config PCIE_ECAM_BASE + hex + default 0xe0000000 + +config RCBA_BASE + hex + default 0xfed1c000 + help + Root Complex register block memory-mapped base address. + +config ACPI_PM1_BASE + hex + default 0x1000 + help + ACPI Power Managment 1 (PM1) i/o-mapped base address. + This device is defined in ACPI specification, with 16 bytes in size. + +config ACPI_PBLK_BASE + hex + default 0x1010 + help + ACPI Processor Block (PBLK) i/o-mapped base address. + This device is defined in ACPI specification, with 16 bytes in size. + +config SPI_DMA_BASE + hex + default 0x1020 + help + SPI DMA i/o-mapped base address. + +config GPIO_BASE + hex + default 0x1080 + help + GPIO i/o-mapped base address. + +config ACPI_GPE0_BASE + hex + default 0x1100 + help + ACPI General Purpose Event 0 (GPE0) i/o-mapped base address. + This device is defined in ACPI specification, with 64 bytes in size. + +config WDT_BASE + hex + default 0x1140 + help + Watchdog timer i/o-mapped base address. + +config SYS_CAR_ADDR + hex + default ESRAM_BASE + +config SYS_CAR_SIZE + hex + default 0x8000 + help + Space in bytes in eSRAM used as Cache-As-ARM (CAR). + Note this size must not exceed eSRAM's total size. + +config TSC_FREQ_IN_MHZ + int + default 400 + +endif diff --git a/arch/x86/cpu/quark/Makefile b/arch/x86/cpu/quark/Makefile new file mode 100644 index 0000000000..e87b4248e6 --- /dev/null +++ b/arch/x86/cpu/quark/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += car.o dram.o msg_port.o quark.o +obj-y += mrc.o mrc_util.o hte.o smc.o +obj-$(CONFIG_PCI) += pci.o diff --git a/arch/x86/cpu/quark/car.S b/arch/x86/cpu/quark/car.S new file mode 100644 index 0000000000..3432ffa081 --- /dev/null +++ b/arch/x86/cpu/quark/car.S @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <asm/pci.h> +#include <asm/post.h> +#include <asm/arch/quark.h> +#include <asm/arch/msg_port.h> + +.globl car_init +car_init: + post_code(POST_CAR_START) + + /* + * Quark SoC contains an embedded 512KiB SRAM (eSRAM) that is + * initialized by hardware. eSRAM is the ideal place to be used + * for Cache-As-RAM (CAR) before system memory is available. + * + * Relocate this eSRAM to a suitable location in the physical + * memory map and enable it. + */ + + /* Host Memory Bound Register P03h:R08h */ + mov $((MSG_PORT_HOST_BRIDGE << 16) | (HM_BOUND << 8)), %eax + mov $(DRAM_BASE + DRAM_MAX_SIZE + ESRAM_SIZE), %edx + lea 1f, %esp + jmp msg_port_write +1: + + /* eSRAM Block Page Control Register P05h:R82h */ + mov $((MSG_PORT_MEM_MGR << 16) | (ESRAM_BLK_CTRL << 8)), %eax + mov $(ESRAM_BLOCK_MODE | (CONFIG_ESRAM_BASE >> 24)), %edx + lea 2f, %esp + jmp msg_port_write +2: + + post_code(POST_CAR_CPU_CACHE) + jmp car_init_ret + +msg_port_read: + /* + * Parameter: + * eax[23:16] - Message Port ID + * eax[15:08] - Register Address + * + * Return Value: + * eax - Message Port Register value + * + * Return Address: esp + */ + + or $((MSG_OP_READ << 24) | MSG_BYTE_ENABLE), %eax + mov %eax, %ebx + + /* Write MCR B0:D0:F0:RD0 */ + mov $(PCI_CFG_EN | MSG_CTRL_REG), %eax + mov $PCI_REG_ADDR, %dx + out %eax, %dx + mov $PCI_REG_DATA, %dx + mov %ebx, %eax + out %eax, %dx + + /* Read MDR B0:D0:F0:RD4 */ + mov $(PCI_CFG_EN | MSG_DATA_REG), %eax + mov $PCI_REG_ADDR, %dx + out %eax, %dx + mov $PCI_REG_DATA, %dx + in %dx, %eax + + jmp *%esp + +msg_port_write: + /* + * Parameter: + * eax[23:16] - Message Port ID + * eax[15:08] - Register Address + * edx - Message Port Register value to write + * + * Return Address: esp + */ + + or $((MSG_OP_WRITE << 24) | MSG_BYTE_ENABLE), %eax + mov %eax, %esi + mov %edx, %edi + + /* Write MDR B0:D0:F0:RD4 */ + mov $(PCI_CFG_EN | MSG_DATA_REG), %eax + mov $PCI_REG_ADDR, %dx + out %eax, %dx + mov $PCI_REG_DATA, %dx + mov %edi, %eax + out %eax, %dx + + /* Write MCR B0:D0:F0:RD0 */ + mov $(PCI_CFG_EN | MSG_CTRL_REG), %eax + mov $PCI_REG_ADDR, %dx + out %eax, %dx + mov $PCI_REG_DATA, %dx + mov %esi, %eax + out %eax, %dx + + jmp *%esp diff --git a/arch/x86/cpu/quark/dram.c b/arch/x86/cpu/quark/dram.c new file mode 100644 index 0000000000..9cac846c69 --- /dev/null +++ b/arch/x86/cpu/quark/dram.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <asm/post.h> +#include <asm/arch/mrc.h> +#include <asm/arch/quark.h> + +DECLARE_GLOBAL_DATA_PTR; + +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; + } + + /* + * TODO: + * + * We need support fast boot (MRC cache) in the future. + * + * Set boot mode to cold boot for now + */ + mrc_params->boot_mode = BM_COLD; + + /* + * 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; + 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); + + return 0; +} + +void dram_init_banksize(void) +{ + gd->bd->bi_dram[0].start = 0; + gd->bd->bi_dram[0].size = gd->ram_size; +} + +/* + * This function looks for the highest region of memory lower than 4GB which + * has enough space for U-Boot where U-Boot is aligned on a page boundary. + * It overrides the default implementation found elsewhere which simply + * picks the end of ram, wherever that may be. The location of the stack, + * the relocation address, and how far U-Boot is moved by relocation are + * set in the global data structure. + */ +ulong board_get_usable_ram_top(ulong total_size) +{ + return gd->ram_size; +} diff --git a/arch/x86/cpu/quark/hte.c b/arch/x86/cpu/quark/hte.c new file mode 100644 index 0000000000..372815d8c1 --- /dev/null +++ b/arch/x86/cpu/quark/hte.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Ported from Intel released Quark UEFI BIOS + * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei + * + * SPDX-License-Identifier: Intel + */ + +#include <common.h> +#include <asm/arch/mrc.h> +#include <asm/arch/msg_port.h> +#include "mrc_util.h" +#include "hte.h" + +/** + * Enable HTE to detect all possible errors for the given training parameters + * (per-bit or full byte lane). + */ +static void hte_enable_all_errors(void) +{ + msg_port_write(HTE, 0x000200A2, 0xFFFFFFFF); + msg_port_write(HTE, 0x000200A3, 0x000000FF); + msg_port_write(HTE, 0x000200A4, 0x00000000); +} + +/** + * Go and read the HTE register in order to find any error + * + * @return: The errors detected in the HTE status register + */ +static u32 hte_check_errors(void) +{ + return msg_port_read(HTE, 0x000200A7); +} + +/** + * Wait until HTE finishes + */ +static void hte_wait_for_complete(void) +{ + u32 tmp; + + ENTERFN(); + + do {} while ((msg_port_read(HTE, 0x00020012) & BIT30) != 0); + + tmp = msg_port_read(HTE, 0x00020011); + tmp |= BIT9; + tmp &= ~(BIT12 | BIT13); + 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 |= BIT8; + 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 | BIT8); + + 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/arch/x86/cpu/quark/hte.h b/arch/x86/cpu/quark/hte.h new file mode 100644 index 0000000000..6577796fd6 --- /dev/null +++ b/arch/x86/cpu/quark/hte.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Ported from Intel released Quark UEFI BIOS + * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei + * + * SPDX-License-Identifier: Intel + */ + +#ifndef _HTE_H_ +#define _HTE_H_ + +enum { + MRC_MEM_INIT, + MRC_MEM_TEST +}; + +enum { + READ_TRAIN, + WRITE_TRAIN +}; + +/* + * EXP_LOOP_CNT field of HTE_CMD_CTL + * + * This CANNOT be less than 4! + */ +#define HTE_LOOP_CNT 5 + +/* random seed for victim */ +#define HTE_LFSR_VICTIM_SEED 0xF294BA21 + +/* random seed for aggressor */ +#define HTE_LFSR_AGRESSOR_SEED 0xEBA7492D + +u32 hte_mem_init(struct mrc_params *mrc_params, u8 flag); +u16 hte_basic_write_read(struct mrc_params *mrc_params, u32 addr, + u8 first_run, u8 mode); +u16 hte_write_stress_bit_lanes(struct mrc_params *mrc_params, + u32 addr, u8 first_run); +void hte_mem_op(u32 addr, u8 first_run, u8 is_write); + +#endif /* _HTE_H_ */ diff --git a/arch/x86/cpu/quark/mrc.c b/arch/x86/cpu/quark/mrc.c new file mode 100644 index 0000000000..7eb34c5302 --- /dev/null +++ b/arch/x86/cpu/quark/mrc.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Ported from Intel released Quark UEFI BIOS + * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei + * + * SPDX-License-Identifier: Intel + */ + +/* + * This is the main Quark Memory Reference Code (MRC) + * + * These functions are generic and should work for any Quark-based board. + * + * MRC requires two data structures to be passed in which are initialized by + * mrc_adjust_params(). + * + * The basic flow is as follows: + * 01) Check for supported DDR speed configuration + * 02) Set up Memory Manager buffer as pass-through (POR) + * 03) Set Channel Interleaving Mode and Channel Stride to the most aggressive + * setting possible + * 04) Set up the Memory Controller logic + * 05) Set up the DDR_PHY logic + * 06) Initialise the DRAMs (JEDEC) + * 07) Perform the Receive Enable Calibration algorithm + * 08) Perform the Write Leveling algorithm + * 09) Perform the Read Training algorithm (includes internal Vref) + * 10) Perform the Write Training algorithm + * 11) Set Channel Interleaving Mode and Channel Stride to the desired settings + * + * DRAM unit configuration based on Valleyview MRC. + */ + +#include <common.h> +#include <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, + __DATE__, __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/arch/x86/cpu/quark/mrc_util.c b/arch/x86/cpu/quark/mrc_util.c new file mode 100644 index 0000000000..3a79ae551b --- /dev/null +++ b/arch/x86/cpu/quark/mrc_util.c @@ -0,0 +1,1475 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Ported from Intel released Quark UEFI BIOS + * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei + * + * SPDX-License-Identifier: Intel + */ + +#include <common.h> +#include <asm/arch/device.h> +#include <asm/arch/mrc.h> +#include <asm/arch/msg_port.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 &= ~BIT28; + 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 |= BIT28; + 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) +{ + pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, data); + 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 & BIT0) ? (BIT23 | BIT22 | BIT21 | BIT20) : + (BIT11 | BIT10 | BIT9 | BIT8); + temp = (byte_lane & BIT0) ? ((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 & BIT0) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + + (channel * DDRIODQ_CH_OFFSET)); + msk = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24); + 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 & BIT0) ? BIT5 : BIT2; + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (byte_lane & BIT0) ? BIT11 : BIT8; + 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 & BIT0) ? 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 & BIT0) ? 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 & BIT0) ? B1RXDQSPICODE : B0RXDQSPICODE; + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + + (channel * DDRIODQ_CH_OFFSET)); + msk = (BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0); + 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 & BIT0) ? 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 & BIT0) ? (BIT19 | BIT18 | BIT17 | BIT16) : + (BIT7 | BIT6 | BIT5 | BIT4); + temp = pi_count / HALF_CLK; + temp <<= (byte_lane & BIT0) ? 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 & BIT0) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + + (channel * DDRIODQ_CH_OFFSET)); + msk = (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16); + 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 & BIT0) ? BIT4 : BIT1; + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (byte_lane & BIT0) ? BIT10 : BIT7; + 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 & BIT0) ? 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 & BIT0) ? 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 & BIT0) ? (BIT15 | BIT14 | BIT13 | BIT12) : + (BIT3 | BIT2 | BIT1 | BIT0); + temp = pi_count / HALF_CLK; + temp <<= (byte_lane & BIT0) ? 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 & BIT0) ? B1DLLPICODER0 : B0DLLPICODER0; + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + + (channel * DDRIODQ_CH_OFFSET)); + msk = (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8); + 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 & BIT0) ? BIT3 : BIT0; + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (byte_lane & BIT0) ? BIT9 : BIT6; + 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 & BIT0) ? (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 & BIT0) ? 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 = (BIT11 | BIT10 | BIT9 | BIT8); + 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 = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24 | + BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16 | + BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | + BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0); + + 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 |= BIT16; + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= BIT17; + 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 = (BIT15 | BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8); + 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 = (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16 | + BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8); + 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 |= (BIT3 | BIT2 | BIT1 | BIT0); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (BIT11 | BIT10 | BIT9 | BIT8); + 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 = (BIT31 | BIT30 | BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24); + 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 = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24); + 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 |= (BIT5 | BIT4); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + temp |= msk; + + /* select */ + msk |= (BIT13 | BIT12); + 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), + (BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2)); + + /* + * 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 = 0x02000000; /* 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 ? BIT1 : BIT9; /* BL0 */ + msk[1] = rcvn ? BIT0 : BIT8; /* 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 & BIT0); + bit = bit ^ ((lfsr & BIT1) >> 1); + bit = bit ^ ((lfsr & BIT2) >> 2); + bit = bit ^ ((lfsr & BIT22) >> 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)), + ~BIT8, BIT8); + + mrc_alt_write_mask(DDRPHY, + (B01PTRCTL1 + + (channel * DDRIODQ_CH_OFFSET) + + ((bl >> 1) * DDRIODQ_BL_OFFSET)), + BIT8, BIT8); + } + } + + 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/arch/x86/cpu/quark/mrc_util.h b/arch/x86/cpu/quark/mrc_util.h new file mode 100644 index 0000000000..f0ddbce3c5 --- /dev/null +++ b/arch/x86/cpu/quark/mrc_util.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Ported from Intel released Quark UEFI BIOS + * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei + * + * SPDX-License-Identifier: Intel + */ + +#ifndef _MRC_UTIL_H_ +#define _MRC_UTIL_H_ + +/* Turn on this macro to enable MRC debugging output */ +#undef MRC_DEBUG + +/* MRC Debug Support */ +#define DPF debug_cond + +/* debug print type */ + +#ifdef MRC_DEBUG +#define D_ERROR 0x0001 +#define D_INFO 0x0002 +#define D_REGRD 0x0004 +#define D_REGWR 0x0008 +#define D_FCALL 0x0010 +#define D_TRN 0x0020 +#define D_TIME 0x0040 +#else +#define D_ERROR 0 +#define D_INFO 0 +#define D_REGRD 0 +#define D_REGWR 0 +#define D_FCALL 0 +#define D_TRN 0 +#define D_TIME 0 +#endif + +#define ENTERFN(...) debug_cond(D_FCALL, "<%s>\n", __func__) +#define LEAVEFN(...) debug_cond(D_FCALL, "</%s>\n", __func__) +#define REPORTFN(...) debug_cond(D_FCALL, "<%s/>\n", __func__) + +/* Generic Register Bits */ +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +/* 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/arch/x86/cpu/quark/msg_port.c b/arch/x86/cpu/quark/msg_port.c new file mode 100644 index 0000000000..31713e321f --- /dev/null +++ b/arch/x86/cpu/quark/msg_port.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <pci.h> +#include <asm/arch/device.h> +#include <asm/arch/msg_port.h> + +void msg_port_setup(int op, int port, int reg) +{ + 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; + + pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG, + reg & 0xffffff00); + msg_port_setup(MSG_OP_READ, port, reg); + pci_read_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, &value); + + return value; +} + +void msg_port_write(u8 port, u32 reg, u32 value) +{ + pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, value); + 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; + + pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG, + reg & 0xffffff00); + msg_port_setup(MSG_OP_ALT_READ, port, reg); + pci_read_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, &value); + + return value; +} + +void msg_port_alt_write(u8 port, u32 reg, u32 value) +{ + pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, value); + 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; + + pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_CTRL_EXT_REG, + reg & 0xffffff00); + msg_port_setup(MSG_OP_IO_READ, port, reg); + pci_read_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, &value); + + return value; +} + +void msg_port_io_write(u8 port, u32 reg, u32 value) +{ + pci_write_config_dword(QUARK_HOST_BRIDGE, MSG_DATA_REG, value); + 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/arch/x86/cpu/quark/pci.c b/arch/x86/cpu/quark/pci.c new file mode 100644 index 0000000000..354e15a990 --- /dev/null +++ b/arch/x86/cpu/quark/pci.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <pci.h> +#include <asm/pci.h> +#include <asm/arch/device.h> + +DECLARE_GLOBAL_DATA_PTR; + +void board_pci_setup_hose(struct pci_controller *hose) +{ + hose->first_busno = 0; + hose->last_busno = 0; + + /* PCI memory space */ + pci_set_region(hose->regions + 0, + CONFIG_PCI_MEM_BUS, + CONFIG_PCI_MEM_PHYS, + CONFIG_PCI_MEM_SIZE, + PCI_REGION_MEM); + + /* PCI IO space */ + pci_set_region(hose->regions + 1, + CONFIG_PCI_IO_BUS, + CONFIG_PCI_IO_PHYS, + CONFIG_PCI_IO_SIZE, + PCI_REGION_IO); + + pci_set_region(hose->regions + 2, + CONFIG_PCI_PREF_BUS, + CONFIG_PCI_PREF_PHYS, + CONFIG_PCI_PREF_SIZE, + PCI_REGION_PREFETCH); + + pci_set_region(hose->regions + 3, + 0, + 0, + gd->ram_size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + hose->region_count = 4; +} + +int board_pci_post_scan(struct pci_controller *hose) +{ + return 0; +} + +int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) +{ + /* + * TODO: + * + * For some unknown reason, the PCI enumeration process hangs + * when it scans to the PCIe root port 0 (D23:F0) & 1 (D23:F1). + * + * For now we just skip these two devices, and this needs to + * be revisited later. + */ + if (dev == QUARK_HOST_BRIDGE || + dev == QUARK_PCIE0 || dev == QUARK_PCIE1) { + return 1; + } + + return 0; +} diff --git a/arch/x86/cpu/quark/quark.c b/arch/x86/cpu/quark/quark.c new file mode 100644 index 0000000000..dccf7ac5f5 --- /dev/null +++ b/arch/x86/cpu/quark/quark.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <mmc.h> +#include <asm/io.h> +#include <asm/pci.h> +#include <asm/post.h> +#include <asm/processor.h> +#include <asm/arch/device.h> +#include <asm/arch/msg_port.h> +#include <asm/arch/quark.h> + +static struct pci_device_id mmc_supported[] = { + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_SDIO }, +}; + +/* + * TODO: + * + * This whole routine should be removed until we fully convert the ICH SPI + * driver to DM and make use of DT to pass the bios control register offset + */ +static void unprotect_spi_flash(void) +{ + u32 bc; + + bc = pci_read_config32(QUARK_LEGACY_BRIDGE, 0xd8); + bc |= 0x1; /* unprotect the flash */ + pci_write_config32(QUARK_LEGACY_BRIDGE, 0xd8, bc); +} + +static void quark_setup_bars(void) +{ + /* GPIO - D31:F0:R44h */ + pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_GBA, + CONFIG_GPIO_BASE | IO_BAR_EN); + + /* ACPI PM1 Block - D31:F0:R48h */ + pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_PM1BLK, + CONFIG_ACPI_PM1_BASE | IO_BAR_EN); + + /* GPE0 - D31:F0:R4Ch */ + pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_GPE0BLK, + CONFIG_ACPI_GPE0_BASE | IO_BAR_EN); + + /* WDT - D31:F0:R84h */ + pci_write_config_dword(QUARK_LEGACY_BRIDGE, LB_WDTBA, + CONFIG_WDT_BASE | IO_BAR_EN); + + /* RCBA - D31:F0:RF0h */ + 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); +} + +int arch_cpu_init(void) +{ + struct pci_controller *hose; + int ret; + + post_code(POST_CPU_INIT); +#ifdef CONFIG_SYS_X86_TSC_TIMER + timer_set_base(rdtsc()); +#endif + + ret = x86_cpu_init_f(); + if (ret) + return ret; + + ret = pci_early_init_hose(&hose); + if (ret) + return ret; + + /* + * Quark SoC has some non-standard BARs (excluding PCI standard BARs) + * which need be initialized with suggested values + */ + quark_setup_bars(); + + unprotect_spi_flash(); + + return 0; +} + +int print_cpuinfo(void) +{ + post_code(POST_CPU_INFO); + return default_print_cpuinfo(); +} + +void reset_cpu(ulong addr) +{ + /* cold reset */ + outb(0x08, PORT_RESET); +} + +int cpu_mmc_init(bd_t *bis) +{ + return pci_mmc_init("Quark SDHCI", mmc_supported, + ARRAY_SIZE(mmc_supported)); +} diff --git a/arch/x86/cpu/quark/smc.c b/arch/x86/cpu/quark/smc.c new file mode 100644 index 0000000000..e34bec4c80 --- /dev/null +++ b/arch/x86/cpu/quark/smc.c @@ -0,0 +1,2764 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Ported from Intel released Quark UEFI BIOS + * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei + * + * SPDX-License-Identifier: Intel + */ + +#include <common.h> +#include <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_rfc values (in picoseconds) per density */ +static const uint32_t t_rfc[5] = { + 90000, /* 512Mb */ + 110000, /* 1Gb */ + 160000, /* 2Gb */ + 300000, /* 4Gb */ + 350000, /* 8Gb */ +}; + +/* 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}; +static const uint16_t ddr_wctl[] = {1, 217}; +static const uint16_t ddr_wcmd[] = {1, 220}; + +#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, BIT0, BIT0); + + 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 &= ~(BIT0 | BIT1); + dtr0 |= mrc_params->ddr_speed; + dtr0 &= ~(BIT12 | BIT13 | BIT14); + tmp1 = tcl - 5; + dtr0 |= ((tcl - 5) << 12); + dtr0 &= ~(BIT4 | BIT5 | BIT6 | BIT7); + dtr0 |= ((trp - 5) << 4); /* 5 bit DRAM Clock */ + dtr0 &= ~(BIT8 | BIT9 | BIT10 | BIT11); + dtr0 |= ((trcd - 5) << 8); /* 5 bit DRAM Clock */ + + dtr1 &= ~(BIT0 | BIT1 | BIT2); + tmp2 = wl - 3; + dtr1 |= (wl - 3); + dtr1 &= ~(BIT8 | BIT9 | BIT10 | BIT11); + dtr1 |= ((wl + 4 + twr - 14) << 8); /* Change to tWTP */ + dtr1 &= ~(BIT28 | BIT29 | BIT30); + dtr1 |= ((MMAX(trtp, 4) - 3) << 28); /* 4 bit DRAM Clock */ + dtr1 &= ~(BIT24 | BIT25); + dtr1 |= ((trrd - 4) << 24); /* 4 bit DRAM Clock */ + dtr1 &= ~(BIT4 | BIT5); + dtr1 |= (1 << 4); + dtr1 &= ~(BIT20 | BIT21 | BIT22 | BIT23); + dtr1 |= ((tras - 14) << 20); /* 6 bit DRAM Clock */ + dtr1 &= ~(BIT16 | BIT17 | BIT18 | BIT19); + dtr1 |= ((((tfaw + 1) >> 1) - 5) << 16);/* 4 bit DRAM Clock */ + /* Set 4 Clock CAS to CAS delay (multi-burst) */ + dtr1 &= ~(BIT12 | BIT13); + + dtr2 &= ~(BIT0 | BIT1 | BIT2); + dtr2 |= 1; + dtr2 &= ~(BIT8 | BIT9 | BIT10); + dtr2 |= (2 << 8); + dtr2 &= ~(BIT16 | BIT17 | BIT18 | BIT19); + dtr2 |= (2 << 16); + + dtr3 &= ~(BIT0 | BIT1 | BIT2); + dtr3 |= 2; + dtr3 &= ~(BIT4 | BIT5 | BIT6); + dtr3 |= (2 << 4); + + dtr3 &= ~(BIT8 | BIT9 | BIT10 | BIT11); + 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 &= ~(BIT13 | BIT14 | BIT15 | BIT16); + dtr3 |= ((4 + wl + twtr - 11) << 13); + + dtr3 &= ~(BIT22 | BIT23); + if (mrc_params->ddr_speed == DDRFREQ_800) + dtr3 |= ((MMAX(0, 1 - 1)) << 22); + else + dtr3 |= ((MMAX(0, 2 - 1)) << 22); + + dtr4 &= ~(BIT0 | BIT1); + dtr4 |= 1; + dtr4 &= ~(BIT4 | BIT5 | BIT6); + dtr4 |= (1 << 4); + dtr4 &= ~(BIT8 | BIT9 | BIT10); + dtr4 |= ((1 + tmp1 - tmp2 + 2) << 8); + dtr4 &= ~(BIT12 | BIT13 | BIT14); + dtr4 |= ((1 + tmp1 - tmp2 + 2) << 12); + dtr4 &= ~(BIT15 | BIT16); + + 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 |= (BIT24 | BIT25); + dpmc0 &= ~(BIT16 | BIT17 | BIT18); + dpmc0 &= ~BIT23; + msg_port_write(MEM_CTLR, DPMC0, dpmc0); + + /* Disable out of order transactions */ + dsch = msg_port_read(MEM_CTLR, DSCH); + dsch |= (BIT8 | BIT12); + msg_port_write(MEM_CTLR, DSCH, dsch); + + /* Disable issuing the REF command */ + drfc = msg_port_read(MEM_CTLR, DRFC); + drfc &= ~(BIT12 | BIT13 | BIT14); + msg_port_write(MEM_CTLR, DRFC, drfc); + + /* Disable ZQ calibration short */ + dcal = msg_port_read(MEM_CTLR, DCAL); + dcal &= ~(BIT8 | BIT9 | BIT10); + dcal &= ~(BIT12 | BIT13); + 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 |= BIT0; + if (mrc_params->rank_enables & 2) + drp |= BIT1; + 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, BIT16, BIT16); + + /* 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 ? BIT12 : 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 & (BIT1 | BIT0); + 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)), + ~BIT20, BIT20); /* SPID_INIT_COMPLETE=0 */ + /* Deassert IOBUFACT */ + mrc_alt_write_mask(DDRPHY, + (CMDCFGREG0 + (ch * DDRIOCCC_CH_OFFSET)), + ~BIT2, BIT2); /* IOBUFACTRST_N=0 */ + /* Disable WRPTR */ + mrc_alt_write_mask(DDRPHY, + (CMDPTRREG + (ch * DDRIOCCC_CH_OFFSET)), + ~BIT0, BIT0); /* WRPTRENABLE=0 */ + } + } + + /* Put PHY in reset */ + mrc_alt_write_mask(DDRPHY, MASTERRSTN, 0, BIT0); + + /* 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) ? (0x00) : (BIT22)), (BIT22)); + + /* 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), (BIT6 | BIT5)); + /* ODT strength */ + mrc_alt_write_mask(DDRPHY, + (B1RXIOBUFCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + (temp << 5), (BIT6 | BIT5)); + + /* 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, + (BIT28 | BIT27 | BIT26 | BIT25 | BIT24 | + BIT20 | BIT19 | BIT18 | BIT17 | BIT16 | + BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | + BIT4 | BIT3 | BIT2 | BIT1 | BIT0)); + 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, + (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | + BIT16 | BIT13 | BIT12 | BIT11 | BIT10 | + BIT9 | BIT8)); + /* On Duration: ODT, DIFFAMP */ + mrc_alt_write_mask(DDRPHY, + (B1ONDURCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + temp, + (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | + BIT16 | BIT13 | BIT12 | BIT11 | BIT10 | + BIT9 | BIT8)); + + 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, + (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | + BIT16 | BIT15 | BIT14 | BIT13 | BIT12 | + BIT11 | BIT10)); + /* Override: DIFFAMP, ODT */ + mrc_alt_write_mask(DDRPHY, + (B1OVRCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + temp, + (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | + BIT16 | BIT15 | BIT14 | BIT13 | BIT12 | + BIT11 | BIT10)); + + /* 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)), + (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | + BIT16 | BIT12 | BIT11 | BIT10 | BIT9 | + BIT8 | BIT4 | BIT3 | BIT2 | BIT1 | + BIT0)); + mrc_alt_write_mask(DDRPHY, + (B1LATCTL0 + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + (((cas + 7) << 16) | ((cas - 4) << 8) | + ((cwl - 2) << 0)), + (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | + BIT16 | BIT12 | BIT11 | BIT10 | BIT9 | + BIT8 | BIT4 | BIT3 | BIT2 | BIT1 | + BIT0)); + + /* RCVEN Bypass (PO) */ + mrc_alt_write_mask(DDRPHY, + (B0RXIOBUFCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + ((0x0 << 7) | (0x0 << 0)), + (BIT7 | BIT0)); + mrc_alt_write_mask(DDRPHY, + (B1RXIOBUFCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + ((0x0 << 7) | (0x0 << 0)), + (BIT7 | BIT0)); + + /* TX */ + mrc_alt_write_mask(DDRPHY, + (DQCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + (BIT16), (BIT16)); + mrc_alt_write_mask(DDRPHY, + (B01PTRCTL1 + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + (BIT8), (BIT8)); + + /* 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)), + (BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | + BIT2 | BIT1 | BIT0)); + /* 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)), + (BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | + BIT2 | BIT1 | BIT0)); + /* Per-Bit De-Skew Enable */ + mrc_alt_write_mask(DDRPHY, + (B0RXIOBUFCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + (0), (BIT4)); + /* Per-Bit De-Skew Enable */ + mrc_alt_write_mask(DDRPHY, + (B1RXIOBUFCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + (0), (BIT4)); + } + + /* CLKEBB */ + mrc_alt_write_mask(DDRPHY, + (CMDOBSCKEBBCTL + (ch * DDRIOCCC_CH_OFFSET)), + 0, (BIT23)); + + /* Enable tristate control of cmd/address bus */ + mrc_alt_write_mask(DDRPHY, + (CMDCFGREG0 + (ch * DDRIOCCC_CH_OFFSET)), + 0, (BIT1 | BIT0)); + + /* ODT RCOMP */ + mrc_alt_write_mask(DDRPHY, + (CMDRCOMPODT + (ch * DDRIOCCC_CH_OFFSET)), + ((0x03 << 5) | (0x03 << 0)), + (BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | + BIT3 | BIT2 | BIT1 | BIT0)); + + /* CMDPM* registers must be programmed in this order */ + + /* Turn On Delays: SFR (regulator), MPLL */ + mrc_alt_write_mask(DDRPHY, + (CMDPMDLYREG4 + (ch * DDRIOCCC_CH_OFFSET)), + ((0xFFFFU << 16) | (0xFFFF << 0)), + 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)), + ((0xFU << 28) | (0xFFF << 16) | (0xF << 12) | + (0x616 << 0)), 0xFFFFFFFF); + /* MPLL Divider Reset Delays */ + mrc_alt_write_mask(DDRPHY, + (CMDPMDLYREG2 + (ch * DDRIOCCC_CH_OFFSET)), + ((0xFFU << 24) | (0xFF << 16) | (0xFF << 8) | + (0xFF << 0)), 0xFFFFFFFF); + /* Turn Off Delays: VREG, Staggered MDLL, MDLL, PI */ + mrc_alt_write_mask(DDRPHY, + (CMDPMDLYREG1 + (ch * DDRIOCCC_CH_OFFSET)), + ((0xFFU << 24) | (0xFF << 16) | (0xFF << 8) | + (0xFF << 0)), 0xFFFFFFFF); + /* Turn On Delays: MPLL, Staggered MDLL, PI, IOBUFACT */ + mrc_alt_write_mask(DDRPHY, + (CMDPMDLYREG0 + (ch * DDRIOCCC_CH_OFFSET)), + ((0xFFU << 24) | (0xFF << 16) | (0xFF << 8) | + (0xFF << 0)), 0xFFFFFFFF); + /* Allow PUnit signals */ + mrc_alt_write_mask(DDRPHY, + (CMDPMCONFIG0 + (ch * DDRIOCCC_CH_OFFSET)), + ((0x6 << 8) | BIT6 | (0x4 << 0)), + (BIT31 | BIT30 | BIT29 | BIT28 | BIT27 | BIT26 | + BIT25 | BIT24 | BIT23 | BIT22 | BIT21 | BIT11 | + BIT10 | BIT9 | BIT8 | BIT6 | BIT3 | BIT2 | + BIT1 | BIT0)); + /* DLL_VREG Bias Trim, VREF Tuning for DLL_VREG */ + mrc_alt_write_mask(DDRPHY, + (CMDMDLLCTL + (ch * DDRIOCCC_CH_OFFSET)), + ((0x3 << 4) | (0x7 << 0)), + (BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | + BIT0)); + + /* CLK-CTL */ + mrc_alt_write_mask(DDRPHY, + (CCOBSCKEBBCTL + (ch * DDRIOCCC_CH_OFFSET)), + 0, BIT24); /* CLKEBB */ + /* Buffer Enable: CS,CKE,ODT,CLK */ + mrc_alt_write_mask(DDRPHY, + (CCCFGREG0 + (ch * DDRIOCCC_CH_OFFSET)), + ((0x0 << 16) | (0x0 << 12) | (0x0 << 8) | + (0xF << 4) | BIT0), + (BIT19 | BIT18 | BIT17 | BIT16 | BIT15 | BIT14 | + BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | + BIT7 | BIT6 | BIT5 | BIT4 | BIT0)); + /* ODT RCOMP */ + mrc_alt_write_mask(DDRPHY, + (CCRCOMPODT + (ch * DDRIOCCC_CH_OFFSET)), + ((0x03 << 8) | (0x03 << 0)), + (BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | BIT4 | + BIT3 | BIT2 | BIT1 | BIT0)); + /* DLL_VREG Bias Trim, VREF Tuning for DLL_VREG */ + mrc_alt_write_mask(DDRPHY, + (CCMDLLCTL + (ch * DDRIOCCC_CH_OFFSET)), + ((0x3 << 4) | (0x7 << 0)), + (BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | + BIT0)); + + /* + * 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)), + (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | + BIT24 | BIT21 | BIT20 | BIT19 | BIT18 | + BIT17 | BIT16)); + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + (CMDVREFCH0 + (ch * DDRCOMP_CH_OFFSET)), + ((0x0C << 24) | (0x03 << 16)), + (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | + BIT24 | BIT21 | BIT20 | BIT19 | BIT18 | + BIT17 | BIT16)); + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + (CLKVREFCH0 + (ch * DDRCOMP_CH_OFFSET)), + ((0x0F << 24) | (0x03 << 16)), + (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | + BIT24 | BIT21 | BIT20 | BIT19 | BIT18 | + BIT17 | BIT16)); + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + (DQSVREFCH0 + (ch * DDRCOMP_CH_OFFSET)), + ((0x08 << 24) | (0x03 << 16)), + (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | + BIT24 | BIT21 | BIT20 | BIT19 | BIT18 | + BIT17 | BIT16)); + /* RCOMP Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + (CTLVREFCH0 + (ch * DDRCOMP_CH_OFFSET)), + ((0x0C << 24) | (0x03 << 16)), + (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | + BIT24 | BIT21 | BIT20 | BIT19 | BIT18 | + BIT17 | BIT16)); + + /* DQS Swapped Input Enable */ + mrc_alt_write_mask(DDRPHY, + (COMPEN1CH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT19 | BIT17), + (BIT31 | BIT30 | BIT19 | BIT17 | + BIT15 | BIT14)); + + /* 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)), + (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | + BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0)); + /* ODT Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + (DQSVREFCH0 + (ch * DDRCOMP_CH_OFFSET)), + ((0x32 << 8) | (0x03 << 0)), + (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | + BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0)); + /* ODT Vref PU/PD */ + mrc_alt_write_mask(DDRPHY, + (CLKVREFCH0 + (ch * DDRCOMP_CH_OFFSET)), + ((0x0E << 8) | (0x05 << 0)), + (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | + BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0)); + + /* + * 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, + (BIT19 | BIT18 | BIT17 | BIT16 | BIT15 | + BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | + BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | + BIT3 | BIT2 | BIT1 | BIT0)); + /* TCO Vref CLK,DQS,DQ */ + mrc_alt_write_mask(DDRPHY, + (TCOVREFCH0 + (ch * DDRCOMP_CH_OFFSET)), + ((0x05 << 16) | (0x05 << 8) | (0x05 << 0)), + (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | + BIT16 | BIT13 | BIT12 | BIT11 | BIT10 | + BIT9 | BIT8 | BIT5 | BIT4 | BIT3 | BIT2 | + BIT1 | BIT0)); + /* ODTCOMP CMD/CTL PU/PD */ + mrc_alt_write_mask(DDRPHY, + (CCBUFODTCH0 + (ch * DDRCOMP_CH_OFFSET)), + ((0x03 << 8) | (0x03 << 0)), + (BIT12 | BIT11 | BIT10 | BIT9 | BIT8 | + BIT4 | BIT3 | BIT2 | BIT1 | BIT0)); + /* COMP */ + mrc_alt_write_mask(DDRPHY, + (COMPEN0CH0 + (ch * DDRCOMP_CH_OFFSET)), + 0, (BIT31 | BIT30 | BIT8)); + +#ifdef BACKUP_COMPS + /* DQ COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQDRVPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0A << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQDRVPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0A << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQDLYPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x10 << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQDLYPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x10 << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* ODTCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQODTPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* ODTCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQODTPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQTCOPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31), (BIT31)); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQTCOPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31), (BIT31)); + + /* DQS COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQSDRVPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0A << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQSDRVPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0A << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQSDLYPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x10 << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQSDLYPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x10 << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* ODTCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQSODTPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* ODTCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQSODTPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQSTCOPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31), (BIT31)); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQSTCOPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31), (BIT31)); + + /* CLK COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CLKDRVPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0C << 16)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CLKDRVPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0C << 16)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CLKDLYPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x07 << 16)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CLKDLYPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x07 << 16)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* ODTCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CLKODTPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0B << 16)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* ODTCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CLKODTPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0B << 16)), + (BIT31 | (0x0B << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CLKTCOPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31), (BIT31)); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CLKTCOPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31), (BIT31)); + + /* CMD COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CMDDRVPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0D << 16)), + (BIT31 | BIT21 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CMDDRVPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0D << 16)), + (BIT31 | BIT21 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CMDDLYPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0A << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CMDDLYPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0A << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + + /* CTL COMP Overrides */ + /* RCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CTLDRVPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0D << 16)), + (BIT31 | BIT21 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* RCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CTLDRVPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0D << 16)), + (BIT31 | BIT21 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CTLDLYPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0A << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* DCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CTLDLYPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x0A << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); +#else + /* DQ TCOCOMP Overrides */ + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQTCOPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x1F << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQTCOPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x1F << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + + /* DQS TCOCOMP Overrides */ + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (DQSTCOPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x1F << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (DQSTCOPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x1F << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + + /* CLK TCOCOMP Overrides */ + /* TCOCOMP PU */ + mrc_alt_write_mask(DDRPHY, + (CLKTCOPUCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x1F << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); + /* TCOCOMP PD */ + mrc_alt_write_mask(DDRPHY, + (CLKTCOPDCTLCH0 + (ch * DDRCOMP_CH_OFFSET)), + (BIT31 | (0x1F << 16)), + (BIT31 | BIT20 | BIT19 | + BIT18 | BIT17 | BIT16)); +#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), (BIT30), (BIT30)); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (DQANADRVPDCTL), (BIT30), (BIT30)); + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (CMDANADRVPUCTL), (BIT30), (BIT30)); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (CMDANADRVPDCTL), (BIT30), (BIT30)); + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (CLKANADRVPUCTL), (BIT30), (BIT30)); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (CLKANADRVPDCTL), (BIT30), (BIT30)); + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (DQSANADRVPUCTL), (BIT30), (BIT30)); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (DQSANADRVPDCTL), (BIT30), (BIT30)); + /* RCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (CTLANADRVPUCTL), (BIT30), (BIT30)); + /* RCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (CTLANADRVPDCTL), (BIT30), (BIT30)); + /* ODT: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (DQANAODTPUCTL), (BIT30), (BIT30)); + /* ODT: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (DQANAODTPDCTL), (BIT30), (BIT30)); + /* ODT: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (CLKANAODTPUCTL), (BIT30), (BIT30)); + /* ODT: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (CLKANAODTPDCTL), (BIT30), (BIT30)); + /* ODT: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (DQSANAODTPUCTL), (BIT30), (BIT30)); + /* ODT: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (DQSANAODTPDCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (DQANADLYPUCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (DQANADLYPDCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (CMDANADLYPUCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (CMDANADLYPDCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (CLKANADLYPUCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (CLKANADLYPDCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (DQSANADLYPUCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (DQSANADLYPDCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (CTLANADLYPUCTL), (BIT30), (BIT30)); + /* DCOMP: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (CTLANADLYPDCTL), (BIT30), (BIT30)); + /* TCO: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (DQANATCOPUCTL), (BIT30), (BIT30)); + /* TCO: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (DQANATCOPDCTL), (BIT30), (BIT30)); + /* TCO: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (CLKANATCOPUCTL), (BIT30), (BIT30)); + /* TCO: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (CLKANATCOPDCTL), (BIT30), (BIT30)); + /* TCO: Dither PU Enable */ + mrc_alt_write_mask(DDRPHY, (DQSANATCOPUCTL), (BIT30), (BIT30)); + /* TCO: Dither PD Enable */ + mrc_alt_write_mask(DDRPHY, (DQSANATCOPDCTL), (BIT30), (BIT30)); + /* TCOCOMP: Pulse Count */ + mrc_alt_write_mask(DDRPHY, (TCOCNTCTRL), (0x1 << 0), (BIT1 | BIT0)); + /* ODT: CMD/CTL PD/PU */ + mrc_alt_write_mask(DDRPHY, + (CHNLBUFSTATIC), ((0x03 << 24) | (0x03 << 16)), + (BIT28 | BIT27 | BIT26 | BIT25 | BIT24 | + BIT20 | BIT19 | BIT18 | BIT17 | BIT16)); + /* Set 1us counter */ + mrc_alt_write_mask(DDRPHY, + (MSCNTR), (0x64 << 0), + (BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0)); + mrc_alt_write_mask(DDRPHY, + (LATCH1CTL), (0x1 << 28), + (BIT30 | BIT29 | BIT28)); + + /* Release PHY from reset */ + mrc_alt_write_mask(DDRPHY, MASTERRSTN, BIT0, BIT0); + + /* 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)), + (BIT13), + (BIT13)); /* Enable VREG */ + delay_n(3); + } + + /* ECC */ + mrc_alt_write_mask(DDRPHY, (ECCMDLLCTL), + (BIT13), (BIT13)); /* Enable VREG */ + delay_n(3); + /* CMD */ + mrc_alt_write_mask(DDRPHY, + (CMDMDLLCTL + (ch * DDRIOCCC_CH_OFFSET)), + (BIT13), (BIT13)); /* Enable VREG */ + delay_n(3); + /* CLK-CTL */ + mrc_alt_write_mask(DDRPHY, + (CCMDLLCTL + (ch * DDRIOCCC_CH_OFFSET)), + (BIT13), (BIT13)); /* 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)), + (BIT17), + (BIT17)); /* Enable MCDLL */ + delay_n(50); + } + + /* ECC */ + mrc_alt_write_mask(DDRPHY, (ECCMDLLCTL), + (BIT17), (BIT17)); /* Enable MCDLL */ + delay_n(50); + /* CMD */ + mrc_alt_write_mask(DDRPHY, + (CMDMDLLCTL + (ch * DDRIOCCC_CH_OFFSET)), + (BIT18), (BIT18)); /* Enable MCDLL */ + delay_n(50); + /* CLK-CTL */ + mrc_alt_write_mask(DDRPHY, + (CCMDLLCTL + (ch * DDRIOCCC_CH_OFFSET)), + (BIT18), (BIT18)); /* 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)) ? + ((0x1 << 12) | (0x1 << 8) | + (0xF << 4) | (0xF << 0)) : + ((0xF << 12) | (0xF << 8) | + (0xF << 4) | (0xF << 0)); +#else + temp = ((0xF << 12) | (0xF << 8) | + (0xF << 4) | (0xF << 0)); +#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)), + (BIT3 | BIT2 | BIT1 | BIT0), + (BIT3 | BIT2 | BIT1 | BIT0)); + delay_n(3); + /* Enable RXDLL Overrides BL0 */ + mrc_alt_write_mask(DDRPHY, + (B0OVRCTL + + (bl_grp * DDRIODQ_BL_OFFSET) + + (ch * DDRIODQ_CH_OFFSET)), + (BIT3 | BIT2 | BIT1 | BIT0), + (BIT3 | BIT2 | BIT1 | BIT0)); + } + + /* ECC */ + temp = ((0xF << 12) | (0xF << 8) | + (0xF << 4) | (0xF << 0)); + 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) ? (0x3) : (0x1), + (BIT3 | BIT2 | BIT1 | BIT0)); + } + + mrc_alt_write_mask(DDRPHY, + (ECCCLKALIGNREG2 + (ch * DDRIODQ_CH_OFFSET)), + 0x2, + (BIT3 | BIT2 | BIT1 | BIT0)); + mrc_alt_write_mask(DDRPHY, + (CMDCLKALIGNREG2 + (ch * DDRIODQ_CH_OFFSET)), + 0x0, + (BIT3 | BIT2 | BIT1 | BIT0)); + mrc_alt_write_mask(DDRPHY, + (CCCLKALIGNREG2 + (ch * DDRIODQ_CH_OFFSET)), + 0x2, + (BIT3 | BIT2 | BIT1 | BIT0)); + mrc_alt_write_mask(DDRPHY, + (CMDCLKALIGNREG0 + (ch * DDRIOCCC_CH_OFFSET)), + (0x2 << 4), (BIT5 | BIT4)); + /* + * 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)), + (BIT22 | BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | + BIT16 | BIT14 | BIT13 | BIT12 | BIT11 | BIT10 | + BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | + BIT2 | BIT1 | BIT0)); + /* TOTAL_NUM_MODULES, FIRST_U_PARTITION */ + mrc_alt_write_mask(DDRPHY, + (CMDCLKALIGNREG2 + (ch * DDRIOCCC_CH_OFFSET)), + ((0x10 << 16) | (0x4 << 8) | (0x2 << 4)), + (BIT20 | BIT19 | BIT18 | BIT17 | BIT16 | + BIT11 | BIT10 | BIT9 | BIT8 | BIT7 | BIT6 | + BIT5 | BIT4)); +#ifdef HMC_TEST + /* START_CLK_ALIGN=1 */ + mrc_alt_write_mask(DDRPHY, + (CMDCLKALIGNREG0 + (ch * DDRIOCCC_CH_OFFSET)), + BIT24, BIT24); + while (msg_port_alt_read(DDRPHY, + (CMDCLKALIGNREG0 + (ch * DDRIOCCC_CH_OFFSET))) & + BIT24) + ; /* wait for START_CLK_ALIGN=0 */ +#endif + + /* Set RD/WR Pointer Seperation & COUNTEN & FIFOPTREN */ + mrc_alt_write_mask(DDRPHY, + (CMDPTRREG + (ch * DDRIOCCC_CH_OFFSET)), + BIT0, BIT0); /* WRPTRENABLE=1 */ + + /* COMP initial */ + /* enable bypass for CLK buffer (PO) */ + mrc_alt_write_mask(DDRPHY, + (COMPEN0CH0 + (ch * DDRCOMP_CH_OFFSET)), + BIT5, BIT5); + /* Initial COMP Enable */ + mrc_alt_write_mask(DDRPHY, (CMPCTRL), + (BIT0), (BIT0)); + /* wait for Initial COMP Enable = 0 */ + while (msg_port_alt_read(DDRPHY, (CMPCTRL)) & BIT0) + ; + /* disable bypass for CLK buffer (PO) */ + mrc_alt_write_mask(DDRPHY, + (COMPEN0CH0 + (ch * DDRCOMP_CH_OFFSET)), + ~BIT5, BIT5); + + /* IOBUFACT */ + + /* STEP4a */ + mrc_alt_write_mask(DDRPHY, + (CMDCFGREG0 + (ch * DDRIOCCC_CH_OFFSET)), + BIT2, BIT2); /* IOBUFACTRST_N=1 */ + + /* DDRPHY initialization complete */ + mrc_alt_write_mask(DDRPHY, + (CMDPMCONFIG0 + (ch * DDRIOCCC_CH_OFFSET)), + BIT20, BIT20); /* 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, BIT1, (BIT8 | BIT1)); + + /* Assert RESET# for 200us */ + delay_u(200); + + /* DDR3_RESET_SET=1, DDR3_RESET_RESET=0 */ + mrc_alt_write_mask(DDRPHY, CCDDR3RESETCTL, BIT8, (BIT8 | BIT1)); + + 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 |= (BIT4 | 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 ? BIT12 : 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 &= ~BIT6; + + if (mrc_params->ron_value == 0) + emrs1_cmd |= BIT7; + else + emrs1_cmd &= ~BIT7; + + 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 |= BIT14; + mrs0_cmd |= BIT18; + 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 &= ~BIT28; + dco |= BIT31; + 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 |= BIT12; + 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, BIT8); + } + /* 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)), + BIT8, BIT8); + } +#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, 0x0082)); + + /* + * set ODT DRAM Full Time Termination + * disable in MCU + */ + + dtr4 = msg_port_read(MEM_CTLR, DTR4); + dtr4_save = dtr4; + dtr4 |= BIT15; + 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), + (BIT28 | BIT8 | BIT6 | BIT4 | BIT2), + (BIT28 | BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2)); + } + + /* Write Leveling Mode enabled in IO */ + mrc_alt_write_mask(DDRPHY, + CCDDR3RESETCTL + (DDRIOCCC_CH_OFFSET * ch), + BIT16, BIT16); + + /* 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, BIT16); + + 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), + (BIT8 | BIT6 | BIT4 | BIT2), + (BIT28 | BIT9 | BIT8 | BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2)); + } + + /* 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 &= ~(BIT16 | BIT17 | BIT18); + dpmc0 |= (4 << 16); + dpmc0 |= BIT21; + 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 &= ~(BIT8 | BIT9 | BIT12); + msg_port_write(MEM_CTLR, DSCH, dsch); + + dpmc0 = msg_port_read(MEM_CTLR, DPMC0); + dpmc0 &= ~BIT25; + dpmc0 |= (mrc_params->power_down_disable << 25); + dpmc0 &= ~BIT24; + dpmc0 &= ~(BIT16 | BIT17 | BIT18); + dpmc0 |= (4 << 16); + dpmc0 |= BIT21; + msg_port_write(MEM_CTLR, DPMC0, dpmc0); + + /* CMDTRIST = 2h - CMD/ADDR are tristated when no valid command */ + mrc_write_mask(MEM_CTLR, DPMC1, 2 << 4, BIT4 | BIT5); + + 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 &= ~BIT31; + msg_port_write(MEM_CTLR, DCO, dco); + + drp = 0; + if (mrc_params->rank_enables & 1) + drp |= BIT0; + if (mrc_params->rank_enables & 2) + drp |= BIT1; + 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 &= ~BIT28; + dco |= BIT31; + 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 &= ~(BIT12 | BIT13 | BIT14); + drfc |= (mrc_params->refresh_rate << 12); + drfc |= BIT21; + msg_port_write(MEM_CTLR, DRFC, drfc); + + dcal = msg_port_read(MEM_CTLR, DCAL); + dcal &= ~(BIT8 | BIT9 | BIT10); + dcal |= (3 << 8); /* 63ms */ + msg_port_write(MEM_CTLR, DCAL, dcal); + + dpmc0 = msg_port_read(MEM_CTLR, DPMC0); + dpmc0 |= (BIT23 | BIT29); + 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, BIT1, BIT1); + + /* 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)), + (0x00 << 16) | (temp << 10), + (BIT21 | BIT20 | BIT19 | BIT18 | + BIT17 | BIT16 | BIT15 | BIT14 | + BIT13 | BIT12 | BIT11 | BIT10)); + + /* Override: DIFFAMP, ODT */ + mrc_alt_write_mask(DDRPHY, + (B1OVRCTL + (bl * DDRIODQ_BL_OFFSET) + + (channel * DDRIODQ_CH_OFFSET)), + (0x00 << 16) | (temp << 10), + (BIT21 | BIT20 | BIT19 | BIT18 | + BIT17 | BIT16 | BIT15 | BIT14 | + BIT13 | BIT12 | BIT11 | BIT10)); + } + + /* 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 &= ~(BIT14 | BIT15); + drp |= BIT15; + drp |= BIT13; + msg_port_write(MEM_CTLR, DRP, drp); + + /* Disable new request bypass */ + dsch = msg_port_read(MEM_CTLR, DSCH); + dsch |= BIT12; + msg_port_write(MEM_CTLR, DSCH, dsch); + + /* Enable ECC */ + ecc_ctrl = (BIT0 | BIT1 | BIT17); + 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 &= ~(BIT28 | BIT29); + dco |= (BIT0 | BIT8); + msg_port_write(MEM_CTLR, DCO, dco); + + LEAVEFN(); +} diff --git a/arch/x86/cpu/quark/smc.h b/arch/x86/cpu/quark/smc.h new file mode 100644 index 0000000000..46017a1ccb --- /dev/null +++ b/arch/x86/cpu/quark/smc.h @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com> + * + * Ported from Intel released Quark UEFI BIOS + * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei + * + * SPDX-License-Identifier: Intel + */ + +#ifndef _SMC_H_ +#define _SMC_H_ + +/* System Memory Controller Register Defines */ + +/* Memory Controller Message Bus Registers Offsets */ +#define DRP 0x00 +#define DTR0 0x01 +#define DTR1 0x02 +#define DTR2 0x03 +#define DTR3 0x04 +#define DTR4 0x05 +#define DPMC0 0x06 +#define DPMC1 0x07 +#define DRFC 0x08 +#define DSCH 0x09 +#define DCAL 0x0A +#define DRMC 0x0B +#define PMSTS 0x0C +#define DCO 0x0F +#define DSTAT 0x20 +#define SSKPD0 0x4A +#define SSKPD1 0x4B +#define DECCCTRL 0x60 +#define DECCSTAT 0x61 +#define DECCSBECNT 0x62 +#define DECCSBECA 0x68 +#define DECCSBECS 0x69 +#define DECCDBECA 0x6A +#define DECCDBECS 0x6B +#define DFUSESTAT 0x70 +#define SCRMSEED 0x80 +#define SCRMLO 0x81 +#define SCRMHI 0x82 + +/* 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) | (BIT10 << 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) | (BIT10 << 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/arch/x86/cpu/queensbay/Kconfig b/arch/x86/cpu/queensbay/Kconfig index f6b52010c3..397e599f93 100644 --- a/arch/x86/cpu/queensbay/Kconfig +++ b/arch/x86/cpu/queensbay/Kconfig @@ -11,44 +11,6 @@ config INTEL_QUEENSBAY if INTEL_QUEENSBAY -config HAVE_FSP - bool "Add an Firmware Support Package binary" - help - Select this option to add an Firmware Support Package binary to - the resulting U-Boot image. It is a binary blob which U-Boot uses - to set up SDRAM and other chipset specific initialization. - - Note: Without this binary U-Boot will not be able to set up its - SDRAM so will not boot. - -config FSP_FILE - string "Firmware Support Package binary filename" - depends on HAVE_FSP - default "fsp.bin" - help - The filename of the file to use as Firmware Support Package binary - in the board directory. - -config FSP_ADDR - hex "Firmware Support Package binary location" - depends on HAVE_FSP - default 0xfffc0000 - help - FSP is not Position Independent Code (PIC) and the whole FSP has to - be rebased if it is placed at a location which is different from the - perferred base address specified during the FSP build. Use Intel's - Binary Configuration Tool (BCT) to do the rebase. - - The default base address of 0xfffc0000 indicates that the binary must - be located at offset 0xc0000 from the beginning of a 1MB flash device. - -config FSP_TEMP_RAM_ADDR - hex - default 0x2000000 - help - Stack top address which is used in FspInit after DRAM is ready and - CAR is disabled. - config HAVE_CMC bool "Add a Chipset Micro Code state machine binary" help diff --git a/arch/x86/cpu/queensbay/Makefile b/arch/x86/cpu/queensbay/Makefile index 2c2ec01ed2..d8761fdfbd 100644 --- a/arch/x86/cpu/queensbay/Makefile +++ b/arch/x86/cpu/queensbay/Makefile @@ -4,6 +4,6 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y += tnc_car.o tnc_dram.o tnc.o topcliff.o -obj-y += fsp_configs.o fsp_support.o +obj-y += fsp_configs.o +obj-y += tnc.o topcliff.o obj-$(CONFIG_PCI) += tnc_pci.o diff --git a/arch/x86/cpu/queensbay/fsp_configs.c b/arch/x86/cpu/queensbay/fsp_configs.c index af28e457f6..78bc966bbd 100644 --- a/arch/x86/cpu/queensbay/fsp_configs.c +++ b/arch/x86/cpu/queensbay/fsp_configs.c @@ -6,7 +6,7 @@ */ #include <common.h> -#include <asm/arch/fsp/fsp_support.h> +#include <asm/fsp/fsp_support.h> void update_fsp_upd(struct upd_region *fsp_upd) { diff --git a/arch/x86/cpu/queensbay/fsp_support.c b/arch/x86/cpu/queensbay/fsp_support.c deleted file mode 100644 index aed3e2b300..0000000000 --- a/arch/x86/cpu/queensbay/fsp_support.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (C) 2013, Intel Corporation - * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> - * - * SPDX-License-Identifier: Intel - */ - -#include <common.h> -#include <asm/arch/fsp/fsp_support.h> -#include <asm/post.h> - -/** - * Compares two GUIDs - * - * If the GUIDs are identical then true is returned. - * If there are any bit differences in the two GUIDs, then false is returned. - * - * @guid1: A pointer to a 128 bit GUID. - * @guid2: A pointer to a 128 bit GUID. - * - * @retval true: guid1 and guid2 are identical. - * @retval false: guid1 and guid2 are not identical. - */ -static bool compare_guid(const struct efi_guid *guid1, - const struct efi_guid *guid2) -{ - if (memcmp(guid1, guid2, sizeof(struct efi_guid)) == 0) - return true; - else - return false; -} - -u32 __attribute__((optimize("O0"))) find_fsp_header(void) -{ - /* - * This function may be called before the a stack is established, - * so special care must be taken. First, it cannot declare any local - * variable using stack. Only register variable can be used here. - * Secondly, some compiler version will add prolog or epilog code - * for the C function. If so the function call may not work before - * stack is ready. - * - * GCC 4.8.1 has been verified to be working for the following codes. - */ - volatile register u8 *fsp asm("eax"); - - /* Initalize the FSP base */ - fsp = (u8 *)CONFIG_FSP_ADDR; - - /* Check the FV signature, _FVH */ - if (((struct fv_header *)fsp)->sign == EFI_FVH_SIGNATURE) { - /* Go to the end of the FV header and align the address */ - fsp += ((struct fv_header *)fsp)->ext_hdr_off; - fsp += ((struct fv_ext_header *)fsp)->ext_hdr_size; - fsp = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8); - } else { - fsp = 0; - } - - /* Check the FFS GUID */ - if (fsp && - ((struct ffs_file_header *)fsp)->name.data1 == FSP_GUID_DATA1 && - ((struct ffs_file_header *)fsp)->name.data2 == FSP_GUID_DATA2 && - ((struct ffs_file_header *)fsp)->name.data3 == FSP_GUID_DATA3 && - ((struct ffs_file_header *)fsp)->name.data4[0] == FSP_GUID_DATA4_0 && - ((struct ffs_file_header *)fsp)->name.data4[1] == FSP_GUID_DATA4_1 && - ((struct ffs_file_header *)fsp)->name.data4[2] == FSP_GUID_DATA4_2 && - ((struct ffs_file_header *)fsp)->name.data4[3] == FSP_GUID_DATA4_3 && - ((struct ffs_file_header *)fsp)->name.data4[4] == FSP_GUID_DATA4_4 && - ((struct ffs_file_header *)fsp)->name.data4[5] == FSP_GUID_DATA4_5 && - ((struct ffs_file_header *)fsp)->name.data4[6] == FSP_GUID_DATA4_6 && - ((struct ffs_file_header *)fsp)->name.data4[7] == FSP_GUID_DATA4_7) { - /* Add the FFS header size to find the raw section header */ - fsp += sizeof(struct ffs_file_header); - } else { - fsp = 0; - } - - if (fsp && - ((struct raw_section *)fsp)->type == EFI_SECTION_RAW) { - /* Add the raw section header size to find the FSP header */ - fsp += sizeof(struct raw_section); - } else { - fsp = 0; - } - - return (u32)fsp; -} - -void fsp_continue(struct shared_data *shared_data, u32 status, void *hob_list) -{ - u32 stack_len; - u32 stack_base; - u32 stack_top; - - post_code(POST_MRC); - - assert(status == 0); - - /* Get the migrated stack in normal memory */ - stack_base = (u32)fsp_get_bootloader_tmp_mem(hob_list, &stack_len); - assert(stack_base != 0); - stack_top = stack_base + stack_len - sizeof(u32); - - /* - * Old stack base is stored at the very end of the stack top, - * use it to calculate the migrated shared data base - */ - shared_data = (struct shared_data *)(stack_base + - ((u32)shared_data - *(u32 *)stack_top)); - - /* The boot loader main function entry */ - fsp_init_done(hob_list); -} - -void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf) -{ - struct shared_data shared_data; - fsp_init_f init; - struct fsp_init_params params; - struct fspinit_rtbuf rt_buf; - struct vpd_region *fsp_vpd; - struct fsp_header *fsp_hdr; - struct fsp_init_params *params_ptr; - struct upd_region *fsp_upd; - - fsp_hdr = (struct fsp_header *)find_fsp_header(); - if (fsp_hdr == NULL) { - /* No valid FSP info header was found */ - panic("Invalid FSP header"); - } - - fsp_upd = (struct upd_region *)&shared_data.fsp_upd; - memset(&rt_buf, 0, sizeof(struct fspinit_rtbuf)); - - /* Reserve a gap in stack top */ - rt_buf.common.stack_top = (u32 *)stack_top - 32; - rt_buf.common.boot_mode = boot_mode; - rt_buf.common.upd_data = (struct upd_region *)fsp_upd; - - /* Get VPD region start */ - fsp_vpd = (struct vpd_region *)(fsp_hdr->img_base + - fsp_hdr->cfg_region_off); - - /* Verifify the VPD data region is valid */ - assert((fsp_vpd->img_rev == VPD_IMAGE_REV) && - (fsp_vpd->sign == VPD_IMAGE_ID)); - - /* Copy default data from Flash */ - memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset), - sizeof(struct upd_region)); - - /* Verifify the UPD data region is valid */ - assert(fsp_upd->terminator == UPD_TERMINATOR); - - /* Override any UPD setting if required */ - update_fsp_upd(fsp_upd); - - memset(¶ms, 0, sizeof(struct fsp_init_params)); - params.nvs_buf = nvs_buf; - params.rt_buf = (struct fspinit_rtbuf *)&rt_buf; - params.continuation = (fsp_continuation_f)asm_continuation; - - init = (fsp_init_f)(fsp_hdr->img_base + fsp_hdr->fsp_init); - params_ptr = ¶ms; - - shared_data.fsp_hdr = fsp_hdr; - shared_data.stack_top = (u32 *)stack_top; - - post_code(POST_PRE_MRC); - - /* - * Use ASM code to ensure the register value in EAX & ECX - * will be passed into BlContinuationFunc - */ - asm volatile ( - "pushl %0;" - "call *%%eax;" - ".global asm_continuation;" - "asm_continuation:;" - "movl %%ebx, %%eax;" /* shared_data */ - "movl 4(%%esp), %%edx;" /* status */ - "movl 8(%%esp), %%ecx;" /* hob_list */ - "jmp fsp_continue;" - : : "m"(params_ptr), "a"(init), "b"(&shared_data) - ); - - /* - * Should never get here. - * Control will continue from fsp_continue. - * This line below is to prevent the compiler from optimizing - * structure intialization. - * - * DO NOT REMOVE! - */ - init(¶ms); -} - -u32 fsp_notify(struct fsp_header *fsp_hdr, u32 phase) -{ - fsp_notify_f notify; - struct fsp_notify_params params; - struct fsp_notify_params *params_ptr; - u32 status; - - if (!fsp_hdr) - fsp_hdr = (struct fsp_header *)find_fsp_header(); - - if (fsp_hdr == NULL) { - /* No valid FSP info header */ - panic("Invalid FSP header"); - } - - notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify); - params.phase = phase; - params_ptr = ¶ms; - - /* - * Use ASM code to ensure correct parameter is on the stack for - * FspNotify as U-Boot is using different ABI from FSP - */ - asm volatile ( - "pushl %1;" /* push notify phase */ - "call *%%eax;" /* call FspNotify */ - "addl $4, %%esp;" /* clean up the stack */ - : "=a"(status) : "m"(params_ptr), "a"(notify), "m"(*params_ptr) - ); - - return status; -} - -u32 fsp_get_usable_lowmem_top(const void *hob_list) -{ - const struct hob_header *hdr; - struct hob_res_desc *res_desc; - phys_addr_t phys_start; - u32 top; - - /* Get the HOB list for processing */ - hdr = hob_list; - - /* * Collect memory ranges */ - top = FSP_LOWMEM_BASE; - while (!end_of_hob(hdr)) { - if (hdr->type == HOB_TYPE_RES_DESC) { - res_desc = (struct hob_res_desc *)hdr; - if (res_desc->type == RES_SYS_MEM) { - phys_start = res_desc->phys_start; - /* Need memory above 1MB to be collected here */ - if (phys_start >= FSP_LOWMEM_BASE && - phys_start < (phys_addr_t)FSP_HIGHMEM_BASE) - top += (u32)(res_desc->len); - } - } - hdr = get_next_hob(hdr); - } - - return top; -} - -u64 fsp_get_usable_highmem_top(const void *hob_list) -{ - const struct hob_header *hdr; - struct hob_res_desc *res_desc; - phys_addr_t phys_start; - u64 top; - - /* Get the HOB list for processing */ - hdr = hob_list; - - /* Collect memory ranges */ - top = FSP_HIGHMEM_BASE; - while (!end_of_hob(hdr)) { - if (hdr->type == HOB_TYPE_RES_DESC) { - res_desc = (struct hob_res_desc *)hdr; - if (res_desc->type == RES_SYS_MEM) { - phys_start = res_desc->phys_start; - /* Need memory above 1MB to be collected here */ - if (phys_start >= (phys_addr_t)FSP_HIGHMEM_BASE) - top += (u32)(res_desc->len); - } - } - hdr = get_next_hob(hdr); - } - - return top; -} - -u64 fsp_get_reserved_mem_from_guid(const void *hob_list, u64 *len, - struct efi_guid *guid) -{ - const struct hob_header *hdr; - struct hob_res_desc *res_desc; - - /* Get the HOB list for processing */ - hdr = hob_list; - - /* Collect memory ranges */ - while (!end_of_hob(hdr)) { - if (hdr->type == HOB_TYPE_RES_DESC) { - res_desc = (struct hob_res_desc *)hdr; - if (res_desc->type == RES_MEM_RESERVED) { - if (compare_guid(&res_desc->owner, guid)) { - if (len) - *len = (u32)(res_desc->len); - - return (u64)(res_desc->phys_start); - } - } - } - hdr = get_next_hob(hdr); - } - - return 0; -} - -u32 fsp_get_fsp_reserved_mem(const void *hob_list, u32 *len) -{ - const struct efi_guid guid = FSP_HOB_RESOURCE_OWNER_FSP_GUID; - u64 length; - u32 base; - - base = (u32)fsp_get_reserved_mem_from_guid(hob_list, - &length, (struct efi_guid *)&guid); - if ((len != 0) && (base != 0)) - *len = (u32)length; - - return base; -} - -u32 fsp_get_tseg_reserved_mem(const void *hob_list, u32 *len) -{ - const struct efi_guid guid = FSP_HOB_RESOURCE_OWNER_TSEG_GUID; - u64 length; - u32 base; - - base = (u32)fsp_get_reserved_mem_from_guid(hob_list, - &length, (struct efi_guid *)&guid); - if ((len != 0) && (base != 0)) - *len = (u32)length; - - return base; -} - -const struct hob_header *fsp_get_next_hob(uint type, const void *hob_list) -{ - const struct hob_header *hdr; - - hdr = hob_list; - - /* Parse the HOB list until end of list or matching type is found */ - while (!end_of_hob(hdr)) { - if (hdr->type == type) - return hdr; - - hdr = get_next_hob(hdr); - } - - return NULL; -} - -const struct hob_header *fsp_get_next_guid_hob(const struct efi_guid *guid, - const void *hob_list) -{ - const struct hob_header *hdr; - struct hob_guid *guid_hob; - - hdr = hob_list; - while ((hdr = fsp_get_next_hob(HOB_TYPE_GUID_EXT, - hdr)) != NULL) { - guid_hob = (struct hob_guid *)hdr; - if (compare_guid(guid, &(guid_hob->name))) - break; - hdr = get_next_hob(hdr); - } - - return hdr; -} - -void *fsp_get_guid_hob_data(const void *hob_list, u32 *len, - struct efi_guid *guid) -{ - const struct hob_header *guid_hob; - - guid_hob = fsp_get_next_guid_hob(guid, hob_list); - if (guid_hob == NULL) { - return NULL; - } else { - if (len) - *len = get_guid_hob_data_size(guid_hob); - - return get_guid_hob_data(guid_hob); - } -} - -void *fsp_get_nvs_data(const void *hob_list, u32 *len) -{ - const struct efi_guid guid = FSP_NON_VOLATILE_STORAGE_HOB_GUID; - - return fsp_get_guid_hob_data(hob_list, len, (struct efi_guid *)&guid); -} - -void *fsp_get_bootloader_tmp_mem(const void *hob_list, u32 *len) -{ - const struct efi_guid guid = FSP_BOOTLOADER_TEMP_MEM_HOB_GUID; - - return fsp_get_guid_hob_data(hob_list, len, (struct efi_guid *)&guid); -} diff --git a/arch/x86/cpu/queensbay/tnc.c b/arch/x86/cpu/queensbay/tnc.c index 8637cdca2d..30ab725bb9 100644 --- a/arch/x86/cpu/queensbay/tnc.c +++ b/arch/x86/cpu/queensbay/tnc.c @@ -9,7 +9,7 @@ #include <asm/pci.h> #include <asm/post.h> #include <asm/arch/tnc.h> -#include <asm/arch/fsp/fsp_support.h> +#include <asm/fsp/fsp_support.h> #include <asm/processor.h> static void unprotect_spi_flash(void) @@ -43,30 +43,3 @@ int arch_cpu_init(void) return 0; } - -int print_cpuinfo(void) -{ - post_code(POST_CPU_INFO); - return default_print_cpuinfo(); -} - -void reset_cpu(ulong addr) -{ - /* cold reset */ - outb(0x06, PORT_RESET); -} - -void board_final_cleanup(void) -{ - u32 status; - - /* call into FspNotify */ - debug("Calling into FSP (notify phase INIT_PHASE_BOOT): "); - status = fsp_notify(NULL, INIT_PHASE_BOOT); - if (status != FSP_SUCCESS) - debug("fail, error code %x\n", status); - else - debug("OK\n"); - - return; -} diff --git a/arch/x86/cpu/queensbay/tnc_car.S b/arch/x86/cpu/queensbay/tnc_car.S deleted file mode 100644 index 5e09568b85..0000000000 --- a/arch/x86/cpu/queensbay/tnc_car.S +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <config.h> -#include <asm/post.h> - -.globl car_init -car_init: - /* - * Note: ebp holds the BIST value (built-in self test) so far, but ebp - * will be destroyed through the FSP call, thus we have to test the - * BIST value here before we call into FSP. - */ - test %ebp, %ebp - jz car_init_start - post_code(POST_BIST_FAILURE) - jmp die - -car_init_start: - post_code(POST_CAR_START) - lea find_fsp_header_romstack, %esp - jmp find_fsp_header - -find_fsp_header_ret: - /* EAX points to FSP_INFO_HEADER */ - mov %eax, %ebp - - /* sanity test */ - cmp $CONFIG_FSP_ADDR, %eax - jb die - - /* calculate TempRamInitEntry address */ - mov 0x30(%ebp), %eax - add 0x1c(%ebp), %eax - - /* call FSP TempRamInitEntry to setup temporary stack */ - lea temp_ram_init_romstack, %esp - jmp *%eax - -temp_ram_init_ret: - addl $4, %esp - cmp $0, %eax - jnz car_init_fail - - post_code(POST_CAR_CPU_CACHE) - - /* - * The FSP TempRamInit initializes the ecx and edx registers to - * point to a temporary but writable memory range (Cache-As-RAM). - * ecx: the start of this temporary memory range, - * edx: the end of this range. - */ - - /* stack grows down from top of CAR */ - movl %edx, %esp - - /* - * TODO: - * - * According to FSP architecture spec, the fsp_init() will not return - * to its caller, instead it requires the bootloader to provide a - * so-called continuation function to pass into the FSP as a parameter - * of fsp_init, and fsp_init() will call that continuation function - * directly. - * - * The call to fsp_init() may need to be moved out of the car_init() - * to cpu_init_f() with the help of some inline assembly codes. - * Note there is another issue that fsp_init() will setup another stack - * using the fsp_init parameter stack_top after DRAM is initialized, - * which means any data on the previous stack (on the CAR) gets lost - * (ie: U-Boot global_data). FSP is supposed to support such scenario, - * however it does not work. This should be revisited in the future. - */ - movl $CONFIG_FSP_TEMP_RAM_ADDR, %eax - xorl %edx, %edx - xorl %ecx, %ecx - call fsp_init - -.global fsp_init_done -fsp_init_done: - /* - * We come here from FspInit with eax pointing to the HOB list. - * Save eax to esi temporarily. - */ - movl %eax, %esi - /* - * Re-initialize the ebp (BIST) to zero, as we already reach here - * which means we passed BIST testing before. - */ - xorl %ebp, %ebp - jmp car_init_ret - -car_init_fail: - post_code(POST_CAR_FAILURE) - -die: - hlt - jmp die - hlt - - /* - * The function call before CAR initialization is tricky. It cannot - * be called using the 'call' instruction but only the 'jmp' with - * the help of a handcrafted stack in the ROM. The stack needs to - * contain the function return address as well as the parameters. - */ - .balign 4 -find_fsp_header_romstack: - .long find_fsp_header_ret - - .balign 4 -temp_ram_init_romstack: - .long temp_ram_init_ret - .long temp_ram_init_params -temp_ram_init_params: -_dt_ucode_base_size: - /* These next two fields are filled in by ifdtool */ - .long 0 /* microcode base */ - .long 0 /* microcode size */ - .long CONFIG_SYS_MONITOR_BASE /* code region base */ - .long CONFIG_SYS_MONITOR_LEN /* code region size */ diff --git a/arch/x86/cpu/queensbay/tnc_dram.c b/arch/x86/cpu/queensbay/tnc_dram.c deleted file mode 100644 index df79a39dd8..0000000000 --- a/arch/x86/cpu/queensbay/tnc_dram.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include <common.h> -#include <asm/arch/fsp/fsp_support.h> -#include <asm/e820.h> -#include <asm/post.h> - -DECLARE_GLOBAL_DATA_PTR; - -int dram_init(void) -{ - phys_size_t ram_size = 0; - const struct hob_header *hdr; - struct hob_res_desc *res_desc; - - hdr = gd->arch.hob_list; - while (!end_of_hob(hdr)) { - if (hdr->type == HOB_TYPE_RES_DESC) { - res_desc = (struct hob_res_desc *)hdr; - if (res_desc->type == RES_SYS_MEM || - res_desc->type == RES_MEM_RESERVED) { - ram_size += res_desc->len; - } - } - hdr = get_next_hob(hdr); - } - - gd->ram_size = ram_size; - post_code(POST_DRAM); - - return 0; -} - -void dram_init_banksize(void) -{ - gd->bd->bi_dram[0].start = 0; - gd->bd->bi_dram[0].size = gd->ram_size; -} - -/* - * This function looks for the highest region of memory lower than 4GB which - * has enough space for U-Boot where U-Boot is aligned on a page boundary. - * It overrides the default implementation found elsewhere which simply - * picks the end of ram, wherever that may be. The location of the stack, - * the relocation address, and how far U-Boot is moved by relocation are - * set in the global data structure. - */ -ulong board_get_usable_ram_top(ulong total_size) -{ - return fsp_get_usable_lowmem_top(gd->arch.hob_list); -} - -unsigned install_e820_map(unsigned max_entries, struct e820entry *entries) -{ - unsigned num_entries = 0; - const struct hob_header *hdr; - struct hob_res_desc *res_desc; - - hdr = gd->arch.hob_list; - - while (!end_of_hob(hdr)) { - if (hdr->type == HOB_TYPE_RES_DESC) { - res_desc = (struct hob_res_desc *)hdr; - entries[num_entries].addr = res_desc->phys_start; - entries[num_entries].size = res_desc->len; - - if (res_desc->type == RES_SYS_MEM) - entries[num_entries].type = E820_RAM; - else if (res_desc->type == RES_MEM_RESERVED) - entries[num_entries].type = E820_RESERVED; - } - hdr = get_next_hob(hdr); - num_entries++; - } - - return num_entries; -} diff --git a/arch/x86/cpu/queensbay/tnc_pci.c b/arch/x86/cpu/queensbay/tnc_pci.c index 39bff49c8d..6c291f9ee9 100644 --- a/arch/x86/cpu/queensbay/tnc_pci.c +++ b/arch/x86/cpu/queensbay/tnc_pci.c @@ -7,7 +7,7 @@ #include <common.h> #include <pci.h> #include <asm/pci.h> -#include <asm/arch/fsp/fsp_support.h> +#include <asm/fsp/fsp_support.h> DECLARE_GLOBAL_DATA_PTR; @@ -44,18 +44,3 @@ void board_pci_setup_hose(struct pci_controller *hose) hose->region_count = 4; } - -int board_pci_post_scan(struct pci_controller *hose) -{ - u32 status; - - /* call into FspNotify */ - debug("Calling into FSP (notify phase INIT_PHASE_PCI): "); - status = fsp_notify(NULL, INIT_PHASE_PCI); - if (status != FSP_SUCCESS) - debug("fail, error code %x\n", status); - else - debug("OK\n"); - - return 0; -} diff --git a/arch/x86/cpu/queensbay/topcliff.c b/arch/x86/cpu/queensbay/topcliff.c index b01422a965..9faf1b92bb 100644 --- a/arch/x86/cpu/queensbay/topcliff.c +++ b/arch/x86/cpu/queensbay/topcliff.c @@ -5,43 +5,16 @@ */ #include <common.h> -#include <errno.h> -#include <malloc.h> -#include <pci.h> +#include <mmc.h> #include <pci_ids.h> -#include <sdhci.h> static struct pci_device_id mmc_supported[] = { { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_0 }, { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TCF_SDIO_1 }, - { } }; int cpu_mmc_init(bd_t *bis) { - struct sdhci_host *mmc_host; - pci_dev_t devbusfn; - u32 iobase; - int ret; - int i; - - for (i = 0; i < ARRAY_SIZE(mmc_supported); i++) { - devbusfn = pci_find_devices(mmc_supported, i); - if (devbusfn == -1) - return -ENODEV; - - mmc_host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); - if (!mmc_host) - return -ENOMEM; - - mmc_host->name = "Topcliff SDHCI"; - pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &iobase); - mmc_host->ioaddr = (void *)iobase; - mmc_host->quirks = 0; - ret = add_sdhci(mmc_host, 0, 0); - if (ret) - return ret; - } - - return 0; + return pci_mmc_init("Topcliff SDHCI", mmc_supported, + ARRAY_SIZE(mmc_supported)); } |