From 7686a56574a6773717b49a51786f301970d1c69c Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Sun, 18 May 2014 11:05:56 +0200 Subject: sandy/ivybridge: Native raminit. Based on damo22's work and my X230 tracing. Works for my X230 in a variety of RAM configs. Also-By: Damien Zammit Change-Id: I1aa024c55a8416fc53b25e7123037df0e55a2769 Signed-off-by: Vladimir Serbinenko Signed-off-by: Damien Zammit Reviewed-on: http://review.coreboot.org/5786 Tested-by: build bot (Jenkins) Reviewed-by: Edward O'Callaghan --- src/cpu/intel/Makefile.inc | 1 + src/cpu/x86/smm/smmhandler_tseg.S | 2 +- src/cpu/x86/smm/smmrelocate.S | 4 +- src/device/dram/ddr3.c | 11 +- src/include/device/dram/ddr3.h | 5 + src/mainboard/lenovo/x230/Kconfig | 12 +- src/mainboard/lenovo/x230/romstage.c | 135 +- src/northbridge/intel/Makefile.inc | 1 + src/northbridge/intel/sandybridge/Kconfig | 13 +- src/northbridge/intel/sandybridge/Makefile.inc | 10 +- src/northbridge/intel/sandybridge/gma.c | 4 +- src/northbridge/intel/sandybridge/raminit_native.c | 3893 ++++++++++++++++++++ src/northbridge/intel/sandybridge/raminit_native.h | 29 + .../intel/sandybridge/raminit_patterns.h | 639 ++++ src/southbridge/intel/bd82x6x/Makefile.inc | 6 +- src/southbridge/intel/bd82x6x/early_me_native.c | 272 ++ src/southbridge/intel/bd82x6x/early_pch_native.c | 375 ++ src/southbridge/intel/bd82x6x/early_thermal.c | 70 + src/southbridge/intel/bd82x6x/pch.h | 2 + src/southbridge/intel/bd82x6x/smi.c | 3 - src/southbridge/intel/bd82x6x/usb_ehci.c | 33 + 21 files changed, 5411 insertions(+), 109 deletions(-) create mode 100644 src/northbridge/intel/sandybridge/raminit_native.c create mode 100644 src/northbridge/intel/sandybridge/raminit_native.h create mode 100644 src/northbridge/intel/sandybridge/raminit_patterns.h create mode 100644 src/southbridge/intel/bd82x6x/early_me_native.c create mode 100644 src/southbridge/intel/bd82x6x/early_pch_native.c create mode 100644 src/southbridge/intel/bd82x6x/early_thermal.c diff --git a/src/cpu/intel/Makefile.inc b/src/cpu/intel/Makefile.inc index 0392f69cda..68ea05de9a 100644 --- a/src/cpu/intel/Makefile.inc +++ b/src/cpu/intel/Makefile.inc @@ -19,6 +19,7 @@ subdirs-$(CONFIG_CPU_INTEL_SOCKET_RPGA989) += socket_rPGA989 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += model_2065x subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += model_206ax +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_model_206ax diff --git a/src/cpu/x86/smm/smmhandler_tseg.S b/src/cpu/x86/smm/smmhandler_tseg.S index b33fcdf86f..380935acda 100644 --- a/src/cpu/x86/smm/smmhandler_tseg.S +++ b/src/cpu/x86/smm/smmhandler_tseg.S @@ -57,7 +57,7 @@ #define SMI_UNLOCKED 1 #define __PRE_RAM__ -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM diff --git a/src/cpu/x86/smm/smmrelocate.S b/src/cpu/x86/smm/smmrelocate.S index bdc977190b..bc90fab22a 100644 --- a/src/cpu/x86/smm/smmrelocate.S +++ b/src/cpu/x86/smm/smmrelocate.S @@ -48,7 +48,7 @@ #if CONFIG_SMM_TSEG -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM @@ -195,7 +195,7 @@ smm_relocate: xorl %edx, %edx wrmsr -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE /* * IED base is top 4M of TSEG */ diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c index 9b4f490630..a43caed5ab 100644 --- a/src/device/dram/ddr3.c +++ b/src/device/dram/ddr3.c @@ -110,7 +110,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) { int ret; u16 crc, spd_crc; - u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width, sdram_width; + u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width; u8 reg8; u32 mtb; /* medium time base */ unsigned int val, param; @@ -209,8 +209,8 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) printram(" Invalid SDRAM width\n"); ret = SPD_STATUS_INVALID_FIELD; } - sdram_width = (4 << val); - printram(" SDRAM width : %u\n", sdram_width); + dimm->width = (4 << val); + printram(" SDRAM width : %u\n", dimm->width); /* Memory bus width */ reg8 = spd[8]; @@ -236,7 +236,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) * capacity_shift * The rest is the JEDEC formula */ dimm->size_mb = ((1 << (capacity_shift + (25 - 20))) * bus_width - * dimm->ranks) / sdram_width; + * dimm->ranks) / dimm->width; /* Fine Timebase (FTB) Dividend/Divisor */ /* Dividend */ @@ -334,6 +334,9 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) printram(" DIMM Rank1 Address bits mirrored!!!\n"); } + dimm->reference_card = spd[62] & 0x1f; + printram(" DIMM Reference card %c\n", 'A' + dimm->reference_card); + return ret; } diff --git a/src/include/device/dram/ddr3.h b/src/include/device/dram/ddr3.h index b19c51c15b..ef0d0ec606 100644 --- a/src/include/device/dram/ddr3.h +++ b/src/include/device/dram/ddr3.h @@ -37,6 +37,7 @@ * @{ */ #define TCK_1066MHZ 240 +#define TCK_933MHZ 275 #define TCK_800MHZ 320 #define TCK_666MHZ 384 #define TCK_533MHZ 480 @@ -137,6 +138,8 @@ typedef struct dimm_attr_st { u16 cas_supported; /* Flags extracted from SPD */ dimm_flags_t flags; + /* SDRAM width */ + u8 width; /* Number of ranks */ u8 ranks; /* Number or row address bits */ @@ -158,6 +161,8 @@ typedef struct dimm_attr_st { u32 tWTR; u32 tRTP; u32 tFAW; + + u8 reference_card; } dimm_attr; /** Result of the SPD decoding process */ diff --git a/src/mainboard/lenovo/x230/Kconfig b/src/mainboard/lenovo/x230/Kconfig index eb9cadc3f9..cc6228d054 100644 --- a/src/mainboard/lenovo/x230/Kconfig +++ b/src/mainboard/lenovo/x230/Kconfig @@ -3,7 +3,7 @@ if BOARD_LENOVO_X230 config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select CPU_INTEL_SOCKET_RPGA989 - select NORTHBRIDGE_INTEL_IVYBRIDGE + select NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE select SOUTHBRIDGE_INTEL_C216 select EC_LENOVO_PMH7 select EC_LENOVO_H8 @@ -18,8 +18,8 @@ config BOARD_SPECIFIC_OPTIONS # dummy select SERIRQ_CONTINUOUS_MODE config HAVE_MRC_CACHE - bool - default n + bool + default y config HAVE_IFD_BIN bool @@ -41,10 +41,6 @@ config MMCONF_BASE_ADDRESS hex default 0xf0000000 -config CACHE_ROM_SIZE_OVERRIDE - hex - default 0x800000 - config IRQ_SLOT_COUNT int default 18 @@ -57,7 +53,7 @@ config USBDEBUG_HCD_INDEX int default 2 -config DRAM_GATE_GPIO +config DRAM_RESET_GATE_GPIO int default 10 diff --git a/src/mainboard/lenovo/x230/romstage.c b/src/mainboard/lenovo/x230/romstage.c index 8441db33d7..8c82e61539 100644 --- a/src/mainboard/lenovo/x230/romstage.c +++ b/src/mainboard/lenovo/x230/romstage.c @@ -33,7 +33,7 @@ #include #include #include "northbridge/intel/sandybridge/sandybridge.h" -#include "northbridge/intel/sandybridge/raminit.h" +#include "northbridge/intel/sandybridge/raminit_native.h" #include "southbridge/intel/bd82x6x/pch.h" #include "southbridge/intel/bd82x6x/gpio.h" #include @@ -109,66 +109,58 @@ static void rcba_config(void) RCBA32(BUC) = 0; } +static void +init_usb (void) +{ + const u32 rcba_dump[64] = { + /* 3500 */ 0x20000153, 0x20000153, 0x20000f57, 0x20000f57, + /* 3510 */ 0x20000f57, 0x20000f57, 0x20000153, 0x2000055b, + /* 3520 */ 0x20000153, 0x2000055b, 0x20000f57, 0x20000f57, + /* 3530 */ 0x20000f57, 0x20000f57, 0x00000000, 0x00000000, + /* 3540 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 3550 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 3560 */ 0x024c8001, 0x000024a3, 0x00040002, 0x01000050, + /* 3570 */ 0x02000772, 0x16000f9f, 0x1800ff4f, 0x0001d630, + /* 3580 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 3590 */ 0x00000000, 0x00000000, 0x00000000, 0x00000040, + /* 35a0 */ 0x04000201, 0x00000200, 0x00000000, 0x00000000, + /* 35b0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 35c0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 35d0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 35e0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 35f0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }; + int i; + /* Activate PMBAR. */ + pci_write_config32(PCI_DEV(0, 0x1f, 0), PMBASE, DEFAULT_PMBASE | 1); + pci_write_config32(PCI_DEV(0, 0x1f, 0), PMBASE + 4, 0); + pci_write_config8(PCI_DEV(0, 0x1f, 0), 0x44 /* ACPI_CNTL */ , 0x80); /* Enable ACPI BAR */ + + /* Unlock registers. */ + outw (inw (DEFAULT_PMBASE | 0x003c) | 2, DEFAULT_PMBASE | 0x003c); + + for (i = 0; i < 64; i++) + write32 (DEFAULT_RCBABASE | (0x3500 + 4 * i), rcba_dump[i]); + + pcie_write_config32 (PCI_DEV (0, 0x14, 0), 0xe4, 0x00000000); + + /* Relock registers. */ + outw (0x0000, DEFAULT_PMBASE | 0x003c); +} + + void main(unsigned long bist) { - int boot_mode = 0; - int cbmem_was_initted; + int s3resume = 0; u32 pm1_cnt; u16 pm1_sts; + spd_raw_data spd[4]; if (MCHBAR16(SSKPD) == 0xCAFE) { outb(0x6, 0xcf9); hlt (); } - struct pei_data pei_data = { - .pei_version = PEI_VERSION, - .mchbar = DEFAULT_MCHBAR, - .dmibar = DEFAULT_DMIBAR, - .epbar = DEFAULT_EPBAR, - .pciexbar = CONFIG_MMCONF_BASE_ADDRESS, - .smbusbar = SMBUS_IO_BASE, - .wdbbar = 0x4000000, - .wdbsize = 0x1000, - .hpet_address = CONFIG_HPET_ADDRESS, - .rcba = DEFAULT_RCBABASE, - .pmbase = DEFAULT_PMBASE, - .gpiobase = DEFAULT_GPIOBASE, - .thermalbase = 0xfed08000, - .system_type = 0, // 0 Mobile, 1 Desktop/Server - .tseg_size = CONFIG_SMM_TSEG_SIZE, - .spd_addresses = { 0xA0, 0x00,0xA2,0x00 }, - .ts_addresses = { 0x00, 0x00, 0x00, 0x00 }, - .ec_present = 1, - .gbe_enable = 1, - .ddr3lv_support = 0, - // 0 = leave channel enabled - // 1 = disable dimm 0 on channel - // 2 = disable dimm 1 on channel - // 3 = disable dimm 0+1 on channel - .dimm_channel0_disabled = 2, - .dimm_channel1_disabled = 2, - .max_ddr3_freq = 1600, - .usb_port_config = { - /* enabled usb oc pin length */ - { 1, 0, 0x0080 }, /* P0 (left, fan side), OC 0 */ - { 1, 1, 0x0080 }, /* P1 (left touchpad side), OC 1 */ - { 1, 3, 0x0080 }, /* P2: dock, OC 3 */ - { 1, 0, 0x0040 }, /* P3: wwan, no OC */ - { 1, 0, 0x0080 }, /* P4: Wacom tablet on X230t, otherwise empty */ - { 1, 0, 0x0080 }, /* P5: Expresscard, no OC */ - { 0, 0, 0x0000 }, /* P6: Empty */ - { 1, 0, 0x0080 }, /* P7: dock, no OC */ - { 0, 0, 0x0000 }, /* P8: Empty */ - { 1, 5, 0x0080 }, /* P9: Right (EHCI debug), OC 5 */ - { 1, 0, 0x0040 }, /* P10: fingerprint reader, no OC */ - { 1, 0, 0x0040 }, /* P11: bluetooth, no OC. */ - { 1, 0, 0x0040 }, /* P12: wlan, no OC */ - { 1, 0, 0x0080 }, /* P13: webcam, no OC */ - }, - .ddr_refresh_rate_config = 2, /* Force double refresh rate */ - }; - timestamp_init(get_initial_timestamp()); timestamp_add_now(TS_START_ROMSTAGE); @@ -183,6 +175,8 @@ void main(unsigned long bist) setup_pch_gpios(&x230_gpio_map); + init_usb(); + /* Initialize console device(s) */ console_init(); @@ -204,7 +198,7 @@ void main(unsigned long bist) if ((pm1_sts & WAK_STS) && ((pm1_cnt >> 10) & 7) == 5) { if (acpi_s3_resume_allowed()) { printk(BIOS_DEBUG, "Resume from S3 detected.\n"); - boot_mode = 2; + s3resume = 1; /* Clear SLP_TYPE. This will break stage2 but * we care for that when we get there. */ @@ -218,31 +212,16 @@ void main(unsigned long bist) /* Enable SPD ROMs and DDR-III DRAM */ enable_smbus(); - /* Prepare USB controller early in S3 resume */ - if (boot_mode == 2) - enable_usb_bar(); - post_code(0x39); post_code(0x3a); - pei_data.boot_mode = boot_mode; timestamp_add_now(TS_BEFORE_INITRAM); - /* MRC.bin has a bug and sometimes halts (instead of reboot?). - */ - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) & ~(1 << 5); /* reset */ - outw((0 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* let timer go */ - } - - sdram_initialize(&pei_data); + memset (spd, 0, sizeof (spd)); + read_spd (&spd[0], 0x50); + read_spd (&spd[2], 0x51); - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) | (1 << 5); /* No reset */ - outw((1 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */ - } + init_dram_ddr3 (spd, 1, TCK_800MHZ, s3resume); timestamp_add_now(TS_AFTER_INITRAM); post_code(0x3c); @@ -250,13 +229,7 @@ void main(unsigned long bist) rcba_config(); post_code(0x3d); - quick_ram_check(); - post_code(0x3e); - MCHBAR16(SSKPD) = 0xCAFE; - cbmem_was_initted = !cbmem_recovery(boot_mode==2); - if (boot_mode!=2) - save_mrc_data(&pei_data); #if CONFIG_HAVE_ACPI_RESUME /* If there is no high memory area, we didn't boot before, so @@ -266,18 +239,14 @@ void main(unsigned long bist) *(u32 *)CBMEM_BOOT_MODE = 0; *(u32 *)CBMEM_RESUME_BACKUP = 0; - if ((boot_mode == 2) && cbmem_was_initted) { + if (s3resume) { void *resume_backup_memory = cbmem_find(CBMEM_ID_RESUME); if (resume_backup_memory) { - *(u32 *)CBMEM_BOOT_MODE = boot_mode; + *(u32 *)CBMEM_BOOT_MODE = 2; *(u32 *)CBMEM_RESUME_BACKUP = (u32)resume_backup_memory; } /* Magic for S3 resume */ pci_write_config32(PCI_DEV(0, 0x00, 0), SKPAD, 0xcafed00d); - } else if (boot_mode == 2) { - /* Failed S3 resume, reset to come up cleanly */ - outb(0x6, 0xcf9); - hlt(); } else { pci_write_config32(PCI_DEV(0, 0x00, 0), SKPAD, 0xcafebabe); } diff --git a/src/northbridge/intel/Makefile.inc b/src/northbridge/intel/Makefile.inc index 808a1b21aa..f1c25405c8 100644 --- a/src/northbridge/intel/Makefile.inc +++ b/src/northbridge/intel/Makefile.inc @@ -15,6 +15,7 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I5000) += i5000 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += nehalem subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += sandybridge +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_sandybridge diff --git a/src/northbridge/intel/sandybridge/Kconfig b/src/northbridge/intel/sandybridge/Kconfig index f13b58d910..2ceb945a10 100644 --- a/src/northbridge/intel/sandybridge/Kconfig +++ b/src/northbridge/intel/sandybridge/Kconfig @@ -33,7 +33,15 @@ config NORTHBRIDGE_INTEL_IVYBRIDGE select DYNAMIC_CBMEM select CPU_INTEL_MODEL_306AX -if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE +config NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + bool + select CACHE_MRC_BIN + select MMCONF_SUPPORT + select MMCONF_SUPPORT_DEFAULT + select CPU_INTEL_MODEL_306AX + select HAVE_DEBUG_RAM_SETUP + +if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config VGA_BIOS_ID string @@ -50,7 +58,8 @@ config MRC_CACHE_SIZE config DCACHE_RAM_BASE hex - default 0xff7e0000 + default 0xff7e0000 if !NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + default 0xfefe0000 if NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config DCACHE_RAM_SIZE hex diff --git a/src/northbridge/intel/sandybridge/Makefile.inc b/src/northbridge/intel/sandybridge/Makefile.inc index a201745c92..536656bf44 100644 --- a/src/northbridge/intel/sandybridge/Makefile.inc +++ b/src/northbridge/intel/sandybridge/Makefile.inc @@ -25,7 +25,10 @@ ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c ramstage-y += mrccache.c romstage-y += ram_calc.c -romstage-y += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += raminit_native.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += ../../../device/dram/ddr3.c romstage-y += mrccache.c romstage-y += early_init.c romstage-y += report_platform.c @@ -48,7 +51,10 @@ $(obj)/mrc.cache: $(obj)/config.h cbfs-files-$(CONFIG_HAVE_MRC_CACHE) += mrc.cache mrc.cache-file := $(obj)/mrc.cache -mrc.cache-position := 0xfffd0000 +mrc-cache-position-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) := 0xfffd0000 +mrc-cache-position-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) := 0xfffd0000 +mrc-cache-position-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) := 0xfffe0000 +mrc.cache-position := $(mrc-cache-position-y) mrc.cache-type := 0xac endif diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index d271a1afcb..a82a284062 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -650,9 +650,7 @@ static void gma_func0_init(struct device *dev) physbase = pci_read_config32(dev, 0x5c) & ~0xf; graphics_base = dev->resource_list[1].base; - int lightup_ok = i915lightup(conf, physbase, iobase, mmiobase, graphics_base); - if (lightup_ok) - gfx_set_init_done(1); + i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif } diff --git a/src/northbridge/intel/sandybridge/raminit_native.c b/src/northbridge/intel/sandybridge/raminit_native.c new file mode 100644 index 0000000000..b1e29a2261 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.c @@ -0,0 +1,3893 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Damien Zammit + * Copyright (C) 2014 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "raminit_native.h" +#include "sandybridge.h" +#include +#include + +/* Management Engine is in the southbridge */ +#include "southbridge/intel/bd82x6x/me.h" +/* For SPD. */ +#include "southbridge/intel/bd82x6x/smbus.h" +#include "arch/cpu.h" +#include "cpu/x86/msr.h" + +/* FIXME: no ECC support. */ +/* FIXME: no support for 3-channel chipsets. */ +/* FIXME: no S3. */ + +#define BASEFREQ 133 +#define tDLLK 512 + +#define IS_SANDY_CPU(x) ((x & 0xffff0) == 0x206a0) +#define IS_SANDY_CPU_C(x) ((x & 0xf) == 4) +#define IS_SANDY_CPU_D0(x) ((x & 0xf) == 5) +#define IS_SANDY_CPU_D1(x) ((x & 0xf) == 6) +#define IS_SANDY_CPU_D2(x) ((x & 0xf) == 7) + +#define IS_IVY_CPU(x) ((x & 0xffff0) == 0x306a0) +#define IS_IVY_CPU_C(x) ((x & 0xf) == 4) +#define IS_IVY_CPU_K(x) ((x & 0xf) == 5) +#define IS_IVY_CPU_D(x) ((x & 0xf) == 6) +#define IS_IVY_CPU_E(x) ((x & 0xf) >= 8) + +#define NUM_CHANNELS 2 +#define NUM_SLOTRANKS 4 +#define NUM_SLOTS 2 +#define NUM_LANES 8 + +/* FIXME: Vendor BIOS uses 64 but our algorithms are less + performant and even 1 seems to be enough in practice. */ +#define NUM_PATTERNS 4 + +typedef struct odtmap_st { + u16 rttwr; + u16 rttnom; +} odtmap; + +typedef struct dimm_info_st { + dimm_attr dimm[NUM_CHANNELS][NUM_SLOTS]; +} dimm_info; + +struct ram_rank_timings { + /* Register 4024. One byte per slotrank. */ + u8 val_4024; + /* Register 4028. One nibble per slotrank. */ + u8 val_4028; + + int val_320c; + + struct ram_lane_timings { + /* lane register offset 0x10. */ + u16 timA; /* bits 0 - 5, bits 16 - 18 */ + u8 rising; /* bits 8 - 14 */ + u8 falling; /* bits 20 - 26. */ + + /* lane register offset 0x20. */ + int timC; /* bit 0 - 5, 19. */ + u16 timB; /* bits 8 - 13, 15 - 17. */ + } lanes[NUM_LANES]; +}; + +struct ramctr_timing_st; + +typedef struct ramctr_timing_st { + int mobile; + + u16 cas_supported; + /* tLatencies are in units of ns, scaled by x256 */ + u32 tCK; + u32 tAA; + u32 tWR; + u32 tRCD; + u32 tRRD; + u32 tRP; + u32 tRAS; + u32 tRFC; + u32 tWTR; + u32 tRTP; + u32 tFAW; + /* Latencies in terms of clock cycles + * They are saved separately as they are needed for DRAM MRS commands*/ + u8 CAS; /* CAS read latency */ + u8 CWL; /* CAS write latency */ + + u32 tREFI; + u32 tMOD; + u32 tXSOffset; + u32 tWLO; + u32 tCKE; + u32 tXPDLL; + u32 tXP; + u32 tAONPD; + + u16 reg_5064b0; /* bits 0-11. */ + + u8 rankmap[NUM_CHANNELS]; + int ref_card_offset[NUM_CHANNELS]; + u32 mad_dimm[NUM_CHANNELS]; + int channel_size_mb[NUM_CHANNELS]; + u32 reg_4004_b30[NUM_CHANNELS]; + + int reg_c14_offset; + int reg_320c_range_threshold; + + int edge_offset[3]; + int timC_offset[3]; + + int extended_temperature_range; + int auto_self_refresh; + + int rank_mirror[NUM_CHANNELS][NUM_SLOTRANKS]; + + struct ram_rank_timings timings[NUM_CHANNELS][NUM_SLOTRANKS]; +} ramctr_timing; + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) +#define NORTHBRIDGE PCI_DEV(0, 0x0, 0) +#define FOR_ALL_LANES for (lane = 0; lane < NUM_LANES; lane++) +#define FOR_ALL_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) +#define FOR_ALL_POPULATED_RANKS for (slotrank = 0; slotrank < NUM_SLOTRANKS; slotrank++) if (ctrl->rankmap[channel] & (1 << slotrank)) +#define FOR_ALL_POPULATED_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) if (ctrl->rankmap[channel]) +#define MAX_EDGE_TIMING 71 +#define MAX_TIMC 127 +#define MAX_TIMB 511 +#define MAX_TIMA 127 + +static void program_timings(ramctr_timing * ctrl, int channel); + +static const char *ecc_decoder[] = { + "inactive", + "active on IO", + "disabled on IO", + "active" +}; + +static void wait_txt_clear(void) +{ + struct cpuid_result cp; + + cp = cpuid_ext(0x1, 0x0); + /* Check if TXT is supported? */ + if (!(cp.ecx & 0x40)) + return; + /* Some TXT public bit. */ + if (!(read32(0xfed30010) & 1)) + return; + /* Wait for TXT clear. */ + while (!(read8(0xfed40000) & (1 << 7))) ; +} + +static void sfence(void) +{ + asm volatile ("sfence"); +} + +/* + * Dump in the log memory controller configuration as read from the memory + * controller registers. + */ +static void report_memory_config(void) +{ + u32 addr_decoder_common, addr_decode_ch[NUM_CHANNELS]; + int i; + + addr_decoder_common = MCHBAR32(0x5000); + addr_decode_ch[0] = MCHBAR32(0x5004); + addr_decode_ch[1] = MCHBAR32(0x5008); + + printk(BIOS_DEBUG, "memcfg DDR3 clock %d MHz\n", + (MCHBAR32(0x5e04) * 13333 * 2 + 50) / 100); + printk(BIOS_DEBUG, "memcfg channel assignment: A: %d, B % d, C % d\n", + addr_decoder_common & 3, (addr_decoder_common >> 2) & 3, + (addr_decoder_common >> 4) & 3); + + for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) { + u32 ch_conf = addr_decode_ch[i]; + printk(BIOS_DEBUG, "memcfg channel[%d] config (%8.8x):\n", i, + ch_conf); + printk(BIOS_DEBUG, " ECC %s\n", + ecc_decoder[(ch_conf >> 24) & 3]); + printk(BIOS_DEBUG, " enhanced interleave mode %s\n", + ((ch_conf >> 22) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " rank interleave %s\n", + ((ch_conf >> 21) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " DIMMA %d MB width x%d %s rank%s\n", + ((ch_conf >> 0) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? 16 : 8, + ((ch_conf >> 17) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? "" : ", selected"); + printk(BIOS_DEBUG, " DIMMB %d MB width x%d %s rank%s\n", + ((ch_conf >> 8) & 0xff) * 256, + ((ch_conf >> 20) & 1) ? 16 : 8, + ((ch_conf >> 18) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? ", selected" : ""); + } +} + +static void post_system_agent_init(void) +{ + /* If PCIe init is skipped, set the PEG clock gating */ + MCHBAR32(0x7010) = MCHBAR32(0x7010) | 0x01; +} + +void read_spd(spd_raw_data * spd, u8 addr) +{ + int j; + for (j = 0; j < 256; j++) + (*spd)[j] = do_smbus_read_byte(SMBUS_IO_BASE, addr, j); +} + +static void dram_find_spds_ddr3(spd_raw_data * spd, dimm_info * dimm, + ramctr_timing * ctrl) +{ + int dimms = 0; + int channel, slot, spd_slot; + + memset (ctrl->rankmap, 0, sizeof (ctrl->rankmap)); + + ctrl->extended_temperature_range = 1; + ctrl->auto_self_refresh = 1; + + FOR_ALL_CHANNELS { + ctrl->channel_size_mb[channel] = 0; + + for (slot = 0; slot < NUM_SLOTS; slot++) { + spd_slot = 2 * channel + slot; + spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + if (dimm->dimm[channel][slot].dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { + // set dimm invalid + dimm->dimm[channel][slot].ranks = 0; + dimm->dimm[channel][slot].size_mb = 0; + continue; + } + + dram_print_spd_ddr3(&dimm->dimm[channel][slot]); + dimms++; + ctrl->rank_mirror[channel][slot * 2] = 0; + ctrl->rank_mirror[channel][slot * 2 + 1] = dimm->dimm[channel][slot].flags.pins_mirrored; + ctrl->channel_size_mb[channel] += dimm->dimm[channel][slot].size_mb; + + ctrl->auto_self_refresh &= dimm->dimm[channel][slot].flags.asr; + ctrl->extended_temperature_range &= dimm->dimm[channel][slot].flags.ext_temp_refresh; + + ctrl->rankmap[channel] = ((1 << dimm->dimm[channel][slot].ranks) - 1) << (2 * slot); + printk(BIOS_DEBUG, "rankmap[%d] = 0x%x\n", channel, ctrl->rankmap[channel]); + } + if ((ctrl->rankmap[channel] & 3) && (ctrl->rankmap[channel] & 0xc) + && dimm->dimm[channel][0].reference_card <= 5 && dimm->dimm[channel][1].reference_card <= 5) { + const int ref_card_offset_table[6][6] = { + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 1, 1, }, + { 2, 2, 2, 1, 0, 0, }, + { 2, 2, 2, 1, 0, 0, }, + }; + ctrl->ref_card_offset[channel] = ref_card_offset_table[dimm->dimm[channel][0].reference_card] + [dimm->dimm[channel][1].reference_card]; + } else + ctrl->ref_card_offset[channel] = 0; + } + + if (!dimms) + die("No DIMMs were found"); +} + +static void dram_find_common_params(const dimm_info * dimms, + ramctr_timing * ctrl) +{ + size_t valid_dimms; + int channel, slot; + ctrl->cas_supported = 0xff; + valid_dimms = 0; + FOR_ALL_CHANNELS for (slot = 0; slot < 2; slot++) { + const dimm_attr *dimm = &dimms->dimm[channel][slot]; + if (dimm->dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) + continue; + valid_dimms++; + + /* Find all possible CAS combinations */ + ctrl->cas_supported &= dimm->cas_supported; + + /* Find the smallest common latencies supported by all DIMMs */ + ctrl->tCK = MAX(ctrl->tCK, dimm->tCK); + ctrl->tAA = MAX(ctrl->tAA, dimm->tAA); + ctrl->tWR = MAX(ctrl->tWR, dimm->tWR); + ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD); + ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD); + ctrl->tRP = MAX(ctrl->tRP, dimm->tRP); + ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS); + ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC); + ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR); + ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP); + ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW); + } + + if (!ctrl->cas_supported) + die("Unsupported DIMM combination. " + "DIMMS do not support common CAS latency"); + if (!valid_dimms) + die("No valid DIMMs found"); +} + +static u8 get_CWL(u8 CAS) +{ + /* Get CWL based on CAS using the following rule: + * _________________________________________ + * CAS: | 4T | 5T | 6T | 7T | 8T | 9T | 10T | 11T | + * CWL: | 5T | 5T | 5T | 6T | 6T | 7T | 7T | 8T | + */ + static const u8 cas_cwl_map[] = { 5, 5, 5, 6, 6, 7, 7, 8 }; + if (CAS > 11) + return 8; + return cas_cwl_map[CAS - 4]; +} + +/* Frequency multiplier. */ +static u32 get_FRQ(u32 tCK) +{ + u32 FRQ; + FRQ = 256000 / (tCK * BASEFREQ); + if (FRQ > 8) + return 8; + if (FRQ < 3) + return 3; + return FRQ; +} + +static u32 get_REFI(u32 tCK) +{ + /* Get REFI based on MCU frequency using the following rule: + * _________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * REFI: | 3120 | 4160 | 5200 | 6240 | 7280 | 8320 | + */ + static const u32 frq_refi_map[] = + { 3120, 4160, 5200, 6240, 7280, 8320 }; + return frq_refi_map[get_FRQ(tCK) - 3]; +} + +static u8 get_XSOffset(u32 tCK) +{ + /* Get XSOffset based on MCU frequency using the following rule: + * _________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XSOffset : | 4 | 6 | 7 | 8 | 10 | 11 | + */ + static const u8 frq_xs_map[] = { 4, 6, 7, 8, 10, 11 }; + return frq_xs_map[get_FRQ(tCK) - 3]; +} + +static u8 get_MOD(u32 tCK) +{ + /* Get MOD based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * MOD : | 12 | 12 | 12 | 12 | 15 | 16 | + */ + static const u8 frq_mod_map[] = { 12, 12, 12, 12, 15, 16 }; + return frq_mod_map[get_FRQ(tCK) - 3]; +} + +static u8 get_WLO(u32 tCK) +{ + /* Get WLO based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * WLO : | 4 | 5 | 6 | 6 | 8 | 8 | + */ + static const u8 frq_wlo_map[] = { 4, 5, 6, 6, 8, 8 }; + return frq_wlo_map[get_FRQ(tCK) - 3]; +} + +static u8 get_CKE(u32 tCK) +{ + /* Get CKE based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * CKE : | 3 | 3 | 4 | 4 | 5 | 6 | + */ + static const u8 frq_cke_map[] = { 3, 3, 4, 4, 5, 6 }; + return frq_cke_map[get_FRQ(tCK) - 3]; +} + +static u8 get_XPDLL(u32 tCK) +{ + /* Get XPDLL based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XPDLL : | 10 | 13 | 16 | 20 | 23 | 26 | + */ + static const u8 frq_xpdll_map[] = { 10, 13, 16, 20, 23, 26 }; + return frq_xpdll_map[get_FRQ(tCK) - 3]; +} + +static u8 get_XP(u32 tCK) +{ + /* Get XP based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XP : | 3 | 4 | 4 | 5 | 6 | 7 | + */ + static const u8 frq_xp_map[] = { 3, 4, 4, 5, 6, 7 }; + return frq_xp_map[get_FRQ(tCK) - 3]; +} + +static u8 get_AONPD(u32 tCK) +{ + /* Get AONPD based on MCU frequency using the following rule: + * ________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * AONPD : | 4 | 5 | 6 | 8 | 8 | 10 | + */ + static const u8 frq_aonpd_map[] = { 4, 5, 6, 8, 8, 10 }; + return frq_aonpd_map[get_FRQ(tCK) - 3]; +} + +static u32 get_COMP2(u32 tCK) +{ + /* Get COMP2 based on MCU frequency using the following rule: + * ___________________________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * COMP : | D6BEDCC | CE7C34C | CA57A4C | C6369CC | C42514C | C21410C | + */ + static const u32 frq_comp2_map[] = { 0xD6BEDCC, 0xCE7C34C, 0xCA57A4C, + 0xC6369CC, 0xC42514C, 0xC21410C + }; + return frq_comp2_map[get_FRQ(tCK) - 3]; +} + +static void dram_timing(ramctr_timing * ctrl) +{ + u8 val; + u32 val32; + + /* Maximum supported DDR3 frequency is 1066MHz (DDR3 2133) so make sure + * we cap it if we have faster DIMMs. + * Then, align it to the closest JEDEC standard frequency */ + if (ctrl->tCK <= TCK_1066MHZ) { + ctrl->tCK = TCK_1066MHZ; + ctrl->edge_offset[0] = 16; + ctrl->edge_offset[1] = 7; + ctrl->edge_offset[2] = 7; + ctrl->timC_offset[0] = 18; + ctrl->timC_offset[1] = 7; + ctrl->timC_offset[2] = 7; + ctrl->reg_c14_offset = 16; + ctrl->reg_5064b0 = 0x218; + ctrl->reg_320c_range_threshold = 13; + } else if (ctrl->tCK <= TCK_933MHZ) { + ctrl->tCK = TCK_933MHZ; + ctrl->edge_offset[0] = 14; + ctrl->edge_offset[1] = 6; + ctrl->edge_offset[2] = 6; + ctrl->timC_offset[0] = 15; + ctrl->timC_offset[1] = 6; + ctrl->timC_offset[2] = 6; + ctrl->reg_c14_offset = 14; + ctrl->reg_5064b0 = 0x1d5; + ctrl->reg_320c_range_threshold = 15; + } else if (ctrl->tCK <= TCK_800MHZ) { + ctrl->tCK = TCK_800MHZ; + ctrl->edge_offset[0] = 13; + ctrl->edge_offset[1] = 5; + ctrl->edge_offset[2] = 5; + ctrl->timC_offset[0] = 14; + ctrl->timC_offset[1] = 5; + ctrl->timC_offset[2] = 5; + ctrl->reg_c14_offset = 12; + ctrl->reg_5064b0 = 0x193; + ctrl->reg_320c_range_threshold = 15; + } else if (ctrl->tCK <= TCK_666MHZ) { + ctrl->tCK = TCK_666MHZ; + ctrl->edge_offset[0] = 10; + ctrl->edge_offset[1] = 4; + ctrl->edge_offset[2] = 4; + ctrl->timC_offset[0] = 11; + ctrl->timC_offset[1] = 4; + ctrl->timC_offset[2] = 4; + ctrl->reg_c14_offset = 10; + ctrl->reg_5064b0 = 0x150; + ctrl->reg_320c_range_threshold = 16; + } else if (ctrl->tCK <= TCK_533MHZ) { + ctrl->tCK = TCK_533MHZ; + ctrl->edge_offset[0] = 8; + ctrl->edge_offset[1] = 3; + ctrl->edge_offset[2] = 3; + ctrl->timC_offset[0] = 9; + ctrl->timC_offset[1] = 3; + ctrl->timC_offset[2] = 3; + ctrl->reg_c14_offset = 8; + ctrl->reg_5064b0 = 0x10d; + ctrl->reg_320c_range_threshold = 17; + } else { + ctrl->tCK = TCK_400MHZ; + ctrl->edge_offset[0] = 6; + ctrl->edge_offset[1] = 2; + ctrl->edge_offset[2] = 2; + ctrl->timC_offset[0] = 6; + ctrl->timC_offset[1] = 2; + ctrl->timC_offset[2] = 2; + ctrl->reg_c14_offset = 8; + ctrl->reg_5064b0 = 0xcd; + ctrl->reg_320c_range_threshold = 17; + } + + val32 = (1000 << 8) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected DRAM frequency: %u MHz\n", val32); + + /* Find CAS and CWL latencies */ + val = (ctrl->tAA + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Minimum CAS latency : %uT\n", val); + /* Find lowest supported CAS latency that satisfies the minimum value */ + while (!((ctrl->cas_supported >> (val - 4)) & 1) + && (ctrl->cas_supported >> (val - 4))) { + val++; + } + /* Is CAS supported */ + if (!(ctrl->cas_supported & (1 << (val - 4)))) + printk(BIOS_DEBUG, "CAS not supported\n"); + printk(BIOS_DEBUG, "Selected CAS latency : %uT\n", val); + ctrl->CAS = val; + ctrl->CWL = get_CWL(ctrl->CAS); + printk(BIOS_DEBUG, "Selected CWL latency : %uT\n", ctrl->CWL); + + /* Find tRCD */ + ctrl->tRCD = (ctrl->tRCD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRCD : %uT\n", ctrl->tRCD); + + ctrl->tRP = (ctrl->tRP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRP : %uT\n", ctrl->tRP); + + /* Find tRAS */ + ctrl->tRAS = (ctrl->tRAS + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRAS : %uT\n", ctrl->tRAS); + + /* Find tWR */ + ctrl->tWR = (ctrl->tWR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWR : %uT\n", ctrl->tWR); + + /* Find tFAW */ + ctrl->tFAW = (ctrl->tFAW + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tFAW : %uT\n", ctrl->tFAW); + + /* Find tRRD */ + ctrl->tRRD = (ctrl->tRRD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRRD : %uT\n", ctrl->tRRD); + + /* Find tRTP */ + ctrl->tRTP = (ctrl->tRTP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRTP : %uT\n", ctrl->tRTP); + + /* Find tWTR */ + ctrl->tWTR = (ctrl->tWTR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWTR : %uT\n", ctrl->tWTR); + + /* Refresh-to-Active or Refresh-to-Refresh (tRFC) */ + ctrl->tRFC = (ctrl->tRFC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRFC : %uT\n", ctrl->tRFC); + + ctrl->tREFI = get_REFI(ctrl->tCK); + ctrl->tMOD = get_MOD(ctrl->tCK); + ctrl->tXSOffset = get_XSOffset(ctrl->tCK); + ctrl->tWLO = get_WLO(ctrl->tCK); + ctrl->tCKE = get_CKE(ctrl->tCK); + ctrl->tXPDLL = get_XPDLL(ctrl->tCK); + ctrl->tXP = get_XP(ctrl->tCK); + ctrl->tAONPD = get_AONPD(ctrl->tCK); +} + +static void dram_freq(ramctr_timing * ctrl) +{ + u8 val2; + u32 reg1 = 0; + + /* Step 1 - Set target PCU frequency */ + + if (ctrl->tCK <= TCK_1066MHZ) { + ctrl->tCK = TCK_1066MHZ; + } else if (ctrl->tCK <= TCK_933MHZ) { + ctrl->tCK = TCK_933MHZ; + } else if (ctrl->tCK <= TCK_800MHZ) { + ctrl->tCK = TCK_800MHZ; + } else if (ctrl->tCK <= TCK_666MHZ) { + ctrl->tCK = TCK_666MHZ; + } else if (ctrl->tCK <= TCK_533MHZ) { + ctrl->tCK = TCK_533MHZ; + } else { + ctrl->tCK = TCK_400MHZ; + } + + /* Frequency mulitplier. */ + u32 FRQ = get_FRQ(ctrl->tCK); + + /* Step 2 - Select frequency in the MCU */ + reg1 = FRQ; + reg1 |= 0x80000000; // set running bit + MCHBAR32(0x5e00) = reg1; + while (reg1 & 0x80000000) { + printk(BIOS_DEBUG, " PLL busy..."); + reg1 = MCHBAR32(0x5e00); + } + printk(BIOS_DEBUG, "done\n"); + + /* Step 3 - Verify lock frequency */ + reg1 = MCHBAR32(0x5e04); + val2 = (u8) reg1; + if (val2 < FRQ) { + printk(BIOS_DEBUG, "Lock frequency is lower, recalculating\n"); + ctrl->tCK = 256000 / (val2 * BASEFREQ); + } + printk(BIOS_DEBUG, "MCU frequency is set at : %d MHz\n", + (1000 << 8) / ctrl->tCK); +} + +static void dram_xover(ramctr_timing * ctrl) +{ + u32 reg; + int channel; + + FOR_ALL_CHANNELS { + // enable xover clk + printk(BIOS_DEBUG, "[%x] = %x\n", channel * 0x100 + 0xc14, + (ctrl->rankmap[channel] << 24)); + MCHBAR32(channel * 0x100 + 0xc14) = (ctrl->rankmap[channel] << 24); + + // enable xover ctl + reg = 0; + if (ctrl->rankmap[channel] & 0x5) { + reg |= 0x20000; + } + if (ctrl->rankmap[channel] & 0xa) { + reg |= 0x4000000; + } + // enable xover cmd + reg |= 0x4000; + printk(BIOS_DEBUG, "[%x] = %x\n", 0x100 * channel + 0x320c, + reg); + MCHBAR32(0x100 * channel + 0x320c) = reg; + } +} + +static void dram_timing_regs(ramctr_timing * ctrl) +{ + u32 reg, addr, val32, cpu, stretch; + struct cpuid_result cpures; + int channel; + + FOR_ALL_CHANNELS { + // DBP + reg = 0; + reg |= ctrl->tRCD; + reg |= (ctrl->tRP << 4); + reg |= (ctrl->CAS << 8); + reg |= (ctrl->CWL << 12); + reg |= (ctrl->tRAS << 16); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4000, + reg); + MCHBAR32(0x400 * channel + 0x4000) = reg; + + // RAP + reg = 0; + reg |= ctrl->tRRD; + reg |= (ctrl->tRTP << 4); + reg |= (ctrl->tCKE << 8); + reg |= (ctrl->tWTR << 12); + reg |= (ctrl->tFAW << 16); + reg |= (ctrl->tWR << 24); + reg |= (3 << 30); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4004, + reg); + MCHBAR32(0x400 * channel + 0x4004) = reg; + + // OTHP + addr = 0x400 * channel + 0x400c; + reg = 0; + reg |= ctrl->tXPDLL; + reg |= (ctrl->tXP << 5); + reg |= (ctrl->tAONPD << 8); + reg |= 0xa0000; + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + + MCHBAR32(0x400 * channel + 0x4014) = 0; + + MCHBAR32(addr) |= 0x00020000; + + // ODT stretch + reg = 0; + + cpures = cpuid(0); + cpu = cpures.eax; + if (IS_IVY_CPU(cpu) + || (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_D2(cpu))) { + stretch = 2; + addr = 0x400 * channel + 0x400c; + printk(BIOS_DEBUG, "[%x] = %x\n", + 0x400 * channel + 0x400c, reg); + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0xc0000) | (stretch << 18); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0x30000) | (stretch << 16); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + + } else if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) { + stretch = 3; + addr = 0x400 * channel + 0x401c; + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0x3000) | (stretch << 12); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0xc00) | (stretch << 10); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + } else { + stretch = 0; + } + + // REFI + reg = 0; + val32 = ctrl->tREFI; + reg = (reg & ~0xffff) | val32; + val32 = ctrl->tRFC; + reg = (reg & ~0x1ff0000) | (val32 << 16); + val32 = (u32) (ctrl->tREFI * 9) / 1024; + reg = (reg & ~0xfe000000) | (val32 << 25); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4298, + reg); + MCHBAR32(0x400 * channel + 0x4298) = reg; + + MCHBAR32(0x400 * channel + 0x4294) |= 0xff; + + // SRFTP + reg = 0; + val32 = tDLLK; + reg = (reg & ~0xfff) | val32; + val32 = ctrl->tXSOffset; + reg = (reg & ~0xf000) | (val32 << 12); + val32 = tDLLK - ctrl->tXSOffset; + reg = (reg & ~0x3ff0000) | (val32 << 16); + val32 = ctrl->tMOD - 8; + reg = (reg & ~0xf0000000) | (val32 << 28); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x42a4, + reg); + MCHBAR32(0x400 * channel + 0x42a4) = reg; + } +} + +static void dram_dimm_mapping(dimm_info * info, ramctr_timing * ctrl) +{ + int t; + u32 reg, val32; + int channel; + + FOR_ALL_CHANNELS { + dimm_attr *dimmA = 0; + dimm_attr *dimmB = 0; + reg = 0; + val32 = 0; + if (info->dimm[channel][0].size_mb >= + info->dimm[channel][1].size_mb) { + // dimm 0 is bigger, set it to dimmA + dimmA = &info->dimm[channel][0]; + dimmB = &info->dimm[channel][1]; + reg |= (0 << 16); + } else { + // dimm 1 is bigger, set it to dimmA + dimmA = &info->dimm[channel][1]; + dimmB = &info->dimm[channel][0]; + reg |= (1 << 16); + // swap dimm info + t = ctrl->rank_mirror[channel][1]; + ctrl->rank_mirror[channel][1] = + ctrl->rank_mirror[channel][3]; + ctrl->rank_mirror[channel][3] = t; + } + // dimmA + if (dimmA && (dimmA->ranks > 0)) { + val32 = dimmA->size_mb / 256; + reg = (reg & ~0xff) | val32; + val32 = dimmA->ranks - 1; + reg = (reg & ~0x20000) | (val32 << 17); + val32 = (dimmA->width / 8) - 1; + reg = (reg & ~0x80000) | (val32 << 19); + } + // dimmB + if (dimmB && (dimmB->ranks > 0)) { + val32 = dimmB->size_mb / 256; + reg = (reg & ~0xff00) | (val32 << 8); + val32 = dimmB->ranks - 1; + reg = (reg & ~0x40000) | (val32 << 18); + val32 = (dimmB->width / 8) - 1; + reg = (reg & ~0x100000) | (val32 << 20); + } + reg = (reg & ~0x200000) | (1 << 21); // rank interleave + reg = (reg & ~0x400000) | (1 << 22); // enhanced interleave + + // Save MAD-DIMM register + if ((dimmA && (dimmA->ranks > 0)) + || (dimmB && (dimmB->ranks > 0))) { + ctrl->mad_dimm[channel] = reg; + } else { + ctrl->mad_dimm[channel] = 0; + } + } +} + +static void dram_dimm_set_mapping(ramctr_timing * ctrl) +{ + int channel; + FOR_ALL_CHANNELS { + MCHBAR32(0x5004 + channel * 4) = ctrl->mad_dimm[channel]; + } +} + +static void dram_zones(ramctr_timing * ctrl, int training) +{ + u32 reg, ch0size, ch1size; + u8 val; + reg = 0; + val = 0; + if (training) { + ch0size = ctrl->channel_size_mb[0] ? 256 : 0; + ch1size = ctrl->channel_size_mb[1] ? 256 : 0; + } else { + ch0size = ctrl->channel_size_mb[0]; + ch1size = ctrl->channel_size_mb[1]; + } + + if (ch0size >= ch1size) { + reg = MCHBAR32(0x5014); + val = ch1size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x24; + } else { + reg = MCHBAR32(0x5014); + val = ch0size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x21; + } +} + +static void dram_memorymap(ramctr_timing * ctrl, int me_uma_size) +{ + u32 reg, val, reclaim; + u32 tom, gfxstolen, gttsize; + size_t tsegsize, mmiosize, toludbase, touudbase, gfxstolenbase, gttbase, + tsegbase, mestolenbase; + size_t tsegbasedelta, remapbase, remaplimit; + uint16_t ggc; + + mmiosize = 0x400; + + ggc = pci_read_config16(NORTHBRIDGE, GGC); + if (!(ggc & 2)) { + gfxstolen = ((ggc >> 3) & 0x1f) * 32; + gttsize = ((ggc >> 8) & 0x3); + } else { + gfxstolen = 0; + gttsize = 0; + } + + tsegsize = CONFIG_SMM_TSEG_SIZE >> 20; + + tom = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1]; + + mestolenbase = tom - me_uma_size; + + toludbase = MIN(4096 - mmiosize + gfxstolen + gttsize + tsegsize, + tom - me_uma_size); + gfxstolenbase = toludbase - gfxstolen; + gttbase = gfxstolenbase - gttsize; + + tsegbase = gttbase - tsegsize; + + // Round tsegbase down to nearest address aligned to tsegsize + tsegbasedelta = tsegbase & (tsegsize - 1); + tsegbase &= ~(tsegsize - 1); + + gttbase -= tsegbasedelta; + gfxstolenbase -= tsegbasedelta; + toludbase -= tsegbasedelta; + + // Test if it is possible to reclaim a hole in the ram addressing + if (tom - me_uma_size > toludbase) { + // Reclaim is possible + reclaim = 1; + remapbase = MAX(4096, tom - me_uma_size); + remaplimit = + remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1; + touudbase = remaplimit + 1; + } else { + // Reclaim not possible + reclaim = 0; + touudbase = tom - me_uma_size; + } + + // Update memory map in pci-e configuration space + + // TOM (top of memory) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa0); + val = tom & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa0, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa4); + val = tom & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa4, reg); + + // TOLUD (top of low used dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xbc); + val = toludbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xbc, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xbc, reg); + + // TOUUD LSB (top of upper usable dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa8); + val = touudbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa8, reg); + + // TOUUD MSB + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xac); + val = touudbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xac, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xac, reg); + + if (reclaim) { + // REMAP BASE + pcie_write_config32(PCI_DEV(0, 0, 0), 0x90, remapbase << 20); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x94, remapbase >> 12); + + // REMAP LIMIT + pcie_write_config32(PCI_DEV(0, 0, 0), 0x98, remaplimit << 20); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x9c, remaplimit >> 12); + } + // TSEG + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb8); + val = tsegbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb8, reg); + + // GFX stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb0); + val = gfxstolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb0, reg); + + // GTT stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb4); + val = gttbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb4, reg); + + if (me_uma_size) { + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x7c); + val = (0x80000 - me_uma_size) & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x7c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x7c, reg); + + // ME base + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x70); + val = mestolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x70, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x70, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x74); + val = mestolenbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x74, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x74, reg); + + // ME mask + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x78); + val = (0x80000 - me_uma_size) & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + reg = (reg & ~0x400) | (1 << 10); // set lockbit on ME mem + + reg = (reg & ~0x800) | (1 << 11); // set ME memory enable + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x78, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x78, reg); + } +} + +static void dram_ioregs(ramctr_timing * ctrl) +{ + u32 reg, comp2; + + int channel; + + // IO clock + FOR_ALL_CHANNELS { + MCHBAR32(0xc00 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO command + FOR_ALL_CHANNELS { + MCHBAR32(0x3200 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO control + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + + // Rcomp + printk(BIOS_DEBUG, "RCOMP..."); + reg = 0; + while (reg == 0) { + reg = MCHBAR32(0x5084) & 0x10000; + } + printk(BIOS_DEBUG, "done\n"); + + // Set comp2 + comp2 = get_COMP2(ctrl->tCK); + MCHBAR32(0x3714) = comp2; + printk(BIOS_DEBUG, "COMP2 done\n"); + + // Set comp1 + FOR_ALL_POPULATED_CHANNELS { + reg = MCHBAR32(0x1810 + channel * 0x100); //ch0 + reg = (reg & ~0xe00) | (1 << 9); //odt + reg = (reg & ~0xe00000) | (1 << 21); //clk drive up + reg = (reg & ~0x38000000) | (1 << 27); //ctl drive up + MCHBAR32(0x1810 + channel * 0x100) = reg; + } + printk(BIOS_DEBUG, "COMP1 done\n"); + + printk(BIOS_DEBUG, "FORCE RCOMP and wait 20us..."); + MCHBAR32(0x5f08) |= 0x100; + udelay(20); + printk(BIOS_DEBUG, "done\n"); +} + +static void wait_428c(int channel) +{ + while (1) { + if (read32(DEFAULT_MCHBAR | 0x428c | (channel << 10)) & 0x50) + return; + } +} + +static void write_reset(ramctr_timing * ctrl) +{ + int channel, slotrank; + + /* choose a populated channel. */ + channel = (ctrl->rankmap[0]) ? 0 : 1; + + wait_428c(channel); + + /* choose a populated rank. */ + slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x80c01); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x400001); + wait_428c(channel); +} + +static void dram_jedecreset(ramctr_timing * ctrl) +{ + u32 reg, addr; + int channel; + + while (!(MCHBAR32(0x5084) & 0x10000)) ; + do { + reg = MCHBAR32(0x428c); + } while ((reg & 0x14) == 0); + + // Set state of memory controller + reg = 0x112; + MCHBAR32(0x5030) = reg; + MCHBAR32(0x4ea0) = 0; + reg |= 2; //ddr reset + MCHBAR32(0x5030) = reg; + + // Assert dimm reset signal + reg = MCHBAR32(0x5030); + reg &= ~0x2; + MCHBAR32(0x5030) = reg; + + // Wait 200us + udelay(200); + + // Deassert dimm reset signal + MCHBAR32(0x5030) |= 2; + + // Wait 500us + udelay(500); + + // Enable DCLK + MCHBAR32(0x5030) |= 4; + + // XXX Wait 20ns + udelay(1); + + FOR_ALL_CHANNELS { + // Set valid rank CKE + reg = 0; + reg = (reg & ~0xf) | ctrl->rankmap[channel]; + addr = 0x400 * channel + 0x42a0; + MCHBAR32(addr) = reg; + + // Wait 10ns for ranks to settle + //udelay(0.01); + + reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4); + MCHBAR32(addr) = reg; + + // Write reset using a NOP + write_reset(ctrl); + } +} + +static odtmap get_ODT(ramctr_timing * ctrl, u8 rank) +{ + /* Get ODT based on rankmap: */ + int dimms_per_ch = 0; + int channel; + + FOR_ALL_CHANNELS { + dimms_per_ch = max ((ctrl->rankmap[channel] & 1) + + ((ctrl->rankmap[channel] >> 2) & 1), + dimms_per_ch); + } + + if (dimms_per_ch == 1) { + return (const odtmap){60, 60}; + } else if (dimms_per_ch == 2) { + return (const odtmap){120, 30}; + } else { + printk(BIOS_DEBUG, + "Huh, no dimms? m0 = %d m1 = %d dpc = %d\n", + ctrl->rankmap[0], + ctrl->rankmap[1], dimms_per_ch); + die(""); + } +} + +static void write_mrreg(ramctr_timing * ctrl, int channel, int slotrank, + int reg, u32 val) +{ + wait_428c(channel); + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + if (ctrl->rank_mirror[channel][slotrank]) { + reg = ((reg >> 1) & 1) | ((reg << 1) & 2); + val = (val & ~0x1f8) | ((val >> 1) & 0xa8) + | ((val & 0xa8) << 1); + } + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | (ctrl->tMOD << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); +} + +static u32 make_mr0(ramctr_timing * ctrl, u8 rank) +{ + u16 mr0reg, mch_cas, mch_wr; + static const u8 mch_wr_t[12] = { 1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0 }; + mr0reg = 0x100; + + // Convert CAS to MCH register friendly + if (ctrl->CAS < 12) { + mch_cas = (u16) ((ctrl->CAS - 4) << 1); + } else { + mch_cas = (u16) (ctrl->CAS - 12); + mch_cas = ((mch_cas << 1) | 0x1); + } + + // Convert tWR to MCH register friendly + mch_wr = mch_wr_t[ctrl->tWR - 5]; + + mr0reg = (mr0reg & ~0x4) | (mch_cas & 0x1); + mr0reg = (mr0reg & ~0x70) | ((mch_cas & 0xe) << 3); + mr0reg = (mr0reg & ~0xe00) | (mch_wr << 9); + // Fast (desktop) 0x1 or slow (mobile) 0x0 + mr0reg = (mr0reg & ~0x1000) | (!ctrl->mobile << 12); + return mr0reg; +} + +static void dram_mr0(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS write_mrreg(ctrl, channel, rank, 0, + make_mr0(ctrl, rank)); +} + +static u32 encode_odt(u32 odt) +{ + switch (odt) { + case 30: + return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4 + case 60: + return (1 << 2); // RZQ/4 + case 120: + return (1 << 6); // RZQ/2 + default: + case 0: + return 0; + } +} + +static u32 make_mr1(ramctr_timing * ctrl, u8 rank) +{ + odtmap odt; + u32 mr1reg; + + odt = get_ODT(ctrl, rank); + mr1reg = 0x2; + + mr1reg |= encode_odt(odt.rttnom); + + return mr1reg; +} + +static void dram_mr1(ramctr_timing * ctrl, u8 rank) +{ + u16 mr1reg; + int channel; + + mr1reg = make_mr1(ctrl, rank); + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 1, mr1reg); + } +} + +static void dram_mr2(ramctr_timing * ctrl, u8 rank) +{ + u16 pasr, cwl, mr2reg; + odtmap odt; + int channel; + int srt; + + pasr = 0; + cwl = ctrl->CWL - 5; + odt = get_ODT(ctrl, rank); + + srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh; + + mr2reg = 0; + mr2reg = (mr2reg & ~0x7) | pasr; + mr2reg = (mr2reg & ~0x38) | (cwl << 3); + mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6); + mr2reg = (mr2reg & ~0x80) | (srt << 7); + mr2reg |= (odt.rttwr / 60) << 9; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 2, mr2reg); + } +} + +static void dram_mr3(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 3, 0); + } +} + +static void dram_mrscommands(ramctr_timing * ctrl) +{ + u8 rank; + u32 reg, addr; + int channel; + + for (rank = 0; rank < 4; rank++) { + // MR2 + printk(BIOS_SPEW, "MR2 rank %d...", rank); + dram_mr2(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR3 + printk(BIOS_SPEW, "MR3 rank %d...", rank); + dram_mr3(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR1 + printk(BIOS_SPEW, "MR1 rank %d...", rank); + dram_mr1(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR0 + printk(BIOS_SPEW, "MR0 rank %d...", rank); + dram_mr0(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + } + + write32(DEFAULT_MCHBAR + 0x4e20, 0x7); + write32(DEFAULT_MCHBAR + 0x4e30, 0xf1001); + write32(DEFAULT_MCHBAR + 0x4e00, 0x60002); + write32(DEFAULT_MCHBAR + 0x4e10, 0); + write32(DEFAULT_MCHBAR + 0x4e24, 0x1f003); + write32(DEFAULT_MCHBAR + 0x4e34, 0x1901001); + write32(DEFAULT_MCHBAR + 0x4e04, 0x60400); + write32(DEFAULT_MCHBAR + 0x4e14, 0x288); + write32(DEFAULT_MCHBAR + 0x4e84, 0x40004); + + // Drain + FOR_ALL_CHANNELS { + // Wait for ref drained + wait_428c(channel); + } + + // Refresh enable + MCHBAR32(0x5030) |= 8; + + FOR_ALL_POPULATED_CHANNELS { + addr = 0x400 * channel + 0x4020; + reg = MCHBAR32(addr); + reg &= ~0x200000; + MCHBAR32(addr) = reg; + + wait_428c(channel); + + rank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + // Drain + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (rank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x1); + + // Drain + wait_428c(channel); + } +} + +const u32 lane_registers[] = { + 0x0000, 0x0200, 0x0400, 0x0600, + 0x1000, 0x1200, 0x1400, 0x1600, + 0x0800 +}; + +static void program_timings(ramctr_timing * ctrl, int channel) +{ + u32 reg32, reg_4024, reg_c14, reg_c18, reg_4028; + int lane; + int slotrank, slot; + int full_shift = 0; + u16 slot320c[NUM_SLOTS]; + + FOR_ALL_POPULATED_RANKS { + if (full_shift < -ctrl->timings[channel][slotrank].val_320c) + full_shift = -ctrl->timings[channel][slotrank].val_320c; + } + + for (slot = 0; slot < NUM_SLOTS; slot++) + switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) { + case 0: + default: + slot320c[slot] = 0x7f; + break; + case 1: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 0].val_320c + + full_shift; + break; + case 2: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 1].val_320c + + full_shift; + break; + case 3: + slot320c[slot] = + (ctrl->timings[channel][2 * slot].val_320c + + ctrl->timings[channel][2 * slot + + 1].val_320c) / 2 + + full_shift; + break; + } + + reg32 = (1 << 17) | (1 << 14); + reg32 |= ((slot320c[0] & 0x3f) << 6) | ((slot320c[0] & 0x40) << 9); + reg32 |= (slot320c[1] & 0x7f) << 18; + reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6); + + MCHBAR32(0x320c + 0x100 * channel) = reg32; + + reg_c14 = ctrl->rankmap[channel] << 24; + reg_c18 = 0; + + FOR_ALL_POPULATED_RANKS { + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + int offset_val_c14; + if (shift < 0) + shift = 0; + offset_val_c14 = ctrl->reg_c14_offset + shift; + reg_c14 |= (offset_val_c14 & 0x3f) << (6 * slotrank); + reg_c18 |= ((offset_val_c14 >> 6) & 1) << slotrank; + } + + MCHBAR32(0xc14 + channel * 0x100) = reg_c14; + MCHBAR32(0xc18 + channel * 0x100) = reg_c18; + + reg_4028 = MCHBAR32(0x4028 + 0x400 * channel); + reg_4028 &= 0xffff0000; + + reg_4024 = 0; + + FOR_ALL_POPULATED_RANKS { + int post_timA_min_high = 7, post_timA_max_high = 0; + int pre_timA_min_high = 7, pre_timA_max_high = 0; + int shift_402x = 0; + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + + if (shift < 0) + shift = 0; + + FOR_ALL_LANES { + if (post_timA_min_high > + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_min_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_min_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + if (post_timA_max_high < + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_max_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_max_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + } + + if (pre_timA_max_high - pre_timA_min_high < + post_timA_max_high - post_timA_min_high) + shift_402x = +1; + else if (pre_timA_max_high - pre_timA_min_high > + post_timA_max_high - post_timA_min_high) + shift_402x = -1; + + reg_4028 |= + (ctrl->timings[channel][slotrank].val_4028 + shift_402x - + post_timA_min_high) << (4 * slotrank); + reg_4024 |= + (ctrl->timings[channel][slotrank].val_4024 + + shift_402x) << (8 * slotrank); + + FOR_ALL_LANES { + MCHBAR32(lane_registers[lane] + 0x10 + 0x100 * channel + + 4 * slotrank) + = + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) & 0x3f) + | + ((ctrl->timings[channel][slotrank].lanes[lane]. + rising + shift) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift - + (post_timA_min_high << 6)) & 0x1c0) << 10) + | (ctrl->timings[channel][slotrank].lanes[lane]. + falling << 20)); + + MCHBAR32(lane_registers[lane] + 0x20 + 0x100 * channel + + 4 * slotrank) + = + (((ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift) & 0x3f) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x3f) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x1c0) << 9) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift) & 0x40) << 13)); + } + } + MCHBAR32(0x4024 + 0x400 * channel) = reg_4024; + MCHBAR32(0x4028 + 0x400 * channel) = reg_4028; +} + +static void test_timA(ramctr_timing * ctrl, int channel, int slotrank) +{ + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->tMOD << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x4040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x100f | ((ctrl->CAS + 36) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->tMOD << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); +} + +static int does_lane_work(ramctr_timing * ctrl, int channel, int slotrank, + int lane) +{ + u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA; + return ((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + channel * 0x100 + 4 + + ((timA / 32) & 1) * 4) + >> (timA % 32)) & 1); +} + +struct run { + int middle; + int end; + int start; + int all; + int length; +}; + +static struct run get_longest_zero_run(int *seq, int sz) +{ + int i, ls; + int bl = 0, bs = 0; + struct run ret; + + ls = 0; + for (i = 0; i < 2 * sz; i++) + if (seq[i % sz]) { + if (i - ls > bl) { + bl = i - ls; + bs = ls; + } + ls = i + 1; + } + if (bl == 0) { + ret.middle = sz / 2; + ret.start = 0; + ret.end = sz; + ret.all = 1; + return ret; + } + + ret.start = bs % sz; + ret.end = (bs + bl - 1) % sz; + ret.middle = (bs + (bl - 1) / 2) % sz; + ret.length = bl; + ret.all = 0; + + return ret; +} + +static void discover_timA_coarse(ramctr_timing * ctrl, int channel, + int slotrank, int *upperA) +{ + int timA; + int statistics[NUM_LANES][128]; + int lane; + + for (timA = 0; timA < 128; timA++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].timA = timA; + } + program_timings(ctrl, channel); + + test_timA(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timA] = + !does_lane_work(ctrl, channel, slotrank, lane); + printk(BIOS_SPEW, "Astat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timA, + statistics[lane][timA]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle; + upperA[lane] = rn.end; + if (upperA[lane] < rn.middle) + upperA[lane] += 128; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + printk(BIOS_SPEW, "Aend: %d, %d, %d, %x\n", channel, slotrank, + lane, upperA[lane]); + } +} + +static void discover_timA_fine(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int timA_delta; + int statistics[NUM_LANES][51]; + int lane, i; + + memset(statistics, 0, sizeof(statistics)); + + for (timA_delta = -25; timA_delta <= 25; timA_delta++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = upperA[lane] + timA_delta + 0x40; + program_timings(ctrl, channel); + + for (i = 0; i < 100; i++) { + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + statistics[lane][timA_delta + 25] += + does_lane_work(ctrl, channel, slotrank, + lane); + } + } + } + FOR_ALL_LANES { + int last_zero, first_all; + + for (last_zero = -25; last_zero <= 25; last_zero++) + if (statistics[lane][last_zero + 25]) + break; + last_zero--; + for (first_all = -25; first_all <= 25; first_all++) + if (statistics[lane][first_all + 25] == 100) + break; + + printk(BIOS_SPEW, "lane %d: %d, %d\n", lane, last_zero, + first_all); + + ctrl->timings[channel][slotrank].lanes[lane].timA = + (last_zero + first_all) / 2 + upperA[lane]; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + } +} + +static void discover_402x(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int works[NUM_LANES]; + int lane; + while (1) { + int all_works = 1, some_works = 0; + program_timings(ctrl, channel); + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + works[lane] = + !does_lane_work(ctrl, channel, slotrank, lane); + if (works[lane]) + some_works = 1; + else + all_works = 0; + } + if (all_works) + return; + if (!some_works) { + if (ctrl->timings[channel][slotrank].val_4024 < 2) + die("402x discovery failed"); + ctrl->timings[channel][slotrank].val_4024 -= 2; + printk(BIOS_SPEW, "4024 -= 2;\n"); + continue; + } + ctrl->timings[channel][slotrank].val_4028 += 2; + printk(BIOS_SPEW, "4028 += 2;\n"); + if (ctrl->timings[channel][slotrank].val_4028 >= 0x10) + die("402x discovery failed"); + FOR_ALL_LANES if (works[lane]) { + ctrl->timings[channel][slotrank].lanes[lane].timA += + 128; + upperA[lane] += 128; + printk(BIOS_SPEW, "increment %d, %d, %d\n", channel, + slotrank, lane); + } + } +} + +struct timA_minmax { + int timA_min_high, timA_max_high; +}; + +static void pre_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + int lane; + mnmx->timA_min_high = 7; + mnmx->timA_max_high = 0; + + FOR_ALL_LANES { + if (mnmx->timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_min_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + if (mnmx->timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_max_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + } +} + +static void post_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + struct timA_minmax post; + int shift_402x = 0; + + /* Get changed maxima. */ + pre_timA_change(ctrl, channel, slotrank, &post); + + if (mnmx->timA_max_high - mnmx->timA_min_high < + post.timA_max_high - post.timA_min_high) + shift_402x = +1; + else if (mnmx->timA_max_high - mnmx->timA_min_high > + post.timA_max_high - post.timA_min_high) + shift_402x = -1; + else + shift_402x = 0; + + ctrl->timings[channel][slotrank].val_4028 += shift_402x; + ctrl->timings[channel][slotrank].val_4024 += shift_402x; + printk(BIOS_SPEW, "4024 += %d;\n", shift_402x); + printk(BIOS_SPEW, "4028 += %d;\n", shift_402x); +} + +static void read_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + u32 r32; + int all_high, some_high; + int upperA[NUM_LANES]; + struct timA_minmax mnmx; + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + write32(DEFAULT_MCHBAR + 0x3400, (slotrank << 2) | 0x8001); + + ctrl->timings[channel][slotrank].val_4028 = 4; + ctrl->timings[channel][slotrank].val_4024 = 55; + program_timings(ctrl, channel); + + discover_timA_coarse(ctrl, channel, slotrank, upperA); + + all_high = 1; + some_high = 0; + FOR_ALL_LANES { + if (ctrl->timings[channel][slotrank].lanes[lane]. + timA >= 0x40) + some_high = 1; + else + all_high = 0; + } + + if (all_high) { + ctrl->timings[channel][slotrank].val_4028--; + printk(BIOS_SPEW, "4028--;\n"); + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + timA -= 0x40; + upperA[lane] -= 0x40; + + } + } else if (some_high) { + ctrl->timings[channel][slotrank].val_4024++; + ctrl->timings[channel][slotrank].val_4028++; + printk(BIOS_SPEW, "4024++;\n"); + printk(BIOS_SPEW, "4028++;\n"); + } + + program_timings(ctrl, channel); + + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_402x(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_timA_fine(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].timA -= mnmx.timA_min_high * 0x40; + } + ctrl->timings[channel][slotrank].val_4028 -= mnmx.timA_min_high; + printk(BIOS_SPEW, "4028 -= %d;\n", mnmx.timA_min_high); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + + printk(BIOS_SPEW, "4/8: %d, %d, %x, %x\n", channel, slotrank, + ctrl->timings[channel][slotrank].val_4024, + ctrl->timings[channel][slotrank].val_4028); + + FOR_ALL_LANES + printk(BIOS_SPEW, "%d, %d, %d, %x\n", channel, slotrank, + lane, + ctrl->timings[channel][slotrank].lanes[lane].timA); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + } + + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + 4 * lane, 0); + } +} + +static void test_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int lane; + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x4140 + 0x400 * channel + 4 * lane); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) + | 4 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (6 << 16)); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8041001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x80411f4); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) + | 8 | (ctrl->CAS << 16)); + + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x40011f4 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timC; + int statistics[NUM_LANES][MAX_TIMC + 1]; + int lane; + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + for (timC = 0; timC <= MAX_TIMC; timC++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timC = timC; + program_timings(ctrl, channel); + + test_timC(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timC] = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + printk(BIOS_SPEW, "Cstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timC, + statistics[lane][timC]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_TIMC + 1); + ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle; + if (rn.all) + die("timC discovery failed"); + printk(BIOS_SPEW, "Cval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timC); + } +} + +static int get_precedening_channels(ramctr_timing * ctrl, int target_channel) +{ + int channel, ret = 0; + FOR_ALL_POPULATED_CHANNELS if (channel < target_channel) + ret++; + return ret; +} + +static void fill_pattern0(ramctr_timing * ctrl, int channel, u32 a, u32 b) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + printk(BIOS_SPEW, "channel_offset=%x\n", channel_offset); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + 4 * j, j & 2 ? b : a); + sfence(); +} + +static int num_of_channels(const ramctr_timing * ctrl) +{ + int ret = 0; + int channel; + FOR_ALL_POPULATED_CHANNELS ret++; + return ret; +} + +static void fill_pattern1(ramctr_timing * ctrl, int channel) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + j * 4, 0xffffffff); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + channel_step + j * 4, 0); + sfence(); +} + +static void precharge(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tMOD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tMOD << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } + + program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tMOD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tMOD << 16)); + + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + } +} + +static void test_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + write_mrreg(ctrl, channel, slotrank, 1, + 0x80 | make_mr1(ctrl, slotrank)); + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWLO) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + 8 | (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f107); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4000c01 | ((ctrl->CAS + 38) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 4); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x400 * channel + 0x4284, 0x40001); + wait_428c(channel); + + write_mrreg(ctrl, channel, slotrank, 1, + 0x1080 | make_mr1(ctrl, slotrank)); +} + +static void discover_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timB; + int statistics[NUM_LANES][128]; + int lane; + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052 | (slotrank << 2)); + + for (timB = 0; timB < 128; timB++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].timB = timB; + } + program_timings(ctrl, channel); + + test_timB(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timB] = + !((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + + channel * 0x100 + 4 + ((timB / 32) & 1) * 4) + >> (timB % 32)) & 1); + printk(BIOS_SPEW, "Bstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timB, + statistics[lane][timB]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timB = rn.start; + if (rn.all) + die("timB discovery failed"); + printk(BIOS_SPEW, "Bval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timB); + } +} + +static int get_timB_high_adjust(u64 val) +{ + int i; + if (val >= 0xfffffffffffff000LL) + return 3; + if (val >= 0xfffffffffff00000LL) + return -1; + if (val >= 0xfffffff000000000LL) + return -2; + if (val >= 0xfff0000000000000LL) + return -3; + + for (i = 0; i < 8; i++) + if (val >> (8 * (7 - i) + 4)) + return i; + return 8; +} + +static void adjust_high_timB(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + write32(DEFAULT_MCHBAR + 0x3400, 0x200); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern1(ctrl, channel); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 1); + } + FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS { + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x10001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRCD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x8041003); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x3e2); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | ((ctrl->tRP) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0xc01 | ((ctrl->tRCD) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x3f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4000c01 | + ((ctrl->tRP + + ctrl->timings[channel][slotrank].val_4024 + + ctrl->timings[channel][slotrank].val_4028) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60008); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); + wait_428c(channel); + FOR_ALL_LANES { + u64 res = + read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 4); + res |= + ((u64) read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 8)) << 32; + ctrl->timings[channel][slotrank].lanes[lane].timB += + get_timB_high_adjust(res) * 64; + + printk(BIOS_DEBUG, "High adjust %d:%016llx\n", lane, res); + printk(BIOS_SPEW, "Bval+: %d, %d, %d, %x\n", channel, + slotrank, lane, + ctrl->timings[channel][slotrank].lanes[lane]. + timB); + } + } + write32(DEFAULT_MCHBAR + 0x3400, 0); +} + +static void write_op(ramctr_timing * ctrl, int channel) +{ + int slotrank; + + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); +} + +static void write_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS + write32(DEFAULT_MCHBAR + 0x4008 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4008 + + 0x400 * channel) | 0x8000000); + + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + } + + FOR_ALL_CHANNELS + FOR_ALL_POPULATED_RANKS + write_mrreg(ctrl, channel, slotrank, 1, + make_mr1(ctrl, slotrank) | 0x1080); + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + discover_timB(ctrl, channel, slotrank); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + write_mrreg(ctrl, channel, + slotrank, 1, make_mr1(ctrl, slotrank)); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + FOR_ALL_POPULATED_CHANNELS + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) | 8); + + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + ~0x00200000 & read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel)); + read32(DEFAULT_MCHBAR + 0x428c + 0x400 * channel); + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + printk(BIOS_SPEW, "CPE\n"); + precharge(ctrl); + printk(BIOS_SPEW, "CPF\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + discover_timC(ctrl, channel, slotrank); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + adjust_high_timB(ctrl); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static int test_320c(ramctr_timing * ctrl, int channel, int slotrank) +{ + struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank]; + int timC_delta; + int lanes_ok = 0; + int ctr = 0; + int lane; + + for (timC_delta = -5; timC_delta <= 5; timC_delta++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].timC = + saved_rt.lanes[lane].timC + timC_delta; + } + program_timings(ctrl, channel); + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 4 * lane + 0x4f40, 0); + } + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + ((max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10) + | 8 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | ctr | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4244 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4001020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4248 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, 0xf1001); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + u32 r32 = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + + if (r32 == 0) + lanes_ok |= 1 << lane; + } + ctr++; + if (lanes_ok == ((1 << NUM_LANES) - 1)) + break; + } + + ctrl->timings[channel][slotrank] = saved_rt; + + printk(BIOS_SPEW, "3lanes: %x\n", lanes_ok); + return lanes_ok != ((1 << NUM_LANES) - 1); +} + +#include "raminit_patterns.h" + +static void fill_pattern5(ramctr_timing * ctrl, int channel, int patno) +{ + unsigned i, j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + + if (patno) { + u8 base8 = 0x80 >> ((patno - 1) % 8); + u32 base = base8 | (base8 << 8) | (base8 << 16) | (base8 << 24); + for (i = 0; i < 32; i++) { + for (j = 0; j < 16; j++) { + u32 val = use_base[patno - 1][i] & (1 << (j / 2)) ? base : 0; + if (invert[patno - 1][i] & (1 << (j / 2))) + val = ~val; + write32(0x04000000 + channel_offset + i * channel_step + + j * 4, val); + } + } + + } else { + for (i = 0; i < sizeof(pattern) / sizeof(pattern[0]); i++) { + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + i * channel_step + + j * 4, pattern[i][j]); + } + sfence(); + } +} + +static void reprogram_320c(ramctr_timing * ctrl) +{ + int channel, slotrank; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + /* jedec reset */ + dram_jedecreset(ctrl); + /* mrs commands. */ + dram_mrscommands(ctrl); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); +} + +#define MIN_C320C_LEN 13 + +static int try_reg_4004_b30(ramctr_timing * ctrl, int r4004b30) +{ + struct ram_rank_timings saved_timings[NUM_CHANNELS][NUM_SLOTRANKS]; + int channel, slotrank; + int c320c; + int stat[NUM_SLOTRANKS][256]; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + saved_timings[channel][slotrank] = ctrl->timings[channel][slotrank]; + } + + FOR_ALL_POPULATED_CHANNELS { + ctrl->reg_4004_b30[channel] = r4004b30; + } + + FOR_ALL_POPULATED_CHANNELS + MCHBAR32(0x4004 + 0x400 * channel) = + ctrl->tRRD + | (ctrl->tRTP << 4) + | (ctrl->tCKE << 8) + | (ctrl->tWTR << 12) + | (ctrl->tFAW << 16) + | (ctrl->tWR << 24) + | (ctrl->reg_4004_b30[channel] << 30); + + + FOR_ALL_CHANNELS { + int delta = 0; + if (ctrl->reg_4004_b30[channel] == 2) + delta = 2; + else if (ctrl->reg_4004_b30[channel] == 0) + delta = 4; + + FOR_ALL_POPULATED_RANKS { + ctrl->timings[channel][slotrank].val_4024 -= delta; + } + } + + FOR_ALL_POPULATED_CHANNELS { + for (c320c = -127; c320c <= 127; c320c++) { + FOR_ALL_POPULATED_RANKS { + ctrl->timings[channel][slotrank].val_320c = c320c; + } + program_timings(ctrl, channel); + reprogram_320c(ctrl); + FOR_ALL_POPULATED_RANKS { + stat[slotrank][c320c + 127] = + test_320c(ctrl, channel, slotrank); + printk(BIOS_SPEW, "3stat: %d, %d, %d: %d\n", + channel, slotrank, c320c, + stat[slotrank][c320c + 127]); + } + } + FOR_ALL_POPULATED_RANKS { + struct run rn = + get_longest_zero_run(stat[slotrank], 255); + ctrl->timings[channel][slotrank].val_320c = + rn.middle - 127; + printk(BIOS_SPEW, "3val: %d, %d: %d\n", channel, + slotrank, + ctrl->timings[channel][slotrank].val_320c); + if (rn.all || rn.length < MIN_C320C_LEN) { + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + ctrl->timings[channel][slotrank] = saved_timings[channel][slotrank]; + } + return 0; + } + } + } + return 1; +} + +static void command_training(ramctr_timing * ctrl) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel, 0); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + if (!try_reg_4004_b30(ctrl, 0) && !try_reg_4004_b30(ctrl, 2)) + die("c320c discovery failed"); + + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + + reprogram_320c(ctrl); +} + +static void discover_edges_real(ramctr_timing * ctrl, int channel, int slotrank, + int *edges) +{ + int edge; + int statistics[NUM_LANES][MAX_EDGE_TIMING + 1]; + int lane; + + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].rising = + edge; + ctrl->timings[channel][slotrank].lanes[lane].falling = + edge; + } + printk(BIOS_SPEW, "edge %02x\n", edge); + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + 4 * lane + + 0x4140); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->tMOD << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x40411f4); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->tMOD << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + FOR_ALL_LANES { + statistics[lane][edge] = + read32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + lane * 4); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_EDGE_TIMING + 1); + edges[lane] = rn.middle; + if (rn.all) + die("edge discovery failed"); + printk(BIOS_SPEW, "eval %d, %d, %d, %02x\n", channel, slotrank, + lane, edges[lane]); + } +} + +static void discover_edges(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + u32 r32; + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 4 * lane + + 0x400 * channel + 0x4080, 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0, 0); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x400 * channel + + lane * 4 + 0x4140); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } + + program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tMOD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tMOD << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } + + program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tMOD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tMOD << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + lane * 4, + ~read32(DEFAULT_MCHBAR + 0x4040 + + 0x400 * channel + lane * 4) & 0xff); + } + + fill_pattern0(ctrl, channel, 0, 0xffffffff); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } + + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void discover_edges_write_real(ramctr_timing * ctrl, int channel, + int slotrank, int *edges) +{ + int edge; + u32 raw_statistics[MAX_EDGE_TIMING + 1]; + int statistics[MAX_EDGE_TIMING + 1]; + const int reg3000b24[] = { 0, 0xc, 0x2c }; + int lane, i; + int lower[NUM_LANES]; + int upper[NUM_LANES]; + int pat; + + FOR_ALL_LANES { + lower[lane] = 0; + upper[lane] = MAX_EDGE_TIMING; + } + + for (i = 0; i < 3; i++) { + write32(DEFAULT_MCHBAR + 0x3000 + 0x100 * channel, + reg3000b24[i] << 24); + for (pat = 0; pat < NUM_PATTERNS; pat++) { + fill_pattern5(ctrl, channel, pat); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + printk(BIOS_SPEW, "patterned\n"); + printk(BIOS_SPEW, "[%x] = 0x%08x\n(%d, %d)\n", + 0x3000 + 0x100 * channel, reg3000b24[i] << 24, channel, + slotrank); + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + rising = edge; + ctrl->timings[channel][slotrank].lanes[lane]. + falling = edge; + } + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + + 4 * lane + 0x4140); + } + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x4 | (ctrl->tRCD << 16) + | (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << + 10)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, + 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8005020 | ((ctrl->tWTR + ctrl->CWL + 8) << + 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4005020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + lane * 4); + } + + raw_statistics[edge] = + MCHBAR32(0x436c + 0x400 * channel); + } + FOR_ALL_LANES { + struct run rn; + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) + statistics[edge] = + ! !(raw_statistics[edge] & (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_EDGE_TIMING + 1); + printk(BIOS_SPEW, + "edges: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, rn.middle, + rn.end, rn.start + ctrl->edge_offset[i], + rn.end - ctrl->edge_offset[i]); + lower[lane] = + max(rn.start + ctrl->edge_offset[i], lower[lane]); + upper[lane] = + min(rn.end - ctrl->edge_offset[i], upper[lane]); + edges[lane] = (lower[lane] + upper[lane]) / 2; + + } + } + } + + write32(DEFAULT_MCHBAR + 0x3000, 0); + printk(BIOS_SPEW, "CPA\n"); +} + +static void discover_edges_write(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank) +{ + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) + << 10) | (ctrl->tRCD << 16) | 4); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x80011e0 | + ((ctrl->tWTR + ctrl->CWL + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + + 0x400 * channel, + 0x40011e0 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + + 0x400 * channel, + 0x1001 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC_write(ramctr_timing * ctrl) +{ + const u8 rege3c_b24[3] = { 0, 0xf, 0x2f }; + int i, pat; + + int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + lower[channel][slotrank][lane] = 0; + upper[channel][slotrank][lane] = MAX_TIMC; + } + + write32(DEFAULT_MCHBAR + 0x4ea8, 1); + + for (i = 0; i < 3; i++) + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100), + (rege3c_b24[i] << 24) + | (read32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100)) + & ~0x3f000000)); + udelay(2); + for (pat = 0; pat < NUM_PATTERNS; pat++) { + FOR_ALL_POPULATED_RANKS { + int timC; + u32 raw_statistics[MAX_TIMC + 1]; + int statistics[MAX_TIMC + 1]; + + fill_pattern5(ctrl, channel, pat); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + for (timC = 0; timC < MAX_TIMC + 1; timC++) { + FOR_ALL_LANES + ctrl->timings[channel][slotrank].lanes[lane].timC = timC; + program_timings(ctrl, channel); + + test_timC_write (ctrl, channel, slotrank); + + raw_statistics[timC] = + MCHBAR32(0x436c + 0x400 * channel); + } + FOR_ALL_LANES { + struct run rn; + for (timC = 0; timC <= MAX_TIMC; timC++) + statistics[timC] = + !!(raw_statistics[timC] & + (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_TIMC + 1); + if (rn.all) + die("timC write discovery failed"); + printk(BIOS_SPEW, + "timC: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, + rn.middle, rn.end, + rn.start + ctrl->timC_offset[i], + rn.end - ctrl->timC_offset[i]); + lower[channel][slotrank][lane] = + max(rn.start + ctrl->timC_offset[i], + lower[channel][slotrank][lane]); + upper[channel][slotrank][lane] = + min(rn.end - ctrl->timC_offset[i], + upper[channel][slotrank][lane]); + + } + } + } + } + + FOR_ALL_CHANNELS { + write32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c, + 0 | (read32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c) & + ~0x3f000000)); + udelay(2); + } + + write32(DEFAULT_MCHBAR + 0x4ea8, 0); + + printk(BIOS_SPEW, "CPB\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + printk(BIOS_SPEW, "timC [%d, %d, %d] = 0x%x\n", channel, + slotrank, lane, + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2); + ctrl->timings[channel][slotrank].lanes[lane].timC = + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2; + } + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } +} + +static void normalize_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + int mat = 0; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + int delta; + FOR_ALL_LANES mat = + max(ctrl->timings[channel][slotrank].lanes[lane].timA, mat); + delta = (mat >> 6) - ctrl->timings[channel][slotrank].val_4028; + ctrl->timings[channel][slotrank].val_4024 += delta; + ctrl->timings[channel][slotrank].val_4028 += delta; + } + + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } +} + +static void write_controller_mr(ramctr_timing * ctrl) +{ + int channel, slotrank; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + write32(DEFAULT_MCHBAR | 0x0004 | (channel << 8) | + lane_registers[slotrank], make_mr0(ctrl, slotrank)); + write32(DEFAULT_MCHBAR | 0x0008 | (channel << 8) | + lane_registers[slotrank], make_mr1(ctrl, slotrank)); + } +} + +static void channel_test(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS + if (read32(DEFAULT_MCHBAR | 0x42a0 | (channel << 10)) & 0xa000) + die("Mini channel test failed (1)\n"); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0x12345678, 0x98765432); + + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + for (slotrank = 0; slotrank < 4; slotrank++) + FOR_ALL_CHANNELS + if (ctrl->rankmap[channel] & (1 << slotrank)) { + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR | (0x4f40 + 4 * lane), 0); + write32(DEFAULT_MCHBAR | (0x4d40 + 4 * lane), 0); + } + wait_428c(channel); + write32(DEFAULT_MCHBAR | 0x4220 | (channel << 10), 0x0001f006); + write32(DEFAULT_MCHBAR | 0x4230 | (channel << 10), 0x0028a004); + write32(DEFAULT_MCHBAR | 0x4200 | (channel << 10), + 0x00060000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4210 | (channel << 10), 0x00000244); + write32(DEFAULT_MCHBAR | 0x4224 | (channel << 10), 0x0001f201); + write32(DEFAULT_MCHBAR | 0x4234 | (channel << 10), 0x08281064); + write32(DEFAULT_MCHBAR | 0x4204 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4214 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x4228 | (channel << 10), 0x0001f105); + write32(DEFAULT_MCHBAR | 0x4238 | (channel << 10), 0x04281064); + write32(DEFAULT_MCHBAR | 0x4208 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4218 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x422c | (channel << 10), 0x0001f002); + write32(DEFAULT_MCHBAR | 0x423c | (channel << 10), 0x00280c01); + write32(DEFAULT_MCHBAR | 0x420c | (channel << 10), + 0x00060400 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x421c | (channel << 10), 0x00000240); + write32(DEFAULT_MCHBAR | 0x4284 | (channel << 10), 0x000c0001); + wait_428c(channel); + FOR_ALL_LANES + if (read32(DEFAULT_MCHBAR | 0x4340 | (channel << 10))) + die("Mini channel test failed (2)\n"); + } +} + +static void set_scrambling_seed(ramctr_timing * ctrl) +{ + int channel; + + /* FIXME: we hardcode seeds. Do we need to use some PRNG for them? + I don't think so. */ + static u32 seeds[NUM_CHANNELS][3] = { + {0x00009a36, 0xbafcfdcf, 0x46d1ab68}, + {0x00028bfa, 0x53fe4b49, 0x19ed5483} + }; + FOR_ALL_POPULATED_CHANNELS { + MCHBAR32(0x4020 + 0x400 * channel) &= ~0x10000000; + write32(DEFAULT_MCHBAR | 0x4034, seeds[channel][0]); + write32(DEFAULT_MCHBAR | 0x403c, seeds[channel][1]); + write32(DEFAULT_MCHBAR | 0x4038, seeds[channel][2]); + } +} + +static void set_4f8c(void) +{ + struct cpuid_result cpures; + u32 cpu; + + cpures = cpuid(0); + cpu = (cpures.eax); + if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) { + MCHBAR32(0x4f8c) = 0x141D1519; + } else { + MCHBAR32(0x4f8c) = 0x551D1519; + } +} + +static void prepare_training(ramctr_timing * ctrl) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS { + // Always drive command bus + MCHBAR32(0x4004 + 0x400 * channel) |= 0x20000000; + } + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + } +} + +static void set_4008c(ramctr_timing * ctrl) +{ + int channel, slotrank; + u32 reg; + FOR_ALL_POPULATED_CHANNELS { + u32 b20, b4_8_12; + int min_320c = 10000; + int max_320c = -10000; + + FOR_ALL_POPULATED_RANKS { + max_320c = max(ctrl->timings[channel][slotrank].val_320c, max_320c); + min_320c = min(ctrl->timings[channel][slotrank].val_320c, min_320c); + } + + if (max_320c - min_320c > 51) + b20 = 0; + else + b20 = ctrl->ref_card_offset[channel]; + + if (ctrl->reg_320c_range_threshold < max_320c - min_320c) + b4_8_12 = 0x3330; + else + b4_8_12 = 0x2220; + + reg = read32(DEFAULT_MCHBAR | 0x400c | (channel << 10)); + write32(DEFAULT_MCHBAR | 0x400c | (channel << 10), + (reg & 0xFFF0FFFF) + | (ctrl->ref_card_offset[channel] << 16) + | (ctrl->ref_card_offset[channel] << 18)); + write32(DEFAULT_MCHBAR | 0x4008 | (channel << 10), + 0x0a000000 + | (b20 << 20) + | ((ctrl->ref_card_offset[channel] + 2) << 16) + | b4_8_12); + } +} + +static void set_42a0(ramctr_timing * ctrl) +{ + int channel; + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR | (0x42a0 + 0x400 * channel), + 0x00001000 | ctrl->rankmap[channel]); + MCHBAR32(0x4004 + 0x400 * channel) &= ~0x20000000; // OK + } +} + +static int encode_5d10(int ns) +{ + return (ns + 499) / 500; +} + +/* FIXME: values in this function should be hardware revision-dependent. */ +static void final_registers(ramctr_timing * ctrl) +{ + int channel; + int t1_cycles = 0, t1_ns = 0, t2_ns; + int t3_ns; + u32 r32; + + write32(DEFAULT_MCHBAR | 0x4cd4, 0x00000046); + + write32(DEFAULT_MCHBAR | 0x400c, (read32(DEFAULT_MCHBAR | 0x400c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x440c, (read32(DEFAULT_MCHBAR | 0x440c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x4cb0, 0x00000740); + write32(DEFAULT_MCHBAR | 0x4380, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4780, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4f88, 0x5f7003ff); // OK + write32(DEFAULT_MCHBAR | 0x5064, 0x00073000 | ctrl->reg_5064b0); // OK + + FOR_ALL_CHANNELS { + switch (ctrl->rankmap[channel]) { + /* Unpopulated channel. */ + case 0: + write32(DEFAULT_MCHBAR + 0x4384 + channel * 0x400, 0); + break; + /* Only single-ranked dimms. */ + case 1: + case 4: + case 5: + write32(DEFAULT_MCHBAR + 0x4384 + channel * 0x400, 0x373131); + break; + /* Dual-ranked dimms present. */ + default: + write32(DEFAULT_MCHBAR + 0x4384 + channel * 0x400, 0x9b6ea1); + break; + } + } + + write32 (DEFAULT_MCHBAR | 0x5880, 0xca9171e5); + write32 (DEFAULT_MCHBAR | 0x5888, + (read32 (DEFAULT_MCHBAR | 0x5888) & ~0xffffff) | 0xe4d5d0); + write32 (DEFAULT_MCHBAR | 0x58a8, read32 (DEFAULT_MCHBAR | 0x58a8) & ~0x1f); + write32 (DEFAULT_MCHBAR | 0x4294, + (read32 (DEFAULT_MCHBAR | 0x4294) & ~0x30000) + | (1 << 16)); + write32 (DEFAULT_MCHBAR | 0x4694, + (read32 (DEFAULT_MCHBAR | 0x4694) & ~0x30000) + | (1 << 16)); + + MCHBAR32(0x5030) |= 1; // OK + MCHBAR32(0x5030) |= 0x80; // OK + MCHBAR32(0x5f18) = 0xfa; // OK + + /* Find a populated channel. */ + FOR_ALL_POPULATED_CHANNELS + break; + + t1_cycles = ((read32(DEFAULT_MCHBAR + 0x4290 + channel * 0x400) >> 8) & 0xff); + r32 = read32(DEFAULT_MCHBAR + 0x5064); + if (r32 & 0x20000) + t1_cycles += (r32 & 0xfff); + t1_cycles += (read32(DEFAULT_MCHBAR + channel * 0x400 + 0x42a4) & 0xfff); + t1_ns = t1_cycles * ctrl->tCK / 256 + 544; + if (!(r32 & 0x20000)) + t1_ns += 500; + + t2_ns = 10 * ((read32(DEFAULT_MCHBAR + 0x5f10) >> 8) & 0xfff); + if ( read32(DEFAULT_MCHBAR + 0x5f00) & 8 ) + { + t3_ns = 10 * ((read32(DEFAULT_MCHBAR + 0x5f20) >> 8) & 0xfff); + t3_ns += 10 * (read32(DEFAULT_MCHBAR + 0x5f18) & 0xff); + } + else + { + t3_ns = 500; + } + printk(BIOS_DEBUG, "t123: %d, %d, %d\n", + t1_ns, t2_ns, t3_ns); + write32 (DEFAULT_MCHBAR + 0x5d10, + ((encode_5d10(t1_ns) + encode_5d10(t2_ns)) << 16) + | (encode_5d10(t1_ns) << 8) + | ((encode_5d10(t3_ns) + encode_5d10(t2_ns) + encode_5d10(t1_ns)) << 24) + | (read32(DEFAULT_MCHBAR + 0x5d10) & 0xC0C0C0C0) + | 0xc); +} + +static void save_timings(ramctr_timing * ctrl) +{ + struct mrc_data_container *mrcdata; + int output_len = ALIGN(sizeof (*ctrl), 16); + + /* Save the MRC S3 restore data to cbmem */ + mrcdata = cbmem_add + (CBMEM_ID_MRCDATA, + output_len + sizeof(struct mrc_data_container)); + + printk(BIOS_DEBUG, "Relocate MRC DATA from %p to %p (%u bytes)\n", + ctrl, mrcdata, output_len); + + mrcdata->mrc_signature = MRC_DATA_SIGNATURE; + mrcdata->mrc_data_size = output_len; + mrcdata->reserved = 0; + memcpy(mrcdata->mrc_data, ctrl, sizeof (*ctrl)); + + /* Zero the unused space in aligned buffer. */ + if (output_len > sizeof (*ctrl)) + memset(mrcdata->mrc_data+sizeof (*ctrl), 0, + output_len - sizeof (*ctrl)); + + mrcdata->mrc_checksum = compute_ip_checksum(mrcdata->mrc_data, + mrcdata->mrc_data_size); +} + +static void restore_timings(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS + MCHBAR32(0x4004 + 0x400 * channel) = + ctrl->tRRD + | (ctrl->tRTP << 4) + | (ctrl->tCKE << 8) + | (ctrl->tWTR << 12) + | (ctrl->tFAW << 16) + | (ctrl->tWR << 24) + | (ctrl->reg_4004_b30[channel] << 30); + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + } + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + 4 * lane, 0); + } + + FOR_ALL_POPULATED_CHANNELS + write32(DEFAULT_MCHBAR + 0x4008 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4008 + + 0x400 * channel) | 0x8000000); + + FOR_ALL_POPULATED_CHANNELS { + udelay (1); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + + printk(BIOS_SPEW, "CPE\n"); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + printk(BIOS_SPEW, "CP5b\n"); + + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + + u32 reg, addr; + + while (!(MCHBAR32(0x5084) & 0x10000)) ; + do { + reg = MCHBAR32(0x428c); + } while ((reg & 0x14) == 0); + + // Set state of memory controller + MCHBAR32(0x5030) = 0x116; + MCHBAR32(0x4ea0) = 0; + + // Wait 500us + udelay(500); + + FOR_ALL_CHANNELS { + // Set valid rank CKE + reg = 0; + reg = (reg & ~0xf) | ctrl->rankmap[channel]; + addr = 0x400 * channel + 0x42a0; + MCHBAR32(addr) = reg; + + // Wait 10ns for ranks to settle + //udelay(0.01); + + reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4); + MCHBAR32(addr) = reg; + + // Write reset using a NOP + write_reset(ctrl); + } + + /* mrs commands. */ + dram_mrscommands(ctrl); + + printk(BIOS_SPEW, "CP5c\n"); + + write32(DEFAULT_MCHBAR + 0x3000, 0); + + FOR_ALL_CHANNELS { + write32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c, + 0 | (read32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c) & + ~0x3f000000)); + udelay(2); + } + + write32(DEFAULT_MCHBAR + 0x4ea8, 0); +} + +void init_dram_ddr3(spd_raw_data * spds, int mobile, int min_tck, + int s3resume) +{ + int me_uma_size; + int cbmem_was_inited; + + MCHBAR32(0x5f00) |= 1; + + report_platform_info(); + + /* Wait for ME to be ready */ + intel_early_me_init(); + me_uma_size = intel_early_me_uma_size(); + + printk(BIOS_DEBUG, "Starting native Platform init\n"); + + u32 reg_5d10; + + wait_txt_clear(); + + wrmsr(0x000002e6, (msr_t) { .lo = 0, .hi = 0 }); + + reg_5d10 = read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + if ((pcie_read_config16(SOUTHBRIDGE, 0xa2) & 0xa0) == 0x20 /* 0x0004 */ + && reg_5d10 && !s3resume) { + write32(DEFAULT_MCHBAR | 0x5d10, 0); + /* Need reset. */ + outb(0x6, 0xcf9); + + hlt(); + } + + ramctr_timing ctrl; + + memset(&ctrl, 0, sizeof (ctrl)); + + early_pch_init_native(); + early_thermal_init(); + + ctrl.mobile = mobile; + ctrl.tCK = min_tck; + + /* FIXME: for non-S3 we should be able to use timing caching with + proper verification. Right now we use timings only for S3 case. + */ + if (s3resume) { + struct mrc_data_container *mrc_cache; + + mrc_cache = find_current_mrc_cache(); + if (!mrc_cache || mrc_cache->mrc_data_size < sizeof (ctrl)) { + /* Failed S3 resume, reset to come up cleanly */ + outb(0x6, 0xcf9); + hlt(); + } + memcpy(&ctrl, mrc_cache->mrc_data, sizeof (ctrl)); + } + + if (!s3resume) { + dimm_info info; + + /* Get DDR3 SPD data */ + dram_find_spds_ddr3(spds, &info, &ctrl); + + /* Find fastest common supported parameters */ + dram_find_common_params(&info, &ctrl); + + dram_dimm_mapping(&info, &ctrl); + } + + /* Set MCU frequency */ + dram_freq(&ctrl); + + if (!s3resume) { + /* Calculate timings */ + dram_timing(&ctrl); + } + + /* Set version register */ + MCHBAR32(0x5034) = 0xC04EB002; + + /* Enable crossover */ + dram_xover(&ctrl); + + /* Set timing and refresh registers */ + dram_timing_regs(&ctrl); + + /* Power mode preset */ + MCHBAR32(0x4e80) = 0x5500; + + /* Set scheduler parameters */ + MCHBAR32(0x4c20) = 0x10100005; + + /* Set cpu specific register */ + set_4f8c(); + + /* Clear IO reset bit */ + MCHBAR32(0x5030) &= ~0x20; + + /* Set MAD-DIMM registers */ + dram_dimm_set_mapping(&ctrl); + printk(BIOS_DEBUG, "Done dimm mapping\n"); + + /* Zone config */ + dram_zones(&ctrl, 1); + + /* Set memory map */ + dram_memorymap(&ctrl, me_uma_size); + printk(BIOS_DEBUG, "Done memory map\n"); + + /* Set IO registers */ + dram_ioregs(&ctrl); + printk(BIOS_DEBUG, "Done io registers\n"); + + udelay(1); + + if (s3resume) { + restore_timings(&ctrl); + } else { + /* Do jedec ddr3 reset sequence */ + dram_jedecreset(&ctrl); + printk(BIOS_DEBUG, "Done jedec reset\n"); + + /* MRS commands */ + dram_mrscommands(&ctrl); + printk(BIOS_DEBUG, "Done MRS commands\n"); + dram_mrscommands(&ctrl); + + /* Prepare for memory training */ + prepare_training(&ctrl); + + read_training(&ctrl); + write_training(&ctrl); + + printk(BIOS_SPEW, "CP5a\n"); + + discover_edges(&ctrl); + + printk(BIOS_SPEW, "CP5b\n"); + + command_training(&ctrl); + + printk(BIOS_SPEW, "CP5c\n"); + + discover_edges_write(&ctrl); + + discover_timC_write(&ctrl); + + normalize_training(&ctrl); + } + + set_4008c(&ctrl); + + write_controller_mr(&ctrl); + + if (!s3resume) { + channel_test(&ctrl); + } + + /* FIXME: should be hardware revision-dependent. */ + write32(DEFAULT_MCHBAR | 0x5024, 0x00a030ce); + + set_scrambling_seed(&ctrl); + + set_42a0(&ctrl); + + final_registers(&ctrl); + + /* Zone config */ + dram_zones(&ctrl, 0); + + if (!s3resume) + quick_ram_check(); + + intel_early_me_status(); + intel_early_me_init_done(ME_INIT_STATUS_SUCCESS); + intel_early_me_status(); + + post_system_agent_init(); + report_memory_config(); + + cbmem_was_inited = !cbmem_recovery(s3resume); + if (!s3resume) + save_timings(&ctrl); + if (s3resume && !cbmem_was_inited) { + /* Failed S3 resume, reset to come up cleanly */ + outb(0x6, 0xcf9); + hlt(); + } +} diff --git a/src/northbridge/intel/sandybridge/raminit_native.h b/src/northbridge/intel/sandybridge/raminit_native.h new file mode 100644 index 0000000000..7ed75ff5e7 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RAMINIT_H +#define RAMINIT_H + +#include + +/* The order is ch0dimmA, ch0dimmB, ch1dimmA, ch1dimmB. */ +void init_dram_ddr3(spd_raw_data *spds, int mobile, int min_tck, int s3resume); +void read_spd(spd_raw_data *spd, u8 addr); + +#endif /* RAMINIT_H */ diff --git a/src/northbridge/intel/sandybridge/raminit_patterns.h b/src/northbridge/intel/sandybridge/raminit_patterns.h new file mode 100644 index 0000000000..61c67b47fa --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_patterns.h @@ -0,0 +1,639 @@ +const u32 pattern[][16] = { + {0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff}, + {0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000}, + {0xe62d6424, 0x9277e09e, 0x8f43dc3f, 0x76eae589, + 0x0010fdc6, 0xdc55e01c, 0x5effb0ab, 0x6cba5d29, + 0xa43d1e64, 0xab5c2e0f, 0x7796ed16, 0x96023bf4, + 0xa74c831d, 0x90f138c0, 0x17830a8a, 0x5ac17c47}, + {0x359ebbeb, 0x2b9b4512, 0xef584d98, 0x106bf7cb, + 0x363525ad, 0xb3a4dfdc, 0xa6b9fcd8, 0xd21689ec, + 0x84a3695b, 0xbd9c2e27, 0xdb3d0f44, 0x988158f1, + 0xcca91d3f, 0xb62a6d12, 0xe905e4cf, 0x7f1fa626}, + {0xe58efeae, 0xcd006081, 0xa9119403, 0xbcfbd35f, + 0x213b3bf7, 0x7bfcb773, 0xc85143f9, 0x0bdbff50, + 0xa3053c90, 0x51d66cb7, 0x296f4387, 0xb715f99e, + 0xfaddc989, 0xbb1de8a7, 0x39206b4d, 0x80174a57}, + {0xa1622ac1, 0xb4f4a5f0, 0x16dc2bc3, 0x50fb0954, + 0x2e261721, 0x52b82c3c, 0x821902b8, 0x0d4b6c38, + 0x1f618631, 0x047956f3, 0xd4337f5a, 0x591f8002, + 0x27f28db2, 0xfae37369, 0xb3f27580, 0x3cdb6397}, + {0x3dee23be, 0x19f36408, 0x227f4a6a, 0x024603c5, + 0xd5e062db, 0x6d8d4c5c, 0x7ff693b0, 0x76641be9, + 0x9e74f41c, 0xe7bc7f33, 0x2636f2e9, 0x70279750, + 0xce2355aa, 0x32d230ef, 0x22f9b468, 0xadd4e7a2}, + {0x936c0fed, 0xba0612d5, 0xa97c1ea7, 0x10e29d67, + 0x1c4c5dc8, 0x83645621, 0xcd8b521c, 0xb8301817, + 0xac7d6571, 0xcc41d200, 0x4ebdefdd, 0xd2917bde, + 0x60f75acc, 0x7791534b, 0x26ea2a83, 0x6b74513a}, + {0xd1957b85, 0xc6f8f9ca, 0xf04fb4be, 0xfeb786fb, + 0xa1dea3aa, 0x67fe7db6, 0x25d49c87, 0xe3d54870, + 0x93dc1f86, 0x7d0c1a18, 0x9272e128, 0x68e1b876, + 0xce284c9e, 0x8fa18792, 0x5785a340, 0xb6fcf198}, + {0xff7d8e4a, 0x0c21ee43, 0xe820b388, 0xb4443c0e, + 0xa1e6e498, 0x5c426110, 0x1b434ef3, 0xbef05b91, + 0xa6907968, 0x53662ac3, 0x6defac32, 0x2c11c29c, + 0x6175cced, 0xb17dd3ad, 0x6e6a1076, 0x1372b1fa}, + {0x4408ed06, 0x49460ffd, 0xb49d26cb, 0x6a3662a5, + 0x5e857047, 0xa387cd4a, 0x04edc81e, 0xfd94d8d4, + 0x2fe48d91, 0x9d2356bc, 0x96131878, 0xaca3fce4, + 0xbb312c6c, 0x5023b090, 0x3614be70, 0xa14dfabb}, + {0xd4cc1e83, 0x757a1930, 0xc3d16a61, 0x9e0d6681, + 0x8a081fa9, 0xbd11c888, 0x1672f010, 0xa083f71c, + 0x1ec02eef, 0xc4586ca8, 0x6d322b35, 0x56054679, + 0x1552a0ff, 0x5cb7707e, 0xdfb55d4a, 0xcc76cc07}, + {0x507cf71f, 0x2166421a, 0x54be4af0, 0xfd42158c, + 0x417b1f7f, 0x9466860b, 0x3a0075bf, 0x2055575c, + 0xcedfe7ab, 0xbe85aa5f, 0x39d0c2e3, 0x851c19df, + 0x39a35a3f, 0x3fb10d7d, 0x20b14899, 0x703b7f08}, + {0x8a7d9dd1, 0x33235565, 0xbd3d2e57, 0xa48c2726, + 0x0d5e2e13, 0xae421ff9, 0x8784a224, 0xf66c1510, + 0x057627aa, 0x8fb0cb41, 0x4289975a, 0xb181adfa, + 0x59f2059a, 0xe86feb05, 0x84222fc1, 0x319b3ce9}, + {0xe1e243b8, 0x3b0bcc1a, 0x70396f00, 0x5caff44d, + 0xe96961b3, 0xad73f692, 0x8b841a2d, 0xf5838839, + 0xec9c9d04, 0xcc2b5562, 0xf8ca2549, 0xa9c52ff8, + 0x3b2fde68, 0x3d4dc7f0, 0xa57387d0, 0x051199ad}, + {0x5f0ce4fc, 0xd830fbb7, 0x90abeb8f, 0x96d9cdbb, + 0x58f80a80, 0x0baaca36, 0x81a23623, 0x77127614, + 0xaa8382cd, 0x0922fbca, 0xd84d37e1, 0x721297df, + 0x160f3b3a, 0x10a1ecdc, 0x151c92f4, 0xc1fdcdab}, + {0x261c45cc, 0xfeddd2da, 0xfc3cb1c1, 0x6639641f, + 0x2c011892, 0x7108bee2, 0x8545e0b9, 0x7dd36dab, + 0x07d91950, 0x1520adcb, 0xf84aa939, 0x07d9bb2d, + 0xdf1ed826, 0xaee3c814, 0x1dca1e81, 0xc8e9f486}, + {0x933d306a, 0xaab7103d, 0xa8be37be, 0x49612f3a, + 0xb0cf28e5, 0xf9648902, 0x106d7c11, 0xf32e1813, + 0x21af36ef, 0xe695e4c4, 0x7ee1831d, 0x2aeda467, + 0x99d0c655, 0x3f0691ab, 0xcd68f7c1, 0xb469a20e}, + {0x8557aef0, 0x3eb0e373, 0x0853ac31, 0xe5bded62, + 0x3eddb0dd, 0x6bbf1caf, 0x2119c3d9, 0xe1732350, + 0x55456c75, 0xf6119375, 0x498dd1ad, 0x13f80916, + 0xb97f9f5e, 0x921d9f4c, 0xabdee367, 0x1d6bb8bf}, + {0xd165a3be, 0xd8b41598, 0xa20e1809, 0xefd5c8ce, + 0x18935c80, 0xdf1911f9, 0xc9e449eb, 0xb887a4d7, + 0x4a324f6f, 0x533e8031, 0x1c21c074, 0xa95f1ea5, + 0x765b320a, 0x839d7dfb, 0xc7d3aa93, 0xe534ae3d}, + {0xbe8592c8, 0x068457e6, 0x89b94fa3, 0xd522ad02, + 0x7e7db0b7, 0x2c5b896f, 0x9f8ecb37, 0x05b983ff, + 0x3fe9b25f, 0x34a6215b, 0x0592ba34, 0xd564f85a, + 0x156c426d, 0x25ad5460, 0xe7b5e8b7, 0xa73285c6}, + {0x5ad8d838, 0x27b42d36, 0xcc806ad1, 0x157a058a, + 0x7297735a, 0xffd6df8d, 0xff96f7a2, 0x155b27ea, + 0x84708101, 0x979fd78b, 0x49797d0c, 0x0dc93e3c, + 0x20287332, 0xed759f88, 0xe5068529, 0xb83aa781}, + {0xc38b302c, 0x57b54075, 0xac810692, 0xb0d493e7, + 0x4adda486, 0x0665ce2e, 0xb2a9c003, 0xafacc4ce, + 0x4d5e906d, 0xb3d52fab, 0xe6962c6b, 0x850f4dd1, + 0x5021656c, 0x5df6c06b, 0x9255125b, 0x2363c478}, + {0x188b715c, 0xe8b884b0, 0x5e6d0b9a, 0x1f0051e1, + 0xd2d35d4c, 0xbfeaecbe, 0xc84bb0ad, 0x67a232d6, + 0x99001587, 0xbf4313e1, 0x74f64061, 0x2c1fc562, + 0xb6fe8ca6, 0x5226a239, 0xf5198574, 0x61b51dca}, + {0x51dcecd3, 0xbadbe596, 0xebe3e84a, 0x772bfdfc, + 0x03656ac5, 0xa7c36e91, 0x6cd32cf0, 0xc3f699dd, + 0x7d5aba01, 0x51e38e82, 0x23103a98, 0x20298b9d, + 0x19436510, 0x63ad7e6c, 0x8bc2b33f, 0x27079917}, + {0x8bd5be78, 0xf2403bfa, 0x780ebdb6, 0x94c53b64, + 0x6241c2e2, 0x5bfb081e, 0x6799e88f, 0xc997b7d1, + 0x466ac8b1, 0xbf5909da, 0x497ea39f, 0x402ffb48, + 0xd7470c2d, 0x8510aba9, 0x6c52a1c9, 0x812ca967}, + {0x031f7ab4, 0xd32fe890, 0x36ae6de5, 0x083dcde4, + 0x99a7f12f, 0xe44864a7, 0x02b75fff, 0xf25dda35, + 0x7679ff4f, 0xed421e01, 0xd9c2cfa1, 0xd36b4e82, + 0x5315d908, 0xc7ebcb2a, 0xb6f3e4c1, 0xf5bfbae9}, + {0x3f4a2a96, 0x64d8bd5a, 0x19acd70d, 0xf62fcdd9, + 0x5de99cdf, 0x32f3b7cb, 0x2c020578, 0x4e9bafb8, + 0x74919a08, 0xaba33e91, 0xa6bd2254, 0x2435a9b9, + 0x47e2a1b4, 0xe837a28e, 0xe113f1b0, 0x7654bd79}, + {0x05537a6c, 0x77be1a5c, 0x4c7492c9, 0x9086bfb0, + 0x257adc18, 0xf4787fc1, 0xe3fb6d53, 0x9525e589, + 0x445a65bc, 0x833f7d08, 0x69cf1f7e, 0x9a6372e1, + 0xceedb52e, 0x31032997, 0xd1c36828, 0x132772d6}, + {0x0a166972, 0x89beaf3b, 0x8d780fbc, 0x8aea5392, + 0x58347a41, 0x1e381ec2, 0xcc6280c8, 0xee0863e1, + 0x976e2dd2, 0x8c6ee6e2, 0xa0ca57cd, 0x95114a7d, + 0x3c096704, 0xa941769d, 0x2de20c05, 0x0bf8f812}, + {0x22779d6c, 0x94e12e8f, 0x5ce40299, 0xea1b55b0, + 0x9ebec05d, 0xe076cd2b, 0x8fef5648, 0x6a284c65, + 0xa790b705, 0xf0b19997, 0x0d8ca8af, 0x17440419, + 0xef4f702f, 0x33cbcbb1, 0x83d60f26, 0x48988397}, + {0x0fed7f53, 0xb5acbb67, 0xc031c73f, 0x5364d9ef, + 0xa6dbd12d, 0x82174a6c, 0xccf8e7ab, 0xc473c036, + 0xcff493d8, 0xad9afc3b, 0x316a24e8, 0x1842bea4, + 0x4cc0c82e, 0x28ccd91e, 0xd7311b5d, 0x50a89860}, +}; + +static const u8 use_base[63][32] = { + { + 0x0e, 0x9e, 0xa1, 0x39, 0x06, 0x26, 0xc5, 0xe9, 0xed, 0x07, 0x49, 0x3b, 0x34, 0x7f, 0x1c, 0xa8, + 0xdf, 0x7b, 0xb7, 0xb8, 0x28, 0xbe, 0x8a, 0x70, 0x17, 0xe5, 0xc0, 0x44, 0x4a, 0x8e, 0x61, 0x3b, + }, + { + 0x42, 0xe6, 0xe0, 0x6a, 0xb3, 0x08, 0x28, 0xaf, 0xfa, 0xb9, 0xb7, 0x32, 0x83, 0x5c, 0xef, 0x3d, + 0x90, 0x91, 0x64, 0x31, 0xe9, 0x3c, 0x92, 0xe6, 0xa3, 0xd4, 0x6a, 0xc6, 0x01, 0xa6, 0xeb, 0xe6, + }, + { + 0x39, 0x7f, 0x6f, 0x81, 0xb4, 0x33, 0x4a, 0xde, 0x4f, 0x77, 0x28, 0x47, 0x08, 0xf9, 0x3a, 0x55, + 0x21, 0x57, 0x27, 0x59, 0xf5, 0x96, 0xad, 0xc1, 0x10, 0x33, 0xe0, 0xe2, 0xf8, 0xb6, 0x49, 0xbd, + }, + { + 0xdf, 0x57, 0x60, 0x27, 0x95, 0x50, 0x3a, 0x8c, 0x34, 0x8b, 0xae, 0xc5, 0x69, 0x26, 0xca, 0x39, + 0x55, 0x98, 0xfb, 0x05, 0x3c, 0x1c, 0x8d, 0xf8, 0xb9, 0x99, 0x05, 0x40, 0xe5, 0x5e, 0x2f, 0xf6, + }, + { + 0xc1, 0x6a, 0xea, 0xd6, 0x39, 0x56, 0x08, 0x89, 0x83, 0x4c, 0xef, 0xda, 0xb2, 0x69, 0x76, 0xe4, + 0x75, 0x3f, 0x39, 0x13, 0x96, 0xb5, 0x41, 0x84, 0x00, 0x64, 0x79, 0x47, 0xe4, 0xcb, 0xc3, 0xd0, + }, + { + 0xf8, 0xb1, 0x19, 0x76, 0x51, 0x99, 0xd7, 0x45, 0x38, 0x40, 0xbf, 0x10, 0x4c, 0x89, 0x43, 0xa9, + 0x89, 0xe2, 0x85, 0x3f, 0xb4, 0xe8, 0xbf, 0x5e, 0xc2, 0xb4, 0x16, 0x6d, 0x1c, 0x61, 0xca, 0x40, + }, + { + 0x1c, 0xdc, 0xa6, 0xdb, 0x71, 0x8b, 0xf9, 0xbb, 0xee, 0xc2, 0xa5, 0x66, 0xa4, 0xbc, 0xb6, 0x89, + 0x58, 0xb9, 0x6f, 0x57, 0x71, 0x57, 0x5c, 0xf0, 0xed, 0xcf, 0x2c, 0x2e, 0x1d, 0x34, 0xc3, 0x00, + }, + { + 0x1d, 0x30, 0x03, 0xb9, 0x15, 0x8e, 0x47, 0x8c, 0xf2, 0x4e, 0x2d, 0xf1, 0xbf, 0x96, 0xa7, 0xa1, + 0x3f, 0x26, 0xc3, 0xc9, 0x08, 0x0b, 0xa8, 0xdd, 0x9b, 0xeb, 0xbc, 0x77, 0x1c, 0x10, 0x03, 0x77, + }, + { + 0x50, 0x7e, 0x62, 0x26, 0xcb, 0x49, 0x7b, 0x1a, 0xd4, 0x54, 0xf1, 0x25, 0x3d, 0xa2, 0xe6, 0x8a, + 0xb3, 0x62, 0xf1, 0x7e, 0x03, 0xef, 0x1b, 0x27, 0x21, 0xcc, 0xfc, 0x72, 0x30, 0x0c, 0x69, 0xad, + }, + { + 0x11, 0xf5, 0xb2, 0xfa, 0x2d, 0xbc, 0xa1, 0xd9, 0x74, 0x15, 0x59, 0xf2, 0xc6, 0x66, 0x4f, 0xde, + 0x84, 0x82, 0x4f, 0xe8, 0x33, 0xd5, 0xc5, 0xdd, 0xba, 0x0c, 0xc7, 0x51, 0x1f, 0x3c, 0x6d, 0x44, + }, + { + 0xcf, 0xf5, 0x3b, 0xc1, 0xbd, 0x5f, 0x9c, 0xad, 0x57, 0xfb, 0xfc, 0xbe, 0x95, 0xa0, 0x48, 0x58, + 0x8a, 0x68, 0x97, 0x71, 0xf3, 0xc0, 0xd1, 0x31, 0x33, 0xb9, 0x3c, 0xe9, 0x4f, 0xbb, 0x8d, 0xeb, + }, + { + 0x29, 0x0c, 0xa1, 0xc8, 0x04, 0xdc, 0xf9, 0x25, 0x85, 0x7e, 0xea, 0x6d, 0x75, 0x28, 0x69, 0x3f, + 0x3a, 0x83, 0xe4, 0x33, 0x31, 0x77, 0x57, 0x2e, 0xa9, 0xa8, 0x05, 0xfe, 0x19, 0xb7, 0xc4, 0xd1, + }, + { + 0x6d, 0x5d, 0x3f, 0x4f, 0x8a, 0x6a, 0x77, 0x2d, 0xf7, 0x9f, 0x73, 0xab, 0x40, 0xd9, 0x89, 0x57, + 0x69, 0xd7, 0xc8, 0xc3, 0x69, 0x54, 0x93, 0x7c, 0x9f, 0x4a, 0xcc, 0xaf, 0xcc, 0x0e, 0xe0, 0xb8, + }, + { + 0xb6, 0xd5, 0x36, 0x3f, 0x1c, 0x34, 0x54, 0x9b, 0xfc, 0xec, 0x5b, 0xb0, 0x26, 0xa6, 0xc0, 0x61, + 0x6d, 0x4c, 0x4f, 0x86, 0x2a, 0xbd, 0x34, 0x35, 0x52, 0x2c, 0x82, 0x01, 0x66, 0x0e, 0x80, 0x01, + }, + { + 0x48, 0x39, 0x43, 0xb8, 0xf9, 0x2b, 0x25, 0xe8, 0xf7, 0xf0, 0x1a, 0xed, 0x33, 0x1e, 0x30, 0xba, + 0x15, 0x37, 0xeb, 0xae, 0x97, 0xa7, 0x36, 0xa4, 0xc7, 0x1f, 0x91, 0x01, 0x38, 0x80, 0x5a, 0x76, + }, + { + 0x74, 0xbe, 0x15, 0x6b, 0x85, 0x28, 0xe4, 0xc4, 0x13, 0x68, 0x67, 0x03, 0x27, 0x7e, 0x32, 0x08, + 0x87, 0x23, 0xda, 0xf2, 0x47, 0xdd, 0xac, 0x2c, 0xc5, 0x7f, 0x06, 0xc8, 0x17, 0x4c, 0x6c, 0x81, + }, + { + 0x0d, 0x9f, 0x68, 0xbb, 0xa6, 0x6c, 0x7d, 0x3f, 0x81, 0xcf, 0x9a, 0x52, 0x87, 0xce, 0x98, 0x25, + 0x40, 0x2b, 0x1b, 0xdf, 0xd3, 0x6b, 0x53, 0xed, 0x80, 0xd2, 0x3d, 0xca, 0xdf, 0x07, 0x4e, 0x6b, + }, + { + 0xff, 0xf1, 0xd2, 0x1f, 0xbd, 0xd6, 0xa7, 0x3d, 0xb4, 0xc6, 0x88, 0x9d, 0x7b, 0x05, 0x04, 0x0b, + 0x4c, 0x6f, 0x11, 0x7f, 0x19, 0x18, 0x48, 0xf1, 0x26, 0xad, 0xd0, 0x60, 0xfa, 0x40, 0x35, 0xbb, + }, + { + 0xae, 0xec, 0x0e, 0x2e, 0xfd, 0x46, 0xf0, 0x9c, 0x06, 0x1b, 0x62, 0xbf, 0xf0, 0x3e, 0xba, 0xdf, + 0xb2, 0xa2, 0x83, 0x83, 0xda, 0x04, 0x15, 0xec, 0x1e, 0x2e, 0x1a, 0x64, 0x08, 0x8e, 0xd3, 0x87, + }, + { + 0x61, 0x14, 0x05, 0x0b, 0xdb, 0xf4, 0xf0, 0xa3, 0x41, 0x11, 0x7b, 0xd9, 0xa1, 0x40, 0x4f, 0x62, + 0x98, 0x37, 0xa0, 0x90, 0x3d, 0x78, 0x63, 0x24, 0xbc, 0x8e, 0x9e, 0x99, 0x2e, 0xc7, 0xb1, 0x6d, + }, + { + 0x0f, 0xf1, 0x5f, 0xe4, 0x94, 0x3d, 0x24, 0x0d, 0xa2, 0xcf, 0xed, 0xbb, 0x55, 0x8e, 0xdb, 0x07, + 0x12, 0x05, 0x79, 0x82, 0xb1, 0x3a, 0x71, 0x76, 0xbb, 0x8b, 0xcb, 0xcc, 0x00, 0x40, 0x2e, 0xab, + }, + { + 0x48, 0x59, 0xfa, 0x46, 0x15, 0x5e, 0xa2, 0x0d, 0xe8, 0x81, 0x69, 0xe6, 0x2f, 0x5f, 0x6d, 0xaf, + 0xad, 0xc7, 0x30, 0xd6, 0xec, 0x03, 0x1e, 0x19, 0xdd, 0x1d, 0x00, 0x94, 0x0b, 0x93, 0x5a, 0x28, + }, + { + 0xfa, 0x64, 0x62, 0x57, 0x02, 0x7e, 0x37, 0xb8, 0x10, 0xa9, 0x4b, 0xe7, 0xbf, 0x03, 0xdc, 0x1c, + 0xe7, 0x21, 0x68, 0x60, 0x53, 0xec, 0xf0, 0xfc, 0xbf, 0xe5, 0x8e, 0xca, 0x77, 0xdb, 0xa5, 0xae, + }, + { + 0xc1, 0xa9, 0x9f, 0xc2, 0x87, 0x11, 0xad, 0x1c, 0xc5, 0x56, 0x61, 0xc0, 0x20, 0x33, 0xc9, 0x85, + 0xf0, 0x81, 0x36, 0x18, 0xdb, 0xaa, 0x7e, 0x79, 0x36, 0x2d, 0xfc, 0x8f, 0x72, 0x3c, 0x4c, 0xa3, + }, + { + 0xb4, 0xd3, 0x38, 0xed, 0xd3, 0x6c, 0x03, 0x26, 0x0e, 0x1c, 0x8a, 0xa8, 0x72, 0x17, 0xcb, 0xf7, + 0x87, 0xf3, 0x3f, 0x73, 0x5d, 0x12, 0xe8, 0x3b, 0x55, 0xb6, 0xcc, 0xc6, 0x85, 0xad, 0x9f, 0x9e, + }, + { + 0xe9, 0x97, 0xde, 0x7f, 0xe0, 0x9a, 0xc1, 0xdc, 0x96, 0x4c, 0xc3, 0x0d, 0x2d, 0xd4, 0x98, 0x54, + 0x89, 0x1c, 0x57, 0x7d, 0x17, 0x96, 0xe9, 0x2f, 0x02, 0x73, 0x07, 0x5c, 0x8b, 0x44, 0xd7, 0x75, + }, + { + 0xaf, 0xf2, 0x39, 0xc4, 0x23, 0xff, 0xd5, 0x85, 0xf3, 0x11, 0x44, 0x9e, 0xab, 0x59, 0x29, 0x33, + 0xe6, 0xc2, 0x1d, 0xba, 0xa8, 0x5b, 0x70, 0xc3, 0xc0, 0xf8, 0x56, 0x3b, 0x8e, 0x43, 0x4c, 0x4b, + }, + { + 0x46, 0x93, 0xb5, 0x8a, 0x71, 0x67, 0xcb, 0x42, 0x6b, 0x1b, 0x43, 0x58, 0x8b, 0x18, 0xb9, 0xf3, + 0xdc, 0x91, 0x1c, 0x08, 0x83, 0xe3, 0x63, 0xfa, 0xf9, 0x99, 0x99, 0xcb, 0x05, 0x15, 0x7b, 0x85, + }, + { + 0xcf, 0xc6, 0x3c, 0x01, 0xbd, 0xfb, 0x00, 0x8c, 0x8b, 0x07, 0x69, 0xac, 0x61, 0xdb, 0x30, 0xd5, + 0x7c, 0x71, 0x98, 0x69, 0x2e, 0x4a, 0x4a, 0x44, 0xe4, 0x72, 0x6f, 0xf2, 0x9d, 0x58, 0x02, 0x23, + }, + { + 0x29, 0x10, 0xe1, 0xe5, 0x3f, 0xe6, 0x26, 0xaf, 0x30, 0x3c, 0xfe, 0x1f, 0x97, 0x21, 0xdd, 0x8c, + 0xbb, 0x90, 0xdb, 0x2a, 0xb5, 0xb4, 0x46, 0x43, 0x50, 0x0b, 0x6b, 0xa8, 0x8f, 0x55, 0xfb, 0xd2, + }, + { + 0x89, 0x56, 0x19, 0xf9, 0x4a, 0xec, 0x38, 0x99, 0xaa, 0xcc, 0xc5, 0x15, 0xdf, 0x19, 0xee, 0x1a, + 0xc0, 0x94, 0xaf, 0x0f, 0x7b, 0xf4, 0x06, 0xf1, 0x70, 0xa3, 0x79, 0x06, 0xba, 0x4e, 0x09, 0xa6, + }, + { + 0x53, 0x3a, 0x35, 0x98, 0xa9, 0xcb, 0x2c, 0x72, 0x73, 0xb2, 0xb0, 0x63, 0x38, 0xfc, 0xa3, 0xa5, + 0xa3, 0xf1, 0x4d, 0x04, 0x2f, 0x1b, 0x83, 0xbd, 0xed, 0x20, 0x8e, 0xa5, 0x1e, 0x9d, 0xd6, 0x69, + }, + { + 0x78, 0x44, 0xe3, 0x22, 0xa4, 0x7c, 0x53, 0xef, 0x60, 0x8e, 0x39, 0x95, 0x7d, 0x9d, 0x3d, 0x0b, + 0x4a, 0x4f, 0xf4, 0x73, 0xf8, 0xfe, 0xe7, 0x65, 0x86, 0x72, 0x13, 0x0b, 0x4e, 0x8f, 0x4d, 0x7b, + }, + { + 0x46, 0xba, 0xd2, 0x82, 0x46, 0x18, 0x6e, 0x46, 0x96, 0x05, 0x1d, 0xfb, 0xf6, 0xde, 0xba, 0xb5, + 0x6b, 0x73, 0x7a, 0xd5, 0x76, 0x11, 0xb7, 0x7c, 0x0d, 0x61, 0xba, 0xf9, 0x05, 0x12, 0x2b, 0x4e, + }, + { + 0x41, 0x08, 0x8b, 0x63, 0x10, 0xeb, 0x85, 0x10, 0xf3, 0x05, 0x84, 0xc0, 0x81, 0x1f, 0x7f, 0xd3, + 0x9d, 0x01, 0xdc, 0x32, 0xd0, 0xe2, 0xd1, 0xd1, 0x41, 0x8a, 0xa0, 0xf2, 0x64, 0xda, 0x8b, 0x0e, + }, + { + 0x99, 0xf1, 0xf2, 0x88, 0xe8, 0x72, 0x8b, 0x40, 0x17, 0xcc, 0xa7, 0x4c, 0x7d, 0xf1, 0xc4, 0x3f, + 0xbf, 0xf8, 0xbe, 0xc9, 0xce, 0xce, 0x2b, 0xe9, 0x4d, 0x5b, 0xd1, 0xef, 0x76, 0xae, 0x3b, 0xbb, + }, + { + 0x64, 0x69, 0x47, 0x03, 0xe9, 0x3b, 0xc2, 0x2d, 0x2d, 0x1a, 0xdd, 0xa4, 0xac, 0xb6, 0x6e, 0x7a, + 0x41, 0x7d, 0x82, 0xed, 0x54, 0xf7, 0x05, 0xd8, 0xae, 0x07, 0x6c, 0x1c, 0xf6, 0x51, 0xa1, 0xc0, + }, + { + 0x84, 0xaf, 0xe8, 0x59, 0x0c, 0x14, 0x24, 0xec, 0xa0, 0xd2, 0xf3, 0x8f, 0x2f, 0x22, 0xe4, 0x52, + 0xf8, 0xa7, 0x39, 0x74, 0x7f, 0x80, 0x23, 0x3f, 0x30, 0xe3, 0xcb, 0x26, 0x12, 0x5c, 0xa7, 0x93, + }, + { + 0x48, 0xc8, 0x41, 0x42, 0xc5, 0xe7, 0x4d, 0x91, 0x54, 0xa6, 0x5b, 0xba, 0x46, 0x89, 0xd5, 0x42, + 0x46, 0xbc, 0xf2, 0x67, 0x4a, 0xf9, 0x88, 0xe9, 0x02, 0xa5, 0xc2, 0x38, 0x9d, 0x6a, 0x75, 0xcd, + }, + { + 0xab, 0x9b, 0x2c, 0x09, 0xda, 0xaa, 0xe9, 0xe2, 0xd7, 0xf1, 0xa4, 0x86, 0x41, 0x14, 0xcb, 0x4e, + 0x2b, 0xd1, 0x5a, 0x9a, 0x46, 0x47, 0x90, 0xb2, 0x63, 0x16, 0xcb, 0x60, 0x1c, 0x12, 0xe3, 0x2b, + }, + { + 0x54, 0x21, 0xc0, 0xbc, 0xb8, 0x29, 0xce, 0xa4, 0xf2, 0x38, 0xb4, 0x0d, 0x7d, 0xfa, 0xd4, 0x3e, + 0x28, 0xf4, 0xc4, 0xf4, 0x4a, 0x9f, 0x11, 0xf2, 0xe2, 0x31, 0xa3, 0x72, 0x16, 0xbb, 0xd9, 0x05, + }, + { + 0xdc, 0x6e, 0xbe, 0x64, 0xa2, 0xb2, 0x64, 0x46, 0x00, 0xee, 0xd2, 0xa5, 0x81, 0x86, 0x44, 0xa4, + 0x5c, 0xc4, 0xba, 0xc4, 0xa2, 0x44, 0x36, 0x77, 0xb2, 0xec, 0x0d, 0xbe, 0x8f, 0xc0, 0x80, 0x6f, + }, + { + 0x51, 0x51, 0xb2, 0x50, 0x11, 0x0b, 0x60, 0xb8, 0x8e, 0xc2, 0xf7, 0xa8, 0xb4, 0xd2, 0x48, 0xc3, + 0x51, 0x43, 0x52, 0xeb, 0x37, 0xac, 0xd0, 0xf7, 0x61, 0xa1, 0xe9, 0xe3, 0xa7, 0xdf, 0x26, 0xa9, + }, + { + 0x10, 0xd9, 0xf2, 0xfe, 0x71, 0x8e, 0x49, 0x2c, 0x64, 0x53, 0xb0, 0x9e, 0x7a, 0xcc, 0x86, 0xab, + 0x06, 0xd7, 0xc7, 0x13, 0xd1, 0xcb, 0x21, 0x42, 0x97, 0x29, 0x5d, 0xff, 0xaa, 0xec, 0x52, 0x40, + }, + { + 0x7b, 0x35, 0x93, 0xb9, 0xca, 0xbb, 0x7e, 0x09, 0x8f, 0x34, 0x32, 0x02, 0xe7, 0x0c, 0xcf, 0xd1, + 0x6c, 0x2d, 0x11, 0xcd, 0xa5, 0x3b, 0xba, 0x14, 0x47, 0xc5, 0x2c, 0xb8, 0xf6, 0xfb, 0x9d, 0x77, + }, + { + 0x45, 0xce, 0x12, 0x8e, 0xa2, 0x4e, 0x56, 0x58, 0xa7, 0xcf, 0x26, 0x4e, 0x32, 0x21, 0xe0, 0x2b, + 0xed, 0x8d, 0xe2, 0xd9, 0x51, 0x32, 0x9b, 0xdc, 0x7b, 0x0e, 0x14, 0x0a, 0xdb, 0x63, 0x6b, 0x42, + }, + { + 0x9d, 0x49, 0x72, 0xea, 0x89, 0xc7, 0xa2, 0xfd, 0x9a, 0x55, 0xd7, 0x05, 0x16, 0xeb, 0xf7, 0x49, + 0xb6, 0xcf, 0x56, 0xf7, 0x2b, 0xef, 0x6d, 0xbe, 0xdd, 0x0e, 0x90, 0x01, 0xaf, 0x1b, 0xbb, 0xaa, + }, + { + 0x2c, 0xb7, 0x26, 0x43, 0x02, 0xc5, 0x7b, 0xc6, 0x71, 0x1f, 0xf2, 0x28, 0xb4, 0xea, 0x5f, 0x02, + 0xc6, 0xeb, 0xb4, 0xf7, 0x6f, 0x01, 0x7f, 0xfe, 0x3a, 0xb0, 0xe3, 0xf3, 0x31, 0xd3, 0xcb, 0x0f, + }, + { + 0x75, 0x54, 0x90, 0xec, 0xed, 0xcc, 0xbc, 0x5c, 0x9b, 0x44, 0x30, 0x57, 0x57, 0xe0, 0xec, 0x58, + 0x20, 0x54, 0x20, 0xb0, 0x92, 0x3a, 0x06, 0x9f, 0x69, 0xcb, 0x88, 0xee, 0x52, 0x6e, 0x4c, 0xf6, + }, + { + 0x2a, 0x08, 0xd8, 0xdd, 0x2e, 0x07, 0x54, 0x0b, 0x37, 0xc6, 0x09, 0x26, 0x98, 0xf7, 0x45, 0xf6, + 0x05, 0x4f, 0x35, 0xe6, 0xb0, 0x44, 0xff, 0x62, 0x89, 0x47, 0x69, 0x30, 0xea, 0x3b, 0x7b, 0x81, + }, + { + 0x0f, 0x7f, 0x34, 0xbf, 0xf9, 0xce, 0xe7, 0x4c, 0xe1, 0x7a, 0x24, 0x74, 0xeb, 0x8b, 0xb0, 0x65, + 0x18, 0x19, 0xaf, 0x44, 0x45, 0x5e, 0xd9, 0xa4, 0xf0, 0xd8, 0xe8, 0x09, 0x22, 0x58, 0x4e, 0x4d, + }, + { + 0x48, 0x41, 0x7a, 0xd2, 0x26, 0xda, 0x39, 0xb2, 0xba, 0x03, 0x17, 0xe9, 0x80, 0x8e, 0x53, 0x4c, + 0xac, 0x64, 0x6b, 0xa7, 0x5f, 0xed, 0x64, 0x60, 0xb7, 0x5e, 0x7a, 0x68, 0x1a, 0x8a, 0xda, 0x2b, + }, + { + 0x52, 0x01, 0x25, 0xb0, 0x59, 0xcf, 0x61, 0x30, 0x52, 0xe7, 0x77, 0x66, 0xc9, 0x35, 0xac, 0xfe, + 0xc0, 0xa2, 0x18, 0x58, 0xf1, 0xc1, 0x8f, 0x28, 0x72, 0x2f, 0xdc, 0x36, 0x10, 0x5b, 0xd9, 0x6f, + }, + { + 0x05, 0xbf, 0x47, 0x32, 0x0e, 0xa7, 0xc6, 0xd6, 0x13, 0xa1, 0x72, 0x38, 0x8c, 0x1a, 0xbc, 0x6d, + 0x7c, 0xd8, 0x34, 0xac, 0x69, 0x5b, 0x43, 0xc4, 0x00, 0xf8, 0xf2, 0xa7, 0xaf, 0x9f, 0xb0, 0xde, + }, + { + 0x54, 0x67, 0xce, 0xf2, 0xfc, 0x91, 0xd2, 0x4d, 0x73, 0xc6, 0xc9, 0xc3, 0xfe, 0xcf, 0x72, 0xa0, + 0xc7, 0x43, 0xff, 0x87, 0x4b, 0xe3, 0x7f, 0xba, 0xd4, 0xa2, 0xa3, 0x14, 0x8f, 0xa0, 0x39, 0x91, + }, + { + 0xbd, 0x24, 0x35, 0xb5, 0x0e, 0x7a, 0x13, 0xb3, 0x92, 0x83, 0xaa, 0xbd, 0x0c, 0x22, 0x93, 0xbb, + 0x2a, 0xe7, 0x7a, 0x56, 0x2d, 0xae, 0x41, 0x62, 0x52, 0xb2, 0xe9, 0xbd, 0x29, 0x05, 0x51, 0x95, + }, + { + 0x97, 0x8f, 0x21, 0x82, 0x72, 0x4f, 0x65, 0xc8, 0xfc, 0x2e, 0x5e, 0x8a, 0x1a, 0xce, 0x34, 0x39, + 0x12, 0xe6, 0x9b, 0xc3, 0xab, 0x51, 0xa9, 0x40, 0xf7, 0xbe, 0x94, 0xd0, 0x66, 0xda, 0xa5, 0x39, + }, + { + 0x5a, 0x48, 0x43, 0x9a, 0x8e, 0x22, 0x85, 0x9b, 0x28, 0xc9, 0x63, 0xa2, 0x57, 0xa6, 0xe2, 0x16, + 0x64, 0xec, 0x3c, 0x59, 0x13, 0xc4, 0x7b, 0x51, 0xea, 0xfe, 0x2e, 0x70, 0xbd, 0xd9, 0x77, 0x85, + }, + { + 0x2b, 0x74, 0xb6, 0x95, 0x18, 0x94, 0x54, 0x6d, 0xae, 0xdd, 0xe9, 0xb2, 0xf9, 0xbd, 0xce, 0x27, + 0xa9, 0x87, 0x42, 0x13, 0x22, 0x29, 0x87, 0x7a, 0x04, 0xe3, 0xbe, 0x2f, 0x9c, 0x18, 0xbb, 0x13, + }, + { + 0x80, 0x95, 0x43, 0xaa, 0x19, 0x90, 0x03, 0x4f, 0x47, 0xbf, 0xf5, 0x8e, 0x2d, 0x55, 0x23, 0xb7, + 0x7b, 0x5d, 0xaa, 0x34, 0x37, 0xb2, 0x70, 0x86, 0x5e, 0xc4, 0x94, 0xf3, 0x61, 0xfd, 0x87, 0x65, + }, + { + 0xd4, 0xbc, 0x03, 0x65, 0xb0, 0xc5, 0x44, 0x81, 0x7b, 0x06, 0x94, 0x79, 0xca, 0x1f, 0xe2, 0x28, + 0x53, 0xc8, 0xa7, 0x10, 0x13, 0x77, 0xb7, 0x5c, 0x9a, 0x34, 0x1e, 0xd5, 0x78, 0xb1, 0x21, 0x61, + }, + { + 0x90, 0x0e, 0x7f, 0xa3, 0x24, 0x18, 0x12, 0xbf, 0x45, 0xd2, 0x52, 0xa3, 0x99, 0x74, 0x89, 0xd2, + 0x12, 0x8d, 0x32, 0x3c, 0xd0, 0x28, 0x54, 0x98, 0x6c, 0x9e, 0xdd, 0xc0, 0xd5, 0xf1, 0x8a, 0xb1, + }, + { + 0x82, 0xad, 0x7a, 0x5c, 0x4d, 0x81, 0x54, 0x41, 0x79, 0x42, 0x54, 0x5c, 0x49, 0x41, 0xed, 0x49, + 0xc7, 0x06, 0x61, 0xbb, 0x89, 0x2b, 0x90, 0x04, 0x1f, 0x8c, 0x31, 0x3b, 0x39, 0x4f, 0xf8, 0x33, + }, +}; +static const u8 invert[63][32] = { + { + 0x95, 0xb2, 0xa8, 0xe3, 0xac, 0xcf, 0x27, 0x3e, 0x1c, 0xa3, 0xcf, 0x7a, 0x20, 0xb4, 0x52, 0x83, + 0x0e, 0x21, 0x2d, 0xfe, 0x6f, 0x2e, 0x38, 0x13, 0x01, 0x2e, 0xa0, 0x58, 0x58, 0x6d, 0x4a, 0x6f, + }, + { + 0x63, 0x26, 0x5c, 0xd2, 0x9a, 0xc6, 0x8c, 0x5d, 0xc2, 0x0d, 0xba, 0x4f, 0x79, 0x88, 0xd1, 0x15, + 0x64, 0x55, 0x90, 0x7b, 0x76, 0x2d, 0x60, 0x04, 0x92, 0x77, 0x18, 0xd0, 0xba, 0x7f, 0xee, 0x3a, + }, + { + 0x57, 0xc1, 0x0b, 0x23, 0x06, 0x57, 0x0c, 0xde, 0xa1, 0xa5, 0x8d, 0xc6, 0x8e, 0xbd, 0x9e, 0x09, + 0xe5, 0xed, 0xe3, 0xfb, 0xb1, 0xa0, 0xda, 0x73, 0xfc, 0x3e, 0x5e, 0x6d, 0x38, 0x36, 0x26, 0xec, + }, + { + 0x8e, 0xe5, 0x30, 0x36, 0x9e, 0x30, 0x82, 0x02, 0xf6, 0x7c, 0x06, 0x71, 0xbb, 0x6e, 0x09, 0x68, + 0x16, 0xca, 0x10, 0x32, 0x90, 0xcc, 0x7a, 0x99, 0x18, 0x70, 0xe6, 0xe7, 0x3a, 0x78, 0x86, 0xe6, + }, + { + 0x18, 0x98, 0x14, 0xa7, 0xb7, 0x1f, 0x24, 0xed, 0xd0, 0xfc, 0x71, 0xa0, 0x7e, 0xef, 0xdd, 0xe2, + 0xa2, 0xf8, 0x2a, 0xc2, 0x5d, 0x94, 0x03, 0x13, 0x29, 0x39, 0x86, 0xed, 0x08, 0x99, 0x83, 0xab, + }, + { + 0xcd, 0x22, 0xa0, 0xbc, 0xea, 0xe7, 0xde, 0xca, 0x0c, 0x72, 0xbd, 0xf7, 0x40, 0x46, 0x92, 0xc5, + 0xa4, 0xf3, 0x48, 0x9a, 0x8f, 0x52, 0xab, 0x19, 0x07, 0x98, 0xae, 0x9b, 0xe7, 0xfc, 0xbd, 0x05, + }, + { + 0xd2, 0xce, 0x28, 0x79, 0x3f, 0xdd, 0xa1, 0x1c, 0x21, 0xe4, 0xeb, 0x54, 0x85, 0x5c, 0x9d, 0x64, + 0xd5, 0x5b, 0xb6, 0x06, 0x43, 0xcc, 0x80, 0x8b, 0xe3, 0xdb, 0x26, 0xf5, 0x7e, 0x5f, 0x81, 0x9d, + }, + { + 0x54, 0x42, 0xe9, 0x30, 0xd2, 0x2c, 0xba, 0x16, 0xa7, 0x99, 0x28, 0xe7, 0x54, 0x61, 0xee, 0x17, + 0xf0, 0x70, 0x34, 0xe2, 0xe7, 0x66, 0x16, 0x00, 0x95, 0x7b, 0xbd, 0xd8, 0x07, 0xab, 0xa9, 0x5b, + }, + { + 0xe7, 0x58, 0x21, 0x24, 0x0e, 0x32, 0xbd, 0x6b, 0xcb, 0xa6, 0xd9, 0x91, 0xd7, 0xfd, 0xe4, 0x4c, + 0x58, 0xd6, 0x06, 0x11, 0x02, 0x90, 0xe5, 0x1d, 0x91, 0x2f, 0x0f, 0x43, 0xe3, 0xc7, 0x66, 0xd8, + }, + { + 0x45, 0x65, 0x5e, 0x17, 0xe1, 0x00, 0xfd, 0x42, 0x30, 0x25, 0x0e, 0xa5, 0x26, 0x8a, 0x17, 0xfe, + 0xd0, 0xa2, 0xff, 0x7a, 0x09, 0xd3, 0x5a, 0xb4, 0x71, 0x84, 0x29, 0x03, 0x71, 0x70, 0x9b, 0x6e, + }, + { + 0xc7, 0x2d, 0xe6, 0xef, 0xba, 0x0b, 0x97, 0x9a, 0x91, 0xf2, 0xda, 0x26, 0x62, 0xe5, 0xbe, 0x5d, + 0xc5, 0x5d, 0x71, 0xc1, 0xb7, 0x3f, 0xb3, 0xb8, 0x74, 0xd0, 0x0c, 0x03, 0x74, 0xc0, 0x0c, 0xe4, + }, + { + 0x56, 0x38, 0x1e, 0x31, 0xca, 0x3b, 0xb5, 0xc4, 0xff, 0x5a, 0x9e, 0x86, 0xfe, 0x98, 0x0c, 0x27, + 0x23, 0x2c, 0xa0, 0x76, 0x6f, 0xae, 0xf3, 0xde, 0x71, 0x40, 0x0c, 0xdc, 0x41, 0xf9, 0x89, 0x99, + }, + { + 0x2c, 0x27, 0xed, 0x69, 0x50, 0x53, 0xc5, 0x97, 0xf4, 0x88, 0x9a, 0x2b, 0xce, 0x8a, 0xc5, 0xfb, + 0x0d, 0xbc, 0x6f, 0x9c, 0x84, 0x30, 0xf3, 0xcb, 0xc1, 0x30, 0xa4, 0xb5, 0x46, 0xd4, 0xcb, 0xea, + }, + { + 0xb7, 0xf0, 0x86, 0x66, 0xd3, 0x55, 0x64, 0xc9, 0x1b, 0x9b, 0x3d, 0x79, 0x13, 0x0a, 0x3e, 0xa1, + 0xcf, 0x54, 0x17, 0x77, 0xeb, 0x32, 0x1c, 0x47, 0x7d, 0xf0, 0xb4, 0x11, 0x3d, 0xd7, 0xef, 0x04, + }, + { + 0xf7, 0x7e, 0x71, 0xb6, 0x5e, 0xed, 0xf3, 0xfb, 0x56, 0x82, 0x22, 0x61, 0x29, 0xa2, 0x5d, 0xc6, + 0xcd, 0x03, 0x47, 0xc7, 0xcc, 0xe4, 0xf2, 0xa4, 0x3f, 0xed, 0x36, 0xae, 0xa7, 0x30, 0x84, 0x22, + }, + { + 0x13, 0x70, 0xe7, 0x97, 0x14, 0xfa, 0xa9, 0xb7, 0xd5, 0xa1, 0xa4, 0xfd, 0xe8, 0x0c, 0x92, 0xcf, + 0xc4, 0xdc, 0x5d, 0xb9, 0xa4, 0xa2, 0x16, 0xf8, 0x67, 0xdc, 0x12, 0x47, 0xb7, 0x75, 0xfd, 0x3c, + }, + { + 0x29, 0xaa, 0xdd, 0xb5, 0xdc, 0x7f, 0xce, 0xad, 0x02, 0x65, 0x27, 0x5e, 0xa5, 0x5d, 0x23, 0x0f, + 0xa7, 0x51, 0x4d, 0xf2, 0x7d, 0x2a, 0x31, 0xbf, 0x32, 0x3b, 0x80, 0xe3, 0xda, 0x56, 0xdb, 0xfc, + }, + { + 0x70, 0xd0, 0x50, 0x6d, 0xf2, 0xb3, 0x6f, 0xc1, 0x9a, 0x98, 0x02, 0x87, 0xb5, 0x31, 0x3d, 0x19, + 0xaa, 0xf4, 0xd2, 0xd4, 0x48, 0xb1, 0x08, 0x06, 0x98, 0x39, 0x00, 0x06, 0x20, 0xe5, 0x0c, 0xe1, + }, + { + 0xe6, 0xaf, 0x94, 0xa0, 0xdf, 0xc3, 0x6b, 0xb4, 0xcf, 0x78, 0xc0, 0xe8, 0x56, 0xdc, 0xac, 0xbb, + 0x5e, 0x9e, 0xda, 0x90, 0x1e, 0x7f, 0x44, 0x06, 0xe0, 0x00, 0x6a, 0xd9, 0xd1, 0xf9, 0x56, 0xac, + }, + { + 0x15, 0xa2, 0x90, 0x13, 0x4f, 0xa0, 0x9d, 0x0d, 0x9c, 0xf8, 0xc9, 0x20, 0x1c, 0x8e, 0x68, 0xcb, + 0x1f, 0x75, 0xb3, 0xb2, 0x14, 0xff, 0x19, 0x20, 0x5f, 0x30, 0xb1, 0x05, 0x36, 0x7c, 0xa2, 0xed, + }, + { + 0x9a, 0xb2, 0xf5, 0xfd, 0x04, 0x3e, 0x6b, 0x4a, 0x1d, 0x3a, 0x63, 0x96, 0x00, 0xad, 0x6c, 0x7c, + 0x4f, 0xaf, 0x4d, 0xb5, 0x03, 0x4a, 0xf7, 0x28, 0x7f, 0x1f, 0x38, 0xad, 0xfd, 0xc7, 0x4b, 0x7f, + }, + { + 0xf4, 0x5a, 0x9f, 0xf6, 0xd0, 0x1a, 0x23, 0x76, 0xee, 0x15, 0x10, 0x2c, 0x30, 0xbd, 0x45, 0xfc, + 0x65, 0x60, 0x20, 0xc5, 0x9b, 0xb4, 0x42, 0x83, 0xe9, 0x03, 0xd5, 0xec, 0xba, 0xb2, 0x3b, 0xb8, + }, + { + 0xf4, 0x1b, 0xc1, 0x73, 0x1a, 0x6c, 0x88, 0xfd, 0xc2, 0xfb, 0xe8, 0x7e, 0xcb, 0x8a, 0x0e, 0x0e, + 0x6a, 0x13, 0x54, 0xb0, 0x7b, 0xb8, 0x68, 0x90, 0x21, 0x38, 0x4e, 0x1f, 0x86, 0x51, 0x14, 0x2c, + }, + { + 0x6c, 0xd3, 0xc3, 0x8f, 0x06, 0x45, 0xec, 0x65, 0x87, 0x02, 0x3d, 0x89, 0x61, 0xde, 0x80, 0x42, + 0xf6, 0xe0, 0x8d, 0x91, 0xf0, 0x3a, 0x7a, 0x66, 0xba, 0x1c, 0xc7, 0xb6, 0x3d, 0xc4, 0x7f, 0x91, + }, + { + 0x53, 0xf6, 0x90, 0x34, 0x88, 0x3e, 0xb7, 0xef, 0x56, 0x39, 0x6e, 0x1f, 0x48, 0x14, 0xe4, 0x09, + 0xc6, 0xea, 0xc4, 0xd9, 0xed, 0x2e, 0x2e, 0x33, 0x03, 0x00, 0xb9, 0xac, 0x22, 0x65, 0xe9, 0x1b, + }, + { + 0x55, 0x78, 0x89, 0x36, 0xa0, 0x07, 0xa6, 0x99, 0xbf, 0x7c, 0xb5, 0xbd, 0xb6, 0x1e, 0xc3, 0x58, + 0xb3, 0x0f, 0x78, 0x64, 0x74, 0x77, 0x00, 0x50, 0x2e, 0x4c, 0x6a, 0xa1, 0xe8, 0x93, 0x89, 0x5d, + }, + { + 0x09, 0xf8, 0xdd, 0xe0, 0x42, 0xa4, 0x2e, 0x9d, 0xff, 0x0e, 0x70, 0x73, 0x9c, 0x87, 0xa0, 0x81, + 0x4d, 0xb1, 0xc3, 0xf3, 0xff, 0x96, 0x3b, 0x2a, 0xdf, 0x6d, 0x97, 0xba, 0x06, 0xa7, 0x7e, 0x0a, + }, + { + 0x1f, 0x46, 0xb4, 0x72, 0x14, 0x5b, 0x85, 0x01, 0x83, 0xcc, 0x24, 0x17, 0xc2, 0x07, 0xda, 0x60, + 0x6c, 0xab, 0xfa, 0xe5, 0xd9, 0xb4, 0xf0, 0x3f, 0xca, 0xf1, 0x30, 0x8d, 0xd2, 0x4e, 0xe3, 0xb4, + }, + { + 0x4f, 0xb3, 0x0d, 0x98, 0x38, 0x70, 0x28, 0xa2, 0xca, 0x5d, 0x2c, 0xdf, 0x1f, 0xce, 0xff, 0xcc, + 0x75, 0x49, 0xa0, 0xef, 0x54, 0xd9, 0x32, 0x1b, 0x17, 0xb6, 0x7e, 0x7a, 0xa6, 0x5f, 0x7a, 0xff, + }, + { + 0x81, 0x6b, 0x06, 0x73, 0x5b, 0x32, 0x0d, 0x37, 0xb4, 0x50, 0x63, 0x52, 0x25, 0x72, 0x5c, 0xf5, + 0x5d, 0x58, 0xa6, 0xbf, 0x08, 0xcc, 0x1d, 0x70, 0x2d, 0x12, 0x5d, 0xd7, 0xbd, 0xca, 0xe7, 0x10, + }, + { + 0x8f, 0xfc, 0x57, 0x17, 0xce, 0x47, 0x10, 0x79, 0xae, 0x66, 0xa5, 0xcc, 0x98, 0x0b, 0x77, 0xe8, + 0xa2, 0x6e, 0xc1, 0x0d, 0x03, 0xe3, 0x5b, 0xed, 0x38, 0x0e, 0x31, 0x2d, 0x19, 0x4d, 0xd6, 0x2a, + }, + { + 0xc6, 0x9d, 0x2d, 0x0b, 0xad, 0x6d, 0x0c, 0x61, 0x2d, 0x62, 0xe4, 0xce, 0x73, 0x47, 0x72, 0x20, + 0x0c, 0x8e, 0x8f, 0xe9, 0xe7, 0x67, 0x66, 0x2e, 0x17, 0x54, 0xec, 0x15, 0x3e, 0x1b, 0xf3, 0x04, + }, + { + 0xf4, 0xee, 0x03, 0x30, 0x2e, 0x1e, 0xa4, 0xb1, 0x86, 0x6d, 0xc3, 0x54, 0xf3, 0xc5, 0x61, 0xa6, + 0xb9, 0x28, 0x29, 0x11, 0x91, 0xcb, 0xbd, 0xc9, 0x77, 0x62, 0x09, 0x8c, 0xa4, 0x40, 0x84, 0x97, + }, + { + 0xff, 0x98, 0x9b, 0xbc, 0xc2, 0xf0, 0xf8, 0x7f, 0x5c, 0x86, 0x74, 0x33, 0xee, 0x42, 0x6e, 0xab, + 0xd4, 0xd2, 0x1a, 0x0d, 0x41, 0x2d, 0xac, 0xa1, 0x3e, 0x56, 0xed, 0x4b, 0x27, 0x5a, 0x65, 0xe4, + }, + { + 0x2b, 0xb1, 0xe3, 0x64, 0xaa, 0x32, 0x17, 0x57, 0x7c, 0x67, 0xb8, 0x6b, 0x00, 0x53, 0xbe, 0x3e, + 0xec, 0xd1, 0x1b, 0xc4, 0xc3, 0x8d, 0xe6, 0x19, 0xe8, 0x3a, 0x25, 0x98, 0x4e, 0xe9, 0xd4, 0x60, + }, + { + 0xa6, 0x2e, 0xb3, 0xc8, 0xcd, 0xc9, 0xc2, 0x8e, 0xe1, 0xf0, 0x8f, 0x96, 0x8e, 0xc6, 0x37, 0x11, + 0xbc, 0x6c, 0x0c, 0xf6, 0xb6, 0x83, 0x38, 0x96, 0x7a, 0x74, 0x5a, 0xa7, 0xe1, 0x11, 0x8d, 0x8b, + }, + { + 0x90, 0xf2, 0x4d, 0xbd, 0x83, 0x39, 0xe6, 0x54, 0xf6, 0x75, 0xf6, 0x2c, 0x28, 0x3d, 0xd1, 0xcf, + 0xe1, 0xfb, 0x9f, 0x97, 0x19, 0xca, 0x4d, 0x2c, 0x38, 0x3d, 0x36, 0xed, 0x19, 0xe9, 0x4a, 0x0b, + }, + { + 0x1a, 0x61, 0xb8, 0x19, 0x59, 0x16, 0x74, 0xec, 0xdb, 0x7b, 0xeb, 0xd6, 0xae, 0xcd, 0xe9, 0x55, + 0xdb, 0x45, 0xdc, 0xf2, 0x35, 0x84, 0xe9, 0xe6, 0x17, 0x48, 0xac, 0x38, 0x05, 0x21, 0x2c, 0x8e, + }, + { + 0x41, 0xac, 0x17, 0x42, 0xcc, 0x17, 0x10, 0x02, 0x07, 0x7e, 0xfc, 0x4d, 0x77, 0x06, 0x70, 0xcb, + 0x40, 0x8b, 0x47, 0x47, 0x07, 0x29, 0x82, 0xca, 0x93, 0x69, 0x2f, 0x3a, 0x64, 0xc6, 0xcb, 0x23, + }, + { + 0xa2, 0xcb, 0x2d, 0x02, 0x5d, 0x30, 0x9f, 0x32, 0xf5, 0xc5, 0x13, 0xff, 0xfc, 0xe2, 0xfb, 0x26, + 0x3b, 0x3b, 0xaf, 0xa4, 0x37, 0x6d, 0x45, 0xbf, 0xdb, 0xb9, 0xee, 0xec, 0x92, 0xa5, 0x1d, 0x0d, + }, + { + 0xa4, 0xef, 0x08, 0xb7, 0xb4, 0x68, 0x74, 0x93, 0xb2, 0xda, 0xba, 0xe9, 0x05, 0xf5, 0x09, 0xb6, + 0x53, 0xdd, 0x17, 0x60, 0xbb, 0x1e, 0xb0, 0x71, 0xd8, 0x47, 0x85, 0x02, 0x13, 0xbe, 0xa2, 0x67, + }, + { + 0x31, 0x50, 0x90, 0xb0, 0x83, 0x4a, 0xcf, 0x3f, 0xbe, 0x88, 0x90, 0x4b, 0xe1, 0x9f, 0xe6, 0xd0, + 0xfd, 0x01, 0x8e, 0xfc, 0xc0, 0x8c, 0x2f, 0x9b, 0x48, 0x70, 0x9d, 0x4e, 0x22, 0x21, 0x07, 0x09, + }, + { + 0x15, 0x9f, 0x37, 0x45, 0x52, 0x99, 0x6e, 0xe9, 0x1a, 0x25, 0x56, 0x0b, 0x19, 0xf1, 0xca, 0x9f, + 0x29, 0xe5, 0x23, 0xa6, 0x0b, 0x94, 0x0a, 0xe3, 0x74, 0xaa, 0xd5, 0x35, 0xaf, 0x6e, 0xb2, 0x24, + }, + { + 0x68, 0xab, 0xa6, 0x8b, 0x5f, 0xc7, 0x93, 0x1a, 0x06, 0x51, 0x2c, 0x3b, 0xad, 0x44, 0x6b, 0x69, + 0x1a, 0x1d, 0x41, 0xca, 0x8e, 0x59, 0x2c, 0x83, 0x71, 0x48, 0x8c, 0xaf, 0x50, 0x85, 0x00, 0xf3, + }, + { + 0xe2, 0xa6, 0x38, 0x93, 0xca, 0xe3, 0xd0, 0x36, 0xf4, 0xe9, 0x53, 0xfb, 0xa0, 0xd0, 0x13, 0xd3, + 0x2b, 0x7d, 0x46, 0xc8, 0x8b, 0xc7, 0x8c, 0xca, 0x59, 0xec, 0x66, 0x17, 0x70, 0xbb, 0xf9, 0x92, + }, + { + 0x89, 0xca, 0x92, 0x19, 0x01, 0xb8, 0x4b, 0x97, 0x06, 0x1a, 0x12, 0x91, 0x72, 0x11, 0xeb, 0x12, + 0x8b, 0x12, 0xd9, 0xdc, 0xc9, 0xb2, 0x37, 0xf8, 0x3e, 0x02, 0x03, 0xbe, 0x45, 0x45, 0xc9, 0x42, + }, + { + 0x45, 0x2c, 0x80, 0xe1, 0x3a, 0x0a, 0xdf, 0xa3, 0xd2, 0x4a, 0x23, 0x0d, 0x47, 0x0d, 0x49, 0xad, + 0xdf, 0xb0, 0x42, 0xdf, 0x87, 0x85, 0xa6, 0x8f, 0x9f, 0x7e, 0x9b, 0xa4, 0x42, 0x64, 0xcb, 0xfb, + }, + { + 0xc7, 0x39, 0x26, 0xb1, 0x90, 0x4d, 0xc1, 0x7a, 0xea, 0x31, 0x1b, 0xae, 0x1a, 0x5c, 0x1f, 0x4f, + 0x44, 0x2f, 0x87, 0x08, 0x5d, 0xa6, 0x74, 0xfd, 0xab, 0xb2, 0x4b, 0x01, 0xed, 0xd5, 0x4c, 0xe6, + }, + { + 0x2a, 0xef, 0xfa, 0x25, 0x3c, 0xd2, 0xc8, 0x08, 0x9c, 0x33, 0x3c, 0x47, 0xb3, 0xb5, 0x44, 0x34, + 0x97, 0xee, 0xe8, 0x52, 0x1c, 0x15, 0xb3, 0xe0, 0xda, 0xef, 0x77, 0xde, 0x15, 0x39, 0x4b, 0x38, + }, + { + 0x8a, 0x3a, 0x59, 0x61, 0x9d, 0x3e, 0x9b, 0x38, 0xc9, 0x84, 0x80, 0xaf, 0xb4, 0x37, 0x8a, 0x67, + 0x47, 0xc9, 0x6c, 0x72, 0xef, 0x39, 0x50, 0x28, 0x6c, 0x8f, 0xad, 0x09, 0x75, 0x26, 0xc9, 0xa9, + }, + { + 0x27, 0x1e, 0x8b, 0xf6, 0x6b, 0x56, 0x5d, 0x17, 0x58, 0xac, 0xdf, 0x27, 0xd9, 0x3e, 0x5b, 0xdd, + 0xaf, 0xbc, 0xb7, 0xf9, 0x76, 0x3b, 0x40, 0x06, 0xbc, 0x6e, 0xec, 0xaa, 0xb2, 0xdc, 0x9a, 0x0c, + }, + { + 0x09, 0x14, 0xef, 0x19, 0xc7, 0x7d, 0xc8, 0xa3, 0xd0, 0xaa, 0x7f, 0x09, 0x18, 0xaf, 0xd3, 0xde, + 0xbf, 0x05, 0xfc, 0xf9, 0xeb, 0xc0, 0x8e, 0xcf, 0xc7, 0x4c, 0x2f, 0x3f, 0xd4, 0x51, 0x41, 0xb2, + }, + { + 0xb7, 0x33, 0xf3, 0x72, 0x6d, 0x12, 0xc1, 0x5c, 0x5d, 0x81, 0xb3, 0x63, 0x81, 0x50, 0x81, 0xc0, + 0x4a, 0xea, 0x18, 0x7c, 0xa6, 0x2d, 0x23, 0xba, 0x4b, 0xb9, 0x31, 0xd9, 0xab, 0x20, 0x60, 0x77, + }, + { + 0xbb, 0x1c, 0x50, 0x2d, 0xdc, 0x18, 0x27, 0x98, 0x60, 0xde, 0x17, 0xd9, 0x17, 0x3c, 0xd5, 0x98, + 0xc3, 0x12, 0xfd, 0x8b, 0x25, 0x1e, 0xa5, 0xc1, 0xa8, 0xef, 0xec, 0x05, 0x3e, 0xfc, 0xd1, 0xfc, + }, + { + 0xe6, 0x3b, 0x9f, 0x33, 0x85, 0xeb, 0x91, 0xd0, 0xad, 0x2f, 0xc2, 0x96, 0x61, 0x64, 0xbc, 0x12, + 0x15, 0x79, 0x65, 0x93, 0xc2, 0x63, 0xa3, 0x27, 0x88, 0x60, 0x39, 0x35, 0x21, 0x05, 0xe9, 0x49, + }, + { + 0x9b, 0x68, 0xe7, 0xc8, 0xea, 0x0e, 0x5c, 0xa4, 0x03, 0x3a, 0x4e, 0x31, 0xb1, 0x92, 0xad, 0x9d, + 0x7e, 0xb5, 0x93, 0x81, 0xdd, 0x7a, 0xe9, 0xa1, 0x69, 0x28, 0x6a, 0xaf, 0x48, 0x05, 0x94, 0xc0, + }, + { + 0x66, 0x3d, 0x87, 0xc1, 0x48, 0x9d, 0xdf, 0x99, 0x25, 0xd7, 0xb0, 0xfa, 0x03, 0x8b, 0x62, 0x60, + 0xc5, 0x07, 0x3f, 0xa2, 0xc8, 0xbd, 0x70, 0xdb, 0x40, 0x6c, 0x65, 0xbf, 0x15, 0xfc, 0x1e, 0xc9, + }, + { + 0x38, 0x9c, 0x1a, 0x5b, 0x4f, 0x84, 0xca, 0xe1, 0x30, 0x6a, 0xf0, 0xb6, 0xf1, 0x61, 0xd3, 0xb0, + 0xa6, 0x6d, 0x0d, 0x11, 0x03, 0xe0, 0xcb, 0x9f, 0xbe, 0x7e, 0xc2, 0x7a, 0x53, 0x9d, 0x39, 0xcb, + }, + { + 0xf3, 0x47, 0x4a, 0x37, 0xcd, 0x19, 0x27, 0x0f, 0xfb, 0x3f, 0xcb, 0x81, 0x1e, 0x0f, 0xfd, 0x1f, + 0x2e, 0x8d, 0xff, 0xe9, 0x52, 0x8b, 0x8e, 0x52, 0x3c, 0x82, 0xe6, 0x44, 0xf6, 0x92, 0xd4, 0xd4, + }, + { + 0x3a, 0xe8, 0x4d, 0xcc, 0x4a, 0x0e, 0xaa, 0xf6, 0x32, 0x88, 0x4c, 0xee, 0xaa, 0x9c, 0xeb, 0x59, + 0xb5, 0xb8, 0x06, 0x89, 0x49, 0xc9, 0xa6, 0xf7, 0xa6, 0x14, 0x44, 0x55, 0x5e, 0x3e, 0x86, 0x08, + }, + { + 0xca, 0x3d, 0x95, 0x21, 0xf3, 0xbb, 0x78, 0x29, 0x6a, 0x38, 0xd3, 0xe4, 0x48, 0x98, 0x6f, 0x0e, + 0xaf, 0x46, 0xa5, 0x02, 0xdd, 0xfb, 0x52, 0x42, 0x9b, 0x69, 0x97, 0xe6, 0x68, 0x21, 0x0d, 0x69, + }, + { + 0x3a, 0x8a, 0x14, 0x6e, 0xa2, 0x24, 0x8f, 0x89, 0x5e, 0x99, 0x8a, 0x5b, 0x90, 0xb1, 0xf3, 0x64, + 0x4d, 0x10, 0xef, 0x45, 0xa9, 0xfb, 0xbb, 0xc0, 0xf5, 0x66, 0xdf, 0x15, 0xae, 0xd0, 0xd9, 0x56, + }, + { + 0x62, 0x50, 0x52, 0xb5, 0xb9, 0x76, 0xa7, 0xcb, 0xe6, 0xf7, 0x3a, 0x9f, 0xa4, 0x1e, 0x0a, 0x4d, + 0x88, 0xa4, 0x1c, 0xea, 0x11, 0x8c, 0xfb, 0xbe, 0x70, 0x62, 0xec, 0x4e, 0x00, 0x56, 0x0e, 0xa9, + }, +}; diff --git a/src/southbridge/intel/bd82x6x/Makefile.inc b/src/southbridge/intel/bd82x6x/Makefile.inc index 64038e5e78..fa21277b2a 100644 --- a/src/southbridge/intel/bd82x6x/Makefile.inc +++ b/src/southbridge/intel/bd82x6x/Makefile.inc @@ -45,10 +45,14 @@ smm-$(CONFIG_SPI_FLASH_SMM) += ../common/spi.c ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c me.c me_8.x.c finalize.c pch.c -romstage-y += early_usb.c early_smbus.c early_me.c me_status.c gpio.c +romstage-y += early_usb.c early_smbus.c me_status.c gpio.c romstage-y += reset.c romstage-y += early_spi.c early_pch.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += early_thermal.c early_pch_native.c early_me_native.c + ifeq ($(CONFIG_BUILD_WITH_FAKE_IFD),y) IFD_BIN_PATH := $(objgenerated)/ifdfake.bin IFD_SECTIONS := $(addprefix -b ,$(CONFIG_IFD_BIOS_SECTION:"%"=%)) \ diff --git a/src/southbridge/intel/bd82x6x/early_me_native.c b/src/southbridge/intel/bd82x6x/early_me_native.c new file mode 100644 index 0000000000..ab54ffd61c --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_me_native.c @@ -0,0 +1,272 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include "me.h" +#include "pch.h" + +static const char *me_ack_values[] = { + [ME_HFS_ACK_NO_DID] = "No DID Ack received", + [ME_HFS_ACK_RESET] = "Non-power cycle reset", + [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset", + [ME_HFS_ACK_S3] = "Go to S3", + [ME_HFS_ACK_S4] = "Go to S4", + [ME_HFS_ACK_S5] = "Go to S5", + [ME_HFS_ACK_GBL_RESET] = "Global Reset", + [ME_HFS_ACK_CONTINUE] = "Continue to boot" +}; + +static inline void pci_read_dword_ptr(void *ptr, int offset) +{ + u32 dword = pci_read_config32(PCH_ME_DEV, offset); + memcpy(ptr, &dword, sizeof(dword)); +} + +static inline void pci_write_dword_ptr(void *ptr, int offset) +{ + u32 dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + pci_write_config32(PCH_ME_DEV, offset, dword); +} + +void intel_early_me_status(void) +{ + struct me_hfs hfs; + struct me_gmes gmes; + + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + pci_read_dword_ptr(&gmes, PCI_ME_GMES); + + intel_me_status(&hfs, &gmes); +} + +int intel_early_me_init(void) +{ + int count; + struct me_uma uma; + struct me_hfs hfs; + + printk(BIOS_INFO, "Intel ME early init\n"); + + /* Wait for ME UMA SIZE VALID bit to be set */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) + break; + udelay(ME_DELAY); + } + if (!count) { + printk(BIOS_ERR, "ERROR: ME is not ready!\n"); + return -1; + } + + /* Check for valid firmware */ + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + if (hfs.fpt_bad) { + printk(BIOS_WARNING, "WARNING: ME has bad firmware\n"); + return -1; + } + + printk(BIOS_INFO, "Intel ME firmware is ready\n"); + return 0; +} + +int intel_early_me_uma_size(void) +{ + struct me_uma uma; + + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) { + printk(BIOS_DEBUG, "ME: Requested %uMB UMA\n", uma.size); + return uma.size; + } + + printk(BIOS_DEBUG, "ME: Invalid UMA size\n"); + return 0; +} + +static inline void set_global_reset(int enable) +{ + u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3); + + /* Clear CF9 Without Resume Well Reset Enable */ + etr3 &= ~ETR3_CWORWRE; + + /* CF9GR indicates a Global Reset */ + if (enable) + etr3 |= ETR3_CF9GR; + else + etr3 &= ~ETR3_CF9GR; + + pci_write_config32(PCH_LPC_DEV, ETR3, etr3); +} + +int intel_early_me_init_done(u8 status) +{ + u8 reset, errorcode, opmode; + u16 reg16; + u32 mebase_l, mebase_h; + u32 millisec; + u32 hfs, me_fws2; + struct me_did did = { + .init_done = ME_INIT_DONE, + .status = status + }; + u32 meDID; + + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xff000) >> 12; + + opmode = (hfs & 0xf0) >> 4; + errorcode = hfs & 0xf; + + if (opmode != ME_HFS_MODE_NORMAL) { + printk(BIOS_NOTICE, "ME: Wrong mode : %d\n", opmode); + //return 0; + } + if (errorcode) { + printk(BIOS_NOTICE, "ME: HFS error : %d\n", errorcode); + //return 0; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + // Poll cpu replaced for 50ms + millisec = 0; + while ((((me_fws2 & 0x100) >> 8) == 0) && millisec < 50) { + udelay(1000); + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + millisec++; + } + if (millisec >= 50 || ((me_fws2 & 0x100) >> 8) == 0x0) { + printk(BIOS_NOTICE, "Waited long enough, or CPU was not replaced, continue...\n"); + } else if ((me_fws2 & 0x100) == 0x100) { + if ((me_fws2 & 0x80) == 0x80) { + printk(BIOS_NOTICE, "CPU was replaced & warm reset required...\n"); + reg16 = pcie_read_config16(PCI_DEV(0, 31, 0), 0xa2) & ~0x80; + pcie_write_config16(PCI_DEV(0, 31, 0), 0xa2, reg16); + set_global_reset(0); + outb(0x6, 0xcf9); + hlt(); + } + + if (((me_fws2 & 0x10) == 0x10) && (me_fws2 & 0x80) == 0x00) { + printk(BIOS_NOTICE, "Full training required\n"); + } + } + + printk(BIOS_NOTICE, "PASSED! Tell ME that DRAM is ready\n"); + + /* MEBASE from MESEG_BASE[35:20] */ + mebase_l = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_L); + mebase_h = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_H) & 0xf; + did.uma_base = (mebase_l >> 20) | (mebase_h << 12); + + meDID = did.uma_base | (1 << 28);// | (1 << 23); + pci_write_config32(PCI_DEV(0, 0x16, 0), PCI_ME_H_GS, meDID); + + udelay(1100); + + /* Must wait for ME acknowledgement */ + millisec = 0; + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + while ((((hfs & 0xf0) >> 4) != ME_HFS_BIOS_DRAM_ACK) && (millisec < 5000)) { + udelay(1000); + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + millisec++; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + + /* Return the requested BIOS action */ + printk(BIOS_NOTICE, "ME: Requested BIOS Action: %s\n", + me_ack_values[(hfs & 0xe) >> 1]); + + reset = inb(0xcf9); + reset &= 0xf1; + switch ((hfs & 0xe) >> 1) { + case ME_HFS_ACK_NO_DID: + case ME_HFS_ACK_CONTINUE: + /* Continue to boot */ + return 0; + case ME_HFS_ACK_RESET: + /* Non-power cycle reset */ + set_global_reset(0); + reset |= 0x06; + break; + case ME_HFS_ACK_PWR_CYCLE: + /* Power cycle reset */ + set_global_reset(0); + reset |= 0x0e; + break; + case ME_HFS_ACK_GBL_RESET: + /* Global reset */ + set_global_reset(1); + reset |= 0x0e; + break; + case ME_HFS_ACK_S3: + case ME_HFS_ACK_S4: + case ME_HFS_ACK_S5: + break; + } + + /* Perform the requested reset */ + if (reset) { + outb(reset, 0xcf9); + hlt(); + } + return -1; +} diff --git a/src/southbridge/intel/bd82x6x/early_pch_native.c b/src/southbridge/intel/bd82x6x/early_pch_native.c new file mode 100644 index 0000000000..5cd6315430 --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_pch_native.c @@ -0,0 +1,375 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pch.h" +/* For DMI bar. */ +#include "northbridge/intel/sandybridge/sandybridge.h" + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) + +static void +wait_2338 (void) +{ + while (read8 (DEFAULT_RCBA | 0x2338) & 1); +} + +static u32 +read_2338 (u32 edx) +{ + u32 ret; + + write32 (DEFAULT_RCBA | 0x2330, edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + ret = read32 (DEFAULT_RCBA | 0x2334); + wait_2338 (); + read8 (DEFAULT_RCBA | 0x2338); + return ret; +} + +static void +write_2338 (u32 edx, u32 val) +{ + read_2338 (edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + + write32 (DEFAULT_RCBA | 0x2334, val); + wait_2338 (); + write16 (DEFAULT_RCBA | 0x2338, + (read16 (DEFAULT_RCBA | 0x2338) & 0x1ff) | 0x600); + read8 (DEFAULT_RCBA | 0x2338); +} + + +static void +init_dmi (void) +{ + int i; + + write32 (DEFAULT_DMIBAR | 0x0914, + read32 (DEFAULT_DMIBAR | 0x0914) | 0x80000000); + write32 (DEFAULT_DMIBAR | 0x0934, + read32 (DEFAULT_DMIBAR | 0x0934) | 0x80000000); + for (i = 0; i < 4; i++) + { + write32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4)) & 0xf3ffffff); + write32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4)) | 0x800); + } + write32 (DEFAULT_DMIBAR | 0x0c30, (read32 (DEFAULT_DMIBAR | 0x0c30) + & 0xfffffff) | 0x40000000); + for (i = 0; i < 2; i++) + { + write32 (DEFAULT_DMIBAR | 0x0904 | (i << 5), + read32 (DEFAULT_DMIBAR | 0x0904 | (i << 5)) & 0xfe3fffff); + write32 (DEFAULT_DMIBAR | 0x090c | (i << 5), + read32 (DEFAULT_DMIBAR | 0x090c | (i << 5)) & 0xfff1ffff); + } + write32 (DEFAULT_DMIBAR | 0x090c, + read32 (DEFAULT_DMIBAR | 0x090c) & 0xfe1fffff); + write32 (DEFAULT_DMIBAR | 0x092c, + read32 (DEFAULT_DMIBAR | 0x092c) & 0xfe1fffff); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x090c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x090c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x092c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x092c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0c04); // !!! = 0x2e680008 + write32 (DEFAULT_DMIBAR | 0x0c04, 0x2e680008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0910); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0910, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0930); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0930, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0a04); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a04, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a14); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a14, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a24); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a24, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a34); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a34, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0900); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0900, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0920); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0920, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0c0c); // !!! = 0x16063400 + write32 (DEFAULT_DMIBAR | 0x0c0c, 0x00063400); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0258); // !!! = 0x40000600 + write32 (DEFAULT_DMIBAR | 0x0258, 0x60000600); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x022c); // !!! = 0x00c26460 + write32 (DEFAULT_DMIBAR | 0x022c, 0x00c2403c); + read8 (DEFAULT_RCBA | 0x21a4); // !!! = 0x42 + + read32 (DEFAULT_RCBA | 0x21a4); // !!! = 0x00012c42 + read32 (DEFAULT_RCBA | 0x2340); // !!! = 0x0013001b + write32 (DEFAULT_RCBA | 0x2340, 0x003a001b); + read8 (DEFAULT_RCBA | 0x21b0); // !!! = 0x01 + write8 (DEFAULT_RCBA | 0x21b0, 0x02); + read32 (DEFAULT_DMIBAR | 0x0084); // !!! = 0x0041ac41 + write32 (DEFAULT_DMIBAR | 0x0084, 0x0041ac42); + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0041 + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + + read32 (DEFAULT_DMIBAR | 0x0014); // !!! = 0x8000007f + write32 (DEFAULT_DMIBAR | 0x0014, 0x80000019); + read32 (DEFAULT_DMIBAR | 0x0020); // !!! = 0x01000000 + write32 (DEFAULT_DMIBAR | 0x0020, 0x81000022); + read32 (DEFAULT_DMIBAR | 0x002c); // !!! = 0x02000000 + write32 (DEFAULT_DMIBAR | 0x002c, 0x82000044); + read32 (DEFAULT_DMIBAR | 0x0038); // !!! = 0x07000080 + write32 (DEFAULT_DMIBAR | 0x0038, 0x87000080); + read8 (DEFAULT_DMIBAR | 0x0004); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0004, 0x01); + + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x01200654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x012a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + read8 (DEFAULT_RCBA | 0x1114); // !!! = 0x00 + write8 (DEFAULT_RCBA | 0x1114, 0x05); + read32 (DEFAULT_RCBA | 0x2014); // !!! = 0x80000011 + write32 (DEFAULT_RCBA | 0x2014, 0x80000019); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2020, 0x81000022); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x81000022 + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2030, 0x82000044); + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x82000044 + read32 (DEFAULT_RCBA | 0x2040); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2040, 0x87000080); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + write32 (DEFAULT_RCBA | 0x0050, 0x812a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x812a0654 + read16 (DEFAULT_RCBA | 0x201a); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2026); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2036); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2046); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x001a); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0026); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0032); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x003e); // !!! = 0x0000 +} + +void +early_pch_init_native (void) +{ + pcie_write_config8 (SOUTHBRIDGE, 0xa6, + pcie_read_config8 (SOUTHBRIDGE, 0xa6) | 2); + + write32 (DEFAULT_RCBA | 0x2088, 0x00109000); + read32 (DEFAULT_RCBA | 0x20ac); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x20ac, 0x40000000); + write32 (DEFAULT_RCBA | 0x100c, 0x01110000); + write8 (DEFAULT_RCBA | 0x2340, 0x1b); + read32 (DEFAULT_RCBA | 0x2314); // !!! = 0x0a080000 + write32 (DEFAULT_RCBA | 0x2314, 0x0a280000); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xc809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + write32 (DEFAULT_RCBA | 0x2324, 0x00854c74); + read8 (DEFAULT_RCBA | 0x0400); // !!! = 0x00 + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + + write_2338 (0xea007f62, 0x00590133); + write_2338 (0xec007f62, 0x00590133); + write_2338 (0xec007f64, 0x59555588); + write_2338 (0xea0040b9, 0x0001051c); + write_2338 (0xeb0040a1, 0x800084ff); + write_2338 (0xec0040a1, 0x800084ff); + write_2338 (0xea004001, 0x00008400); + write_2338 (0xeb004002, 0x40201758); + write_2338 (0xec004002, 0x40201758); + write_2338 (0xea004002, 0x00601758); + write_2338 (0xea0040a1, 0x810084ff); + write_2338 (0xeb0040b1, 0x0001c598); + write_2338 (0xec0040b1, 0x0001c598); + write_2338 (0xeb0040b6, 0x0001c598); + write_2338 (0xea0000a9, 0x80ff969f); + write_2338 (0xea0001a9, 0x80ff969f); + write_2338 (0xeb0040b2, 0x0001c396); + write_2338 (0xeb0040b3, 0x0001c396); + write_2338 (0xec0040b2, 0x0001c396); + write_2338 (0xea0001a9, 0x80ff94ff); + write_2338 (0xea000151, 0x0088037f); + write_2338 (0xea0000a9, 0x80ff94ff); + write_2338 (0xea000051, 0x0088037f); + + write_2338 (0xea007f05, 0x00010642); + write_2338 (0xea0040b7, 0x0001c91c); + write_2338 (0xea0040b8, 0x0001c91c); + write_2338 (0xeb0040a1, 0x820084ff); + write_2338 (0xec0040a1, 0x820084ff); + write_2338 (0xea007f0a, 0xc2480000); + + write_2338 (0xec00404d, 0x1ff177f); + write_2338 (0xec000084, 0x5a600000); + write_2338 (0xec000184, 0x5a600000); + write_2338 (0xec000284, 0x5a600000); + write_2338 (0xec000384, 0x5a600000); + write_2338 (0xec000094, 0x000f0501); + write_2338 (0xec000194, 0x000f0501); + write_2338 (0xec000294, 0x000f0501); + write_2338 (0xec000394, 0x000f0501); + write_2338 (0xec000096, 0x00000001); + write_2338 (0xec000196, 0x00000001); + write_2338 (0xec000296, 0x00000001); + write_2338 (0xec000396, 0x00000001); + write_2338 (0xec000001, 0x00008c08); + write_2338 (0xec000101, 0x00008c08); + write_2338 (0xec000201, 0x00008c08); + write_2338 (0xec000301, 0x00008c08); + write_2338 (0xec0040b5, 0x0001c518); + write_2338 (0xec000087, 0x06077597); + write_2338 (0xec000187, 0x06077597); + write_2338 (0xec000287, 0x06077597); + write_2338 (0xec000387, 0x06077597); + write_2338 (0xea000050, 0x00bb0157); + write_2338 (0xea000150, 0x00bb0157); + write_2338 (0xec007f60, 0x77777d77); + write_2338 (0xea00008d, 0x01320000); + write_2338 (0xea00018d, 0x01320000); + write_2338 (0xec0007b2, 0x04514b5e); + write_2338 (0xec00078c, 0x40000200); + write_2338 (0xec000780, 0x02000020); + + init_dmi(); +} diff --git a/src/southbridge/intel/bd82x6x/early_thermal.c b/src/southbridge/intel/bd82x6x/early_thermal.c new file mode 100644 index 0000000000..02ec9a7436 --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_thermal.c @@ -0,0 +1,70 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "pch.h" +#include "cpu/intel/model_206ax/model_206ax.h" +#include + +/* Early thermal init, must be done prior to giving ME its memory + which is done at the end of raminit. */ +void early_thermal_init(void) +{ + device_t dev; + msr_t msr; + + dev = PCI_DEV(0x0, 0x1f, 0x6); + + /* Program address for temporary BAR. */ + pci_write_config32(dev, 0x40, 0x40000000); + pci_write_config32(dev, 0x44, 0x0); + + /* Activate temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) | 5); + + + write16 (0x40000004, 0x3a2b); + write8 (0x4000000c, 0xff); + write8 (0x4000000d, 0x00); + write8 (0x4000000e, 0x40); + write8 (0x40000082, 0x00); + write8 (0x40000001, 0xba); + + /* Perform init. */ + /* Configure TJmax. */ + msr = rdmsr(MSR_TEMPERATURE_TARGET); + write16(0x40000012, ((msr.lo >> 16) & 0xff) << 6); + /* Northbridge temperature slope and offset. */ + write16(0x40000016, 0x808c); + + write16 (0x40000014, 0xde87); + + /* Enable thermal data reporting, processor, PCH and northbridge. */ + write16(0x4000001a, (read16(0x4000001a) & ~0xf) | 0x10f0); + + /* Disable temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) & ~1); + pci_write_config32(dev, 0x40, 0); + + write32 (DEFAULT_RCBA | 0x38b0, + (read32 (DEFAULT_RCBA | 0x38b0) & 0xffff8003) | 0x403c); +} diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h index 90de85566f..83128e2540 100644 --- a/src/southbridge/intel/bd82x6x/pch.h +++ b/src/southbridge/intel/bd82x6x/pch.h @@ -74,6 +74,8 @@ void enable_smbus(void); void enable_usb_bar(void); int smbus_read_byte(unsigned device, unsigned address); int early_spi_read(u32 offset, u32 size, u8 *buffer); +void early_thermal_init(void); +void early_pch_init_native(void); #endif #endif diff --git a/src/southbridge/intel/bd82x6x/smi.c b/src/southbridge/intel/bd82x6x/smi.c index 0166edfc49..a20232ee88 100644 --- a/src/southbridge/intel/bd82x6x/smi.c +++ b/src/southbridge/intel/bd82x6x/smi.c @@ -29,10 +29,7 @@ #include #include #include "pch.h" - -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE #include "northbridge/intel/sandybridge/sandybridge.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/bd82x6x/usb_ehci.c b/src/southbridge/intel/bd82x6x/usb_ehci.c index 78f92d9683..97f20bd96a 100644 --- a/src/southbridge/intel/bd82x6x/usb_ehci.c +++ b/src/southbridge/intel/bd82x6x/usb_ehci.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include @@ -36,11 +37,43 @@ static void usb_ehci_init(struct device *dev) RCBA32(0x35b0) = reg32; printk(BIOS_DEBUG, "EHCI: Setting up controller.. "); + + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + pci_write_config32(dev, 0x84, 0x930c8811); + pci_write_config32(dev, 0x88, 0x24000d30); + pci_write_config32(dev, 0xf4, 0x80408588); + pci_write_config32(dev, 0xf4, 0x80808588); + pci_write_config32(dev, 0xf4, 0x00808588); + pci_write_config32(dev, 0xfc, 0x205b1708); +#endif + reg32 = pci_read_config32(dev, PCI_COMMAND); reg32 |= PCI_COMMAND_MASTER; //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + struct resource *res; + u8 access_cntl; + + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); +#endif + printk(BIOS_DEBUG, "done.\n"); } -- cgit v1.2.3