Maciej Matuszczyk has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/43406 )
Change subject: [WIP] [DONOTMERGE] x3x and ga-ep35c-ds3r port ......................................................................
[WIP] [DONOTMERGE] x3x and ga-ep35c-ds3r port
Change-Id: I511d25fe5d3880e8e039f2f88d3bb19fe79cf394 Signed-off-by: Maciej Matuszczyk maccraft123mc@gmail.com --- A src/mainboard/gigabyte/ga-ep35c-ds3r/Kconfig A src/mainboard/gigabyte/ga-ep35c-ds3r/Kconfig.name A src/mainboard/gigabyte/ga-ep35c-ds3r/Makefile.inc A src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/ec.asl A src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/gpe.asl A src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/ich10_pci_irqs.asl A src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/superio.asl A src/mainboard/gigabyte/ga-ep35c-ds3r/acpi_tables.c A src/mainboard/gigabyte/ga-ep35c-ds3r/board_info.txt A src/mainboard/gigabyte/ga-ep35c-ds3r/cmos.default A src/mainboard/gigabyte/ga-ep35c-ds3r/cmos.layout A src/mainboard/gigabyte/ga-ep35c-ds3r/cstates.c A src/mainboard/gigabyte/ga-ep35c-ds3r/data.vbt A src/mainboard/gigabyte/ga-ep35c-ds3r/devicetree.cb A src/mainboard/gigabyte/ga-ep35c-ds3r/dsdt.asl A src/mainboard/gigabyte/ga-ep35c-ds3r/early_init.c A src/mainboard/gigabyte/ga-ep35c-ds3r/gma-mainboard.ads A src/mainboard/gigabyte/ga-ep35c-ds3r/gpio.c A src/mainboard/gigabyte/ga-ep35c-ds3r/hda_verb.c A src/northbridge/intel/x3x/Kconfig A src/northbridge/intel/x3x/Makefile.inc A src/northbridge/intel/x3x/acpi.c A src/northbridge/intel/x3x/acpi/hostbridge.asl A src/northbridge/intel/x3x/acpi/peg.asl A src/northbridge/intel/x3x/acpi/x3x.asl A src/northbridge/intel/x3x/bootblock.c A src/northbridge/intel/x3x/chip.h A src/northbridge/intel/x3x/dq_dqs.c A src/northbridge/intel/x3x/early_init.c A src/northbridge/intel/x3x/gma.c A src/northbridge/intel/x3x/iomap.h A src/northbridge/intel/x3x/memmap.c A src/northbridge/intel/x3x/northbridge.c A src/northbridge/intel/x3x/raminit.c A src/northbridge/intel/x3x/raminit_ddr23.c A src/northbridge/intel/x3x/raminit_tables.c A src/northbridge/intel/x3x/rcven.c A src/northbridge/intel/x3x/romstage.c A src/northbridge/intel/x3x/x3x.h 39 files changed, 6,777 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/06/43406/1
diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/Kconfig b/src/mainboard/gigabyte/ga-ep35c-ds3r/Kconfig new file mode 100644 index 0000000..4aabfe9 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/Kconfig @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-only + +if BOARD_GIGABYTE_GA_EP35C_DS3R + +config BOARD_SPECIFIC_OPTIONS + def_bool y + select CPU_INTEL_SOCKET_LGA775 + select NORTHBRIDGE_INTEL_X3X + select SOUTHBRIDGE_INTEL_I82801IX + select SUPERIO_ITE_IT8718F + #select HAVE_ACPI_TABLES TODO + select BOARD_ROMSIZE_KB_1024 + select PCIEXP_ASPM + select PCIEXP_CLK_PM + select PCIEXP_L1_SUB_STATE + #select REALTEK_8168_RESET + select HAVE_OPTION_TABLE + select HAVE_CMOS_DEFAULT + select HAVE_ACPI_RESUME + #select INTEL_GMA_HAVE_VBT + #select MAINBOARD_HAS_LIBGFXINIT + +config MAINBOARD_DIR + string + default "gigabyte/ga-ep35c-ds3r" + +config MAINBOARD_PART_NUMBER + string + default "GA-EP35C-DS3R" + +endif # BOARD_GIGABYTE_GA_EP35C_DS3R diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/Kconfig.name b/src/mainboard/gigabyte/ga-ep35c-ds3r/Kconfig.name new file mode 100644 index 0000000..9227054 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/Kconfig.name @@ -0,0 +1,2 @@ +config BOARD_GIGABYTE_GA_EP35C_DS3R + bool "GA-EP35C-DS3R" diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/Makefile.inc b/src/mainboard/gigabyte/ga-ep35c-ds3r/Makefile.inc new file mode 100644 index 0000000..4100476 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/Makefile.inc @@ -0,0 +1,7 @@ +ramstage-y += cstates.c +romstage-y += gpio.c + +bootblock-y += early_init.c +romstage-y += early_init.c + +ramstage-$(CONFIG_MAINBOARD_USE_LIBGFXINIT) += gma-mainboard.ads diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/ec.asl b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/ec.asl new file mode 100644 index 0000000..2997587 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/ec.asl @@ -0,0 +1 @@ +/* dummy */ diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/gpe.asl b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/gpe.asl new file mode 100644 index 0000000..62e7b37 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/gpe.asl @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Scope (_GPE) +{ + Method(_L18, 0, NotSerialized) + { + /* Read EC register to clear wake status */ + Store(_SB.PCI0.LPCB.EC.WAKE, Local0) + /* So that we don't get a warning that Local0 is unused. */ + Increment (Local0) + } +} diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/ich10_pci_irqs.asl b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/ich10_pci_irqs.asl new file mode 100644 index 0000000..21066fb --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/ich10_pci_irqs.asl @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* This is board specific information: IRQ routing for the + * 0:1e.0 PCI bridge of the ICH9 + */ + +/* TODO: which slots are actually relevant? */ +If (PICM) { + Return (Package() { + // PCI Slot 1 routes ABCD + Package() { 0x0000ffff, 0, 0, 16}, + Package() { 0x0000ffff, 1, 0, 17}, + Package() { 0x0000ffff, 2, 0, 18}, + Package() { 0x0000ffff, 3, 0, 19}, + + // PCI Slot 2 routes BCDA + Package() { 0x0001ffff, 0, 0, 17}, + Package() { 0x0001ffff, 1, 0, 18}, + Package() { 0x0001ffff, 2, 0, 19}, + Package() { 0x0001ffff, 3, 0, 16}, + + // PCI Slot 3 routes CDAB + Package() { 0x0002ffff, 0, 0, 18}, + Package() { 0x0002ffff, 1, 0, 19}, + Package() { 0x0002ffff, 2, 0, 16}, + Package() { 0x0002ffff, 3, 0, 17}, + + // PCI Slot 4 routes ABCD + Package() { 0x0003ffff, 0, 0, 16}, + Package() { 0x0003ffff, 1, 0, 17}, + Package() { 0x0003ffff, 2, 0, 18}, + Package() { 0x0003ffff, 3, 0, 19}, + + // PCI Slot 5 routes ABCD + Package() { 0x0004ffff, 0, 0, 16}, + Package() { 0x0004ffff, 1, 0, 17}, + Package() { 0x0004ffff, 2, 0, 18}, + Package() { 0x0004ffff, 3, 0, 19}, + + // PCI Slot 6 routes BCDA + Package() { 0x0005ffff, 0, 0, 17}, + Package() { 0x0005ffff, 1, 0, 18}, + Package() { 0x0005ffff, 2, 0, 19}, + Package() { 0x0005ffff, 3, 0, 16}, + + // FIXME: what's this supposed to mean? (adopted from ich7) + //Package() { 0x0008ffff, 0, 0, 20}, + }) +} Else { + Return (Package() { + // PCI Slot 1 routes ABCD + Package() { 0x0000ffff, 0, _SB.PCI0.LPCB.LNKA, 0}, + Package() { 0x0000ffff, 1, _SB.PCI0.LPCB.LNKB, 0}, + Package() { 0x0000ffff, 2, _SB.PCI0.LPCB.LNKC, 0}, + Package() { 0x0000ffff, 3, _SB.PCI0.LPCB.LNKD, 0}, + + // PCI Slot 2 routes BCDA + Package() { 0x0001ffff, 0, _SB.PCI0.LPCB.LNKB, 0}, + Package() { 0x0001ffff, 1, _SB.PCI0.LPCB.LNKC, 0}, + Package() { 0x0001ffff, 2, _SB.PCI0.LPCB.LNKD, 0}, + Package() { 0x0001ffff, 3, _SB.PCI0.LPCB.LNKA, 0}, + + // PCI Slot 3 routes CDAB + Package() { 0x0002ffff, 0, _SB.PCI0.LPCB.LNKC, 0}, + Package() { 0x0002ffff, 1, _SB.PCI0.LPCB.LNKD, 0}, + Package() { 0x0002ffff, 2, _SB.PCI0.LPCB.LNKA, 0}, + Package() { 0x0002ffff, 3, _SB.PCI0.LPCB.LNKB, 0}, + + // PCI Slot 4 routes ABCD + Package() { 0x0003ffff, 0, _SB.PCI0.LPCB.LNKA, 0}, + Package() { 0x0003ffff, 1, _SB.PCI0.LPCB.LNKB, 0}, + Package() { 0x0003ffff, 2, _SB.PCI0.LPCB.LNKC, 0}, + Package() { 0x0003ffff, 3, _SB.PCI0.LPCB.LNKD, 0}, + + // PCI Slot 5 routes ABCD + Package() { 0x0004ffff, 0, _SB.PCI0.LPCB.LNKA, 0}, + Package() { 0x0004ffff, 1, _SB.PCI0.LPCB.LNKB, 0}, + Package() { 0x0004ffff, 2, _SB.PCI0.LPCB.LNKC, 0}, + Package() { 0x0004ffff, 3, _SB.PCI0.LPCB.LNKD, 0}, + + // PCI Slot 6 routes BCDA + Package() { 0x0005ffff, 0, _SB.PCI0.LPCB.LNKB, 0}, + Package() { 0x0005ffff, 1, _SB.PCI0.LPCB.LNKC, 0}, + Package() { 0x0005ffff, 2, _SB.PCI0.LPCB.LNKD, 0}, + Package() { 0x0005ffff, 3, _SB.PCI0.LPCB.LNKA, 0}, + + // FIXME + // Package() { 0x0008ffff, 0, _SB.PCI0.LPCB.LNKE, 0}, + }) +} diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/superio.asl b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/superio.asl new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi/superio.asl diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi_tables.c b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi_tables.c new file mode 100644 index 0000000..209408e --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/acpi_tables.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpi_gnvs.h> +#include <southbridge/intel/i82801ix/nvs.h> + +void acpi_create_gnvs(struct global_nvs *gnvs) +{ + gnvs->pwrs = 1; /* Power state (AC = 1) */ + gnvs->lptp = 0; /* LPT port */ + gnvs->fdcp = 0; /* Floppy Disk Controller */ + gnvs->osys = 2002; /* At least WINXP SP2 (HPET fix) */ + gnvs->apic = 1; /* Enable APIC */ + gnvs->mpen = 1; /* Enable Multi Processing */ + gnvs->cmap = 0x01; /* Enable COM 1 port */ +} diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/board_info.txt b/src/mainboard/gigabyte/ga-ep35c-ds3r/board_info.txt new file mode 100644 index 0000000..170449e --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/board_info.txt @@ -0,0 +1,5 @@ +Category: desktop +Board URL: http://www.gigabyte.com/products/product-page.aspx?pid=3024#ov +ROM package: SOIC-8 +ROM protocol: SPI +ROM socketed: n diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/cmos.default b/src/mainboard/gigabyte/ga-ep35c-ds3r/cmos.default new file mode 100644 index 0000000..8372032 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/cmos.default @@ -0,0 +1,5 @@ +boot_option=Fallback +debug_level=Debug +power_on_after_fail=Enable +nmi=Enable +gfx_uma_size=64M diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/cmos.layout b/src/mainboard/gigabyte/ga-ep35c-ds3r/cmos.layout new file mode 100644 index 0000000..79094e6 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/cmos.layout @@ -0,0 +1,96 @@ +## SPDX-License-Identifier: GPL-2.0-only + +# ----------------------------------------------------------------- +entries + +# ----------------------------------------------------------------- +# Status Register A +# ----------------------------------------------------------------- +# Status Register B +# ----------------------------------------------------------------- +# Status Register C +#96 4 r 0 status_c_rsvd +#100 1 r 0 uf_flag +#101 1 r 0 af_flag +#102 1 r 0 pf_flag +#103 1 r 0 irqf_flag +# ----------------------------------------------------------------- +# Status Register D +#104 7 r 0 status_d_rsvd +#111 1 r 0 valid_cmos_ram +# ----------------------------------------------------------------- +# Diagnostic Status Register +#112 8 r 0 diag_rsvd1 + +# ----------------------------------------------------------------- +0 120 r 0 reserved_memory +#120 264 r 0 unused + +# ----------------------------------------------------------------- +# RTC_BOOT_BYTE (coreboot hardcoded) +384 1 e 4 boot_option +388 4 h 0 reboot_counter +#390 2 r 0 unused? + +# ----------------------------------------------------------------- +# coreboot config options: console +#392 3 r 0 unused +395 4 e 6 debug_level +#399 1 r 0 unused + +# coreboot config options: southbridge +408 1 e 1 nmi +409 2 e 7 power_on_after_fail + +# coreboot config options: cpu +#425 7 r 0 unused + +# coreboot config options: northbridge +432 4 e 11 gfx_uma_size +#435 549 r 0 unused + + +# coreboot config options: check sums +984 16 h 0 check_sum + +1024 144 r 0 recv_enable_results +# ----------------------------------------------------------------- + +enumerations + +#ID value text +1 0 Disable +1 1 Enable +2 0 Enable +2 1 Disable +4 0 Fallback +4 1 Normal +6 0 Emergency +6 1 Alert +6 2 Critical +6 3 Error +6 4 Warning +6 5 Notice +6 6 Info +6 7 Debug +6 8 Spew +7 0 Disable +7 1 Enable +7 2 Keep +11 1 4M +11 2 8M +11 3 16M +11 4 32M +11 5 48M +11 6 64M +11 7 128M +11 8 256M +11 9 96M +11 10 160M +11 11 224M +11 12 352M + +# ----------------------------------------------------------------- +checksums + +checksum 392 983 984 diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/cstates.c b/src/mainboard/gigabyte/ga-ep35c-ds3r/cstates.c new file mode 100644 index 0000000..0a7b9b9 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/cstates.c @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpigen.h> + +static acpi_cstate_t cst_entries[] = {}; + +int get_cst_entries(acpi_cstate_t **entries) +{ + *entries = cst_entries; + return ARRAY_SIZE(cst_entries); +} diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/data.vbt b/src/mainboard/gigabyte/ga-ep35c-ds3r/data.vbt new file mode 100644 index 0000000..4d34a30 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/data.vbt Binary files differ diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/devicetree.cb b/src/mainboard/gigabyte/ga-ep35c-ds3r/devicetree.cb new file mode 100644 index 0000000..07e7893 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/devicetree.cb @@ -0,0 +1,153 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +chip northbridge/intel/x4x # Northbridge + device cpu_cluster 0 on # APIC cluster + chip cpu/intel/socket_LGA775 + device lapic 0 on end + end + chip cpu/intel/model_1067x # CPU + device lapic 0xACAC off end + end + end + device domain 0 on # PCI domain + subsystemid 0x1458 0x5000 inherit + device pci 0.0 on # Host Bridge + subsystemid 0x1458 0x5000 + end + device pci 1.0 on end # PEG + device pci 2.0 on # Integrated graphics controller + subsystemid 0x1458 0xd000 + end + device pci 2.1 on # Integrated graphics controller 2 + subsystemid 0x1458 0xd001 + end + chip southbridge/intel/i82801ix # Southbridge + register "pirqa_routing" = "0x0b" + register "pirqb_routing" = "0x0b" + register "pirqc_routing" = "0x0b" + register "pirqd_routing" = "0x0b" + register "pirqe_routing" = "0x0b" + register "pirqf_routing" = "0x0b" + register "pirqg_routing" = "0x0b" + register "pirqh_routing" = "0x0b" + register "gpe0_en" = "0x40" + + register "gen2_dec" = "0x007c0291" # HWM + + device pci 1b.0 on # Audio + subsystemid 0x1458 0xa002 + end + device pci 1c.0 on end # PCIe 1 + device pci 1c.1 on # PCIe 2 (NIC) + device pci 00.0 on # PCI 10ec:8168 + subsystemid 0x1458 0xe000 + end + end + device pci 1c.2 off end # PCIe 3 + device pci 1c.3 off end # PCIe 4 + device pci 1d.0 on # USB + subsystemid 0x1458 0x5004 + end + device pci 1d.1 on # USB + subsystemid 0x1458 0x5004 + end + device pci 1d.2 on # USB + subsystemid 0x1458 0x5004 + end + device pci 1d.3 on # USB + subsystemid 0x1458 0x5004 + end + device pci 1d.7 on # USB + subsystemid 0x1458 0x5006 + end + device pci 1e.0 on end # PCI bridge + device pci 1e.2 off end # AC'97 Audio + device pci 1e.3 off end # AC'97 Modem + device pci 1f.0 on # ISA bridge + subsystemid 0x1458 0x5001 + chip superio/ite/it8718f # Super I/O + register "TMPIN1.mode" = "THERMAL_RESISTOR" + register "TMPIN2.mode" = "THERMAL_RESISTOR" + register "TMPIN3.mode" = "THERMAL_DIODE" + register "TMPIN3.offset" = "0" + register "ec.vin_mask" = "VIN7 | VIN4 | VIN3 | VIN2 | VIN1 | VIN0" + + register "FAN1.mode" = "FAN_SMART_AUTOMATIC" + register "FAN1.smart.tmpin" = "3" + register "FAN1.smart.tmp_off" = "25" + register "FAN1.smart.tmp_start" = "30" + register "FAN1.smart.tmp_full" = "65" + register "FAN1.smart.tmp_delta" = "3" + register "FAN1.smart.smoothing" = "1" + register "FAN1.smart.pwm_start" = "0" + register "FAN1.smart.slope" = "10" + + register "FAN2.mode" = "FAN_SMART_AUTOMATIC" + register "FAN2.smart.tmpin" = "3" + register "FAN2.smart.tmp_off" = "25" + register "FAN2.smart.tmp_start" = "30" + register "FAN2.smart.tmp_full" = "65" + register "FAN2.smart.tmp_delta" = "3" + register "FAN2.smart.smoothing" = "1" + register "FAN2.smart.pwm_start" = "0" + register "FAN2.smart.slope" = "10" + + device pnp 2e.0 on # Floppy + io 0x60 = 0x3f0 + irq 0x70 = 6 + drq 0x74 = 2 + irq 0xf0 = 0x00 + irq 0xf1 = 0x80 + end + device pnp 2e.1 on # COM1 + io 0x60 = 0x3f8 + irq 0x70 = 4 + end + device pnp 2e.2 on # COM2 + io 0x60 = 0x2f8 + irq 0x70 = 3 + end + device pnp 2e.3 on # Parallel port + io 0x60 = 0x378 + irq 0x70 = 7 + io 0x62 = 0x000 + drq 0x74 = 4 + irq 0xf0 = 0x08 + end + device pnp 2e.4 on # Environment controller + io 0x60 = 0x290 + irq 0x70 = 0x00 + io 0x62 = 0x000 + irq 0xf0 = 0x80 + irq 0xf1 = 0x00 + irq 0xf2 = 0x0a + irq 0xf3 = 0x80 + irq 0xf4 = 0x00 + irq 0xf5 = 0x00 + irq 0xf6 = 0xff + end + device pnp 2e.5 on # Keyboard + io 0x60 = 0x60 + irq 0x70 = 1 + io 0x62 = 0x64 + irq 0xf0 = 0x48 + end + device pnp 2e.6 on # Mouse + irq 0x70 = 12 + irq 0x71 = 2 + irq 0xf0 = 0 + end + end + end + device pci 1f.1 on # PATA/IDE + subsystemid 0x1458 0xb004 + end + device pci 1f.2 on # SATA + subsystemid 0x1458 0xb005 + end + device pci 1f.3 on # SMbus + subsystemid 0x1458 0x5001 + end + end + end +end diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/dsdt.asl b/src/mainboard/gigabyte/ga-ep35c-ds3r/dsdt.asl new file mode 100644 index 0000000..ba82498 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/dsdt.asl @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpi.h> +DefinitionBlock( + "dsdt.aml", + "DSDT", + 0x02, // DSDT revision: ACPI v2.0 and up + OEM_ID, + ACPI_TABLE_CREATOR, + 0x20090419 // OEM revision +) +{ + // global NVS and variables + #include <southbridge/intel/i82801jx/acpi/globalnvs.asl> + #include <southbridge/intel/common/acpi/platform.asl> + + #include <cpu/intel/speedstep/acpi/cpu.asl> + + Scope (_SB) { + Device (PCI0) + { + #include <northbridge/intel/x4x/acpi/x4x.asl> + #include <southbridge/intel/i82801jx/acpi/ich10.asl> + } + } + + #include <southbridge/intel/common/acpi/sleepstates.asl> +} diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/early_init.c b/src/mainboard/gigabyte/ga-ep35c-ds3r/early_init.c new file mode 100644 index 0000000..a45258c --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/early_init.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <bootblock_common.h> +#include <device/pci_ops.h> +#include <southbridge/intel/i82801ix/i82801ix.h> +#include <northbridge/intel/x4x/x4x.h> +#include <superio/ite/it8718f/it8718f.h> +#include <superio/ite/common/ite.h> + +#define SERIAL_DEV PNP_DEV(0x2e, IT8718F_SP1) +#define GPIO_DEV PNP_DEV(0x2e, IT8718F_GPIO) +#define EC_DEV PNP_DEV(0x2e, IT8718F_EC) + +/* Early mainboard specific GPIO setup. + * We should use standard gpio.h eventually + */ + +void bootblock_mainboard_early_init(void) +{ + /* Set default GPIOs on superio */ + ite_reg_write(GPIO_DEV, 0x25, 0x00); + ite_reg_write(GPIO_DEV, 0x26, 0xc7); + ite_reg_write(GPIO_DEV, 0x27, 0x80); + ite_reg_write(GPIO_DEV, 0x28, 0x41); + ite_reg_write(GPIO_DEV, 0x29, 0x0a); + ite_reg_write(GPIO_DEV, 0x2c, 0x01); + ite_reg_write(GPIO_DEV, 0x62, 0x08); + ite_reg_write(GPIO_DEV, 0x72, 0x00); + ite_reg_write(GPIO_DEV, 0x73, 0x00); + ite_reg_write(GPIO_DEV, 0xb8, 0x00); + ite_reg_write(GPIO_DEV, 0xbb, 0x40); + ite_reg_write(GPIO_DEV, 0xc0, 0x00); + ite_reg_write(GPIO_DEV, 0xc1, 0xc7); + ite_reg_write(GPIO_DEV, 0xc2, 0x80); + ite_reg_write(GPIO_DEV, 0xc3, 0x01); + ite_reg_write(GPIO_DEV, 0xc4, 0x0a); + ite_reg_write(GPIO_DEV, 0xc8, 0x00); + ite_reg_write(GPIO_DEV, 0xc9, 0x04); + ite_reg_write(GPIO_DEV, 0xcb, 0x00); + ite_reg_write(GPIO_DEV, 0xcc, 0x02); + ite_reg_write(GPIO_DEV, 0xf0, 0x10); + ite_reg_write(GPIO_DEV, 0xf1, 0x40); + ite_reg_write(GPIO_DEV, 0xf6, 0x26); + ite_reg_write(GPIO_DEV, 0xfc, 0x52); + + ite_reg_write(EC_DEV, 0xf0, 0x80); + ite_reg_write(EC_DEV, 0xf1, 0x00); + ite_reg_write(EC_DEV, 0xf2, 0x0a); + ite_reg_write(EC_DEV, 0xf3, 0x80); + ite_reg_write(EC_DEV, 0x70, 0x00); // Don't use IRQ9 + ite_reg_write(EC_DEV, 0x30, 0x01); // Enable + + ite_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE); + + /* Disable SIO reboot */ + ite_reg_write(GPIO_DEV, 0xEF, 0x7E); +} + +void mb_get_spd_map(u8 spd_map[4]) +{ + spd_map[0] = 0x50; + spd_map[2] = 0x52; +} diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/gma-mainboard.ads b/src/mainboard/gigabyte/ga-ep35c-ds3r/gma-mainboard.ads new file mode 100644 index 0000000..c6422a5 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/gma-mainboard.ads @@ -0,0 +1,15 @@ +-- SPDX-License-Identifier: GPL-2.0-or-later + +with HW.GFX.GMA; +with HW.GFX.GMA.Display_Probing; + +use HW.GFX.GMA; +use HW.GFX.GMA.Display_Probing; + +private package GMA.Mainboard is + + ports : constant Port_List := + (Analog, + others => Disabled); + +end GMA.Mainboard; diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/gpio.c b/src/mainboard/gigabyte/ga-ep35c-ds3r/gpio.c new file mode 100644 index 0000000..4b3295c --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/gpio.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <southbridge/intel/common/gpio.h> + +static const struct pch_gpio_set1 pch_gpio_set1_mode = { + .gpio6 = GPIO_MODE_GPIO, + .gpio7 = GPIO_MODE_GPIO, + .gpio8 = GPIO_MODE_GPIO, + .gpio9 = GPIO_MODE_GPIO, + .gpio10 = GPIO_MODE_GPIO, + .gpio12 = GPIO_MODE_GPIO, + .gpio13 = GPIO_MODE_GPIO, + .gpio14 = GPIO_MODE_GPIO, + .gpio15 = GPIO_MODE_GPIO, + .gpio16 = GPIO_MODE_GPIO, + .gpio18 = GPIO_MODE_GPIO, + .gpio20 = GPIO_MODE_GPIO, + .gpio21 = GPIO_MODE_GPIO, + .gpio24 = GPIO_MODE_GPIO, + .gpio25 = GPIO_MODE_GPIO, + .gpio26 = GPIO_MODE_GPIO, + .gpio27 = GPIO_MODE_GPIO, + .gpio28 = GPIO_MODE_GPIO, +}; + +static const struct pch_gpio_set1 pch_gpio_set1_direction = { + .gpio6 = GPIO_DIR_INPUT, + .gpio7 = GPIO_DIR_INPUT, + .gpio8 = GPIO_DIR_INPUT, + .gpio9 = GPIO_DIR_INPUT, + .gpio10 = GPIO_DIR_INPUT, + .gpio12 = GPIO_DIR_INPUT, + .gpio13 = GPIO_DIR_INPUT, + .gpio14 = GPIO_DIR_INPUT, + .gpio15 = GPIO_DIR_INPUT, + .gpio16 = GPIO_DIR_INPUT, + .gpio18 = GPIO_DIR_OUTPUT, + .gpio20 = GPIO_DIR_OUTPUT, + .gpio21 = GPIO_DIR_INPUT, + .gpio24 = GPIO_DIR_OUTPUT, + .gpio25 = GPIO_DIR_INPUT, + .gpio26 = GPIO_DIR_OUTPUT, + .gpio27 = GPIO_DIR_OUTPUT, + .gpio28 = GPIO_DIR_OUTPUT, +}; + +static const struct pch_gpio_set1 pch_gpio_set1_level = { + .gpio18 = GPIO_LEVEL_HIGH, + .gpio20 = GPIO_LEVEL_HIGH, + .gpio24 = GPIO_LEVEL_LOW, + .gpio26 = GPIO_LEVEL_LOW, + .gpio27 = GPIO_LEVEL_LOW, + .gpio28 = GPIO_LEVEL_LOW, +}; + +static const struct pch_gpio_set1 pch_gpio_set1_invert = { + .gpio6 = GPIO_INVERT, + .gpio7 = GPIO_INVERT, + .gpio8 = GPIO_INVERT, + .gpio12 = GPIO_INVERT, + .gpio13 = GPIO_INVERT, +}; + +static const struct pch_gpio_set1 pch_gpio_set1_blink = { +}; + +static const struct pch_gpio_set2 pch_gpio_set2_mode = { + .gpio32 = GPIO_MODE_GPIO, + .gpio33 = GPIO_MODE_GPIO, + .gpio34 = GPIO_MODE_GPIO, + .gpio37 = GPIO_MODE_GPIO, + .gpio38 = GPIO_MODE_GPIO, + .gpio39 = GPIO_MODE_GPIO, +}; + +static const struct pch_gpio_set2 pch_gpio_set2_direction = { + .gpio32 = GPIO_DIR_OUTPUT, + .gpio33 = GPIO_DIR_OUTPUT, + .gpio34 = GPIO_DIR_OUTPUT, + .gpio37 = GPIO_DIR_INPUT, + .gpio38 = GPIO_DIR_INPUT, + .gpio39 = GPIO_DIR_INPUT, +}; + +static const struct pch_gpio_set2 pch_gpio_set2_level = { + .gpio32 = GPIO_LEVEL_HIGH, + .gpio33 = GPIO_LEVEL_HIGH, + .gpio34 = GPIO_LEVEL_LOW, +}; + +const struct pch_gpio_map mainboard_gpio_map = { + .set1 = { + .mode = &pch_gpio_set1_mode, + .direction = &pch_gpio_set1_direction, + .level = &pch_gpio_set1_level, + .blink = &pch_gpio_set1_blink, + .invert = &pch_gpio_set1_invert, + }, + .set2 = { + .mode = &pch_gpio_set2_mode, + .direction = &pch_gpio_set2_direction, + .level = &pch_gpio_set2_level, + }, +}; diff --git a/src/mainboard/gigabyte/ga-ep35c-ds3r/hda_verb.c b/src/mainboard/gigabyte/ga-ep35c-ds3r/hda_verb.c new file mode 100644 index 0000000..75a0864 --- /dev/null +++ b/src/mainboard/gigabyte/ga-ep35c-ds3r/hda_verb.c @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <device/azalia_device.h> + +const u32 cim_verb_data[] = { + /* coreboot specific header */ + 0x10ec0887, + 0x1458a002, // Subsystem ID + 0x0000000e, // Number of entries + + /* Pin Widget Verb Table */ + + AZALIA_PIN_CFG(0, 0x11, 0x411110f0), + AZALIA_PIN_CFG(0, 0x12, 0x411111f0), + AZALIA_PIN_CFG(0, 0x14, 0x01014410), + AZALIA_PIN_CFG(0, 0x15, 0x411111f0), + AZALIA_PIN_CFG(0, 0x16, 0x411111f0), + AZALIA_PIN_CFG(0, 0x17, 0x411111f0), + AZALIA_PIN_CFG(0, 0x18, 0x01a19c40), + AZALIA_PIN_CFG(0, 0x19, 0x02a19c50), + AZALIA_PIN_CFG(0, 0x1a, 0x0181344f), + AZALIA_PIN_CFG(0, 0x1b, 0x02214c20), + AZALIA_PIN_CFG(0, 0x1c, 0x593301f0), + AZALIA_PIN_CFG(0, 0x1d, 0x4005c603), + AZALIA_PIN_CFG(0, 0x1e, 0x014b6130), + AZALIA_PIN_CFG(0, 0x1f, 0x01cb7160), +}; + +const u32 pc_beep_verbs[0] = {}; + +const u32 pc_beep_verbs_size = ARRAY_SIZE(pc_beep_verbs); +const u32 cim_verb_data_size = ARRAY_SIZE(cim_verb_data); diff --git a/src/northbridge/intel/x3x/Kconfig b/src/northbridge/intel/x3x/Kconfig new file mode 100644 index 0000000..a8cffbc7 --- /dev/null +++ b/src/northbridge/intel/x3x/Kconfig @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config NORTHBRIDGE_INTEL_X3X + bool + +if NORTHBRIDGE_INTEL_X3X + +config NORTHBRIDGE_SPECIFIC_OPTIONS # dummy + def_bool y + select HAVE_DEBUG_RAM_SETUP + select VGA + select INTEL_GMA_ACPI + select CACHE_MRC_SETTINGS + select PARALLEL_MP + +config CBFS_SIZE + hex + default 0x100000 if !SOUTHBRIDGE_INTEL_I82801GX + +config VGA_BIOS_ID + string + default "8086,2e32" + +config MMCONF_BASE_ADDRESS + hex + default 0xe0000000 + +config SMM_RESERVED_SIZE + hex + default 0x100000 + +config MAX_CPUS + int + default 4 + +endif diff --git a/src/northbridge/intel/x3x/Makefile.inc b/src/northbridge/intel/x3x/Makefile.inc new file mode 100644 index 0000000..979d07d --- /dev/null +++ b/src/northbridge/intel/x3x/Makefile.inc @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0-only + +ifeq ($(CONFIG_NORTHBRIDGE_INTEL_X3X),y) + +bootblock-y += bootblock.c + +romstage-y += early_init.c +romstage-y += raminit.c +romstage-y += raminit_ddr23.c +romstage-y += memmap.c +romstage-y += rcven.c +romstage-y += raminit_tables.c +romstage-y += dq_dqs.c +romstage-y += romstage.c + +ramstage-y += acpi.c +ramstage-y += memmap.c +ramstage-y += gma.c +ramstage-y += northbridge.c + +postcar-y += memmap.c + +endif diff --git a/src/northbridge/intel/x3x/acpi.c b/src/northbridge/intel/x3x/acpi.c new file mode 100644 index 0000000..9d8f507 --- /dev/null +++ b/src/northbridge/intel/x3x/acpi.c @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <types.h> +#include <console/console.h> +#include <acpi/acpi.h> +#include <acpi/acpigen.h> +#include <device/device.h> +#include "x3x.h" + +unsigned long acpi_fill_mcfg(unsigned long current) +{ + u32 pciexbar = 0; + u32 length = 0; + + if (!decode_pciebar(&pciexbar, &length)) + return current; + + current += acpi_create_mcfg_mmconfig((acpi_mcfg_mmconfig_t *) current, + pciexbar, 0x0, 0x0, (length >> 20) - 1); + + return current; +} + +unsigned long northbridge_write_acpi_tables(const struct device *device, + unsigned long start, + struct acpi_rsdp *rsdp) +{ + unsigned long current; + current = acpi_align_current(start); + + printk(BIOS_DEBUG, "current = %lx\n", current); + + return current; +} diff --git a/src/northbridge/intel/x3x/acpi/hostbridge.asl b/src/northbridge/intel/x3x/acpi/hostbridge.asl new file mode 100644 index 0000000..2a8a137 --- /dev/null +++ b/src/northbridge/intel/x3x/acpi/hostbridge.asl @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <arch/ioapic.h> + +Name(_HID,EISAID("PNP0A08")) // PCIe +Name(_CID,EISAID("PNP0A03")) // PCI + +Name(_BBN, 0) + +Device (MCHC) +{ + Name(_ADR, 0x00000000) /* 0:0.0 */ + + OperationRegion(MCHP, PCI_Config, 0x00, 0x100) + Field (MCHP, DWordAcc, NoLock, Preserve) + { + Offset (0x40), /* EPBAR */ + EPEN, 1, /* Enable */ + , 11, + EPBR, 24, /* EPBAR */ + + Offset (0x48), /* MCHBAR */ + MHEN, 1, /* Enable */ + , 13, + MHBR, 22, /* MCHBAR */ + + Offset (0x60), /* PCIec BAR */ + PXEN, 1, /* Enable */ + PXSZ, 2, /* BAR size */ + , 23, + PXBR, 10, /* PCIe BAR */ + + Offset (0x68), /* DMIBAR */ + DMEN, 1, /* Enable */ + , 11, + DMBR, 24, /* DMIBAR */ + + /* ... */ + + Offset (0x90), /* PAM0 */ + , 4, + PM0H, 2, + , 2, + Offset (0x91), /* PAM1 */ + PM1L, 2, + , 2, + PM1H, 2, + , 2, + Offset (0x92), /* PAM2 */ + PM2L, 2, + , 2, + PM2H, 2, + , 2, + Offset (0x93), /* PAM3 */ + PM3L, 2, + , 2, + PM3H, 2, + , 2, + Offset (0x94), /* PAM4 */ + PM4L, 2, + , 2, + PM4H, 2, + , 2, + Offset (0x95), /* PAM5 */ + PM5L, 2, + , 2, + PM5H, 2, + , 2, + Offset (0x96), /* PAM6 */ + PM6L, 2, + , 2, + PM6H, 2, + , 2, + + Offset (0xa0), /* Top of Memory */ + TOM, 8, + + Offset (0xb0), /* Top of Low Used Memory */ + , 4, + TLUD, 12, + } +} + +Name (MCRS, ResourceTemplate() +{ + /* Bus Numbers */ + WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, 0x0000, 0x00ff, 0x0000, 0x0100,,, PB00) + + /* IO Region 0 */ + DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, 0x0000, 0x0cf7, 0x0000, 0x0cf8,,, PI00) + + /* PCI Config Space */ + Io (Decode16, 0x0cf8, 0x0cf8, 0x0001, 0x0008) + + /* IO Region 1 */ + DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, 0x0d00, 0xffff, 0x0000, 0xf300,,, PI01) + + /* VGA memory (0xa0000-0xbffff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000a0000, 0x000bffff, 0x00000000, + 0x00020000,,, ASEG) + + /* OPROM reserved (0xc0000-0xc3fff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000c0000, 0x000c3fff, 0x00000000, + 0x00004000,,, OPR0) + + /* OPROM reserved (0xc4000-0xc7fff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000c4000, 0x000c7fff, 0x00000000, + 0x00004000,,, OPR1) + + /* OPROM reserved (0xc8000-0xcbfff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000c8000, 0x000cbfff, 0x00000000, + 0x00004000,,, OPR2) + + /* OPROM reserved (0xcc000-0xcffff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000cc000, 0x000cffff, 0x00000000, + 0x00004000,,, OPR3) + + /* OPROM reserved (0xd0000-0xd3fff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000d0000, 0x000d3fff, 0x00000000, + 0x00004000,,, OPR4) + + /* OPROM reserved (0xd4000-0xd7fff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000d4000, 0x000d7fff, 0x00000000, + 0x00004000,,, OPR5) + + /* OPROM reserved (0xd8000-0xdbfff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000d8000, 0x000dbfff, 0x00000000, + 0x00004000,,, OPR6) + + /* OPROM reserved (0xdc000-0xdffff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000dc000, 0x000dffff, 0x00000000, + 0x00004000,,, OPR7) + + /* BIOS Extension (0xe0000-0xe3fff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000e0000, 0x000e3fff, 0x00000000, + 0x00004000,,, ESG0) + + /* BIOS Extension (0xe4000-0xe7fff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000e4000, 0x000e7fff, 0x00000000, + 0x00004000,,, ESG1) + + /* BIOS Extension (0xe8000-0xebfff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000e8000, 0x000ebfff, 0x00000000, + 0x00004000,,, ESG2) + + /* BIOS Extension (0xec000-0xeffff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000ec000, 0x000effff, 0x00000000, + 0x00004000,,, ESG3) + + /* System BIOS (0xf0000-0xfffff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x000f0000, 0x000fffff, 0x00000000, + 0x00010000,,, FSEG) + + /* PCI Memory Region (Top of memory-0xfebfffff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0x00000000, 0xfebfffff, 0x00000000, + IO_APIC_ADDR,,, PM01) + + /* TPM Area (0xfed40000-0xfed44fff) */ + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, + Cacheable, ReadWrite, + 0x00000000, 0xfed40000, 0xfed44fff, 0x00000000, + 0x00005000,,, TPMR) +}) + +/* Current Resource Settings */ +Method (_CRS, 0, Serialized) +{ + /* Find PCI resource area in MCRS */ + CreateDwordField(MCRS, ^PM01._MIN, PMIN) + CreateDwordField(MCRS, ^PM01._MAX, PMAX) + CreateDwordField(MCRS, ^PM01._LEN, PLEN) + + /* + * Fix up PCI memory region: + * Enter actual TOLUD. The TOLUD register contains bits 20-31 of + * the top of memory address. + */ + PMIN = ^MCHC.TLUD << 20 + PLEN = PMAX - PMIN + 1 + + Return (MCRS) +} diff --git a/src/northbridge/intel/x3x/acpi/peg.asl b/src/northbridge/intel/x3x/acpi/peg.asl new file mode 100644 index 0000000..6a67238 --- /dev/null +++ b/src/northbridge/intel/x3x/acpi/peg.asl @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (PEGP) +{ + Name (_ADR, 0x00010000) + + // PCI Interrupt Routing. + Method (_PRT) + { + If (PICM) { + Return (Package() { + Package() { 0x0000ffff, 0, 0, 16 }, + Package() { 0x0000ffff, 1, 0, 17 }, + Package() { 0x0000ffff, 2, 0, 18 }, + Package() { 0x0000ffff, 3, 0, 19 }, + }) + } Else { + Return (Package() { + Package() { 0x0000ffff, 0, _SB.PCI0.LPCB.LNKA, 0 }, + Package() { 0x0000ffff, 1, _SB.PCI0.LPCB.LNKB, 0 }, + Package() { 0x0000ffff, 2, _SB.PCI0.LPCB.LNKC, 0 }, + Package() { 0x0000ffff, 3, _SB.PCI0.LPCB.LNKD, 0 }, + }) + } + } +} diff --git a/src/northbridge/intel/x3x/acpi/x3x.asl b/src/northbridge/intel/x3x/acpi/x3x.asl new file mode 100644 index 0000000..51deea8 --- /dev/null +++ b/src/northbridge/intel/x3x/acpi/x3x.asl @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include "hostbridge.asl" +#include "../iomap.h" +#include <southbridge/intel/common/rcba.h> + +/* PCI Device Resource Consumption */ +Device (PDRC) +{ + Name (_HID, EISAID("PNP0C02")) + Name (_UID, 1) + + Name (PDRS, ResourceTemplate() { + Memory32Fixed(ReadWrite, DEFAULT_RCBA, 0x00004000) + Memory32Fixed(ReadWrite, DEFAULT_MCHBAR, 0x00004000) + Memory32Fixed(ReadWrite, DEFAULT_DMIBAR, 0x00001000) + Memory32Fixed(ReadWrite, DEFAULT_EPBAR, 0x00001000) + Memory32Fixed(ReadWrite, CONFIG_MMCONF_BASE_ADDRESS, 0x04000000) + Memory32Fixed(ReadWrite, 0xfed20000, 0x00020000) // Misc ICH + Memory32Fixed(ReadWrite, 0xfed40000, 0x00005000) // Misc ICH + Memory32Fixed(ReadWrite, 0xfed45000, 0x0004b000) // Misc ICH + }) + + // Current Resource Settings + Method (_CRS, 0, Serialized) + { + Return(PDRS) + } +} + +// PCIe graphics port 0:1.0 +#include "peg.asl" diff --git a/src/northbridge/intel/x3x/bootblock.c b/src/northbridge/intel/x3x/bootblock.c new file mode 100644 index 0000000..4cefb28 --- /dev/null +++ b/src/northbridge/intel/x3x/bootblock.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <arch/bootblock.h> +#include <device/pci_ops.h> +#include "x3x.h" +#include "iomap.h" + +void bootblock_early_northbridge_init(void) +{ + uint32_t reg32; + + /* Disable LaGrande Technology (LT) */ + reg32 = TPM32(0); + + reg32 = CONFIG_MMCONF_BASE_ADDRESS | 16 | 1; + pci_io_write_config32(PCI_DEV(0, 0, 0), D0F0_PCIEXBAR_LO, reg32); +} diff --git a/src/northbridge/intel/x3x/chip.h b/src/northbridge/intel/x3x/chip.h new file mode 100644 index 0000000..e537a2e --- /dev/null +++ b/src/northbridge/intel/x3x/chip.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef NORTHBRIDGE_INTEL_X3X_CHIP_H +#define NORTHBRIDGE_INTEL_X3X_CHIP_H + +#include <drivers/intel/gma/i915.h> + +struct northbridge_intel_x3x_config { + struct i915_gpu_controller_info gfx; +}; + +#endif diff --git a/src/northbridge/intel/x3x/dq_dqs.c b/src/northbridge/intel/x3x/dq_dqs.c new file mode 100644 index 0000000..0154bab --- /dev/null +++ b/src/northbridge/intel/x3x/dq_dqs.c @@ -0,0 +1,868 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <device/mmio.h> +#include <console/console.h> +#include <delay.h> +#include <string.h> +#include <types.h> +#include "x3x.h" +#include "iomap.h" + +static void print_dll_setting(const struct dll_setting *dll_setting, + u8 default_verbose) +{ + u8 debug_level = default_verbose ? BIOS_DEBUG : RAM_DEBUG; + + printk(debug_level, "%d.%d.%d.%d:%d.%d\n", dll_setting->coarse, + dll_setting->clk_delay, dll_setting->tap, + dll_setting->pi, dll_setting->db_en, + dll_setting->db_sel); +} + +struct db_limit { + u8 tap0; + u8 tap1; + u8 pi0; + u8 pi1; +}; + +static void set_db(const struct sysinfo *s, struct dll_setting *dq_dqs_setting) +{ + struct db_limit limit; + + switch (s->selected_timings.mem_clk) { + default: + case MEM_CLOCK_800MHz: + limit.tap0 = 3; + limit.tap1 = 10; + limit.pi0 = 2; + limit.pi1 = 3; + break; + case MEM_CLOCK_1066MHz: + limit.tap0 = 2; + limit.tap1 = 8; + limit.pi0 = 6; + limit.pi1 = 7; + break; + case MEM_CLOCK_1333MHz: + limit.tap0 = 3; + limit.tap1 = 11; + /* TO CHECK: Might be reverse since this makes little sense */ + limit.pi0 = 6; + limit.pi1 = 4; + break; + } + + if (dq_dqs_setting->tap < limit.tap0) { + dq_dqs_setting->db_en = 1; + dq_dqs_setting->db_sel = 1; + } else if ((dq_dqs_setting->tap == limit.tap0) + && (dq_dqs_setting->pi < limit.pi0)) { + dq_dqs_setting->db_en = 1; + dq_dqs_setting->db_sel = 1; + } else if (dq_dqs_setting->tap < limit.tap1) { + dq_dqs_setting->db_en = 0; + dq_dqs_setting->db_sel = 0; + } else if ((dq_dqs_setting->tap == limit.tap1) + && (dq_dqs_setting->pi < limit.pi1)) { + dq_dqs_setting->db_en = 0; + dq_dqs_setting->db_sel = 0; + } else { + dq_dqs_setting->db_en = 1; + dq_dqs_setting->db_sel = 0; + } +} + +static const u8 max_tap[3] = {12, 10, 13}; + +static int increment_dq_dqs(const struct sysinfo *s, + struct dll_setting *dq_dqs_setting) +{ + u8 max_tap_val = max_tap[s->selected_timings.mem_clk + - MEM_CLOCK_800MHz]; + + if (dq_dqs_setting->pi < 6) { + dq_dqs_setting->pi += 1; + } else if (dq_dqs_setting->tap < max_tap_val) { + dq_dqs_setting->pi = 0; + dq_dqs_setting->tap += 1; + } else if (dq_dqs_setting->clk_delay < 2) { + dq_dqs_setting->pi = 0; + dq_dqs_setting->tap = 0; + dq_dqs_setting->clk_delay += 1; + } else if (dq_dqs_setting->coarse < 1) { + dq_dqs_setting->pi = 0; + dq_dqs_setting->tap = 0; + dq_dqs_setting->clk_delay -= 1; + dq_dqs_setting->coarse += 1; + } else { + return CB_ERR; + } + set_db(s, dq_dqs_setting); + return CB_SUCCESS; +} + +static int decrement_dq_dqs(const struct sysinfo *s, + struct dll_setting *dq_dqs_setting) +{ + u8 max_tap_val = max_tap[s->selected_timings.mem_clk + - MEM_CLOCK_800MHz]; + + if (dq_dqs_setting->pi > 0) { + dq_dqs_setting->pi -= 1; + } else if (dq_dqs_setting->tap > 0) { + dq_dqs_setting->pi = 6; + dq_dqs_setting->tap -= 1; + } else if (dq_dqs_setting->clk_delay > 0) { + dq_dqs_setting->pi = 6; + dq_dqs_setting->tap = max_tap_val; + dq_dqs_setting->clk_delay -= 1; + } else if (dq_dqs_setting->coarse > 0) { + dq_dqs_setting->pi = 6; + dq_dqs_setting->tap = max_tap_val; + dq_dqs_setting->clk_delay += 1; + dq_dqs_setting->coarse -= 1; + } else { + return CB_ERR; + } + set_db(s, dq_dqs_setting); + return CB_SUCCESS; +} + + +#define WT_PATTERN_SIZE 80 + +static const u32 write_training_schedule[WT_PATTERN_SIZE] = { + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xefefefef, 0x10101010, 0xefefefef, 0x10101010, + 0xefefefef, 0x10101010, 0xefefefef, 0x10101010, + 0xefefefef, 0x10101010, 0xefefefef, 0x10101010, + 0xefefefef, 0x10101010, 0xefefefef, 0x10101010, + 0xefefefef, 0xeeeeeeee, 0x11111111, 0x10101010, + 0xefefefef, 0xeeeeeeee, 0x11111111, 0x10101010, + 0xefefefef, 0xeeeeeeee, 0x11111111, 0x10101010, + 0xefefefef, 0xeeeeeeee, 0x11111111, 0x10101010, + 0x03030303, 0x04040404, 0x09090909, 0x10101010, + 0x21212121, 0x40404040, 0x81818181, 0x00000000, + 0x03030303, 0x04040404, 0x09090909, 0x10101010, + 0x21212121, 0x40404040, 0x81818181, 0x00000000, + 0xfdfdfdfd, 0xfafafafa, 0xf7f7f7f7, 0xeeeeeeee, + 0xdfdfdfdf, 0xbebebebe, 0x7f7f7f7f, 0xfefefefe, + 0xfdfdfdfd, 0xfafafafa, 0xf7f7f7f7, 0xeeeeeeee, + 0xdfdfdfdf, 0xbebebebe, 0x7f7f7f7f, 0xfefefefe, +}; + +enum training_modes { + SUCCEEDING = 0, + FAILING = 1 +}; + +static u8 test_dq_aligned(const struct sysinfo *s, + const u8 channel) +{ + u32 address; + int rank, lane; + u8 count, count1; + u8 data[8]; + u8 lane_error = 0; + + FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, channel, rank) { + address = test_address(channel, rank); + for (count = 0; count < WT_PATTERN_SIZE; count++) { + for (count1 = 0; count1 < WT_PATTERN_SIZE; count1++) { + if ((count1 % 16) == 0) + MCHBAR32(0xf90) = 1; + const u32 pattern = + write_training_schedule[count1]; + write32((u32 *)address + 8 * count1, pattern); + write32((u32 *)address + 8 * count1 + 4, + pattern); + } + + const u32 good = write_training_schedule[count]; + write32(&data[0], read32((u32 *)address + 8 * count)); + write32(&data[4], + read32((u32 *)address + 8 * count + 4)); + FOR_EACH_BYTELANE(lane) { + u8 expected = (good >> ((lane % 4) * 8)) & 0xff; + if (data[lane] != expected) + lane_error |= 1 << lane; + } + } + } + return lane_error; +} + +#define CONSISTENCY 10 + +/* + * This function finds either failing or succeeding writes by increasing DQ. + * When it has found a failing or succeeding setting it will increase DQ + * another 10 times to make sure the result is consistent. + * This is probably done because lanes cannot be trained independent from + * each other. + */ +static int find_dq_limit(const struct sysinfo *s, const u8 channel, + struct dll_setting dq_setting[TOTAL_BYTELANES], + u8 dq_lim[TOTAL_BYTELANES], + const enum training_modes expected_result) +{ + int status = CB_SUCCESS; + int lane; + u8 test_result; + u8 pass_count[TOTAL_BYTELANES]; + u8 succes_mask = 0xff; + + printk(RAM_DEBUG, "Looking for %s writes on channel %d\n", + expected_result == FAILING ? "failing" : "succeeding", channel); + memset(pass_count, 0, sizeof(pass_count)); + + while(succes_mask) { + test_result = test_dq_aligned(s, channel); + FOR_EACH_BYTELANE(lane) { + if (((test_result >> lane) & 1) != expected_result) { + status = increment_dq_dqs(s, &dq_setting[lane]); + dqset(channel, lane, &dq_setting[lane]); + dq_lim[lane]++; + } else if (pass_count[lane] < CONSISTENCY) { + status = increment_dq_dqs(s, &dq_setting[lane]); + dqset(channel, lane, &dq_setting[lane]); + dq_lim[lane]++; + pass_count[lane]++; + } else if (pass_count[lane] == CONSISTENCY) { + succes_mask &= ~(1 << lane); + } + if (status == CB_ERR) { + printk(BIOS_CRIT, "Could not find a case of %s " + "writes on CH%d, lane %d\n", + expected_result == FAILING ? "failing" + : "succeeding", channel, lane); + return CB_ERR; + } + } + } + return CB_SUCCESS; +} + +/* + * This attempts to find the ideal delay for DQ to account for the skew between + * the DQ and the DQS signal. + * The training works this way: + * - start from the DQS delay values (DQ is always later than DQS) + * - increment the DQ delay until a succeeding write is found on all bytelayes, + * on all ranks on a channel and save these values + * - again increment the DQ delay until write start to fail on all bytelanes and + * save that value + * - use the mean between the saved succeeding and failing value + * - note: bytelanes cannot be trained independently, so the delays need to be + * adjusted and tested for all of them at the same time + */ +int do_write_training(struct sysinfo *s) +{ + int i; + u8 channel, lane; + u8 dq_lower[TOTAL_BYTELANES]; + u8 dq_upper[TOTAL_BYTELANES]; + struct dll_setting dq_setting[TOTAL_BYTELANES]; + + printk(BIOS_DEBUG, "Starting DQ write training\n"); + + FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) { + printk(BIOS_DEBUG, "Doing DQ write training on CH%d\n", channel); + + /* Start all lanes at DQS values */ + FOR_EACH_BYTELANE(lane) { + dqset(channel, lane, &s->dqs_settings[channel][lane]); + s->dq_settings[channel][lane] = s->dqs_settings[channel][lane]; + } + memset(dq_lower, 0, sizeof(dq_lower)); + /* Start from DQS settings */ + memcpy(dq_setting, s->dqs_settings[channel], sizeof(dq_setting)); + + if (find_dq_limit(s, channel, dq_setting, dq_lower, + SUCCEEDING)) { + printk(BIOS_CRIT, + "Could not find working lower limit DQ setting\n"); + return CB_ERR; + } + + memcpy(dq_upper, dq_lower, sizeof(dq_lower)); + + if (find_dq_limit(s, channel, dq_setting, dq_upper, + FAILING)) { + printk(BIOS_WARNING, + "Could not find failing upper limit DQ setting\n"); + return CB_ERR; + } + + FOR_EACH_BYTELANE(lane) { + dq_lower[lane] -= CONSISTENCY - 1; + dq_upper[lane] -= CONSISTENCY - 1; + u8 dq_center = (dq_upper[lane] + dq_lower[lane]) / 2; + + printk(RAM_DEBUG, "Centered value for DQ DLL:" + " ch%d, lane %d, #steps = %d\n", + channel, lane, dq_center); + for (i = 0; i < dq_center; i++) { + /* Should never happen */ + if (increment_dq_dqs(s, &s->dq_settings[channel][lane]) + == CB_ERR) + printk(BIOS_ERR, + "Huh? write training overflowed!!\n"); + } + } + + /* Reset DQ DLL settings and increment with centered value*/ + printk(BIOS_DEBUG, "Final DQ timings on CH%d\n", channel); + FOR_EACH_BYTELANE(lane) { + printk(BIOS_DEBUG, "\tlane%d: ", lane); + print_dll_setting(&s->dq_settings[channel][lane], 1); + dqset(channel, lane, &s->dq_settings[channel][lane]); + } + } + printk(BIOS_DEBUG, "Done DQ write training\n"); + return CB_SUCCESS; +} + +#define RT_PATTERN_SIZE 40 + +static const u32 read_training_schedule[RT_PATTERN_SIZE] = { + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xffffffff, 0x00000000, 0xffffffff, 0x00000000, + 0xefefefef, 0x10101010, 0xefefefef, 0x10101010, + 0xefefefef, 0x10101010, 0xefefefef, 0x10101010, + 0xefefefef, 0xeeeeeeee, 0x11111111, 0x10101010, + 0xefefefef, 0xeeeeeeee, 0x11111111, 0x10101010, + 0x03030303, 0x04040404, 0x09090909, 0x10101010, + 0x21212121, 0x40404040, 0x81818181, 0x00000000, + 0xfdfdfdfd, 0xfafafafa, 0xf7f7f7f7, 0xeeeeeeee, + 0xdfdfdfdf, 0xbebebebe, 0x7f7f7f7f, 0xfefefefe +}; + +static int rt_increment_dqs(struct rt_dqs_setting *setting) +{ + if (setting->pi < 7) { + setting->pi++; + } else if (setting->tap < 14) { + setting->pi = 0; + setting->tap++; + } else { + return CB_ERR; + } + return CB_SUCCESS; +} + +static u8 test_dqs_aligned(const struct sysinfo *s, const u8 channel) +{ + int i, rank, lane; + volatile u8 data[8]; + u32 address; + u8 bytelane_error = 0; + + FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, channel, rank) { + address = test_address(channel, rank); + for (i = 0; i < RT_PATTERN_SIZE; i++) { + const u32 good = read_training_schedule[i]; + write32(&data[0], read32((u32 *)address + i * 8)); + write32(&data[4], read32((u32 *)address + i * 8 + 4)); + + FOR_EACH_BYTELANE(lane) { + if (data[lane] != (good & 0xff)) + bytelane_error |= 1 << lane; + } + } + } + return bytelane_error; +} + +static int rt_find_dqs_limit(struct sysinfo *s, u8 channel, + struct rt_dqs_setting dqs_setting[TOTAL_BYTELANES], + u8 dqs_lim[TOTAL_BYTELANES], + const enum training_modes expected_result) +{ + int lane; + u8 test_result; + int status = CB_SUCCESS; + + FOR_EACH_BYTELANE(lane) + rt_set_dqs(channel, lane, 0, &dqs_setting[lane]); + + while(status == CB_SUCCESS) { + test_result = test_dqs_aligned(s, channel); + if (test_result == (expected_result == SUCCEEDING ? 0 : 0xff)) + return CB_SUCCESS; + FOR_EACH_BYTELANE(lane) { + if (((test_result >> lane) & 1) != expected_result) { + status = rt_increment_dqs(&dqs_setting[lane]); + dqs_lim[lane]++; + rt_set_dqs(channel, lane, 0, &dqs_setting[lane]); + } + } + } + + if (expected_result == SUCCEEDING) { + printk(BIOS_CRIT, + "Could not find RT DQS setting\n"); + return CB_ERR; + } else { + printk(RAM_DEBUG, + "Read succeeded over all DQS" + " settings, continuing\n"); + return CB_SUCCESS; + } +} + +#define RT_LOOPS 3 + +/* + * This attempts to find the ideal delay for DQS on reads (rx). + * The training works this way: + * - start from the lowest possible delay (0) on all bytelanes + * - increment the DQS rx delays until a succeeding write is found on all + * bytelayes, on all ranks on a channel and save these values + * - again increment the DQS rx delay until write start to fail on all bytelanes + * and save that value + * - use the mean between the saved succeeding and failing value + * - note0: bytelanes cannot be trained independently, so the delays need to be + * adjusted and tested for all of them at the same time + * - note1: At this stage all ranks effectively use the rank0's rt_dqs settings, + * but later on their respective settings are used (TODO where is the + * 'switch' register??). So programming the results for all ranks at the end + * of the training. Programming on all ranks instead of all populated ranks, + * seems to be required, most likely because the signals can't really be generated + * separately. + */ +int do_read_training(struct sysinfo *s) +{ + int loop, channel, i, lane, rank; + u32 address, content; + u8 dqs_lower[TOTAL_BYTELANES]; + u8 dqs_upper[TOTAL_BYTELANES]; + struct rt_dqs_setting dqs_setting[TOTAL_BYTELANES]; + u16 saved_dqs_center[TOTAL_CHANNELS][TOTAL_BYTELANES]; + + memset(saved_dqs_center, 0, sizeof(saved_dqs_center)); + + printk(BIOS_DEBUG, "Starting DQS read training\n"); + + for (loop = 0; loop < RT_LOOPS; loop++) { + FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) { + printk(RAM_DEBUG, "Doing DQS read training on CH%d\n", + channel); + + /* Write pattern to strobe address */ + FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, channel, rank) { + address = test_address(channel, rank); + for (i = 0; i < RT_PATTERN_SIZE; i++) { + content = read_training_schedule[i]; + write32((u32 *)address + 8 * i, content); + write32((u32 *)address + 8 * i + 4, content); + } + } + + memset(dqs_lower, 0, sizeof(dqs_lower)); + memset(&dqs_setting, 0, sizeof(dqs_setting)); + if (rt_find_dqs_limit(s, channel, dqs_setting, dqs_lower, + SUCCEEDING)) { + printk(BIOS_CRIT, + "Could not find working lower limit DQS setting\n"); + return CB_ERR; + } + + FOR_EACH_BYTELANE(lane) + dqs_upper[lane] = dqs_lower[lane]; + + if (rt_find_dqs_limit(s, channel, dqs_setting, dqs_upper, + FAILING)) { + printk(BIOS_CRIT, + "Could not find failing upper limit DQ setting\n"); + return CB_ERR; + } + + printk(RAM_DEBUG, "Centered values, loop %d:\n", loop); + FOR_EACH_BYTELANE(lane) { + u8 center = (dqs_lower[lane] + dqs_upper[lane]) / 2; + printk(RAM_DEBUG, "\t lane%d: #%d\n", lane, center); + saved_dqs_center[channel][lane] += center; + } + } /* END FOR_EACH_POPULATED_CHANNEL */ + } /* end RT_LOOPS */ + + memset(s->rt_dqs, 0, sizeof(s->rt_dqs)); + + FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) { + printk(BIOS_DEBUG, "Final timings on CH%d:\n", channel); + FOR_EACH_BYTELANE(lane) { + saved_dqs_center[channel][lane] /= RT_LOOPS; + while (saved_dqs_center[channel][lane]--) { + if(rt_increment_dqs(&s->rt_dqs[channel][lane]) + == CB_ERR) + /* Should never happen */ + printk(BIOS_ERR, + "Huh? read training overflowed!!\n"); + } + /* Later on separate settings for each rank are used so program + all of them */ + FOR_EACH_RANK_IN_CHANNEL(rank) + rt_set_dqs(channel, lane, rank, + &s->rt_dqs[channel][lane]); + printk(BIOS_DEBUG, "\tlane%d: %d.%d\n", + lane, s->rt_dqs[channel][lane].tap, + s->rt_dqs[channel][lane].pi); + } + } + printk(BIOS_DEBUG, "Done DQS read training\n"); + return CB_SUCCESS; +} + +/* Enable write leveling on selected rank and disable output on other ranks */ +static void set_rank_write_level(struct sysinfo *s, u8 channel, u8 config, + u8 config_rank, u8 target_rank, int wl_enable) +{ + u32 emrs1; + + /* Is shifted by bits 2 later so u8 can be used to reduce size */ + static const u8 emrs1_lut[8][4][4] = { /* [Config][Leveling Rank][Rank] */ + { /* Config 0: 2R2R */ + {0x11, 0x00, 0x91, 0x00}, + {0x00, 0x11, 0x91, 0x00}, + {0x91, 0x00, 0x11, 0x00}, + {0x91, 0x00, 0x00, 0x11} + }, + { // Config 1: 2R1R + {0x11, 0x00, 0x91, 0x00}, + {0x00, 0x11, 0x91, 0x00}, + {0x91, 0x00, 0x11, 0x00}, + {0x00, 0x00, 0x00, 0x00} + }, + { // Config 2: 1R2R + {0x11, 0x00, 0x91, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x91, 0x00, 0x11, 0x00}, + {0x91, 0x00, 0x00, 0x11} + }, + { // Config 3: 1R1R + {0x11, 0x00, 0x91, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x91, 0x00, 0x11, 0x00}, + {0x00, 0x00, 0x00, 0x00} + }, + { // Config 4: 2R0R + {0x11, 0x00, 0x00, 0x00}, + {0x00, 0x11, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00} + }, + { // Config 5: 0R2R + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x11, 0x00}, + {0x00, 0x00, 0x00, 0x11} + }, + { // Config 6: 1R0R + {0x11, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00} + }, + { // Config 7: 0R1R + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x11, 0x00}, + {0x00, 0x00, 0x00, 0x00} + } + }; + + if (wl_enable) { + printk(RAM_DEBUG, "Entering WL mode\n"); + printk(RAM_DEBUG, "Using WL ODT values\n"); + emrs1 = emrs1_lut[config][target_rank][config_rank]; + } else { + printk(RAM_DEBUG, "Exiting WL mode\n"); + emrs1 = ddr3_emrs1_rtt_nom_config[s->dimm_config[channel]][config_rank]; + } + printk(RAM_DEBUG, "Setting ODT for rank%d to ", config_rank); + switch (emrs1) { + case 0: + printk(RAM_DEBUG, "High-Z\n"); + break; + case 0x11: + printk(RAM_DEBUG, "40 Ohm\n"); + break; + case 0x81: + printk(RAM_DEBUG, "30 Ohm\n"); + break; + case 0x80: + printk(RAM_DEBUG, "20 Ohm\n"); + break; + case 0x10: + printk(RAM_DEBUG, "120 Ohm\n"); + break; + case 0x01: + printk(RAM_DEBUG, "60 Ohm\n"); + break; + default: + printk(BIOS_WARNING, "ODT value Undefined!\n"); + break; + } + + emrs1 <<= 2; + /* Set output drive strength to 34 Ohm during write levelling */ + emrs1 |= (1 << 1); + + if (wl_enable && (target_rank != config_rank)) { + printk(RAM_DEBUG, "Disabling output for rank%d\n", config_rank); + emrs1 |= (1 << 12); + } + if (wl_enable && (target_rank == config_rank)) { + printk(RAM_DEBUG, "Enabling WL for rank%d\n", config_rank); + emrs1 |= (1 << 7); + } + send_jedec_cmd(s, config_rank, channel, EMRS1_CMD, emrs1); +} + +#define N_SAMPLES 5 + +static void sample_dq(const struct sysinfo *s, u8 channel, u8 rank, + u8 high_found[8]) { + u32 address = test_address(channel, rank); + int samples, lane; + + memset(high_found, 0, TOTAL_BYTELANES * sizeof(high_found[0])); + for (samples = 0; samples < N_SAMPLES; samples++) { + write32((u32 *)address, 0x12341234); + write32((u32 *)address + 4, 0x12341234); + udelay(5); + FOR_EACH_BYTELANE(lane) { + u8 dq_high = (MCHBAR8(0x561 + 0x400 * channel + + (lane * 4)) >> 7) & 1; + high_found[lane] += dq_high; + } + } +} + +static enum cb_err increment_to_dqs_edge(struct sysinfo *s, u8 channel, u8 rank) +{ + int lane; + u8 saved_24d; + struct dll_setting dqs_setting[TOTAL_BYTELANES]; + u8 bytelane_ok = 0; + u8 dq_sample[TOTAL_BYTELANES]; + + memcpy(dqs_setting, s->dqs_settings[channel], sizeof(dqs_setting)); + FOR_EACH_BYTELANE(lane) + dqsset(channel, lane, &dqs_setting[lane]); + + saved_24d = MCHBAR8(0x24d + 0x400 * channel); + + /* Loop 0: Find DQ sample low, by decreasing */ + while (bytelane_ok != 0xff) { + sample_dq(s, channel, rank, dq_sample); + FOR_EACH_BYTELANE(lane) { + if (bytelane_ok & (1 << lane)) + continue; + + printk(RAM_SPEW, "%d, %d, %02d, %d," + " lane%d sample: %d\n", + dqs_setting[lane].coarse, + dqs_setting[lane].clk_delay, + dqs_setting[lane].tap, + dqs_setting[lane].pi, + lane, + dq_sample[lane]); + + if (dq_sample[lane] > 0) { + if (decrement_dq_dqs(s, &dqs_setting[lane])) { + printk(BIOS_EMERG, + "DQS setting channel%d, " + "lane %d reached a minimum!\n", + channel, lane); + return CB_ERR; + } + } else { + bytelane_ok |= (1 << lane); + } + dqsset(channel, lane, &dqs_setting[lane]); + } + } + + printk(RAM_DEBUG, "DQS settings on PASS #0:\n"); + FOR_EACH_BYTELANE(lane) { + printk(RAM_DEBUG, "lane %d: ", lane); + print_dll_setting(&dqs_setting[lane], 0); + } + + /* Loop 1: Find DQ sample high, by increasing */ + bytelane_ok = 0; + while (bytelane_ok != 0xff) { + sample_dq(s, channel, rank, dq_sample); + FOR_EACH_BYTELANE(lane) { + if (bytelane_ok & (1 << lane)) + continue; + + printk(RAM_SPEW, "%d, %d, %02d, %d, lane%d sample: %d\n", + dqs_setting[lane].coarse, + dqs_setting[lane].clk_delay, + dqs_setting[lane].tap, + dqs_setting[lane].pi, + lane, + dq_sample[lane]); + + if (dq_sample[lane] == N_SAMPLES) { + bytelane_ok |= (1 << lane); + } else { + if (increment_dq_dqs(s, &dqs_setting[lane])) { + printk(BIOS_EMERG, + "DQS setting channel%d, " + "lane %d reached a maximum!\n", + channel, lane); + return CB_ERR; + } + } + dqsset(channel, lane, &dqs_setting[lane]); + } + } + + printk(RAM_DEBUG, "DQS settings on PASS #1:\n"); + FOR_EACH_BYTELANE(lane) { + printk(RAM_DEBUG, "lane %d: ", lane); + print_dll_setting(&dqs_setting[lane], 0); + } + + printk(BIOS_DEBUG, "final WL DQS settings on CH%d\n", channel); + FOR_EACH_BYTELANE(lane) { + printk(BIOS_DEBUG, "\tlane%d: ", lane); + print_dll_setting(&dqs_setting[lane], 1); + s->dqs_settings[channel][lane] = dqs_setting[lane]; + } + + MCHBAR8(0x24d + 0x400 * channel) = saved_24d; + return CB_SUCCESS; +} + +/* + * DDR3 uses flyby topology where the clock signal takes a different path + * than the data signal, to allow for better signal intergrity. + * Therefore the delay on the data signals needs to account for this. + * This is done by by sampleling the the DQS write (tx) signal back over + * the DQ signal and looking for delay values where the sample transitions + * from high to low. + * Here the following is done: + * - enable write levelling on the first populated rank + * - disable output on other populated ranks + * - start from safe DQS (tx) delays (other transitions can be + * found at different starting values but are generally bad) + * - loop0: decrease DQS (tx) delays until low is sampled, + * loop1: increase DQS (tx) delays until high is sampled, + * That way we are sure to hit a low-high transition + * - put all ranks in normal mode of operation again + * - note: All ranks need to be leveled together + */ +void search_write_leveling(struct sysinfo *s) +{ + int i, ch, count; + u8 config, rank0, rank1, lane; + struct dll_setting dq_setting; + + u8 chanconfig_lut[16]={0, 6, 4, 6, 7, 3, 1, 3, 5, 2, 0, 2, 7, 3, 1, 3}; + + u8 odt_force[8][4] = { /* [Config][leveling rank] */ + {0x5, 0x6, 0x5, 0x9}, + {0x5, 0x6, 0x5, 0x0}, + {0x5, 0x0, 0x5, 0x9}, + {0x5, 0x0, 0x5, 0x0}, + {0x1, 0x2, 0x0, 0x0}, + {0x0, 0x0, 0x4, 0x8}, + {0x1, 0x0, 0x0, 0x0}, + {0x0, 0x0, 0x4, 0x0} + }; + + printk(BIOS_DEBUG, "Starting write levelling.\n"); + + FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { + printk(BIOS_DEBUG, "\tCH%d\n", ch); + config = chanconfig_lut[s->dimm_config[ch]]; + + MCHBAR8(0x5d8 + 0x400 * ch) = + MCHBAR8(0x5d8 + 0x400 * ch) & ~0x0e; + MCHBAR16(0x5c4 + 0x400 * ch) = (MCHBAR16(0x5c4 + 0x400 * ch) & + ~0x3fff) | 0x3fff; + MCHBAR8(0x265 + 0x400 * ch) = + MCHBAR8(0x265 + 0x400 * ch) & ~0x1f; + /* find the first populated rank */ + FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank0) + break; + + /* Enable WL for the first populated rank and disable output + for others */ + FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank1) + set_rank_write_level(s, ch, config, rank1, rank0, 1); + + MCHBAR8(0x298 + 2 + 0x400 * ch) = + (MCHBAR8(0x298 + 2 + 0x400 * ch) & ~0x0f) + | odt_force[config][rank0]; + MCHBAR8(0x271 + 0x400 * ch) = (MCHBAR8(0x271 + 0x400 * ch) + & ~0x7e) | 0x4e; + MCHBAR8(0x5d9 + 0x400 * ch) = + (MCHBAR8(0x5d9 + 0x400 * ch) & ~0x04) | 0x04; + MCHBAR32(0x1a0) = (MCHBAR32(0x1a0) & ~0x07ffffff) + | 0x00014000; + + if (increment_to_dqs_edge(s, ch, rank0)) + die("Write Leveling failed!"); + + MCHBAR8(0x298 + 2 + 0x400 * ch) = + MCHBAR8(0x298 + 2 + 0x400 * ch) & ~0x0f; + MCHBAR8(0x271 + 0x400 * ch) = + (MCHBAR8(0x271 + 0x400 * ch) & ~0x7e) + | 0x0e; + MCHBAR8(0x5d9 + 0x400 * ch) = + (MCHBAR8(0x5d9 + 0x400 * ch) & ~0x04); + MCHBAR32(0x1a0) = (MCHBAR32(0x1a0) + & ~0x07ffffff) | 0x00555801; + + /* Disable WL on the trained rank */ + set_rank_write_level(s, ch, config, rank0, rank0, 0); + send_jedec_cmd(s, rank0, ch, NORMALOP_CMD, 1 << 12); + + MCHBAR8(0x5d8 + 0x400 * ch) = (MCHBAR8(0x5d8 + 0x400 * ch) + & ~0x0e) | 0x0e; + MCHBAR16(0x5c4 + 0x400 * ch) = (MCHBAR16(0x5c4 + 0x400 * ch) + & ~0x3fff) | 0x1807; + MCHBAR8(0x265 + 0x400 * ch) = MCHBAR8(0x265 + 0x400 * ch) & ~0x1f; + + /* Disable write level mode for all ranks */ + FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank0) + set_rank_write_level(s, ch, config, rank0, rank0, 0); + } + + MCHBAR8(0x5dc) = (MCHBAR8(0x5dc) & ~0x80) | 0x80; + + /* Increment DQ (rx) dll setting by a standard amount past DQS, + This is further trained in write training. */ + switch (s->selected_timings.mem_clk) { + default: + case MEM_CLOCK_800MHz: + count = 39; + break; + case MEM_CLOCK_1066MHz: + count = 32; + break; + case MEM_CLOCK_1333MHz: + count = 42; + break; + } + + FOR_EACH_POPULATED_CHANNEL_AND_BYTELANE(s->dimms, ch, lane) { + dq_setting = s->dqs_settings[ch][lane]; + for (i = 0; i < count; i++) + if (increment_dq_dqs(s, &dq_setting)) + die("Can't further increase DQ past DQS delay"); + dqset(ch, lane, &dq_setting); + } + + printk(BIOS_DEBUG, "Done write levelling.\n"); +} diff --git a/src/northbridge/intel/x3x/early_init.c b/src/northbridge/intel/x3x/early_init.c new file mode 100644 index 0000000..027dacd --- /dev/null +++ b/src/northbridge/intel/x3x/early_init.c @@ -0,0 +1,240 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <stdint.h> +#include <device/pci_ops.h> +#include "iomap.h" + +#if CONFIG(SOUTHBRIDGE_INTEL_I82801GX) +#include <southbridge/intel/i82801gx/i82801gx.h> /* DEFAULT_PMBASE */ +#endif + +#if CONFIG(SOUTHBRIDGE_INTEL_I82801IX) +#include <southbridge/intel/i82801ix/i82801ix.h> /* DEFAULT_PMBASE */ +#endif + +#if CONFIG(SOUTHBRIDGE_INTEL_I82801JX) +#include <southbridge/intel/i82801jx/i82801jx.h> /* DEFAULT_PMBASE */ +#endif + +#include <option.h> +#include "x3x.h" +#include <console/console.h> +#include <romstage_handoff.h> + +void x3x_early_init(void) +{ + const pci_devfn_t d0f0 = PCI_DEV(0, 0, 0); + + /* Setup MCHBAR. */ + pci_write_config32(d0f0, D0F0_MCHBAR_LO, (uintptr_t)DEFAULT_MCHBAR | 1); + + /* Setup DMIBAR. */ + pci_write_config32(d0f0, D0F0_DMIBAR_LO, (uintptr_t)DEFAULT_DMIBAR | 1); + + /* Setup EPBAR. */ + pci_write_config32(d0f0, D0F0_EPBAR_LO, DEFAULT_EPBAR | 1); + + /* Setup HECIBAR */ + pci_write_config32(PCI_DEV(0, 3, 0), 0x10, DEFAULT_HECIBAR); + + /* Set C0000-FFFFF to access RAM on both reads and writes */ + pci_write_config8(d0f0, D0F0_PAM(0), 0x30); + pci_write_config8(d0f0, D0F0_PAM(1), 0x33); + pci_write_config8(d0f0, D0F0_PAM(2), 0x33); + pci_write_config8(d0f0, D0F0_PAM(3), 0x33); + pci_write_config8(d0f0, D0F0_PAM(4), 0x33); + pci_write_config8(d0f0, D0F0_PAM(5), 0x33); + pci_write_config8(d0f0, D0F0_PAM(6), 0x33); + + if (!(pci_read_config32(d0f0, D0F0_CAPID0 + 4) & (1 << (46 - 32)))) { + /* Enable internal GFX */ + pci_write_config32(d0f0, D0F0_DEVEN, BOARD_DEVEN); + + /* Set preallocated IGD size from CMOS */ + u8 gfxsize = 6; /* 6 for 64MiB, default if not set in CMOS */ + get_option(&gfxsize, "gfx_uma_size"); + if (gfxsize > 12) + gfxsize = 6; + /* Need at least 4M for cbmem_top alignment */ + else if (gfxsize < 1) + gfxsize = 1; + /* Set GTT size to 2+2M */ + pci_write_config16(d0f0, D0F0_GGC, 0x0b00 | (gfxsize + 1) << 4); + } else { /* Does not feature internal graphics */ + pci_write_config32(d0f0, D0F0_DEVEN, D0EN | D1EN | PEG1EN); + pci_write_config16(d0f0, D0F0_GGC, (1 << 1)); + } +} + +static void init_egress(void) +{ + u32 reg32; + + /* VC0: TC0 only */ + EPBAR8(0x14) = 1; + EPBAR8(0x4) = 1; + + switch (MCHBAR32(0xc00) & 0x7) { + case 0x0: + /* FSB 1066 */ + EPBAR32(0x2c) = 0x0001a6db; + break; + case 0x2: + /* FSB 800 */ + EPBAR32(0x2c) = 0x00014514; + break; + default: + case 0x4: + /* FSB 1333 */ + EPBAR32(0x2c) = 0x00022861; + break; + } + EPBAR32(0x28) = 0x0a0a0a0a; + EPBAR8(0xc) = (EPBAR8(0xc) & ~0xe) | 2; + EPBAR32(0x1c) = (EPBAR32(0x1c) & ~0x7f0000) | 0x0a0000; + MCHBAR8(0x3c) = MCHBAR8(0x3c) | 0x7; + + /* VC1: ID1, TC7 */ + reg32 = (EPBAR32(0x20) & ~(7 << 24)) | (1 << 24); + reg32 = (reg32 & ~0xfe) | (1 << 7); + EPBAR32(0x20) = reg32; + + /* Init VC1 port arbitration table */ + EPBAR32(0x100) = 0x001000001; + EPBAR32(0x104) = 0x000040000; + EPBAR32(0x108) = 0x000001000; + EPBAR32(0x10c) = 0x000000040; + EPBAR32(0x110) = 0x001000001; + EPBAR32(0x114) = 0x000040000; + EPBAR32(0x118) = 0x000001000; + EPBAR32(0x11c) = 0x000000040; + + /* Load table */ + reg32 = EPBAR32(0x20) | (1 << 16); + EPBAR32(0x20) = reg32; + asm("nop"); + EPBAR32(0x20) = reg32; + + /* Wait for table load */ + while ((EPBAR8(0x26) & (1 << 0)) != 0) + ; + + /* VC1: enable */ + EPBAR32(0x20) |= 1 << 31; + + /* Wait for VC1 */ + while ((EPBAR8(0x26) & (1 << 1)) != 0) + ; + + printk(BIOS_DEBUG, "Done Egress Port\n"); +} + +static void init_dmi(void) +{ + u32 reg32; + + /* Assume IGD present */ + + /* Clear error status */ + DMIBAR32(0x1c4) = 0xffffffff; + DMIBAR32(0x1d0) = 0xffffffff; + + /* VC0: TC0 only */ + DMIBAR8(DMIVC0RCTL) = 1; + DMIBAR8(0x4) = 1; + + /* VC1: ID1, TC7 */ + reg32 = (DMIBAR32(DMIVC1RCTL) & ~(7 << 24)) | (1 << 24); + reg32 = (reg32 & ~0xff) | 1 << 7; + + /* VC1: enable */ + reg32 |= 1 << 31; + reg32 = (reg32 & ~(0x7 << 17)) | (0x4 << 17); + + DMIBAR32(DMIVC1RCTL) = reg32; + + /* Set up VCs in southbridge RCBA */ + RCBA8(0x3022) &= ~1; + + reg32 = (0x5 << 28) | (1 << 6); /* PCIe x4 */ + RCBA32(0x2020) = (RCBA32(0x2020) & ~((0xf << 28) | (0x7 << 6))) | reg32; + + /* Assign VC1 id 1 */ + RCBA32(0x20) = (RCBA32(0x20) & ~(0x7 << 24)) | (1 << 24); + + /* Map TC7 to VC1 */ + RCBA8(0x20) &= 1; + RCBA8(0x20) |= 1 << 7; + + /* Map TC0 to VC0 */ + RCBA8(0x14) &= 1; + + /* Init DMI VC1 port arbitration table */ + RCBA32(0x20) &= 0xfff1ffff; + RCBA32(0x20) |= 1 << 19; + + RCBA32(0x30) = 0x0000000f; + RCBA32(0x34) = 0x000f0000; + RCBA32(0x38) = 0; + RCBA32(0x3c) = 0x000000f0; + RCBA32(0x40) = 0x0f000000; + RCBA32(0x44) = 0; + RCBA32(0x48) = 0x0000f000; + RCBA32(0x4c) = 0; + RCBA32(0x50) = 0x0000000f; + RCBA32(0x54) = 0x000f0000; + RCBA32(0x58) = 0; + RCBA32(0x5c) = 0x000000f0; + RCBA32(0x60) = 0x0f000000; + RCBA32(0x64) = 0; + RCBA32(0x68) = 0x0000f000; + RCBA32(0x6c) = 0; + + RCBA32(0x20) |= 1 << 16; + + /* Enable VC1 */ + RCBA32(0x20) |= 1 << 31; + + /* Wait for VC1 */ + while ((RCBA8(0x26) & (1 << 1)) != 0) + ; + + /* Wait for table load */ + while ((RCBA8(0x26) & (1 << 0)) != 0) + ; + + /* ASPM on DMI link */ + RCBA16(0x1a8) &= ~0x3; + /* FIXME: Do we need to read RCBA16(0x1a8)? */ + RCBA16(0x1a8); + RCBA32(0x2010) = (RCBA32(0x2010) & ~(0x3 << 10)) | (1 << 10); + /* FIXME: Do we need to read RCBA32(0x2010)? */ + RCBA32(0x2010); + + /* Set up VC1 max time */ + RCBA32(0x1c) = (RCBA32(0x1c) & ~0x7f0000) | 0x120000; + + while ((DMIBAR32(0x26) & (1 << 1)) != 0) + ; + printk(BIOS_DEBUG, "Done DMI setup\n"); + + /* ASPM on DMI */ + DMIBAR32(0x200) &= ~(0x3 << 26); + DMIBAR16(0x210) = (DMIBAR16(0x210) & ~(0xff7)) | 0x101; + DMIBAR32(0x88) &= ~0x3; + DMIBAR32(0x88) |= 0x3; + /* FIXME: Do we need to read RCBA16(0x88)? */ + DMIBAR16(0x88); +} + +static void x3x_prepare_resume(int s3resume) +{ + romstage_handoff_init(s3resume); +} + +void x3x_late_init(int s3resume) +{ + init_egress(); + init_dmi(); + x3x_prepare_resume(s3resume); +} diff --git a/src/northbridge/intel/x3x/gma.c b/src/northbridge/intel/x3x/gma.c new file mode 100644 index 0000000..b92820b --- /dev/null +++ b/src/northbridge/intel/x3x/gma.c @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <commonlib/helpers.h> +#include <drivers/intel/gma/intel_bios.h> +#include <drivers/intel/gma/edid.h> +#include <drivers/intel/gma/i915.h> +#include <drivers/intel/gma/opregion.h> +#include <drivers/intel/gma/libgfxinit.h> +#include <pc80/vga.h> +#include <types.h> + +#include "chip.h" +#include "drivers/intel/gma/i915_reg.h" +#include "x3x.h" + +#define BASE_FREQUENCY 96000 + +static void gma_func0_init(struct device *dev) +{ + intel_gma_init_igd_opregion(); + + if (!CONFIG(NO_GFX_INIT)) + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER); + + /* configure GMBUSFREQ */ + pci_update_config16(dev, 0xcc, ~0x1ff, 0xbc); + + int vga_disable = (pci_read_config16(dev, D0F0_GGC) & 2) >> 1; + + if (CONFIG(MAINBOARD_USE_LIBGFXINIT)) { + if (vga_disable) { + printk(BIOS_INFO, + "IGD is not decoding legacy VGA MEM and IO: skipping NATIVE graphic init\n"); + } else { + int lightup_ok; + gma_gfxinit(&lightup_ok); + } + } else { + pci_dev_init(dev); + } +} + +static void gma_func0_disable(struct device *dev) +{ + struct device *dev_host = pcidev_on_root(0, 0); + + /* VGA cycles to discrete GPU */ + pci_or_config16(dev_host, D0F0_GGC, 1 << 1); +} + +static void gma_generate_ssdt(const struct device *device) +{ + const struct northbridge_intel_x3x_config *chip = device->chip_info; + + drivers_intel_gma_displays_ssdt_generate(&chip->gfx); +} + +static const char *gma_acpi_name(const struct device *dev) +{ + return "GFX0"; +} + +static struct device_operations gma_func0_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .acpi_fill_ssdt = gma_generate_ssdt, + .init = gma_func0_init, + .ops_pci = &pci_dev_ops_pci, + .disable = gma_func0_disable, + .acpi_name = gma_acpi_name, +}; + +static const unsigned short pci_device_ids[] = { + 0x2e02, /* Eaglelake */ + 0x2e12, /* Q43/Q45 */ + 0x2e22, /* G43/G45 */ + 0x2e32, /* G41 */ + 0x2e42, /* B43 */ + 0x2e92, /* B43_I */ + 0 +}; + +static const struct pci_driver gma __pci_driver = { + .ops = &gma_func0_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/northbridge/intel/x3x/iomap.h b/src/northbridge/intel/x3x/iomap.h new file mode 100644 index 0000000..627b7fa --- /dev/null +++ b/src/northbridge/intel/x3x/iomap.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef X3X_IOMAP_H +#define X3X_IOMAP_H + +#define DEFAULT_MCHBAR 0xfed14000 /* 16 KB */ +#define DEFAULT_DMIBAR 0xfed18000 /* 4 KB */ +#define DEFAULT_EPBAR 0xfed19000 /* 4 KB */ +#define DEFAULT_HECIBAR 0xfed10000 + +#define TPMBASE 0xfed40000 +#define TPM32(x) (*((volatile u32 *)(TPMBASE + (x)))) + +#endif /* X3X_IOMAP_H */ diff --git a/src/northbridge/intel/x3x/memmap.c b/src/northbridge/intel/x3x/memmap.c new file mode 100644 index 0000000..dadfd7e --- /dev/null +++ b/src/northbridge/intel/x3x/memmap.c @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define __SIMPLE_DEVICE__ + +#include <cbmem.h> +#include <commonlib/helpers.h> +#include <stdint.h> +#include <arch/romstage.h> +#include <device/pci_ops.h> +#include <device/pci_def.h> +#include <console/console.h> +#include <cpu/x86/mtrr.h> +#include <cpu/x86/smm.h> +#include <northbridge/intel/x3x/x3x.h> +#include <program_loading.h> +#include <cpu/intel/smm_reloc.h> + +/** Decodes used Graphics Mode Select (GMS) to kilobytes. */ +u32 decode_igd_memory_size(const u32 gms) +{ + static const u16 ggc2uma[] = { 0, 1, 4, 8, 16, + 32, 48, 64, 128, 256, 96, 160, 224, 352 }; + + if (gms >= ARRAY_SIZE(ggc2uma)) + die("Bad Graphics Mode Select (GMS) setting.\n"); + + return ggc2uma[gms] << 10; +} + +/** Decodes used GTT Graphics Memory Size (GGMS) to kilobytes. */ +u32 decode_igd_gtt_size(const u32 gsm) +{ + static const u8 ggc2gtt[] = { 0, 1, 0, 2, 0, 0, 0, 0, 0, 2, 3, 4}; + + if (gsm >= ARRAY_SIZE(ggc2gtt)) + die("Bad GTT Graphics Memory Size (GGMS) setting.\n"); + + return ggc2gtt[gsm] << 10; +} + +/** Decodes used TSEG size to bytes. */ +u32 decode_tseg_size(const u32 esmramc) +{ + if (!(esmramc & 1)) + return 0; + + switch ((esmramc >> 1) & 3) { + case 0: + return 1 << 20; + case 1: + return 2 << 20; + case 2: + return 8 << 20; + case 3: + default: + die("Bad TSEG setting.\n"); + } +} + +u8 decode_pciebar(u32 *const base, u32 *const len) +{ + *base = 0; + *len = 0; + const pci_devfn_t dev = PCI_DEV(0, 0, 0); + u32 pciexbar = 0; + u32 pciexbar_reg; + u32 reg32; + int max_buses; + const struct { + u16 num_buses; + u32 addr_mask; + } busmask[] = { + {256, 0xf0000000}, + {128, 0xf8000000}, + {64, 0xfc000000}, + {0, 0}, + }; + + pciexbar_reg = pci_read_config32(dev, D0F0_PCIEXBAR_LO); + + if (!(pciexbar_reg & 1)) { + printk(BIOS_WARNING, "WARNING: MMCONF not set\n"); + return 0; + } + + reg32 = (pciexbar_reg >> 1) & 3; + pciexbar = pciexbar_reg & busmask[reg32].addr_mask; + max_buses = busmask[reg32].num_buses; + + if (!pciexbar) { + printk(BIOS_WARNING, "WARNING: pciexbar invalid\n"); + return 0; + } + + *base = pciexbar; + *len = max_buses << 20; + return 1; +} + +static size_t northbridge_get_tseg_size(void) +{ + const u8 esmramc = pci_read_config8(PCI_DEV(0, 0, 0), D0F0_ESMRAMC); + return decode_tseg_size(esmramc); +} + +static uintptr_t northbridge_get_tseg_base(void) +{ + return pci_read_config32(PCI_DEV(0, 0, 0), D0F0_TSEG); +} + + +/* Depending of UMA and TSEG configuration, TSEG might start at any + * 1 MiB alignment. As this may cause very greedy MTRR setup, push + * CBMEM top downwards to 4 MiB boundary. + */ +void *cbmem_top_chipset(void) +{ + uintptr_t top_of_ram = ALIGN_DOWN(northbridge_get_tseg_base(), 4*MiB); + return (void *) top_of_ram; +} + +void smm_region(uintptr_t *start, size_t *size) +{ + *start = northbridge_get_tseg_base(); + *size = northbridge_get_tseg_size(); +} + +void fill_postcar_frame(struct postcar_frame *pcf) +{ + uintptr_t top_of_ram; + + /* Cache 8 MiB region below the top of RAM and 2 MiB above top of + * RAM to cover both cbmem as the TSEG region. + */ + top_of_ram = (uintptr_t)cbmem_top(); + postcar_frame_add_mtrr(pcf, top_of_ram - 8*MiB, 8*MiB, + MTRR_TYPE_WRBACK); + postcar_frame_add_mtrr(pcf, northbridge_get_tseg_base(), + northbridge_get_tseg_size(), MTRR_TYPE_WRBACK); +} diff --git a/src/northbridge/intel/x3x/northbridge.c b/src/northbridge/intel/x3x/northbridge.c new file mode 100644 index 0000000..810ddfb --- /dev/null +++ b/src/northbridge/intel/x3x/northbridge.c @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <cbmem.h> +#include <console/console.h> +#include <device/pci_def.h> +#include <device/pci_ops.h> +#include <stdint.h> +#include <device/device.h> +#include <boot/tables.h> +#include <acpi/acpi.h> +#include <northbridge/intel/x3x/iomap.h> +#include <northbridge/intel/x3x/chip.h> +#include <northbridge/intel/x3x/x3x.h> +#include <cpu/intel/smm_reloc.h> + +static const int legacy_hole_base_k = 0xa0000 / 1024; + +static void mch_domain_read_resources(struct device *dev) +{ + u8 index; + u64 tom, touud; + u32 tomk, tolud, delta_cbmem; + u32 pcie_config_base, pcie_config_size; + u32 uma_sizek = 0; + + const u32 top32memk = 4 * (GiB / KiB); + index = 3; + + pci_domain_read_resources(dev); + + struct device *mch = pcidev_on_root(0, 0); + + /* Top of Upper Usable DRAM, including remap */ + touud = pci_read_config16(mch, D0F0_TOUUD); + touud <<= 20; + + /* Top of Lower Usable DRAM */ + tolud = pci_read_config16(mch, D0F0_TOLUD) & 0xfff0; + tolud <<= 16; + + /* Top of Memory - does not account for any UMA */ + tom = pci_read_config16(mch, D0F0_TOM) & 0x01ff; + tom <<= 26; + + printk(BIOS_DEBUG, "TOUUD 0x%llx TOLUD 0x%08x TOM 0x%llx\n", + touud, tolud, tom); + + tomk = tolud >> 10; + + /* Graphics memory comes next */ + + const u16 ggc = pci_read_config16(mch, D0F0_GGC); + printk(BIOS_DEBUG, "IGD decoded, subtracting "); + + /* Graphics memory */ + const u32 gms_sizek = decode_igd_memory_size((ggc >> 4) & 0xf); + printk(BIOS_DEBUG, "%uM UMA", gms_sizek >> 10); + tomk -= gms_sizek; + uma_sizek += gms_sizek; + + /* GTT Graphics Stolen Memory Size (GGMS) */ + const u32 gsm_sizek = decode_igd_gtt_size((ggc >> 8) & 0xf); + printk(BIOS_DEBUG, " and %uM GTT\n", gsm_sizek >> 10); + tomk -= gsm_sizek; + uma_sizek += gsm_sizek; + + printk(BIOS_DEBUG, "TSEG decoded, subtracting "); + const u32 tseg_sizek = decode_tseg_size( + pci_read_config8(dev, D0F0_ESMRAMC)) >> 10; + uma_sizek += tseg_sizek; + tomk -= tseg_sizek; + + printk(BIOS_DEBUG, "%dM\n", tseg_sizek >> 10); + + /* cbmem_top can be shifted downwards due to alignment. + Mark the region between cbmem_top and tomk as unusable */ + delta_cbmem = tomk - ((uint32_t)cbmem_top() >> 10); + tomk -= delta_cbmem; + uma_sizek += delta_cbmem; + + printk(BIOS_DEBUG, "Unused RAM between cbmem_top and TOM: 0x%xK\n", + delta_cbmem); + + printk(BIOS_INFO, "Available memory below 4GB: %uM\n", tomk >> 10); + + /* Report the memory regions */ + ram_resource(dev, index++, 0, legacy_hole_base_k); + mmio_resource(dev, index++, legacy_hole_base_k, + (0xc0000 >> 10) - legacy_hole_base_k); + reserved_ram_resource(dev, index++, 0xc0000 >> 10, + (0x100000 - 0xc0000) >> 10); + ram_resource(dev, index++, 0x100000 >> 10, (tomk - (0x100000 >> 10))); + + /* + * If >= 4GB installed then memory from TOLUD to 4GB + * is remapped above TOM, TOUUD will account for both + */ + touud >>= 10; /* Convert to KB */ + if (touud > top32memk) { + ram_resource(dev, index++, top32memk, touud - top32memk); + printk(BIOS_INFO, "Available memory above 4GB: %lluM\n", + (touud - top32memk) >> 10); + } + + printk(BIOS_DEBUG, "Adding UMA memory area base=0x%08x " + "size=0x%08x\n", tomk << 10, uma_sizek << 10); + uma_resource(dev, index++, tomk, uma_sizek); + + /* Reserve high memory where the NB BARs are up to 4GiB */ + fixed_mem_resource(dev, index++, DEFAULT_HECIBAR >> 10, + top32memk - (DEFAULT_HECIBAR >> 10), + IORESOURCE_RESERVE); + + if (decode_pciebar(&pcie_config_base, &pcie_config_size)) { + printk(BIOS_DEBUG, "Adding PCIe config bar base=0x%08x " + "size=0x%x\n", pcie_config_base, pcie_config_size); + fixed_mem_resource(dev, index++, pcie_config_base >> 10, + pcie_config_size >> 10, IORESOURCE_RESERVE); + } +} + +static void mch_domain_set_resources(struct device *dev) +{ + struct resource *res; + + for (res = dev->resource_list; res; res = res->next) + report_resource_stored(dev, res, ""); + + assign_resources(dev->link_list); +} + +static void mch_domain_init(struct device *dev) +{ + /* Enable SERR */ + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR); +} + +static const char *northbridge_acpi_name(const struct device *dev) +{ + if (dev->path.type == DEVICE_PATH_DOMAIN) + return "PCI0"; + + if (dev->path.type != DEVICE_PATH_PCI || dev->bus->secondary != 0) + return NULL; + + switch (dev->path.pci.devfn) { + case PCI_DEVFN(0, 0): + return "MCHC"; + } + + return NULL; +} + +void northbridge_write_smram(u8 smram) +{ + struct device *dev = pcidev_on_root(0, 0); + + if (dev == NULL) + die("could not find pci 00:00.0!\n"); + + pci_write_config8(dev, D0F0_SMRAM, smram); +} + +static struct device_operations pci_domain_ops = { + .read_resources = mch_domain_read_resources, + .set_resources = mch_domain_set_resources, + .init = mch_domain_init, + .scan_bus = pci_domain_scan_bus, + .write_acpi_tables = northbridge_write_acpi_tables, + .acpi_fill_ssdt = generate_cpu_entries, + .acpi_name = northbridge_acpi_name, +}; + +static struct device_operations cpu_bus_ops = { + .read_resources = noop_read_resources, + .set_resources = noop_set_resources, + .init = mp_cpu_bus_init, +}; + +static void enable_dev(struct device *dev) +{ + /* Set the operations if it is a special bus type */ + if (dev->path.type == DEVICE_PATH_DOMAIN) + dev->ops = &pci_domain_ops; + else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) + dev->ops = &cpu_bus_ops; +} + +static void hide_pci_fn(const int dev_bit_base, const struct device *dev) +{ + if (!dev || dev->enabled) + return; + const unsigned int fn = PCI_FUNC(dev->path.pci.devfn); + const struct device *const d0f0 = pcidev_on_root(0, 0); + pci_update_config32(d0f0, D0F0_DEVEN, ~(1 << (dev_bit_base + fn)), 0); +} + +static void hide_pci_dev(const int dev, int functions, const int dev_bit_base) +{ + for (; functions >= 0; functions--) + hide_pci_fn(dev_bit_base, pcidev_on_root(dev, functions)); +} + +static void x3x_init(void *const chip_info) +{ + struct device *const d0f0 = pcidev_on_root(0x0, 0); + + /* Hide internal functions based on devicetree info. */ + hide_pci_dev(6, 0, 13); /* PEG1: only on P45 */ + hide_pci_dev(3, 3, 6); /* ME */ + hide_pci_dev(2, 1, 3); /* IGD */ + hide_pci_dev(1, 0, 1); /* PEG0 */ + + const u32 deven = pci_read_config32(d0f0, D0F0_DEVEN); + if (!(deven & (0xf << 6))) + pci_write_config32(d0f0, D0F0_DEVEN, deven & ~(1 << 14)); +} + +struct chip_operations northbridge_intel_x3x_ops = { + CHIP_NAME("Intel 4-Series Northbridge") + .enable_dev = enable_dev, + .init = x3x_init, +}; diff --git a/src/northbridge/intel/x3x/raminit.c b/src/northbridge/intel/x3x/raminit.c new file mode 100644 index 0000000..37cf8fa --- /dev/null +++ b/src/northbridge/intel/x3x/raminit.c @@ -0,0 +1,705 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <device/pci_ops.h> +#include <device/smbus_host.h> +#include <cbmem.h> +#include <cf9_reset.h> +#include <console/console.h> +#include <arch/cpu.h> +#include <spd.h> +#include <string.h> +#include <device/dram/ddr2.h> +#include <device/dram/ddr3.h> +#include <mrc_cache.h> +#include <timestamp.h> +#include <types.h> + +#include "iomap.h" +#include "x3x.h" + +#define MRC_CACHE_VERSION 0 + +static u16 ddr2_get_crc(u8 device, u8 len) +{ + u8 raw_spd[128] = {}; + i2c_eeprom_read(device, 64, 9, &raw_spd[64]); + i2c_eeprom_read(device, 93, 6, &raw_spd[93]); + return spd_ddr2_calc_unique_crc(raw_spd, len); +} + +static u16 ddr3_get_crc(u8 device, u8 len) +{ + u8 raw_spd[256] = {}; + i2c_eeprom_read(device, 117, 11, &raw_spd[117]); + return spd_ddr3_calc_unique_crc(raw_spd, len); +} + +static enum cb_err verify_spds(const u8 *spd_map, + const struct sysinfo *ctrl_cached) +{ + int i; + u16 crc; + + for (i = 0; i < TOTAL_DIMMS; i++) { + if (!(spd_map[i])) + continue; + int len = smbus_read_byte(spd_map[i], 0); + if (len < 0 && ctrl_cached->dimms[i].card_type + == RAW_CARD_UNPOPULATED) + continue; + if (len > 0 && ctrl_cached->dimms[i].card_type + == RAW_CARD_UNPOPULATED) + return CB_ERR; + + if (ctrl_cached->spd_type == DDR2) + crc = ddr2_get_crc(spd_map[i], len); + else + crc = ddr3_get_crc(spd_map[i], len); + + if (crc != ctrl_cached->dimms[i].spd_crc) + return CB_ERR; + } + return CB_SUCCESS; +} + +struct abs_timings { + u32 min_tclk; + u32 min_tRAS; + u32 min_tRP; + u32 min_tRCD; + u32 min_tWR; + u32 min_tRFC; + u32 min_tWTR; + u32 min_tRRD; + u32 min_tRTP; + u32 min_tAA; + u32 min_tCLK_cas[8]; + u32 cas_supported; +}; + +#define CTRL_MIN_TCLK_DDR2 TCK_400MHZ + +static void select_cas_dramfreq_ddr2(struct sysinfo *s, + const struct abs_timings *saved_timings) +{ + u8 try_cas; + /* Currently only these CAS are supported */ + u8 cas_mask = SPD_CAS_LATENCY_DDR2_5 | SPD_CAS_LATENCY_DDR2_6; + + cas_mask &= saved_timings->cas_supported; + try_cas = spd_get_msbs(cas_mask); + + while (cas_mask & (1 << try_cas) && try_cas > 0) { + s->selected_timings.CAS = try_cas; + s->selected_timings.tclk = saved_timings->min_tCLK_cas[try_cas]; + if (s->selected_timings.tclk >= CTRL_MIN_TCLK_DDR2 && + saved_timings->min_tCLK_cas[try_cas] != + saved_timings->min_tCLK_cas[try_cas - 1]) + break; + try_cas--; + } + + + if ((s->selected_timings.CAS < 3) || (s->selected_timings.tclk == 0)) + die("Could not find common memory frequency and CAS\n"); + + switch (s->selected_timings.tclk) { + case TCK_200MHZ: + case TCK_266MHZ: + /* FIXME: this works on vendor BIOS */ + die("Selected dram frequency not supported\n"); + case TCK_333MHZ: + s->selected_timings.mem_clk = MEM_CLOCK_667MHz; + break; + case TCK_400MHZ: + s->selected_timings.mem_clk = MEM_CLOCK_800MHz; + break; + } +} + +static void mchinfo_ddr2(struct sysinfo *s) +{ + const u32 eax = cpuid_ext(0x04, 0).eax; + printk(BIOS_WARNING, "%d CPU cores\n", ((eax >> 26) & 0x3f) + 1); + + u32 capid = pci_read_config16(PCI_DEV(0, 0, 0), 0xe8); + if (!(capid & (1<<(79-64)))) + printk(BIOS_WARNING, "iTPM enabled\n"); + + capid = pci_read_config32(PCI_DEV(0, 0, 0), 0xe4); + if (!(capid & (1<<(57-32)))) + printk(BIOS_WARNING, "ME enabled\n"); + + if (!(capid & (1<<(56-32)))) + printk(BIOS_WARNING, "AMT enabled\n"); + + if (!(capid & (1<<(48-32)))) + printk(BIOS_WARNING, "VT-d enabled\n"); +} + +static int ddr2_save_dimminfo(u8 dimm_idx, u8 *raw_spd, + struct abs_timings *saved_timings, struct sysinfo *s) +{ + struct dimm_attr_ddr2_st decoded_dimm; + int i; + + if (spd_decode_ddr2(&decoded_dimm, raw_spd) != SPD_STATUS_OK) { + printk(BIOS_DEBUG, "Problems decoding SPD\n"); + return CB_ERR; + } + + if (CONFIG(DEBUG_RAM_SETUP)) + dram_print_spd_ddr2(&decoded_dimm); + + if (!(decoded_dimm.width & (0x08 | 0x10))) { + + printk(BIOS_ERR, + "DIMM%d Unsupported width: x%d. Disabling dimm\n", + dimm_idx, s->dimms[dimm_idx].width); + return CB_ERR; + } + s->dimms[dimm_idx].width = (decoded_dimm.width >> 3) - 1; + /* + * This boils down to: + * "Except for the x16 configuration, all DDR2 devices have a + * 1KB page size. For the x16 configuration, the page size is 2KB + * for all densities except the 256Mb device, which has a 1KB page + * size." Micron, 'TN-47-16 Designing for High-Density DDR2 Memory' + * The formula is pagesize in KiB = width * 2^col_bits / 8. + */ + s->dimms[dimm_idx].page_size = decoded_dimm.width * + (1 << decoded_dimm.col_bits) / 8; + + switch (decoded_dimm.banks) { + case 4: + s->dimms[dimm_idx].n_banks = N_BANKS_4; + break; + case 8: + s->dimms[dimm_idx].n_banks = N_BANKS_8; + break; + default: + printk(BIOS_ERR, + "DIMM%d Unsupported #banks: x%d. Disabling dimm\n", + dimm_idx, decoded_dimm.banks); + return CB_ERR; + } + + s->dimms[dimm_idx].ranks = decoded_dimm.ranks; + s->dimms[dimm_idx].rows = decoded_dimm.row_bits; + s->dimms[dimm_idx].cols = decoded_dimm.col_bits; + + saved_timings->cas_supported &= decoded_dimm.cas_supported; + + saved_timings->min_tRAS = + MAX(saved_timings->min_tRAS, decoded_dimm.tRAS); + saved_timings->min_tRP = + MAX(saved_timings->min_tRP, decoded_dimm.tRP); + saved_timings->min_tRCD = + MAX(saved_timings->min_tRCD, decoded_dimm.tRCD); + saved_timings->min_tWR = + MAX(saved_timings->min_tWR, decoded_dimm.tWR); + saved_timings->min_tRFC = + MAX(saved_timings->min_tRFC, decoded_dimm.tRFC); + saved_timings->min_tWTR = + MAX(saved_timings->min_tWTR, decoded_dimm.tWTR); + saved_timings->min_tRRD = + MAX(saved_timings->min_tRRD, decoded_dimm.tRRD); + saved_timings->min_tRTP = + MAX(saved_timings->min_tRTP, decoded_dimm.tRTP); + for (i = 0; i < 8; i++) { + if (!(saved_timings->cas_supported & (1 << i))) + saved_timings->min_tCLK_cas[i] = 0; + else + saved_timings->min_tCLK_cas[i] = + MAX(saved_timings->min_tCLK_cas[i], + decoded_dimm.cycle_time[i]); + } + + s->dimms[dimm_idx].spd_crc = spd_ddr2_calc_unique_crc(raw_spd, + spd_decode_spd_size_ddr2(raw_spd[0])); + return CB_SUCCESS; +} + +static void normalize_tCLK(u32 *tCLK) +{ + if (*tCLK <= TCK_666MHZ) + *tCLK = TCK_666MHZ; + else if (*tCLK <= TCK_533MHZ) + *tCLK = TCK_533MHZ; + else if (*tCLK <= TCK_400MHZ) + *tCLK = TCK_400MHZ; + else + *tCLK = 0; +} + +static void select_cas_dramfreq_ddr3(struct sysinfo *s, + struct abs_timings *saved_timings) +{ + /* + * various constraints must be fulfilled: + * CAS * tCK < 20ns == 160MTB + * tCK_max >= tCK >= tCK_min + * CAS >= roundup(tAA_min/tCK) + * CAS supported + * AND BTW: Clock(MT) = 2000 / tCK(ns) - intel uses MTs but calls them MHz + */ + + u32 min_tCLK; + u8 try_CAS; + u16 capid = (pci_read_config16(PCI_DEV(0, 0, 0), 0xea) >> 4) & 0x3f; + + switch (s->max_fsb) { + default: + case FSB_CLOCK_800MHz: + min_tCLK = TCK_400MHZ; + break; + case FSB_CLOCK_1066MHz: + min_tCLK = TCK_533MHZ; + break; + case FSB_CLOCK_1333MHz: + min_tCLK = TCK_666MHZ; + break; + } + + switch (capid >> 3) { + default: /* Should not happen */ + min_tCLK = TCK_400MHZ; + break; + case 1: + min_tCLK = MAX(min_tCLK, TCK_400MHZ); + break; + case 2: + min_tCLK = MAX(min_tCLK, TCK_533MHZ); + break; + case 3: /* Only on P45 */ + case 0: + min_tCLK = MAX(min_tCLK, TCK_666MHZ); + break; + } + + min_tCLK = MAX(min_tCLK, saved_timings->min_tclk); + if (min_tCLK == 0) { + printk(BIOS_ERR, "DRAM frequency is under lowest supported " + "frequency (400 MHz). Increasing to 400 MHz " + "as last resort"); + min_tCLK = TCK_400MHZ; + } + + while (1) { + normalize_tCLK(&min_tCLK); + if (min_tCLK == 0) + die("Couldn't find compatible clock / CAS settings.\n"); + try_CAS = DIV_ROUND_UP(saved_timings->min_tAA, min_tCLK); + printk(BIOS_SPEW, "Trying CAS %u, tCK %u.\n", try_CAS, min_tCLK); + for (; try_CAS <= DDR3_MAX_CAS; try_CAS++) { + /* + * cas_supported is encoded like the SPD which starts + * at CAS=4. + */ + if ((saved_timings->cas_supported << 4) & (1 << try_CAS)) + break; + } + if ((try_CAS <= DDR3_MAX_CAS) && (try_CAS * min_tCLK < 20 * 256)) { + /* Found good CAS. */ + printk(BIOS_SPEW, "Found compatible tCLK / CAS pair: %u / %u.\n", + min_tCLK, try_CAS); + break; + } + /* + * If no valid tCLK / CAS pair could be found for a tCLK + * increase it after which it gets normalised. This means + * that a lower frequency gets tried. + */ + min_tCLK++; + } + + s->selected_timings.tclk = min_tCLK; + s->selected_timings.CAS = try_CAS; + + switch (s->selected_timings.tclk) { + case TCK_400MHZ: + s->selected_timings.mem_clk = MEM_CLOCK_800MHz; + break; + case TCK_533MHZ: + s->selected_timings.mem_clk = MEM_CLOCK_1066MHz; + break; + case TCK_666MHZ: + s->selected_timings.mem_clk = MEM_CLOCK_1333MHz; + break; + } +} + +/* With DDR3 and 533MHz mem clock and an enabled internal gfx device the display + is not usable in non stacked mode, so select stacked mode accordingly */ +static void workaround_stacked_mode(struct sysinfo *s) +{ + u32 deven; + /* Only a problem on DDR3 */ + if (s->spd_type == DDR2) + return; + /* Does not matter if only one channel is populated */ + if (!CHANNEL_IS_POPULATED(s->dimms, 0) + || !CHANNEL_IS_POPULATED(s->dimms, 1)) + return; + if (s->selected_timings.mem_clk != MEM_CLOCK_1066MHz) + return; + /* IGD0EN gets disabled if not present before this code runs */ + deven = pci_read_config32(PCI_DEV(0, 0, 0), D0F0_DEVEN); + if (deven & IGD0EN) + s->stacked_mode = 1; +} + +static int ddr3_save_dimminfo(u8 dimm_idx, u8 *raw_spd, + struct abs_timings *saved_timings, struct sysinfo *s) +{ + struct dimm_attr_st decoded_dimm; + + if (spd_decode_ddr3(&decoded_dimm, raw_spd) != SPD_STATUS_OK) + return CB_ERR; + + if (CONFIG(DEBUG_RAM_SETUP)) + dram_print_spd_ddr3(&decoded_dimm); + + /* x4 DIMMs are not supported (true for both ddr2 and ddr3) */ + if (!(decoded_dimm.width & (0x8 | 0x10))) { + printk(BIOS_ERR, "DIMM%d Unsupported width: x%d. Disabling dimm\n", + dimm_idx, s->dimms[dimm_idx].width); + return CB_ERR; + } + s->dimms[dimm_idx].width = (decoded_dimm.width >> 3) - 1; + /* + * This boils down to: + * "Except for the x16 configuration, all DDR3 devices have a + * 1KB page size. For the x16 configuration, the page size is 2KB + * for all densities except the 256Mb device, which has a 1KB page size." + * Micron, 'TN-47-16 Designing for High-Density DDR2 Memory' + */ + s->dimms[dimm_idx].page_size = decoded_dimm.width * + (1 << decoded_dimm.col_bits) / 8; + + s->dimms[dimm_idx].n_banks = N_BANKS_8; /* Always 8 banks on ddr3?? */ + + s->dimms[dimm_idx].ranks = decoded_dimm.ranks; + s->dimms[dimm_idx].rows = decoded_dimm.row_bits; + s->dimms[dimm_idx].cols = decoded_dimm.col_bits; + + saved_timings->min_tRAS = + MAX(saved_timings->min_tRAS, decoded_dimm.tRAS); + saved_timings->min_tRP = + MAX(saved_timings->min_tRP, decoded_dimm.tRP); + saved_timings->min_tRCD = + MAX(saved_timings->min_tRCD, decoded_dimm.tRCD); + saved_timings->min_tWR = + MAX(saved_timings->min_tWR, decoded_dimm.tWR); + saved_timings->min_tRFC = + MAX(saved_timings->min_tRFC, decoded_dimm.tRFC); + saved_timings->min_tWTR = + MAX(saved_timings->min_tWTR, decoded_dimm.tWTR); + saved_timings->min_tRRD = + MAX(saved_timings->min_tRRD, decoded_dimm.tRRD); + saved_timings->min_tRTP = + MAX(saved_timings->min_tRTP, decoded_dimm.tRTP); + saved_timings->min_tAA = + MAX(saved_timings->min_tAA, decoded_dimm.tAA); + saved_timings->cas_supported &= decoded_dimm.cas_supported; + + s->dimms[dimm_idx].spd_crc = spd_ddr3_calc_unique_crc(raw_spd, + raw_spd[0]); + + s->dimms[dimm_idx].mirrored = decoded_dimm.flags.pins_mirrored; + + return CB_SUCCESS; +} + + +static void select_discrete_timings(struct sysinfo *s, + const struct abs_timings *timings) +{ + s->selected_timings.tRAS = DIV_ROUND_UP(timings->min_tRAS, + s->selected_timings.tclk); + s->selected_timings.tRP = DIV_ROUND_UP(timings->min_tRP, + s->selected_timings.tclk); + s->selected_timings.tRCD = DIV_ROUND_UP(timings->min_tRCD, + s->selected_timings.tclk); + s->selected_timings.tWR = DIV_ROUND_UP(timings->min_tWR, + s->selected_timings.tclk); + s->selected_timings.tRFC = DIV_ROUND_UP(timings->min_tRFC, + s->selected_timings.tclk); + s->selected_timings.tWTR = DIV_ROUND_UP(timings->min_tWTR, + s->selected_timings.tclk); + s->selected_timings.tRRD = DIV_ROUND_UP(timings->min_tRRD, + s->selected_timings.tclk); + s->selected_timings.tRTP = DIV_ROUND_UP(timings->min_tRTP, + s->selected_timings.tclk); +} +static void print_selected_timings(struct sysinfo *s) +{ + printk(BIOS_DEBUG, "Selected timings:\n"); + printk(BIOS_DEBUG, "\tFSB: %dMHz\n", + fsb_to_mhz(s->selected_timings.fsb_clk)); + printk(BIOS_DEBUG, "\tDDR: %dMHz\n", + ddr_to_mhz(s->selected_timings.mem_clk)); + + printk(BIOS_DEBUG, "\tCAS: %d\n", s->selected_timings.CAS); + printk(BIOS_DEBUG, "\ttRAS: %d\n", s->selected_timings.tRAS); + printk(BIOS_DEBUG, "\ttRP: %d\n", s->selected_timings.tRP); + printk(BIOS_DEBUG, "\ttRCD: %d\n", s->selected_timings.tRCD); + printk(BIOS_DEBUG, "\ttWR: %d\n", s->selected_timings.tWR); + printk(BIOS_DEBUG, "\ttRFC: %d\n", s->selected_timings.tRFC); + printk(BIOS_DEBUG, "\ttWTR: %d\n", s->selected_timings.tWTR); + printk(BIOS_DEBUG, "\ttRRD: %d\n", s->selected_timings.tRRD); + printk(BIOS_DEBUG, "\ttRTP: %d\n", s->selected_timings.tRTP); +} + +static void find_fsb_speed(struct sysinfo *s) +{ + switch (MCHBAR32(0xc00) & 0x7) { + case 0x0: + s->max_fsb = FSB_CLOCK_1066MHz; + break; + case 0x2: + s->max_fsb = FSB_CLOCK_800MHz; + break; + case 0x4: + s->max_fsb = FSB_CLOCK_1333MHz; + break; + default: + s->max_fsb = FSB_CLOCK_800MHz; + printk(BIOS_WARNING, "Can't detect FSB, setting 800MHz\n"); + break; + } + s->selected_timings.fsb_clk = s->max_fsb; +} + +static void decode_spd_select_timings(struct sysinfo *s) +{ + unsigned int device; + u8 dram_type_mask = (1 << DDR2) | (1 << DDR3); + u8 dimm_mask = 0; + u8 raw_spd[256]; + int i, j; + struct abs_timings saved_timings; + memset(&saved_timings, 0, sizeof(saved_timings)); + saved_timings.cas_supported = UINT32_MAX; + + FOR_EACH_DIMM(i) { + s->dimms[i].card_type = RAW_CARD_POPULATED; + device = s->spd_map[i]; + if (!device) { + s->dimms[i].card_type = RAW_CARD_UNPOPULATED; + continue; + } + switch (smbus_read_byte(s->spd_map[i], SPD_MEMORY_TYPE)) { + case DDR2SPD: + dram_type_mask &= 1 << DDR2; + s->spd_type = DDR2; + break; + case DDR3SPD: + dram_type_mask &= 1 << DDR3; + s->spd_type = DDR3; + break; + default: + s->dimms[i].card_type = RAW_CARD_UNPOPULATED; + continue; + } + if (!dram_type_mask) + die("Mixing up dimm types is not supported!\n"); + + printk(BIOS_DEBUG, "Decoding dimm %d\n", i); + if (i2c_eeprom_read(device, 0, 128, raw_spd) != 128) { + printk(BIOS_DEBUG, "i2c block operation failed," + " trying smbus byte operation.\n"); + for (j = 0; j < 128; j++) + raw_spd[j] = smbus_read_byte(device, j); + } + + if (s->spd_type == DDR2){ + if (ddr2_save_dimminfo(i, raw_spd, &saved_timings, s)) { + printk(BIOS_WARNING, + "Encountered problems with SPD, " + "skipping this DIMM.\n"); + s->dimms[i].card_type = RAW_CARD_UNPOPULATED; + continue; + } + } else { /* DDR3 */ + if (ddr3_save_dimminfo(i, raw_spd, &saved_timings, s)) { + printk(BIOS_WARNING, + "Encountered problems with SPD, " + "skipping this DIMM.\n"); + /* something in decoded SPD was unsupported */ + s->dimms[i].card_type = RAW_CARD_UNPOPULATED; + continue; + } + } + dimm_mask |= (1 << i); + } + if (!dimm_mask) + die("No memory installed.\n"); + + if (s->spd_type == DDR2) + select_cas_dramfreq_ddr2(s, &saved_timings); + else + select_cas_dramfreq_ddr3(s, &saved_timings); + select_discrete_timings(s, &saved_timings); + workaround_stacked_mode(s); +} + +static void find_dimm_config(struct sysinfo *s) +{ + int chan, i; + + FOR_EACH_POPULATED_CHANNEL(s->dimms, chan) { + FOR_EACH_POPULATED_DIMM_IN_CHANNEL(s->dimms, chan, i) { + int dimm_config; + if (s->dimms[i].ranks == 1) { + if (s->dimms[i].width == 0) /* x8 */ + dimm_config = 1; + else /* x16 */ + dimm_config = 3; + } else { + if (s->dimms[i].width == 0) /* x8 */ + dimm_config = 2; + else + die("Dual-rank x16 not supported\n"); + } + s->dimm_config[chan] |= + dimm_config << (i % DIMMS_PER_CHANNEL) * 2; + } + printk(BIOS_DEBUG, " Config[CH%d] : %d\n", chan, + s->dimm_config[chan]); + } + +} + +static void checkreset_ddr2(int boot_path) +{ + u8 pmcon2; + u32 pmsts; + + if (boot_path >= 1) { + pmsts = MCHBAR32(PMSTS_MCHBAR); + if (!(pmsts & 1)) + printk(BIOS_DEBUG, + "Channel 0 possibly not in self refresh\n"); + if (!(pmsts & 2)) + printk(BIOS_DEBUG, + "Channel 1 possibly not in self refresh\n"); + } + + pmcon2 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xa2); + + if (pmcon2 & 0x80) { + pmcon2 &= ~0x80; + pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, pmcon2); + + /* do magic 0xf0 thing. */ + pci_and_config8(PCI_DEV(0, 0, 0), 0xf0, ~(1 << 2)); + + pci_or_config8(PCI_DEV(0, 0, 0), 0xf0, (1 << 2)); + + full_reset(); + } + pmcon2 |= 0x80; + pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, pmcon2); +} + +/** + * @param boot_path: 0 = normal, 1 = reset, 2 = resume from s3 + */ +void sdram_initialize(int boot_path, const u8 *spd_map) +{ + struct sysinfo s, *ctrl_cached; + u8 reg8; + int fast_boot, cbmem_was_inited, cache_not_found; + struct region_device rdev; + + timestamp_add_now(TS_BEFORE_INITRAM); + printk(BIOS_DEBUG, "Setting up RAM controller.\n"); + + pci_write_config8(PCI_DEV(0, 0, 0), 0xdf, 0xff); + + memset(&s, 0, sizeof(struct sysinfo)); + + cache_not_found = mrc_cache_get_current(MRC_TRAINING_DATA, + MRC_CACHE_VERSION, &rdev); + + if (cache_not_found || (region_device_sz(&rdev) < sizeof(s))) { + if (boot_path == BOOT_PATH_RESUME) { + /* Failed S3 resume, reset to come up cleanly */ + system_reset(); + } else if (boot_path == BOOT_PATH_WARM_RESET) { + /* On warm reset some of dram calibrations fail + and therefore requiring valid cached settings */ + full_reset(); + } + ctrl_cached = NULL; + } else { + ctrl_cached = rdev_mmap_full(&rdev); + } + + /* verify MRC cache for fast boot */ + if (boot_path != BOOT_PATH_RESUME && ctrl_cached) { + /* check SPD checksum to make sure the DIMMs haven't been + * replaced */ + fast_boot = verify_spds(spd_map, ctrl_cached) == CB_SUCCESS; + if (!fast_boot) { + printk(BIOS_DEBUG, "SPD checksums don't match," + " dimm's have been replaced\n"); + } else { + find_fsb_speed(&s); + fast_boot = s.max_fsb == ctrl_cached->max_fsb; + if (!fast_boot) + printk(BIOS_DEBUG, + "CPU FSB does not match and has been replaced\n"); + } + } else { + fast_boot = boot_path == BOOT_PATH_RESUME; + } + + if (fast_boot) { + printk(BIOS_DEBUG, "Using cached raminit settings\n"); + memcpy(&s, ctrl_cached, sizeof(s)); + s.boot_path = boot_path; + mchinfo_ddr2(&s); + print_selected_timings(&s); + } else { + s.boot_path = boot_path; + s.spd_map[0] = spd_map[0]; + s.spd_map[1] = spd_map[1]; + s.spd_map[2] = spd_map[2]; + s.spd_map[3] = spd_map[3]; + checkreset_ddr2(s.boot_path); + + /* Detect dimms per channel */ + reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xe9); + printk(BIOS_DEBUG, "Dimms per channel: %d\n", + (reg8 & 0x10) ? 1 : 2); + + mchinfo_ddr2(&s); + + find_fsb_speed(&s); + decode_spd_select_timings(&s); + print_selected_timings(&s); + find_dimm_config(&s); + } + + do_raminit(&s, fast_boot); + + pci_and_config8(PCI_DEV(0, 0x1f, 0), 0xa2, (u8)~0x80); + + pci_or_config8(PCI_DEV(0, 0, 0), 0xf4, 1); + + printk(BIOS_DEBUG, "RAM initialization finished.\n"); + + cbmem_was_inited = !cbmem_recovery(s.boot_path == BOOT_PATH_RESUME); + if (!fast_boot) + mrc_cache_stash_data(MRC_TRAINING_DATA, MRC_CACHE_VERSION, + &s, sizeof(s)); + if (s.boot_path == BOOT_PATH_RESUME && !cbmem_was_inited) { + /* Failed S3 resume, reset to come up cleanly */ + system_reset(); + } + + timestamp_add_now(TS_AFTER_INITRAM); + printk(BIOS_DEBUG, "Memory initialized\n"); +} diff --git a/src/northbridge/intel/x3x/raminit_ddr23.c b/src/northbridge/intel/x3x/raminit_ddr23.c new file mode 100644 index 0000000..1150aff --- /dev/null +++ b/src/northbridge/intel/x3x/raminit_ddr23.c @@ -0,0 +1,2222 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <assert.h> +#include <stdint.h> +#include <device/mmio.h> +#include <device/pci_ops.h> +#include <console/console.h> +#include <commonlib/helpers.h> +#include <delay.h> + +#if CONFIG(SOUTHBRIDGE_INTEL_I82801GX) +#include <southbridge/intel/i82801gx/i82801gx.h> +#endif + +#if CONFIG(SOUTHBRIDGE_INTEL_I82801IX) +#include <southbridge/intel/i82801ix/i82801ix.h> +#endif + +#if CONFIG(SOUTHBRIDGE_INTEL_I82801JX) +#include <southbridge/intel/i82801jx/i82801jx.h> +#endif + +#include <string.h> +#include "iomap.h" +#include "x3x.h" + +#define ME_UMA_SIZEMB 0 + +u32 fsb_to_mhz(u32 speed) +{ + return (speed * 267) + 800; +} + +u32 ddr_to_mhz(u32 speed) +{ + static const u16 mhz[] = { 0, 0, 667, 800, 1067, 1333 }; + + if (speed <= 1 || speed >= ARRAY_SIZE(mhz)) + die("RAM init: invalid memory speed %u\n", speed); + + return mhz[speed]; +} + + +static void program_crossclock(struct sysinfo *s) +{ + u8 i, j; + u32 reg32; + MCHBAR16_OR(0xc1c, (1 << 15)); + + static const u32 clkxtab[6][3][13] = { + /* MEMCLK 400 N/A */ + {{}, {}, {} }, + /* MEMCLK 533 N/A */ + {{}, {}, {} }, + /* MEMCLK 667 + * FSB 800 */ + {{0x1f1f1f1f, 0x0d07070b, 0x00000000, 0x10000000, + 0x20010208, 0x04080000, 0x10010002, 0x00000000, + 0x00000000, 0x02000000, 0x04000100, 0x08000000, + 0x10200204}, + /* FSB 1067 */ + {0x6d5b1f1f, 0x0f0f0f0f, 0x00000000, 0x20000000, + 0x80020410, 0x02040008, 0x10000100, 0x00000000, + 0x00000000, 0x04000000, 0x08000102, 0x20000000, + 0x40010208}, + /* FSB 1333 */ + {0x05050303, 0xffffffff, 0xffff0000, 0x00000000, + 0x08020000, 0x00000000, 0x00020001, 0x00000000, + 0x00000000, 0x00000000, 0x08010204, 0x00000000, + 0x04010000} }, + /* MEMCLK 800 + * FSB 800 */ + {{0xffffffff, 0x05030305, 0x0000ffff, 0x0000000, + 0x08010204, 0x00000000, 0x08010204, 0x0000000, + 0x00000000, 0x00000000, 0x00020001, 0x0000000, + 0x04080102}, + /* FSB 1067 */ + {0x07070707, 0x06030303, 0x00000000, 0x00000000, + 0x08010200, 0x00000000, 0x04000102, 0x00000000, + 0x00000000, 0x00000000, 0x00020100, 0x00000000, + 0x04080100}, + /* FSB 1333 */ + {0x0d0b0707, 0x3e1f1f2f, 0x01010000, 0x00000000, + 0x10020400, 0x02000000, 0x00040100, 0x00000000, + 0x00000000, 0x04080000, 0x00100102, 0x00000000, + 0x08100200} }, + /* MEMCLK 1067 */ + {{}, + /* FSB 1067 */ + {0xffffffff, 0x05030305, 0x0000ffff, 0x00000000, + 0x04080102, 0x00000000, 0x08010204, 0x00000000, + 0x00000000, 0x00000000, 0x00020001, 0x00000000, + 0x02040801}, + /* FSB 1333 */ + {0x0f0f0f0f, 0x5b1f1f6d, 0x00000000, 0x00000000, + 0x08010204, 0x04000000, 0x00080102, 0x00000000, + 0x00000000, 0x02000408, 0x00100001, 0x00000000, + 0x04080102} }, + /* MEMCLK 1333 */ + {{}, {}, + /* FSB 1333 */ + {0xffffffff, 0x05030305, 0x0000ffff, 0x00000000, + 0x04080102, 0x00000000, 0x04080102, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x02040801} } + }; + + i = (u8)s->selected_timings.mem_clk; + j = (u8)s->selected_timings.fsb_clk; + + MCHBAR32(0xc04) = clkxtab[i][j][0]; + reg32 = clkxtab[i][j][1]; + if (s->spd_type == DDR3 && s->max_fsb == FSB_CLOCK_1333MHz + && s->selected_timings.mem_clk == MEM_CLOCK_800MHz) { + reg32 &= ~(0xff << 24); + reg32 |= 0x3d << 24; + } + MCHBAR32(0xc50) = reg32; + MCHBAR32(0xc54) = clkxtab[i][j][2]; + MCHBAR8_OR(0xc08, (1 << 7)); + MCHBAR32(0x6d8) = clkxtab[i][j][3]; + MCHBAR32(0x6e0) = clkxtab[i][j][3]; + MCHBAR32(0x6dc) = clkxtab[i][j][4]; + MCHBAR32(0x6e4) = clkxtab[i][j][4]; + MCHBAR32(0x6e8) = clkxtab[i][j][5]; + MCHBAR32(0x6f0) = clkxtab[i][j][5]; + MCHBAR32(0x6ec) = clkxtab[i][j][6]; + MCHBAR32(0x6f4) = clkxtab[i][j][6]; + MCHBAR32(0x6f8) = clkxtab[i][j][7]; + MCHBAR32(0x6fc) = clkxtab[i][j][8]; + MCHBAR32(0x708) = clkxtab[i][j][11]; + MCHBAR32(0x70c) = clkxtab[i][j][12]; +} + +static void setioclk_dram(struct sysinfo *s) +{ + MCHBAR32(0x1bc) = 0x08060402; + MCHBAR16_OR(0x1c0, 0x200); + MCHBAR16_OR(0x1c0, 0x100); + MCHBAR16_OR(0x1c0, 0x20); + MCHBAR16_AND(0x1c0, ~1); + switch (s->selected_timings.mem_clk) { + default: + case MEM_CLOCK_800MHz: + case MEM_CLOCK_1066MHz: + MCHBAR8_AND_OR(0x5d9, ~0x2, 0x2); + MCHBAR8_AND_OR(0x9d9, ~0x2, 0x2); + MCHBAR8_AND_OR(0x189, ~0xf0, 0xc0); + MCHBAR8_AND_OR(0x189, ~0xf0, 0xe0); + MCHBAR8_AND_OR(0x189, ~0xf0, 0xa0); + break; + case MEM_CLOCK_667MHz: + case MEM_CLOCK_1333MHz: + MCHBAR8_AND(0x5d9, ~0x2); + MCHBAR8_AND(0x9d9, ~0x2); + MCHBAR8_AND_OR(0x189, ~0xf0, 0x40); + break; + } + MCHBAR32_OR(0x594, 1 << 31); + MCHBAR32_OR(0x994, 1 << 31); +} + +static void launch_dram(struct sysinfo *s) +{ + u8 i; + u32 launch1; + u32 launch2 = 0; + + static const u32 ddr3_launch1_tab[2][3] = { + /* 1N */ + {0x58000007, /* DDR3 800 */ + 0x58000007, /* DDR3 1067 */ + 0x58100107}, /* DDR3 1333 */ + /* 2N */ + {0x58001117, /* DDR3 800 */ + 0x58001117, /* DDR3 1067 */ + 0x58001117} /* DDR3 1333 */ + }; + + static const u32 ddr3_launch2_tab[2][3][6] = { + { /* 1N */ + /* DDR3 800 */ + {0x08030000, /* CL = 5 */ + 0x0C040100}, /* CL = 6 */ + /* DDR3 1066 */ + {0x00000000, /* CL = 5 */ + 0x00000000, /* CL = 6 */ + 0x10050100, /* CL = 7 */ + 0x14260200}, /* CL = 8 */ + /* DDR3 1333 */ + {0x00000000, /* CL = 5 */ + 0x00000000, /* CL = 6 */ + 0x00000000, /* CL = 7 */ + 0x14060000, /* CL = 8 */ + 0x18070100, /* CL = 9 */ + 0x1C280200}, /* CL = 10 */ + + }, + { /* 2N */ + /* DDR3 800 */ + {0x00040101, /* CL = 5 */ + 0x00250201}, /* CL = 6 */ + /* DDR3 1066 */ + {0x00000000, /* CL = 5 */ + 0x00050101, /* CL = 6 */ + 0x04260201, /* CL = 7 */ + 0x08470301}, /* CL = 8 */ + /* DDR3 1333 */ + {0x00000000, /* CL = 5 */ + 0x00000000, /* CL = 6 */ + 0x00000000, /* CL = 7 */ + 0x08070100, /* CL = 8 */ + 0x0C280200, /* CL = 9 */ + 0x10490300} /* CL = 10 */ + } + }; + + if (s->spd_type == DDR2) { + launch1 = 0x58001117; + if (s->selected_timings.CAS == 5) + launch2 = 0x00220201; + else if (s->selected_timings.CAS == 6) + launch2 = 0x00230302; + else + die("Unsupported CAS\n"); + } else { /* DDR3 */ + /* Default 2N mode */ + s->nmode = 2; + + if (s->selected_timings.mem_clk <= MEM_CLOCK_1066MHz) + s->nmode = 1; + /* 2N on DDR3 1066 with with 2 dimms per channel */ + if ((s->selected_timings.mem_clk == MEM_CLOCK_1066MHz) && + (BOTH_DIMMS_ARE_POPULATED(s->dimms, 0) || + BOTH_DIMMS_ARE_POPULATED(s->dimms, 1))) + s->nmode = 2; + launch1 = ddr3_launch1_tab[s->nmode - 1] + [s->selected_timings.mem_clk - MEM_CLOCK_800MHz]; + launch2 = ddr3_launch2_tab[s->nmode - 1] + [s->selected_timings.mem_clk - MEM_CLOCK_800MHz] + [s->selected_timings.CAS - 5]; + } + + FOR_EACH_POPULATED_CHANNEL(s->dimms, i) { + MCHBAR32(0x400*i + 0x220) = launch1; + MCHBAR32(0x400*i + 0x224) = launch2; + MCHBAR32(0x400*i + 0x21c) = 0; + MCHBAR32_OR(0x400*i + 0x248, 1 << 23); + } + + MCHBAR32_AND_OR(0x2c0, ~0x58000000, 0x48000000); + MCHBAR32_OR(0x2c0, 0x1e0); + MCHBAR32_AND_OR(0x2c4, ~0xf, 0xc); + if (s->spd_type == DDR3) + MCHBAR32_OR(0x2c4, 0x100); +} + +static void clkset0(u8 ch, const struct dll_setting *setting) +{ + MCHBAR16_AND_OR(0x400*ch + 0x5a0, ~0xc440, + (setting->clk_delay << 14) | + (setting->db_sel << 6) | + (setting->db_en << 10)); + MCHBAR8_AND_OR(0x400*ch + 0x581, ~0x70, setting->pi << 4); + MCHBAR8_AND_OR(0x400*ch + 0x581, ~0xf, setting->tap); +} + +static void clkset1(u8 ch, const struct dll_setting *setting) +{ + MCHBAR32_AND_OR(0x400*ch + 0x5a0, ~0x30880, + (setting->clk_delay << 16) | + (setting->db_sel << 7) | + (setting->db_en << 11)); + MCHBAR8_AND_OR(0x400*ch + 0x582, ~0x70, setting->pi << 4); + MCHBAR8_AND_OR(0x400*ch + 0x582, ~0xf, setting->tap); +} + +static void ctrlset0(u8 ch, const struct dll_setting *setting) +{ + MCHBAR32_AND_OR(0x400*ch + 0x59c, ~0x3300000, + (setting->clk_delay << 24) | + (setting->db_sel << 20) | + (setting->db_en << 21)); + MCHBAR8_AND_OR(0x400*ch + 0x584, ~0x70, setting->pi << 4); + MCHBAR8_AND_OR(0x400*ch + 0x584, ~0xf, setting->tap); +} + +static void ctrlset1(u8 ch, const struct dll_setting *setting) +{ + MCHBAR32_AND_OR(0x400*ch + 0x59c, ~0x18c00000, + (setting->clk_delay << 27) | + (setting->db_sel << 22) | + (setting->db_en << 23)); + MCHBAR8_AND_OR(0x400*ch + 0x585, ~0x70, setting->pi << 4); + MCHBAR8_AND_OR(0x400*ch + 0x585, ~0xf, setting->tap); +} + +static void ctrlset2(u8 ch, const struct dll_setting *setting) +{ + MCHBAR32_AND_OR(0x400*ch + 0x598, ~0x18c00000, + (setting->clk_delay << 14) | + (setting->db_sel << 12) | + (setting->db_en << 13)); + MCHBAR8_AND_OR(0x400*ch + 0x586, ~0x70, setting->pi << 4); + MCHBAR8_AND_OR(0x400*ch + 0x586, ~0xf, setting->tap); +} + +static void ctrlset3(u8 ch, const struct dll_setting *setting) +{ + MCHBAR32_AND_OR(0x400*ch + 0x598, ~0x18c00000, + (setting->clk_delay << 10) | + (setting->db_sel << 8) | + (setting->db_en << 9)); + MCHBAR8_AND_OR(0x400*ch + 0x587, ~0x70, setting->pi << 4); + MCHBAR8_AND_OR(0x400*ch + 0x587, ~0xf, setting->tap); +} + +static void cmdset(u8 ch, const struct dll_setting *setting) +{ + MCHBAR8_AND_OR(0x400*ch + 0x598, ~0x30, setting->clk_delay << 4); + MCHBAR8_AND_OR(0x400*ch + 0x594, ~0x60, + (setting->db_sel << 5) | + (setting->db_en << 6)); + MCHBAR8_AND_OR(0x400*ch + 0x580, ~0x70, setting->pi << 4); + MCHBAR8_AND_OR(0x400*ch + 0x580, ~0xf, setting->tap); +} + +/** + * All finer DQ and DQS DLL settings are set to the same value + * for each rank in a channel, while coarse is common. + */ +void dqsset(u8 ch, u8 lane, const struct dll_setting *setting) +{ + int rank; + + MCHBAR32_AND_OR(0x400 * ch + 0x5fc, ~(1 << (lane * 4 + 1)), + setting->coarse << (lane * 4 + 1)); + + for (rank = 0; rank < 4; rank++) { + MCHBAR32_AND_OR(0x400 * ch + 0x5b4 + rank * 4, ~(0x201 << lane), + (setting->db_en << (9 + lane)) | + (setting->db_sel << lane)); + + MCHBAR32_AND_OR(0x400*ch + 0x5c8 + rank * 4, + ~(0x3 << (16 + lane * 2)), + setting->clk_delay << (16+lane * 2)); + + MCHBAR8(0x400*ch + 0x520 + lane * 4 + rank) = + (MCHBAR8(0x400*ch + 0x520 + lane * 4) & ~0x7f) | + (setting->pi << 4) | + setting->tap; + } +} + +void dqset(u8 ch, u8 lane, const struct dll_setting *setting) +{ + int rank; + MCHBAR32_AND_OR(0x400 * ch + 0x5fc, ~(1 << (lane * 4)), + setting->coarse << (lane * 4)); + + for (rank = 0; rank < 4; rank++) { + MCHBAR32_AND_OR(0x400 * ch + 0x5a4 + rank * 4, ~(0x201 << lane), + (setting->db_en << (9 + lane)) | + (setting->db_sel << lane)); + + MCHBAR32_AND_OR(0x400 * ch + 0x5c8 + rank * 4, + ~(0x3 << (lane * 2)), setting->clk_delay << (2 * lane)); + + MCHBAR8_AND_OR(0x400*ch + 0x500 + lane * 4 + rank, ~0x7f, + (setting->pi << 4) | setting->tap); + } +} + +void rt_set_dqs(u8 channel, u8 lane, u8 rank, + struct rt_dqs_setting *dqs_setting) +{ + u16 saved_tap = MCHBAR16(0x540 + 0x400 * channel + lane * 4); + u16 saved_pi = MCHBAR16(0x542 + 0x400 * channel + lane * 4); + printk(RAM_SPEW, "RT DQS: ch%d, r%d, L%d: %d.%d\n", channel, rank, lane, + dqs_setting->tap, + dqs_setting->pi); + + saved_tap &= ~(0xf << (rank * 4)); + saved_tap |= dqs_setting->tap << (rank * 4); + MCHBAR16(0x540 + 0x400 * channel + lane * 4) = saved_tap; + + saved_pi &= ~(0x7 << (rank * 3)); + saved_pi |= dqs_setting->pi << (rank * 3); + MCHBAR16(0x542 + 0x400 * channel + lane * 4) = saved_pi; +} + +static void program_timings(struct sysinfo *s) +{ + u8 i; + u8 twl, ta1, ta2, ta3, ta4; + u8 reg8; + u8 flag1 = 0; + u8 flag2 = 0; + u16 reg16; + u32 reg32; + u16 ddr, fsb; + u8 trpmod = 0; + u8 bankmod = 1; + u8 pagemod = 0; + u8 adjusted_cas; + + adjusted_cas = s->selected_timings.CAS - 3; + + u16 fsb_to_ps[3] = { + 5000, // 800 + 3750, // 1067 + 3000 // 1333 + }; + + u16 ddr_to_ps[6] = { + 5000, // 400 + 3750, // 533 + 3000, // 667 + 2500, // 800 + 1875, // 1067 + 1500 // 1333 + }; + + u16 lut1[6] = { + 0, + 0, + 2600, + 3120, + 4171, + 5200 + }; + + static const u8 ddr3_turnaround_tab[3][6][4] = { + { /* DDR3 800 */ + {0x9, 0x7, 0x7, 0x9}, /* CL = 5 */ + {0x9, 0x7, 0x8, 0x8}, /* CL = 6 */ + }, + { /* DDR3 1066 */ + {0x0, 0x0, 0x0, 0x0}, /* CL = 5 - Not supported */ + {0x9, 0x7, 0x7, 0x9}, /* CL = 6 */ + {0x9, 0x7, 0x8, 0x8}, /* CL = 7 */ + {0x9, 0x7, 0x9, 0x7} /* CL = 8 */ + }, + { /* DDR3 1333 */ + {0x0, 0x0, 0x0, 0x0}, /* CL = 5 - Not supported */ + {0x0, 0x0, 0x0, 0x0}, /* CL = 6 - Not supported */ + {0x0, 0x0, 0x0, 0x0}, /* CL = 7 - Not supported */ + {0x9, 0x7, 0x9, 0x8}, /* CL = 8 */ + {0x9, 0x7, 0xA, 0x7}, /* CL = 9 */ + {0x9, 0x7, 0xB, 0x6}, /* CL = 10 */ + } + }; + + /* [DDR freq][0x26F & 1][pagemod] */ + static const u8 ddr2_x252_tab[2][2][2] = { + { /* DDR2 667 */ + {12, 16}, + {14, 18} + }, + { /* DDR2 800 */ + {14, 18}, + {16, 20} + } + }; + + static const u8 ddr3_x252_tab[3][2][2] = { + { /* DDR3 800 */ + {16, 20}, + {18, 22} + }, + { /* DDR3 1067 */ + {20, 26}, + {26, 26} + }, + { /* DDR3 1333 */ + {20, 30}, + {22, 32}, + } + }; + + if (s->spd_type == DDR2) { + ta1 = 6; + ta2 = 6; + ta3 = 5; + ta4 = 8; + } else { + int ddr3_idx = s->selected_timings.mem_clk - MEM_CLOCK_800MHz; + int cas_idx = s->selected_timings.CAS - 5; + ta1 = ddr3_turnaround_tab[ddr3_idx][cas_idx][0]; + ta2 = ddr3_turnaround_tab[ddr3_idx][cas_idx][1]; + ta3 = ddr3_turnaround_tab[ddr3_idx][cas_idx][2]; + ta4 = ddr3_turnaround_tab[ddr3_idx][cas_idx][3]; + } + + if (s->spd_type == DDR2) + twl = s->selected_timings.CAS - 1; + else /* DDR3 */ + twl = s->selected_timings.mem_clk - MEM_CLOCK_800MHz + 5; + + FOR_EACH_POPULATED_DIMM(s->dimms, i) { + if (s->dimms[i].n_banks == N_BANKS_8) { + trpmod = 1; + bankmod = 0; + } + if (s->dimms[i].page_size == 2048) + pagemod = 1; + } + + FOR_EACH_POPULATED_CHANNEL(s->dimms, i) { + MCHBAR8_OR(0x400*i + 0x26f, 0x3); + MCHBAR8_AND_OR(0x400*i + 0x228, ~0x7, 0x2); + /* tWL - x ?? */ + MCHBAR8_AND_OR(0x400*i + 0x240, ~0xf0, 0 << 4); + MCHBAR8_AND_OR(0x400*i + 0x240, ~0xf, adjusted_cas); + MCHBAR16_AND_OR(0x400*i + 0x265, ~0x3f00, + (adjusted_cas + 9) << 8); + + reg16 = (s->selected_timings.tRAS << 11) | + ((twl + 4 + s->selected_timings.tWR) << 6) | + ((2 + MAX(s->selected_timings.tRTP, 2)) << 2) | 1; + MCHBAR16(0x400*i + 0x250) = reg16; + + reg32 = (bankmod << 21) | + (s->selected_timings.tRRD << 17) | + (s->selected_timings.tRP << 13) | + ((s->selected_timings.tRP + trpmod) << 9) | + s->selected_timings.tRFC; + if (bankmod == 0) { + reg8 = (MCHBAR8(0x400*i + 0x26f) >> 1) & 1; + if (s->spd_type == DDR2) + reg32 |= ddr2_x252_tab[s->selected_timings.mem_clk + - MEM_CLOCK_667MHz][reg8][pagemod] + << 22; + else + reg32 |= ddr3_x252_tab[s->selected_timings.mem_clk + - MEM_CLOCK_800MHz][reg8][pagemod] + << 22; + } + MCHBAR32(0x400*i + 0x252) = reg32; + + MCHBAR16(0x400*i + 0x256) = (s->selected_timings.tRCD << 12) | + (0x4 << 8) | (ta2 << 4) | ta4; + + MCHBAR32(0x400*i + 0x258) = (s->selected_timings.tRCD << 17) | + ((twl + 4 + s->selected_timings.tWTR) << 12) | + (ta3 << 8) | (4 << 4) | ta1; + + MCHBAR16(0x400*i + 0x25b) = ((s->selected_timings.tRP + trpmod) << 9) | + s->selected_timings.tRFC; + + MCHBAR16_AND_OR(0x400*i + 0x260, ~0x3fe, + (s->spd_type == DDR2 ? 100 : 256) << 1); + MCHBAR8(0x400*i + 0x264) = 0xff; + MCHBAR8_AND_OR(0x400*i + 0x25d, ~0x3f, + s->selected_timings.tRAS); + MCHBAR16(0x400*i + 0x244) = 0x2310; + + switch (s->selected_timings.mem_clk) { + case MEM_CLOCK_667MHz: + reg8 = 0; + break; + default: + reg8 = 1; + break; + } + + MCHBAR8_AND_OR(0x400*i + 0x246, ~0x1f, (reg8 << 2) | 1); + + fsb = fsb_to_ps[s->selected_timings.fsb_clk]; + ddr = ddr_to_ps[s->selected_timings.mem_clk]; + reg32 = (u32)((s->selected_timings.CAS + 7 + reg8) * ddr); + reg32 = (u32)((reg32 / fsb) << 8); + reg32 |= 0x0e000000; + if ((fsb_to_mhz(s->selected_timings.fsb_clk) / + ddr_to_mhz(s->selected_timings.mem_clk)) > 2) { + reg32 |= 1 << 24; + } + MCHBAR32_AND_OR(0x400*i + 0x248, ~0x0f001f00, reg32); + + if (twl > 2) + flag1 = 1; + + if (s->selected_timings.mem_clk >= MEM_CLOCK_800MHz) + flag2 = 1; + + reg16 = (u8)(twl - 1 - flag1 - flag2); + reg16 |= reg16 << 4; + if (s->selected_timings.mem_clk == MEM_CLOCK_1333MHz) { + if (reg16) + reg16--; + } + reg16 |= flag1 << 8; + reg16 |= flag2 << 9; + MCHBAR16_AND_OR(0x400*i + 0x24d, ~0x1ff, reg16); + MCHBAR16(0x400*i + 0x25e) = 0x15a5; + MCHBAR32_AND(0x400*i + 0x265, ~0x1f); + MCHBAR32_AND_OR(0x400*i + 0x269, ~0x000fffff, + (0x3f << 14) | lut1[s->selected_timings.mem_clk]); + MCHBAR8_OR(0x400*i + 0x274, 1); + MCHBAR8_AND(0x400*i + 0x24c, ~0x3); + + reg16 = 0; + if (s->spd_type == DDR2) { + switch (s->selected_timings.mem_clk) { + default: + case MEM_CLOCK_667MHz: + reg16 = 0x99; + break; + case MEM_CLOCK_800MHz: + if (s->selected_timings.CAS == 5) + reg16 = 0x19a; + else if (s->selected_timings.CAS == 6) + reg16 = 0x9a; + break; + } + } else { /* DDR3 */ + switch (s->selected_timings.mem_clk) { + default: + case MEM_CLOCK_800MHz: + case MEM_CLOCK_1066MHz: + reg16 = 1; + break; + case MEM_CLOCK_1333MHz: + reg16 = 2; + break; + } + } + + reg16 &= 0x7; + reg16 += twl + 9; + reg16 <<= 10; + MCHBAR16_AND_OR(0x400*i + 0x24d, ~0x7c00, reg16); + MCHBAR8_AND_OR(0x400*i + 0x267, ~0x3f, 0x13); + MCHBAR8_AND_OR(0x400*i + 0x268, ~0xff, 0x4a); + + reg16 = (MCHBAR16(0x400*i + 0x269) & 0xc000) >> 2; + reg16 += 2 << 12; + reg16 |= (0x15 << 6) | 0x1f; + MCHBAR16_AND_OR(0x400*i + 0x26d, ~0x7fff, reg16); + + reg32 = (1 << 25) | (6 << 27); + MCHBAR32_AND_OR(0x400*i + 0x269, ~0xfa300000, reg32); + MCHBAR8_AND(0x400*i + 0x271, ~0x80); + MCHBAR8_AND(0x400*i + 0x274, ~0x6); + } // END EACH POPULATED CHANNEL + + reg16 = 0x1f << 5; + reg16 |= 0xe << 10; + MCHBAR16_AND_OR(0x125, ~0x3fe0, reg16); + MCHBAR16_AND_OR(0x127, ~0x7ff, 0x540); + MCHBAR8_OR(0x129, 0x1f); + MCHBAR8_OR(0x12c, 0xa0); + MCHBAR32_AND_OR(0x241, ~0x1ffff, 0x11); + MCHBAR32_AND_OR(0x641, ~0x1ffff, 0x11); + MCHBAR8_AND(0x246, ~0x10); + MCHBAR8_AND(0x646, ~0x10); + MCHBAR32(0x120) = (2 << 29) | (1 << 28) | (1 << 23) | 0xd7f5f; + reg8 = (u8)((MCHBAR32(0x252) & 0x1e000) >> 13); + MCHBAR8_AND_OR(0x12d, ~0xf0, reg8 << 4); + reg8 = (u8)((MCHBAR32(0x258) & 0x1e0000) >> 17); + MCHBAR8_AND_OR(0x12d, ~0xf, reg8); + MCHBAR8(0x12f) = 0x4c; + reg32 = (1 << 31) | (0x80 << 14) | (1 << 13) | (0xa << 9); + if (s->spd_type == DDR3) { + MCHBAR8(0x114) = 0x42; + reg16 = (512 - MAX(5, s->selected_timings.tRFC + 10000 + / ddr_to_ps[s->selected_timings.mem_clk])) + / 2; + reg16 &= 0x1ff; + reg32 = (reg16 << 22) | (0x80 << 14) | (0xa << 9); + } + MCHBAR32_AND_OR(0x6c0, ~0xffffff00, reg32); + MCHBAR8_AND_OR(0x6c4, ~0x7, 0x2); +} + +static void program_dll(struct sysinfo *s) +{ + u8 i, j, r, reg8, clk, async = 0; + u16 reg16 = 0; + u32 reg32 = 0; + + const u8 rank2clken[8] = { 0x04, 0x01, 0x20, 0x08, 0x01, 0x04, + 0x08, 0x10 }; + + MCHBAR16_AND_OR(0x180, ~0x7e06, 0xc04); + MCHBAR16_AND_OR(0x182, ~0x3ff, 0xc8); + MCHBAR16_AND_OR(0x18a, ~0x1f1f, 0x0f0f); + MCHBAR16_AND_OR(0x1b4, ~0x8020, 0x100); + MCHBAR8_AND_OR(0x194, ~0x77, 0x33); + switch (s->selected_timings.mem_clk) { + default: + case MEM_CLOCK_667MHz: + case MEM_CLOCK_1333MHz: + reg16 = (0xa << 9) | 0xa; + break; + case MEM_CLOCK_800MHz: + reg16 = (0x9 << 9) | 0x9; + break; + case MEM_CLOCK_1066MHz: + reg16 = (0x7 << 9) | 0x7; + break; + } + MCHBAR16_AND_OR(0x19c, ~0x1e0f, reg16); + MCHBAR16_AND_OR(0x19c, ~0x2030, 0x2010); + udelay(1); + MCHBAR16_AND(0x198, ~0x100); + + MCHBAR16_AND_OR(0x1c8, ~0x1f, 0xd); + + udelay(1); + MCHBAR8_AND(0x190, ~1); + udelay(1); // 533ns + MCHBAR32_AND(0x198, ~0x11554000); + udelay(1); + MCHBAR32_AND(0x198, ~0x1455); + udelay(1); + MCHBAR8_AND(0x583, ~0x1c); + MCHBAR8_AND(0x983, ~0x1c); + udelay(1); // 533ns + MCHBAR8_AND(0x583, ~0x3); + MCHBAR8_AND(0x983, ~0x3); + udelay(1); // 533ns + + // ME related + MCHBAR32_AND_OR(0x1a0, ~0x7ffffff, + s->spd_type == DDR2 ? 0x551803 : 0x555801); + + MCHBAR16_AND(0x1b4, ~0x800); + if (s->spd_type == DDR2) { + MCHBAR8_OR(0x1a8, 0xf0); + } else { /* DDR3 */ + reg8 = 0x9; /* 0x9 << 4 ?? */ + if (s->dimms[0].ranks == 2) + reg8 &= ~0x80; + if (s->dimms[3].ranks == 2) + reg8 &= ~0x10; + MCHBAR8_AND_OR(0x1a8, ~0xf0, reg8); + } + + FOR_EACH_CHANNEL(i) { + reg16 = 0; + if ((s->spd_type == DDR3) && (i == 0)) + reg16 = (0x3 << 12); + MCHBAR16_AND_OR(0x400*i + 0x59c, ~0x3000, reg16); + + reg32 = 0; + FOR_EACH_RANK_IN_CHANNEL(r) { + if (!RANK_IS_POPULATED(s->dimms, i, r)) + reg32 |= 0x111 << r; + } + + MCHBAR32_AND_OR(0x400*i + 0x59c, ~0xfff, reg32); + MCHBAR8_AND(0x400*i + 0x594, ~1); + + if (s->spd_type == DDR2) { + if (!CHANNEL_IS_POPULATED(s->dimms, i)) { + printk(BIOS_DEBUG, + "No dimms in channel %d\n", i); + reg8 = 0x3f; + } else if (ONLY_DIMMA_IS_POPULATED(s->dimms, i)) { + printk(BIOS_DEBUG, + "DimmA populated only in channel %d\n", + i); + reg8 = 0x38; + } else if (ONLY_DIMMB_IS_POPULATED(s->dimms, i)) { + printk(BIOS_DEBUG, + "DimmB populated only in channel %d\n", + i); + reg8 = 0x7; + } else if (BOTH_DIMMS_ARE_POPULATED(s->dimms, i)) { + printk(BIOS_DEBUG, + "Both dimms populated in channel %d\n", + i); + reg8 = 0; + } else { + die("Unhandled case\n"); + } + MCHBAR32_AND_OR(0x400*i + 0x5a0, ~0x3f000000, + (u32)(reg8 << 24)); + + } else { /* DDR3 */ + FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, i, r) { + MCHBAR8_AND(0x400 * i + 0x5a0 + 3, + ~rank2clken[r + i * 4]); + } + } + + //reg8 = 0x00; // FIXME don't switch on all clocks anyway + } // END EACH CHANNEL + + if (s->spd_type == DDR2) { + MCHBAR8_OR(0x1a8, 1); + MCHBAR8_AND(0x1a8, ~0x4); + } else { /* DDR3 */ + MCHBAR8_AND(0x1a8, ~1); + MCHBAR8_OR(0x1a8, 0x4); + } + + // Update DLL timing + MCHBAR8_AND(0x1a4, ~0x80); + MCHBAR8_OR(0x1a4, 0x40); + MCHBAR16_AND_OR(0x5f0, ~0x400, 0x400); + + FOR_EACH_POPULATED_CHANNEL(s->dimms, i) { + MCHBAR16_AND_OR(0x400*i + 0x5f0, ~0x3fc, 0x3fc); + MCHBAR32_AND(0x400*i + 0x5fc, ~0xcccccccc); + MCHBAR8_AND_OR(0x400*i + 0x5d9, ~0xf0, + s->spd_type == DDR2 ? 0x70 : 0x60); + MCHBAR16_AND_OR(0x400*i + 0x590, ~0xffff, + s->spd_type == DDR2 ? 0x5555 : 0xa955); + } + + FOR_EACH_POPULATED_CHANNEL(s->dimms, i) { + const struct dll_setting *setting; + + switch (s->selected_timings.mem_clk) { + default: /* Should not happen */ + case MEM_CLOCK_667MHz: + setting = default_ddr2_667_ctrl; + break; + case MEM_CLOCK_800MHz: + if (s->spd_type == DDR2) + setting = default_ddr2_800_ctrl; + else + setting = default_ddr3_800_ctrl[s->nmode - 1]; + break; + case MEM_CLOCK_1066MHz: + setting = default_ddr3_1067_ctrl[s->nmode - 1]; + break; + case MEM_CLOCK_1333MHz: + setting = default_ddr3_1333_ctrl[s->nmode - 1]; + break; + } + + clkset0(i, &setting[CLKSET0]); + clkset1(i, &setting[CLKSET1]); + ctrlset0(i, &setting[CTRL0]); + ctrlset1(i, &setting[CTRL1]); + ctrlset2(i, &setting[CTRL2]); + ctrlset3(i, &setting[CTRL3]); + cmdset(i, &setting[CMD]); + } + + // XXX if not async mode + MCHBAR16_AND(0x180, ~0x8200); + MCHBAR8_OR(0x180, 0x4); + j = 0; + for (i = 0; i < 16; i++) { + MCHBAR8_AND_OR(0x1c8, ~0x1f, i); + MCHBAR8_OR(0x180, 0x10); + while (MCHBAR8(0x180) & 0x10) + ; + if (MCHBAR32(0x184) == 0xffffffff) { + j++; + if (j >= 2) + break; + + if (s->selected_timings.mem_clk == MEM_CLOCK_667MHz) { + j = 2; + break; + } + } else { + j = 0; + } + } + if (i == 1 || ((i == 0) && s->selected_timings.mem_clk == MEM_CLOCK_667MHz)) { + j = 0; + i++; + for (; i < 16; i++) { + MCHBAR8_AND_OR(0x1c8, ~0x1f, i); + MCHBAR8_OR(0x180, 0x4); + while (MCHBAR8(0x180) & 0x10) + ; + if (MCHBAR32(0x184) == 0) { + i++; + break; + } + } + for (; i < 16; i++) { + MCHBAR8_AND_OR(0x1c8, ~0x1f, i); + MCHBAR8_OR(0x180, 0x10); + while (MCHBAR8(0x180) & 0x10) + ; + if (MCHBAR32(0x184) == 0xffffffff) { + j++; + if (j >= 2) + break; + } else { + j = 0; + } + } + if (j < 2) { + MCHBAR8_AND(0x1c8, ~0x1f); + MCHBAR8_OR(0x180, 0x10); + while (MCHBAR8(0x180) & 0x10) + ; + j = 2; + } + } + + if (j < 2) { + MCHBAR8_AND(0x1c8, ~0x1f); + async = 1; + } + + switch (s->selected_timings.mem_clk) { + case MEM_CLOCK_667MHz: + clk = 0x1a; + if (async != 1) { + if (s->selected_timings.fsb_clk == FSB_CLOCK_800MHz) + clk = 0x10; + } + break; + case MEM_CLOCK_800MHz: + case MEM_CLOCK_1066MHz: + if (async != 1) + clk = 0x10; + else + clk = 0x1a; + break; + case MEM_CLOCK_1333MHz: + clk = 0x18; + break; + default: + clk = 0x1a; + break; + } + + if (async != 1) + reg8 = MCHBAR8(0x188) & 0x1e; + + MCHBAR8_AND(0x180, ~0x80); + + if ((s->spd_type == DDR3 && s->selected_timings.mem_clk == MEM_CLOCK_1066MHz) + || (s->spd_type == DDR2 && s->selected_timings.fsb_clk == FSB_CLOCK_800MHz + && s->selected_timings.mem_clk == MEM_CLOCK_667MHz)) { + i = MCHBAR8(0x1c8) & 0xf; + if (s->spd_type == DDR2) + i = (i + 10) % 14; + else /* DDR3 */ + i = (i + 3) % 12; + MCHBAR8_AND_OR(0x1c8, ~0x1f, i); + MCHBAR8_OR(0x180, 0x10); + while (MCHBAR8(0x180) & 0x10) + ; + } + + reg8 = MCHBAR8(0x188) & ~1; + MCHBAR8(0x188) = reg8; + reg8 &= ~0x3e; + reg8 |= clk; + MCHBAR8(0x188) = reg8; + reg8 |= 1; + MCHBAR8(0x188) = reg8; + + if (s->selected_timings.mem_clk == MEM_CLOCK_1333MHz) + MCHBAR8_OR(0x18c, 1); +} + +static void select_default_dq_dqs_settings(struct sysinfo *s) +{ + int ch, lane; + + FOR_EACH_POPULATED_CHANNEL_AND_BYTELANE(s->dimms, ch, lane) { + switch (s->selected_timings.mem_clk) { + case MEM_CLOCK_667MHz: + memcpy(s->dqs_settings[ch], + default_ddr2_667_dqs, + sizeof(s->dqs_settings[ch])); + memcpy(s->dq_settings[ch], + default_ddr2_667_dq, + sizeof(s->dq_settings[ch])); + s->rt_dqs[ch][lane].tap = 7; + s->rt_dqs[ch][lane].pi = 2; + break; + case MEM_CLOCK_800MHz: + if (s->spd_type == DDR2) { + memcpy(s->dqs_settings[ch], + default_ddr2_800_dqs, + sizeof(s->dqs_settings[ch])); + memcpy(s->dq_settings[ch], + default_ddr2_800_dq, + sizeof(s->dq_settings[ch])); + s->rt_dqs[ch][lane].tap = 7; + s->rt_dqs[ch][lane].pi = 0; + } else { /* DDR3 */ + memcpy(s->dqs_settings[ch], + default_ddr3_800_dqs[s->nmode - 1], + sizeof(s->dqs_settings[ch])); + memcpy(s->dq_settings[ch], + default_ddr3_800_dq[s->nmode - 1], + sizeof(s->dq_settings[ch])); + s->rt_dqs[ch][lane].tap = 6; + s->rt_dqs[ch][lane].pi = 3; + } + break; + case MEM_CLOCK_1066MHz: + memcpy(s->dqs_settings[ch], + default_ddr3_1067_dqs[s->nmode - 1], + sizeof(s->dqs_settings[ch])); + memcpy(s->dq_settings[ch], + default_ddr3_1067_dq[s->nmode - 1], + sizeof(s->dq_settings[ch])); + s->rt_dqs[ch][lane].tap = 5; + s->rt_dqs[ch][lane].pi = 3; + break; + case MEM_CLOCK_1333MHz: + memcpy(s->dqs_settings[ch], + default_ddr3_1333_dqs[s->nmode - 1], + sizeof(s->dqs_settings[ch])); + memcpy(s->dq_settings[ch], + default_ddr3_1333_dq[s->nmode - 1], + sizeof(s->dq_settings[ch])); + s->rt_dqs[ch][lane].tap = 7; + s->rt_dqs[ch][lane].pi = 0; + break; + default: /* not supported */ + break; + } + } +} + +/* + * It looks like only the RT DQS register for the first rank + * is used for all ranks. Just set all the 'unused' RT DQS registers + * to the same as rank 0, out of precaution. + */ +static void set_all_dq_dqs_dll_settings(struct sysinfo *s) +{ + // Program DQ/DQS dll settings + int ch, lane, rank; + + FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { + FOR_EACH_BYTELANE(lane) { + FOR_EACH_RANK_IN_CHANNEL(rank) { + rt_set_dqs(ch, lane, rank, + &s->rt_dqs[ch][lane]); + } + dqsset(ch, lane, &s->dqs_settings[ch][lane]); + dqset(ch, lane, &s->dq_settings[ch][lane]); + } + } +} + +static void prog_rcomp(struct sysinfo *s) +{ + u8 i, j, k, reg8; + const u32 ddr2_x32a[8] = { 0x04040404, 0x06050505, 0x09090807, 0x0D0C0B0A, + 0x04040404, 0x08070605, 0x0C0B0A09, 0x100F0E0D }; + const u16 ddr2_x378[6] = { 0, 0xAAAA, 0x7777, 0x7777, 0x7777, 0x7777 }; + const u32 ddr2_x382[6] = { 0, 0x02020202, 0x02020202, 0x02020202, 0x04030303, 0x04030303 }; + const u32 ddr2_x386[6] = { 0, 0x03020202, 0x03020202, 0x03020202, 0x05040404, 0x05040404 }; + const u32 ddr2_x38a[6] = { 0, 0x04040303, 0x04040303, 0x04040303, 0x07070605, 0x07070605 }; + const u32 ddr2_x38e[6] = { 0, 0x06060505, 0x06060505, 0x06060505, 0x09090808, 0x09090808 }; + const u32 ddr2_x392[6] = { 0, 0x02020202, 0x02020202, 0x02020202, 0x03030202, 0x03030202 }; + const u32 ddr2_x396[6] = { 0, 0x03030202, 0x03030202, 0x03030202, 0x05040303, 0x05040303 }; + const u32 ddr2_x39a[6] = { 0, 0x04040403, 0x04040403, 0x04040403, 0x07070605, 0x07070605 }; + const u32 ddr2_x39e[6] = { 0, 0x06060505, 0x06060505, 0x06060505, 0x08080808, 0x08080808 }; + + const u32 ddr3_x32a[8] = {0x06060606, 0x06060606, 0x0b090807, 0x12110f0d, + 0x06060606, 0x08070606, 0x0d0b0a09, 0x16161511}; + const u16 ddr3_x378[6] = {0, 0xbbbb, 0x6666, 0x6666, 0x6666, 0x6666}; + const u32 ddr3_x382[6] = {0, 0x05050505, 0x04040404, 0x04040404, 0x34343434, 0x34343434}; + const u32 ddr3_x386[6] = {0, 0x05050505, 0x04040404, 0x04040404, 0x34343434, 0x34343434}; + const u32 ddr3_x38a[6] = {0, 0x06060605, 0x07060504, 0x07060504, 0x34343434, 0x34343434}; + const u32 ddr3_x38e[6] = {0, 0x09080707, 0x09090808, 0x09090808, 0x34343434, 0x34343434}; + const u32 ddr3_x392[6] = {0, 0x05050505, 0x04040404, 0x04040404, 0x34343434, 0x34343434}; + const u32 ddr3_x396[6] = {0, 0x05050505, 0x04040404, 0x04040404, 0x34343434, 0x34343434}; + const u32 ddr3_x39a[6] = {0, 0x07060606, 0x08070605, 0x08070605, 0x34343434, 0x34343434}; + const u32 ddr3_x39e[6] = {0, 0x09090807, 0x0b0b0a09, 0x0b0b0a09, 0x34343434, 0x34343434}; + + const u16 *x378; + const u32 *x32a, *x382, *x386, *x38a, *x38e; + const u32 *x392, *x396, *x39a, *x39e; + + const u16 addr[6] = { 0x31c, 0x374, 0x3a2, 0x3d0, 0x3fe, 0x42c }; + u8 bit[6] = { 0, 0, 1, 1, 0, 0 }; + + if (s->spd_type == DDR2) { + x32a = ddr2_x32a; + x378 = ddr2_x378; + x382 = ddr2_x382; + x386 = ddr2_x386; + x38a = ddr2_x38a; + x38e = ddr2_x38e; + x392 = ddr2_x392; + x396 = ddr2_x396; + x39a = ddr2_x39a; + x39e = ddr2_x39e; + } else { /* DDR3 */ + x32a = ddr3_x32a; + x378 = ddr3_x378; + x382 = ddr3_x382; + x386 = ddr3_x386; + x38a = ddr3_x38a; + x38e = ddr3_x38e; + x392 = ddr3_x392; + x396 = ddr3_x396; + x39a = ddr3_x39a; + x39e = ddr3_x39e; + } + + FOR_EACH_POPULATED_CHANNEL(s->dimms, i) { + for (j = 0; j < 6; j++) { + if (j == 0) { + MCHBAR32_AND_OR(0x400*i + addr[j], ~0xff000, + 0xaa000); + MCHBAR16_AND_OR(0x400*i + 0x320, ~0xffff, + 0x6666); + for (k = 0; k < 8; k++) { + MCHBAR32_AND_OR(0x400*i + addr[j] + + 0xe + (k << 2), + ~0x3f3f3f3f, x32a[k]); + MCHBAR32_AND_OR(0x400*i + addr[j] + + 0x2e + (k << 2), + ~0x3f3f3f3f, x32a[k]); + } + } else { + MCHBAR16_AND_OR(0x400*i + addr[j], + ~0xf000, 0xa000); + MCHBAR16_AND_OR(0x400*i + addr[j] + 4, + ~0xffff, x378[j]); + MCHBAR32_AND_OR(0x400*i + addr[j] + 0xe, + ~0x3f3f3f3f, x382[j]); + MCHBAR32_AND_OR(0x400*i + addr[j] + 0x12, + ~0x3f3f3f3f, x386[j]); + MCHBAR32_AND_OR(0x400*i + addr[j] + 0x16, + ~0x3f3f3f3f, x38a[j]); + MCHBAR32_AND_OR(0x400*i + addr[j] + 0x1a, + ~0x3f3f3f3f, x38e[j]); + MCHBAR32_AND_OR(0x400*i + addr[j] + 0x1e, + ~0x3f3f3f3f, x392[j]); + MCHBAR32_AND_OR(0x400*i + addr[j] + 0x22, + ~0x3f3f3f3f, x396[j]); + MCHBAR32_AND_OR(0x400*i + addr[j] + 0x26, + ~0x3f3f3f3f, x39a[j]); + MCHBAR32_AND_OR(0x400*i + addr[j] + 0x2a, + ~0x3f3f3f3f, x39e[j]); + } + if (s->spd_type == DDR3 && + BOTH_DIMMS_ARE_POPULATED(s->dimms, i)) { + MCHBAR16_AND_OR(0x378 + 0x400 * i, + ~0xffff, 0xcccc); + } + MCHBAR8_AND_OR(0x400*i + addr[j], ~1, bit[j]); + } + reg8 = (s->spd_type == DDR2) ? 0x12 : 0x36; + MCHBAR8_AND_OR(0x400*i + 0x45a, ~0x3f, reg8); + MCHBAR8_AND_OR(0x400*i + 0x45e, ~0x3f, reg8); + MCHBAR8_AND_OR(0x400*i + 0x462, ~0x3f, reg8); + MCHBAR8_AND_OR(0x400*i + 0x466, ~0x3f, reg8); + } // END EACH POPULATED CHANNEL + + MCHBAR32_AND_OR(0x134, ~0x63c00, 0x63c00); + MCHBAR16_AND_OR(0x174, ~0x63ff, 0x63ff); + MCHBAR16(0x178) = 0x0135; + MCHBAR32_AND_OR(0x130, ~0x7bdffe0, 0x7a9ffa0); + + if (!CHANNEL_IS_POPULATED(s->dimms, 0)) + MCHBAR32_AND(0x130, ~(1 << 27)); + if (!CHANNEL_IS_POPULATED(s->dimms, 1)) + MCHBAR32_AND(0x130, ~(1 << 28)); + + MCHBAR8_OR(0x130, 1); +} + +static void program_odt(struct sysinfo *s) +{ + u8 i; + static u16 ddr2_odt[16][2] = { + { 0x0000, 0x0000 }, // NC_NC + { 0x0000, 0x0001 }, // x8SS_NC + { 0x0000, 0x0011 }, // x8DS_NC + { 0x0000, 0x0001 }, // x16SS_NC + { 0x0004, 0x0000 }, // NC_x8SS + { 0x0101, 0x0404 }, // x8SS_x8SS + { 0x0101, 0x4444 }, // x8DS_x8SS + { 0x0101, 0x0404 }, // x16SS_x8SS + { 0x0044, 0x0000 }, // NC_x8DS + { 0x1111, 0x0404 }, // x8SS_x8DS + { 0x1111, 0x4444 }, // x8DS_x8DS + { 0x1111, 0x0404 }, // x16SS_x8DS + { 0x0004, 0x0000 }, // NC_x16SS + { 0x0101, 0x0404 }, // x8SS_x16SS + { 0x0101, 0x4444 }, // x8DS_x16SS + { 0x0101, 0x0404 }, // x16SS_x16SS + }; + + static const u16 ddr3_odt[16][2] = { + { 0x0000, 0x0000 }, // NC_NC + { 0x0000, 0x0001 }, // x8SS_NC + { 0x0000, 0x0021 }, // x8DS_NC + { 0x0000, 0x0001 }, // x16SS_NC + { 0x0004, 0x0000 }, // NC_x8SS + { 0x0105, 0x0405 }, // x8SS_x8SS + { 0x0105, 0x4465 }, // x8DS_x8SS + { 0x0105, 0x0405 }, // x16SS_x8SS + { 0x0084, 0x0000 }, // NC_x8DS + { 0x1195, 0x0405 }, // x8SS_x8DS + { 0x1195, 0x4465 }, // x8DS_x8DS + { 0x1195, 0x0405 }, // x16SS_x8DS + { 0x0004, 0x0000 }, // NC_x16SS + { 0x0105, 0x0405 }, // x8SS_x16SS + { 0x0105, 0x4465 }, // x8DS_x16SS + { 0x0105, 0x0405 }, // x16SS_x16SS + }; + + FOR_EACH_POPULATED_CHANNEL(s->dimms, i) { + if (s->spd_type == DDR2) { + MCHBAR16(0x400 * i + 0x298) = + ddr2_odt[s->dimm_config[i]][1]; + MCHBAR16(0x400 * i + 0x294) = + ddr2_odt[s->dimm_config[i]][0]; + } else { + MCHBAR16(0x400 * i + 0x298) = + ddr3_odt[s->dimm_config[i]][1]; + MCHBAR16(0x400 * i + 0x294) = + ddr3_odt[s->dimm_config[i]][0]; + } + u16 reg16 = MCHBAR16(0x400*i + 0x29c); + reg16 &= ~0xfff; + reg16 |= (s->spd_type == DDR2 ? 0x66b : 0x778); + MCHBAR16(0x400*i + 0x29c) = reg16; + MCHBAR32_AND_OR(0x400*i + 0x260, ~0x70e3c00, 0x3063c00); + } +} + +static void pre_jedec_memory_map(void) +{ + /* + * Configure the memory mapping in stacked mode (channel 1 being mapped + * above channel 0) and with 128M per rank. + * This simplifies dram trainings a lot since those need a test address. + * + * +-------------+ => 0 + * | ch 0, rank 0| + * +-------------+ => 0x8000000 (128M) + * | ch 0, rank 1| + * +-------------+ => 0x10000000 (256M) + * | ch 0, rank 2| + * +-------------+ => 0x18000000 (384M) + * | ch 0, rank 3| + * +-------------+ => 0x20000000 (512M) + * | ch 1, rank 0| + * +-------------+ => 0x28000000 (640M) + * | ch 1, rank 1| + * +-------------+ => 0x30000000 (768M) + * | ch 1, rank 2| + * +-------------+ => 0x38000000 (896M) + * | ch 1, rank 3| + * +-------------+ + * + * After all trainings are done this is set to the real values specified + * by the SPD. + */ + /* Set rank 0-3 populated */ + MCHBAR32_AND_OR(C0CKECTRL, ~1, 0xf00000); + MCHBAR32_AND_OR(C1CKECTRL, ~1, 0xf00000); + /* Set size of each rank to 128M */ + MCHBAR16(C0DRA01) = 0x0101; + MCHBAR16(C0DRA23) = 0x0101; + MCHBAR16(C1DRA01) = 0x0101; + MCHBAR16(C1DRA23) = 0x0101; + MCHBAR16(C0DRB0) = 0x0002; + MCHBAR16(C0DRB1) = 0x0004; + MCHBAR16(C0DRB2) = 0x0006; + MCHBAR16(C0DRB3) = 0x0008; + MCHBAR16(C1DRB0) = 0x0002; + MCHBAR16(C1DRB1) = 0x0004; + MCHBAR16(C1DRB2) = 0x0006; + /* In stacked mode the last present rank on ch1 needs to have its + size doubled in c1drbx */ + MCHBAR16(C1DRB3) = 0x0010; + MCHBAR8_OR(0x111, STACKED_MEM); + MCHBAR32(0x104) = 0; + MCHBAR16(0x102) = 0x400; + MCHBAR8(0x110) = (2 << 5) | (3 << 3); + MCHBAR16(0x10e) = 0; + MCHBAR32(0x108) = 0; + pci_write_config16(PCI_DEV(0, 0, 0), D0F0_TOLUD, 0x4000); + /* TOM(64M unit) = 1G = TOTAL_CHANNELS * RANKS_PER_CHANNEL * 128M */ + pci_write_config16(PCI_DEV(0, 0, 0), D0F0_TOM, 0x10); + /* TOUUD(1M unit) = 1G = TOTAL_CHANNELS * RANKS_PER_CHANNEL * 128M */ + pci_write_config16(PCI_DEV(0, 0, 0), D0F0_TOUUD, 0x0400); + pci_write_config32(PCI_DEV(0, 0, 0), D0F0_GBSM, 0x40000000); + pci_write_config32(PCI_DEV(0, 0, 0), D0F0_BGSM, 0x40000000); + pci_write_config32(PCI_DEV(0, 0, 0), D0F0_TSEG, 0x40000000); +} + +u32 test_address(int channel, int rank) +{ + ASSERT(channel <= 1 && rank < 4); + return channel * 512 * MiB + rank * 128 * MiB; +} + + +/* DDR3 Rank1 Address mirror + * swap the following pins: + * A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 */ +static u32 mirror_shift_bit(const u32 data, u8 bit) +{ + u32 temp0 = data, temp1 = data; + temp0 &= 1 << bit; + temp0 <<= 1; + temp1 &= 1 << (bit + 1); + temp1 >>= 1; + return (data & ~(3 << bit)) | temp0 | temp1; +} + +void send_jedec_cmd(const struct sysinfo *s, u8 r, u8 ch, u8 cmd, u32 val) +{ + u32 addr = test_address(ch, r); + u8 data8 = cmd; + u32 data32; + + if (s->spd_type == DDR3 && (r & 1) + && s->dimms[ch * 2 + (r >> 1)].mirrored) { + data8 = (u8)mirror_shift_bit(data8, 4); + } + + MCHBAR8_AND_OR(0x271, ~0x3e, data8); + MCHBAR8_AND_OR(0x671, ~0x3e, data8); + data32 = val; + if (s->spd_type == DDR3 && (r & 1) + && s->dimms[ch * 2 + (r >> 1)].mirrored) { + data32 = mirror_shift_bit(data32, 3); + data32 = mirror_shift_bit(data32, 5); + data32 = mirror_shift_bit(data32, 7); + } + data32 <<= 3; + + read32((void *)((data32 | addr))); + udelay(10); + MCHBAR8_AND_OR(0x271, ~0x3e, NORMALOP_CMD); + MCHBAR8_AND_OR(0x671, ~0x3e, NORMALOP_CMD); +} + +static void jedec_ddr2(struct sysinfo *s) +{ + u8 i; + u16 mrsval, ch, r, v; + + u8 odt[16][4] = { + {0x00, 0x00, 0x00, 0x00}, + {0x01, 0x00, 0x00, 0x00}, + {0x01, 0x01, 0x00, 0x00}, + {0x01, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x01, 0x00}, + {0x11, 0x00, 0x11, 0x00}, + {0x11, 0x11, 0x11, 0x00}, + {0x11, 0x00, 0x11, 0x00}, + {0x00, 0x00, 0x01, 0x01}, + {0x11, 0x00, 0x11, 0x11}, + {0x11, 0x11, 0x11, 0x11}, + {0x11, 0x00, 0x11, 0x11}, + {0x00, 0x00, 0x01, 0x00}, + {0x11, 0x00, 0x11, 0x00}, + {0x11, 0x11, 0x11, 0x00}, + {0x11, 0x00, 0x11, 0x00} + }; + + u16 jedec[12][2] = { + {NOP_CMD, 0x0}, + {PRECHARGE_CMD, 0x0}, + {EMRS2_CMD, 0x0}, + {EMRS3_CMD, 0x0}, + {EMRS1_CMD, 0x0}, + {MRS_CMD, 0x100}, // DLL Reset + {PRECHARGE_CMD, 0x0}, + {CBR_CMD, 0x0}, + {CBR_CMD, 0x0}, + {MRS_CMD, 0x0}, // DLL out of reset + {EMRS1_CMD, 0x380}, // OCD calib default + {EMRS1_CMD, 0x0} + }; + + mrsval = (s->selected_timings.CAS << 4) | ((s->selected_timings.tWR - 1) << 9) | 0xb; + + printk(BIOS_DEBUG, "MRS...\n"); + + udelay(200); + + FOR_EACH_POPULATED_RANK(s->dimms, ch, r) { + printk(BIOS_DEBUG, "CH%d: Found Rank %d\n", ch, r); + for (i = 0; i < 12; i++) { + v = jedec[i][1]; + switch (jedec[i][0]) { + case EMRS1_CMD: + v |= (odt[s->dimm_config[ch]][r] << 2); + break; + case MRS_CMD: + v |= mrsval; + break; + default: + break; + } + send_jedec_cmd(s, r, ch, jedec[i][0], v); + udelay(1); + printk(RAM_SPEW, "Jedec step %d\n", i); + } + } + printk(BIOS_DEBUG, "MRS done\n"); +} + +static void jedec_ddr3(struct sysinfo *s) +{ + int ch, r, dimmconfig, cmd, ddr3_freq; + + u8 ddr3_emrs2_rtt_wr_config[16][4] = { /* [config][Rank] */ + {0, 0, 0, 0}, /* NC_NC */ + {0, 0, 0, 0}, /* x8ss_NC */ + {0, 0, 0, 0}, /* x8ds_NC */ + {0, 0, 0, 0}, /* x16ss_NC */ + {0, 0, 0, 0}, /* NC_x8ss */ + {2, 0, 2, 0}, /* x8ss_x8ss */ + {2, 2, 2, 0}, /* x8ds_x8ss */ + {2, 0, 2, 0}, /* x16ss_x8ss */ + {0, 0, 0, 0}, /* NC_x8ss */ + {2, 0, 2, 2}, /* x8ss_x8ds */ + {2, 2, 2, 2}, /* x8ds_x8ds */ + {2, 0, 2, 2}, /* x16ss_x8ds */ + {0, 0, 0, 0}, /* NC_x16ss */ + {2, 0, 2, 0}, /* x8ss_x16ss */ + {2, 2, 2, 0}, /* x8ds_x16ss */ + {2, 0, 2, 0}, /* x16ss_x16ss */ + }; + + printk(BIOS_DEBUG, "MRS...\n"); + + ddr3_freq = s->selected_timings.mem_clk - MEM_CLOCK_800MHz; + FOR_EACH_POPULATED_RANK(s->dimms, ch, r) { + printk(BIOS_DEBUG, "CH%d: Found Rank %d\n", ch, r); + send_jedec_cmd(s, r, ch, NOP_CMD, 0); + udelay(200); + dimmconfig = s->dimm_config[ch]; + cmd = ddr3_freq << 3; /* actually twl - 5 which is same */ + cmd |= ddr3_emrs2_rtt_wr_config[dimmconfig][r] << 9; + send_jedec_cmd(s, r, ch, EMRS2_CMD, cmd); + send_jedec_cmd(s, r, ch, EMRS3_CMD, 0); + cmd = ddr3_emrs1_rtt_nom_config[dimmconfig][r] << 2; + /* Hardcode output drive strength to 34 Ohm / RZQ/7 (why??) */ + cmd |= (1 << 1); + send_jedec_cmd(s, r, ch, EMRS1_CMD, cmd); + /* Burst type interleaved, burst length 8, Reset DLL, + * Precharge PD: DLL on */ + send_jedec_cmd(s, r, ch, MRS_CMD, (1 << 3) | (1 << 8) + | (1 << 12) | ((s->selected_timings.CAS - 4) << 4) + | ((s->selected_timings.tWR - 4) << 9)); + send_jedec_cmd(s, r, ch, ZQCAL_CMD, (1 << 10)); + } + printk(BIOS_DEBUG, "MRS done\n"); +} + +static void sdram_recover_receive_enable(const struct sysinfo *s) +{ + u32 reg32; + u16 medium, coarse_offset; + u8 pi_tap; + int lane, channel; + + FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) { + medium = 0; + coarse_offset = 0; + reg32 = MCHBAR32(0x400 * channel + 0x248); + reg32 &= ~0xf0000; + reg32 |= s->rcven_t[channel].min_common_coarse << 16; + MCHBAR32(0x400 * channel + 0x248) = reg32; + + FOR_EACH_BYTELANE(lane) { + medium |= s->rcven_t[channel].medium[lane] + << (lane * 2); + coarse_offset |= + (s->rcven_t[channel].coarse_offset[lane] & 0x3) + << (lane * 2); + + pi_tap = MCHBAR8(0x400 * channel + 0x560 + lane * 4); + pi_tap &= ~0x7f; + pi_tap |= s->rcven_t[channel].tap[lane]; + pi_tap |= s->rcven_t[channel].pi[lane] << 4; + MCHBAR8(0x400 * channel + 0x560 + lane * 4) = pi_tap; + } + MCHBAR16(0x400 * channel + 0x58c) = medium; + MCHBAR16(0x400 * channel + 0x5fa) = coarse_offset; + } +} + +static void sdram_program_receive_enable(struct sysinfo *s, int fast_boot) +{ + /* Program Receive Enable Timings */ + if (fast_boot) + sdram_recover_receive_enable(s); + else + rcven(s); +} + +static void set_dradrb(struct sysinfo *s) +{ + u8 map, i, ch, r, rankpop0, rankpop1, lastrank_ch1; + u32 c0dra = 0; + u32 c1dra = 0; + u32 c0drb = 0; + u32 c1drb = 0; + u32 dra; + u32 dra0; + u32 dra1; + u16 totalmemorymb; + u32 dual_channel_size, single_channel_size, single_channel_offset; + u32 size_ch0, size_ch1, size_me; + u8 dratab[2][2][2][4] = { + { + { + {0xff, 0xff, 0xff, 0xff}, + {0xff, 0x00, 0x02, 0xff} + }, + { + {0xff, 0x01, 0xff, 0xff}, + {0xff, 0x03, 0xff, 0xff} + } + }, + { + { + {0xff, 0xff, 0xff, 0xff}, + {0xff, 0x04, 0x06, 0x08} + }, + { + {0xff, 0xff, 0xff, 0xff}, + {0x05, 0x07, 0x09, 0xff} + } + } + }; + + u8 drbtab[10] = {0x04, 0x02, 0x08, 0x04, 0x08, 0x04, 0x10, 0x08, 0x20, 0x10}; + + // DRA + rankpop0 = 0; + rankpop1 = 0; + FOR_EACH_POPULATED_RANK(s->dimms, ch, r) { + if (s->dimms[ch<<1].card_type != RAW_CARD_UNPOPULATED + && (r) < s->dimms[ch<<1].ranks) + i = ch << 1; + else + i = (ch << 1) + 1; + + dra = dratab[s->dimms[i].n_banks] + [s->dimms[i].width] + [s->dimms[i].cols-9] + [s->dimms[i].rows-12]; + if (s->dimms[i].n_banks == N_BANKS_8) + dra |= 0x80; + if (ch == 0) { + c0dra |= dra << (r*8); + rankpop0 |= 1 << r; + } else { + c1dra |= dra << (r*8); + rankpop1 |= 1 << r; + } + } + MCHBAR32(0x208) = c0dra; + MCHBAR32(0x608) = c1dra; + + MCHBAR8_AND_OR(0x262, ~0xf0, (rankpop0 << 4) & 0xf0); + MCHBAR8_AND_OR(0x662, ~0xf0, (rankpop1 << 4) & 0xf0); + + if (s->spd_type == DDR3) { + FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { + /* ZQCAL enable */ + MCHBAR32_OR(0x269 + 0x400 * ch, 1 << 26); + } + } + + if (ONLY_DIMMA_IS_POPULATED(s->dimms, 0) || + ONLY_DIMMB_IS_POPULATED(s->dimms, 0)) + MCHBAR8_OR(0x260, 1); + if (ONLY_DIMMA_IS_POPULATED(s->dimms, 1) || + ONLY_DIMMB_IS_POPULATED(s->dimms, 1)) + MCHBAR8_OR(0x660, 1); + + // DRB + lastrank_ch1 = 0; + FOR_EACH_RANK(ch, r) { + if (ch == 0) { + if (RANK_IS_POPULATED(s->dimms, ch, r)) { + dra0 = (c0dra >> (8*r)) & 0x7f; + c0drb = (u16)(c0drb + drbtab[dra0]); + } + MCHBAR16(0x200 + 2*r) = c0drb; + } else { + if (RANK_IS_POPULATED(s->dimms, ch, r)) { + lastrank_ch1 = r; + dra1 = (c1dra >> (8*r)) & 0x7f; + c1drb = (u16)(c1drb + drbtab[dra1]); + } + MCHBAR16(0x600 + 2*r) = c1drb; + } + } + + s->channel_capacity[0] = c0drb << 6; + s->channel_capacity[1] = c1drb << 6; + + /* + * In stacked mode the last present rank on ch1 needs to have its + * size doubled in c1drbx. All subsequent ranks need the same setting + * according to: "Intel 4 Series Chipset Family Datasheet" + */ + if (s->stacked_mode) { + for (r = lastrank_ch1; r < 4; r++) + MCHBAR16(0x600 + 2*r) = 2 * c1drb; + } + + totalmemorymb = s->channel_capacity[0] + s->channel_capacity[1]; + printk(BIOS_DEBUG, "Total memory: %d + %d = %dMiB\n", + s->channel_capacity[0], s->channel_capacity[1], totalmemorymb); + + /* Populated channel sizes in MiB */ + size_ch0 = s->channel_capacity[0]; + size_ch1 = s->channel_capacity[1]; + size_me = ME_UMA_SIZEMB; + + if (s->stacked_mode) { + MCHBAR8_OR(0x111, STACKED_MEM); + } else { + MCHBAR8_AND(0x111, ~STACKED_MEM); + MCHBAR8_OR(0x111, 1 << 4); + } + + if (s->stacked_mode) { + dual_channel_size = 0; + } else if (size_me == 0) { + dual_channel_size = MIN(size_ch0, size_ch1) * 2; + } else { + if (size_ch0 == 0) { + /* ME needs RAM on CH0 */ + size_me = 0; + /* TOTEST: bailout? */ + } else { + /* Set ME UMA size in MiB */ + MCHBAR16(0x100) = size_me; + /* Set ME UMA Present bit */ + MCHBAR32_OR(0x111, 1); + } + dual_channel_size = MIN(size_ch0 - size_me, size_ch1) * 2; + } + + MCHBAR16(0x104) = dual_channel_size; + single_channel_size = size_ch0 + size_ch1 - dual_channel_size; + MCHBAR16(0x102) = single_channel_size; + + map = 0; + if (size_ch0 == 0) + map = 0; + else if (size_ch1 == 0) + map |= 0x20; + else + map |= 0x40; + + if (dual_channel_size == 0) + map |= 0x18; + /* Enable flex mode, we hardcode this everywhere */ + if (size_me == 0) { + if (!(s->stacked_mode && size_ch0 != 0 && size_ch1 != 0)) { + map |= 0x04; + if (size_ch0 <= size_ch1) + map |= 0x01; + } + } else { + if (s->stacked_mode == 0 && size_ch0 - size_me < size_ch1) + map |= 0x04; + } + + MCHBAR8(0x110) = map; + MCHBAR16(0x10e) = 0; + + /* + * "108h[15:0] Single Channel Offset for Ch0" + * This is the 'limit' of the part on CH0 that cannot be matched + * with memory on CH1. MCHBAR16(0x10a) is where the dual channel + * memory on ch0s end and MCHBAR16(0x108) is the limit of the single + * channel size on ch0. + */ + if (s->stacked_mode && size_ch1 != 0) { + single_channel_offset = 0; + } else if (size_me == 0) { + if (size_ch0 > size_ch1) + single_channel_offset = dual_channel_size / 2 + + single_channel_size; + else + single_channel_offset = dual_channel_size / 2; + } else { + if ((size_ch0 > size_ch1) && ((map & 0x7) == 4)) + single_channel_offset = dual_channel_size / 2 + + single_channel_size; + else + single_channel_offset = dual_channel_size / 2 + + size_me; + } + + MCHBAR16(0x108) = single_channel_offset; + MCHBAR16(0x10a) = dual_channel_size / 2; +} + +static void configure_mmap(struct sysinfo *s) +{ + bool reclaim; + u32 gfxsize, gttsize, tsegsize, mmiosize, tom, tolud, touud; + u32 gfxbase, gttbase, tsegbase, reclaimbase, reclaimlimit; + u32 mmiostart, umasizem; + u16 ggc; + u16 ggc2uma[] = { 0, 1, 4, 8, 16, 32, 48, 64, 128, 256, 96, + 160, 224, 352 }; + u8 ggc2gtt[] = { 0, 1, 0, 2, 0, 0, 0, 0, 0, 2, 3, 4}; + + ggc = pci_read_config16(PCI_DEV(0, 0, 0), 0x52); + gfxsize = ggc2uma[(ggc & 0xf0) >> 4]; + gttsize = ggc2gtt[(ggc & 0xf00) >> 8]; + /* TSEG 2M, This amount can easily be covered by SMRR MTRR's, + which requires to have TSEG_BASE aligned to TSEG_SIZE. */ + tsegsize = 2; + mmiosize = 0x800; // 2GB MMIO + umasizem = gfxsize + gttsize + tsegsize; + mmiostart = 0x1000 - mmiosize + umasizem; + tom = s->channel_capacity[0] + s->channel_capacity[1] - ME_UMA_SIZEMB; + tolud = MIN(mmiostart, tom); + + reclaim = false; + if ((tom - tolud) > 0x40) + reclaim = true; + + if (reclaim) { + tolud = tolud & ~0x3f; + tom = tom & ~0x3f; + reclaimbase = MAX(0x1000, tom); + reclaimlimit = reclaimbase + (MIN(0x1000, tom) - tolud) - 0x40; + } + + touud = tom; + if (reclaim) + touud = reclaimlimit + 0x40; + + gfxbase = tolud - gfxsize; + gttbase = gfxbase - gttsize; + tsegbase = gttbase - tsegsize; + + pci_write_config16(PCI_DEV(0, 0, 0), 0xb0, tolud << 4); + pci_write_config16(PCI_DEV(0, 0, 0), 0xa0, tom >> 6); + if (reclaim) { + pci_write_config16(PCI_DEV(0, 0, 0), 0x98, + (u16)(reclaimbase >> 6)); + pci_write_config16(PCI_DEV(0, 0, 0), 0x9a, + (u16)(reclaimlimit >> 6)); + } + pci_write_config16(PCI_DEV(0, 0, 0), 0xa2, touud); + pci_write_config32(PCI_DEV(0, 0, 0), 0xa4, gfxbase << 20); + pci_write_config32(PCI_DEV(0, 0, 0), 0xa8, gttbase << 20); + /* Enable and set TSEG size to 2M */ + pci_update_config8(PCI_DEV(0, 0, 0), D0F0_ESMRAMC, ~0x07, (1 << 1) | (1 << 0)); + pci_write_config32(PCI_DEV(0, 0, 0), 0xac, tsegbase << 20); +} + +static void set_enhanced_mode(struct sysinfo *s) +{ + u8 ch, reg8; + u32 reg32; + + MCHBAR32(0xfb0) = 0x1000d024; + MCHBAR32(0xfb4) = 0xc842; + MCHBAR32(0xfbc) = 0xf; + MCHBAR32(0xfc4) = 0xfe22244; + MCHBAR8(0x12f) = 0x5c; + MCHBAR8_OR(0xfb0, 1); + if (s->selected_timings.mem_clk <= MEM_CLOCK_800MHz) + MCHBAR8_OR(0x12f, 0x2); + else + MCHBAR8_AND(0x12f, ~0x2); + MCHBAR8_AND_OR(0x6c0, ~0xf0, 0xa0); + MCHBAR32(0xfa8) = 0x30d400; + + FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { + MCHBAR8_OR(0x400*ch + 0x26c, 1); + MCHBAR32(0x400*ch + 0x278) = 0x88141881; + MCHBAR16(0x400*ch + 0x27c) = 0x0041; + MCHBAR8(0x400*ch + 0x292) = 0xf2; + MCHBAR16_OR(0x400*ch + 0x272, 0x100); + MCHBAR8_AND_OR(0x400*ch + 0x243, ~0x2, 1); + MCHBAR32(0x400*ch + 0x288) = 0x8040200; + MCHBAR32(0x400*ch + 0x28c) = 0xff402010; + MCHBAR32(0x400*ch + 0x290) = 0x4f2091c; + } + + reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xf0); + pci_write_config8(PCI_DEV(0, 0, 0), 0xf0, reg8 | 1); + MCHBAR32_AND_OR(0xfa0, ~0x20002, 0x2 | (s->selected_timings.fsb_clk == + FSB_CLOCK_1333MHz ? 0x20000 : 0)); + reg32 = 0x219100c2; + if (s->selected_timings.fsb_clk == FSB_CLOCK_1333MHz) { + reg32 |= 1; + if (s->selected_timings.mem_clk == MEM_CLOCK_1066MHz) + reg32 &= ~0x10000; + } else if (s->selected_timings.fsb_clk == FSB_CLOCK_1066MHz) { + reg32 &= ~0x10000; + } + MCHBAR32_AND_OR(0xfa4, ~0x219100c3, reg32); + reg32 = 0x44a00; + switch (s->selected_timings.fsb_clk) { + case FSB_CLOCK_1333MHz: + reg32 |= 0x62; + break; + case FSB_CLOCK_1066MHz: + reg32 |= 0x5a; + break; + default: + case FSB_CLOCK_800MHz: + reg32 |= 0x53; + break; + } + + MCHBAR32(0x2c) = reg32; + MCHBAR32(0x30) = 0x1f5a86; + MCHBAR32(0x34) = 0x1902810; + MCHBAR32(0x38) = 0xf7000000; + reg32 = 0x23014410; + if (s->selected_timings.fsb_clk > FSB_CLOCK_800MHz) + reg32 = (reg32 & ~0x2000000) | 0x44000000; + MCHBAR32(0x3c) = reg32; + reg32 = 0x8f038000; + if (s->selected_timings.fsb_clk == FSB_CLOCK_1333MHz) + reg32 &= ~0x4000000; + MCHBAR32_AND_OR(0x40, ~0x8f038000, reg32); + reg32 = 0x00013001; + if (s->selected_timings.fsb_clk < FSB_CLOCK_1333MHz) + reg32 |= 0x20000; + MCHBAR32(0x20) = reg32; + pci_write_config8(PCI_DEV(0, 0, 0), 0xf0, reg8 & ~1); +} + +static void power_settings(struct sysinfo *s) +{ + u32 reg1, reg2, reg3, reg4, clkgate, x592; + u8 lane, ch; + u8 twl = 0; + u16 x264, x23c; + + if (s->spd_type == DDR2) { + twl = s->selected_timings.CAS - 1; + x264 = 0x78; + + switch (s->selected_timings.mem_clk) { + default: + case MEM_CLOCK_667MHz: + reg1 = 0x99; + reg2 = 0x1048a9; + clkgate = 0x230000; + x23c = 0x7a89; + break; + case MEM_CLOCK_800MHz: + if (s->selected_timings.CAS == 5) { + reg1 = 0x19a; + reg2 = 0x1048aa; + } else { + reg1 = 0x9a; + reg2 = 0x2158aa; + x264 = 0x89; + } + clkgate = 0x280000; + x23c = 0x7b89; + break; + } + reg3 = 0x232; + reg4 = 0x2864; + } else { /* DDR3 */ + int ddr3_idx = s->selected_timings.mem_clk - MEM_CLOCK_800MHz; + int cas_idx = s->selected_timings.CAS - 5; + + twl = s->selected_timings.mem_clk - MEM_CLOCK_800MHz + 5; + reg1 = ddr3_c2_tab[s->nmode - 1][ddr3_idx][cas_idx][0]; + reg2 = ddr3_c2_tab[s->nmode - 1][ddr3_idx][cas_idx][1]; + reg3 = 0x764; + reg4 = 0x78c8; + x264 = ddr3_c2_x264[ddr3_idx][cas_idx]; + x23c = ddr3_c2_x23c[ddr3_idx][cas_idx]; + switch (s->selected_timings.mem_clk) { + case MEM_CLOCK_800MHz: + default: + clkgate = 0x280000; + break; + case MEM_CLOCK_1066MHz: + clkgate = 0x350000; + break; + case MEM_CLOCK_1333MHz: + clkgate = 0xff0000; + break; + } + } + + if (CHANNEL_IS_POPULATED(s->dimms, 0) && CHANNEL_IS_POPULATED(s->dimms, 1)) + MCHBAR32(0x14) = 0x0010461f; + else + MCHBAR32(0x14) = 0x0010691f; + MCHBAR32(0x18) = 0xdf6437f7; + MCHBAR32(0x1c) = 0x0; + MCHBAR32_AND_OR(0x24, ~0xe0000000, 0x60000000); + MCHBAR32_AND_OR(0x44, ~0x1fef0000, 0x6b0000); + MCHBAR16(0x115) = (u16) reg1; + MCHBAR32_AND_OR(0x117, ~0xffffff, reg2); + MCHBAR8(0x124) = 0x7; + // not sure if dummy reads are needed + MCHBAR16_AND_OR(0x12a, 0, 0x80); + MCHBAR8_AND_OR(0x12c, 0, 0xa0); + MCHBAR16_AND(0x174, ~(1 << 15)); + MCHBAR16_AND_OR(0x188, ~0x1f00, 0x1f00); + MCHBAR8_AND(0x18c, ~0x8); + MCHBAR8_OR(0x192, 1); + MCHBAR8_OR(0x193, 0xf); + MCHBAR16_AND_OR(0x1b4, ~0x480, 0x80); + MCHBAR16_AND_OR(0x210, ~0x1fff, 0x3f); // | clockgatingiii + // non-aligned access: possible bug? + MCHBAR32_AND_OR(0x6d1, ~0xff03ff, 0x100 | clkgate); + MCHBAR8_AND_OR(0x212, ~0x7f, 0x7f); + MCHBAR32_AND_OR(0x2c0, ~0xffff0, 0xcc5f0); + MCHBAR8_AND_OR(0x2c4, ~0x70, 0x70); + // non-aligned access: possible bug? + MCHBAR32_AND_OR(0x2d1, ~0xffffff, 0xff2831); // | clockgatingi + MCHBAR32(0x2d4) = 0x40453600; + MCHBAR32(0x300) = 0xc0b0a08; + MCHBAR32(0x304) = 0x6040201; + MCHBAR32_AND_OR(0x30c, ~0x43c0f, 0x41405); + MCHBAR16(0x610) = reg3; + MCHBAR16(0x612) = reg4; + MCHBAR32_AND_OR(0x62c, ~0xc000000, 0x4000000); + MCHBAR32(0xae4) = 0; + MCHBAR32_AND_OR(0xc00, ~0xf0000, 0x10000); + MCHBAR32(0xf00) = 0x393a3b3c; + MCHBAR32(0xf04) = 0x3d3e3f40; + MCHBAR32(0xf08) = 0x393a3b3c; + MCHBAR32(0xf0c) = 0x3d3e3f40; + MCHBAR32_AND(0xf18, ~0xfff00001); + MCHBAR32(0xf48) = 0xfff0ffe0; + MCHBAR32(0xf4c) = 0xffc0ff00; + MCHBAR32(0xf50) = 0xfc00f000; + MCHBAR32(0xf54) = 0xc0008000; + MCHBAR32_AND_OR(0xf6c, ~0xffff0000, 0xffff0000); + MCHBAR32_AND(0xfac, ~0x80000000); + MCHBAR32_AND(0xfb8, ~0xff000000); + MCHBAR32_AND_OR(0xfbc, ~0x7f800, 0xf000); + MCHBAR32(0x1104) = 0x3003232; + MCHBAR32(0x1108) = 0x74; + if (s->selected_timings.fsb_clk == FSB_CLOCK_800MHz) + MCHBAR32(0x110c) = 0xaa; + else + MCHBAR32(0x110c) = 0x100; + MCHBAR32(0x1110) = 0x10810350 & ~0x78; + MCHBAR32(0x1114) = 0; + x592 = 0xff; + if (pci_read_config8(PCI_DEV(0, 0, 0), 0x8) < 3) + x592 = ~0x4; + + FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { + MCHBAR8(0x400*ch + 0x239) = twl + 15; + MCHBAR16(0x400*ch + 0x23c) = x23c; + MCHBAR32_AND_OR(0x400*ch + 0x248, ~0x706033, 0x406033); + MCHBAR32_AND_OR(0x400*ch + 0x260, ~(1 << 16), 1 << 16); + MCHBAR8(0x400*ch + 0x264) = x264; + MCHBAR8_AND_OR(0x400*ch + 0x592, ~0x3f, 0x3c & x592); + MCHBAR8_AND_OR(0x400*ch + 0x593, ~0x1f, 0x1e); + } + + for (lane = 0; lane < 8; lane++) + MCHBAR8_AND(0x561 + (lane << 2), ~(1 << 3)); +} + +static void software_ddr3_reset(struct sysinfo *s) +{ + printk(BIOS_DEBUG, "Software initiated DDR3 reset.\n"); + MCHBAR8_OR(0x1a8, 0x02); + MCHBAR8_AND(0x5da, ~0x80); + MCHBAR8_AND(0x1a8, ~0x02); + MCHBAR8_AND_OR(0x5da, ~0x03, 1); + udelay(200); + MCHBAR8_AND(0x1a8, ~0x02); + MCHBAR8_OR(0x5da, 0x80); + MCHBAR8_AND(0x5da, ~0x80); + udelay(500); + MCHBAR8_OR(0x5da, 0x03); + MCHBAR8_AND(0x5da, ~0x03); + /* After write leveling the dram needs to be reset and reinitialised */ + jedec_ddr3(s); +} + +void do_raminit(struct sysinfo *s, int fast_boot) +{ + u8 ch; + u8 r, bank; + u32 reg32; + + if (s->boot_path != BOOT_PATH_WARM_RESET) { + // Clear self refresh + MCHBAR32(PMSTS_MCHBAR) = MCHBAR32(PMSTS_MCHBAR) + | PMSTS_BOTH_SELFREFRESH; + + // Clear host clk gate reg + MCHBAR32_OR(0x1c, 0xffffffff); + + // Select type + if (s->spd_type == DDR2) + MCHBAR8_AND(0x1a8, ~0x4); + else + MCHBAR8_OR(0x1a8, 0x4); + + // Set freq + MCHBAR32_AND_OR(0xc00, ~0x70, + (s->selected_timings.mem_clk << 4) | (1 << 10)); + + // Overwrite freq if chipset rejects it + s->selected_timings.mem_clk = (MCHBAR8(0xc00) & 0x70) >> 4; + if (s->selected_timings.mem_clk > (s->max_fsb + 3)) + die("Error: DDR is faster than FSB, halt\n"); + } + + // Program clock crossing + program_crossclock(s); + printk(BIOS_DEBUG, "Done clk crossing\n"); + + if (s->boot_path != BOOT_PATH_WARM_RESET) { + setioclk_dram(s); + printk(BIOS_DEBUG, "Done I/O clk\n"); + } + + // Grant to launch + launch_dram(s); + printk(BIOS_DEBUG, "Done launch\n"); + + // Program DRAM timings + program_timings(s); + printk(BIOS_DEBUG, "Done timings\n"); + + // Program DLL + program_dll(s); + if (!fast_boot) + select_default_dq_dqs_settings(s); + set_all_dq_dqs_dll_settings(s); + + // RCOMP + if (s->boot_path != BOOT_PATH_WARM_RESET) { + prog_rcomp(s); + printk(BIOS_DEBUG, "RCOMP\n"); + } + + // ODT + program_odt(s); + printk(BIOS_DEBUG, "Done ODT\n"); + + // RCOMP update + if (s->boot_path != BOOT_PATH_WARM_RESET) { + while (MCHBAR8(0x130) & 1) + ; + printk(BIOS_DEBUG, "Done RCOMP update\n"); + } + + pre_jedec_memory_map(); + + // IOBUFACT + if (CHANNEL_IS_POPULATED(s->dimms, 0)) { + MCHBAR8_AND_OR(0x5dd, ~0x3f, 0x3f); + MCHBAR8_OR(0x5d8, 0x7); + } + if (CHANNEL_IS_POPULATED(s->dimms, 1)) { + if (pci_read_config8(PCI_DEV(0, 0, 0), 0x8) < 2) { + MCHBAR8_AND_OR(0x5dd, ~0x3f, 0x3f); + MCHBAR8_OR(0x5d8, 1); + } + MCHBAR8_OR(0x9dd, 0x3f); + MCHBAR8_OR(0x9d8, 0x7); + } + + /* DDR3 reset */ + if ((s->spd_type == DDR3) && (s->boot_path != BOOT_PATH_RESUME)) { + printk(BIOS_DEBUG, "DDR3 Reset.\n"); + MCHBAR8_AND(0x1a8, ~0x2); + MCHBAR8_OR(0x5da, 0x80); + udelay(500); + MCHBAR8_AND(0x1a8, ~0x2); + MCHBAR8_AND(0x5da, ~0x80); + udelay(500); + } + + // Pre jedec + MCHBAR8_OR(0x40, 0x2); + FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { + MCHBAR32_OR(0x400*ch + 0x260, 1 << 27); + } + MCHBAR16_OR(0x212, 0xf000); + MCHBAR16_OR(0x212, 0xf00); + printk(BIOS_DEBUG, "Done pre-jedec\n"); + + // JEDEC reset + if (s->boot_path != BOOT_PATH_RESUME) { + if (s->spd_type == DDR2) + jedec_ddr2(s); + else /* DDR3 */ + jedec_ddr3(s); + } + + printk(BIOS_DEBUG, "Done jedec steps\n"); + + if (s->spd_type == DDR3) { + if (!fast_boot) + search_write_leveling(s); + if (s->boot_path == BOOT_PATH_NORMAL) + software_ddr3_reset(s); + } + + // After JEDEC reset + MCHBAR8_AND(0x40, ~0x2); + FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { + reg32 = (2 << 18); + reg32 |= post_jedec_tab[s->selected_timings.fsb_clk] + [s->selected_timings.mem_clk - MEM_CLOCK_667MHz][0] + << 13; + if (s->selected_timings.mem_clk == MEM_CLOCK_667MHz && + s->selected_timings.fsb_clk == FSB_CLOCK_1066MHz && + ch == 1) { + reg32 |= (post_jedec_tab[s->selected_timings.fsb_clk] + [s->selected_timings.mem_clk - MEM_CLOCK_667MHz][1] + - 1) << 8; + } else { + reg32 |= post_jedec_tab[s->selected_timings.fsb_clk] + [s->selected_timings.mem_clk - MEM_CLOCK_667MHz][1] + << 8; + } + MCHBAR32_AND_OR(0x400*ch + 0x274, ~0xfff00, reg32); + MCHBAR8_AND(0x400*ch + 0x274, ~0x80); + MCHBAR8_OR(0x400*ch + 0x26c, 1); + MCHBAR32(0x400*ch + 0x278) = 0x88141881; + MCHBAR16(0x400*ch + 0x27c) = 0x41; + MCHBAR8(0x400*ch + 0x292) = 0xf2; + MCHBAR8_OR(0x400*ch + 0x271, 0xe); + } + MCHBAR8_OR(0x2c4, 0x8); + MCHBAR8_OR(0x2c3, 0x40); + MCHBAR8_OR(0x2c4, 0x4); + + printk(BIOS_DEBUG, "Done post-jedec\n"); + + // Set DDR init complete + FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { + MCHBAR32_OR(0x400*ch + 0x268, 0xc0000000); + } + + // Dummy reads + if (s->boot_path == BOOT_PATH_NORMAL) { + FOR_EACH_POPULATED_RANK(s->dimms, ch, r) { + for (bank = 0; bank < 4; bank++) + read32((u32 *)(test_address(ch, r) | 0x800000 | (bank << 12))); + } + } + printk(BIOS_DEBUG, "Done dummy reads\n"); + + // Receive enable + sdram_program_receive_enable(s, fast_boot); + printk(BIOS_DEBUG, "Done rcven\n"); + + // Finish rcven + FOR_EACH_CHANNEL(ch) { + MCHBAR8_AND(0x400*ch + 0x5d8, ~0xe); + MCHBAR8_OR(0x400*ch + 0x5d8, 0x2); + MCHBAR8_OR(0x400*ch + 0x5d8, 0x4); + MCHBAR8_OR(0x400*ch + 0x5d8, 0x8); + } + MCHBAR8_OR(0x5dc, 0x80); + MCHBAR8_AND(0x5dc, ~0x80); + MCHBAR8_OR(0x5dc, 0x80); + + // XXX tRD + + if (!fast_boot) { + if (s->selected_timings.mem_clk > MEM_CLOCK_667MHz) { + if(do_write_training(s)) + die("DQ write training failed!"); + } + if (do_read_training(s)) + die("DQS read training failed!"); + } + + // DRADRB + set_dradrb(s); + printk(BIOS_DEBUG, "Done DRADRB\n"); + + // Memory map + configure_mmap(s); + printk(BIOS_DEBUG, "Done memory map\n"); + + // Enhanced mode + set_enhanced_mode(s); + printk(BIOS_DEBUG, "Done enhanced mode\n"); + + // Periodic RCOMP + MCHBAR16_AND_OR(0x160, ~0xfff, 0x999); + MCHBAR16_OR(0x1b4, 0x3000); + MCHBAR8_OR(0x130, 0x82); + printk(BIOS_DEBUG, "Done PRCOMP\n"); + + // Power settings + power_settings(s); + printk(BIOS_DEBUG, "Done power settings\n"); + + // ME related + /* + * FIXME: This locks some registers like bit1 of GGC + * and is only needed in case of ME being used. + */ + if (ME_UMA_SIZEMB != 0) { + if (RANK_IS_POPULATED(s->dimms, 0, 0) + || RANK_IS_POPULATED(s->dimms, 1, 0)) + MCHBAR8_OR(0xa2f, 1 << 0); + if (RANK_IS_POPULATED(s->dimms, 0, 1) + || RANK_IS_POPULATED(s->dimms, 1, 1)) + MCHBAR8_OR(0xa2f, 1 << 1); + MCHBAR32_OR(0xa30, 1 << 26); + } + + printk(BIOS_DEBUG, "Done raminit\n"); +} diff --git a/src/northbridge/intel/x3x/raminit_tables.c b/src/northbridge/intel/x3x/raminit_tables.c new file mode 100644 index 0000000..3612b2e --- /dev/null +++ b/src/northbridge/intel/x3x/raminit_tables.c @@ -0,0 +1,373 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <stdint.h> +#include "x3x.h" + +const struct dll_setting default_ddr2_667_ctrl[7] = { + /* tap pi db delay coarse*/ + {13, 0, 1, 0, 0, 0}, /* clkset0 */ + {4, 1, 0, 0, 0, 0}, /* ctrl0 */ + {13, 0, 1, 0, 0, 0}, /* clkset1 */ + {4, 5, 0, 0, 0, 0}, /* cmd */ + {4, 1, 0, 0, 0, 0}, /* ctrl1 */ + {4, 1, 0, 0, 0, 0}, /* ctrl2 */ + {4, 1, 0, 0, 0, 0}, /* ctrl3 */ +}; + +const struct dll_setting default_ddr2_667_dqs[TOTAL_BYTELANES] = { + {1, 5, 1, 1, 1, 0}, + {1, 6, 1, 1, 1, 0}, + {2, 0, 1, 1, 1, 0}, + {2, 1, 1, 1, 1, 0}, + {2, 1, 1, 1, 1, 0}, + {14, 6, 1, 0, 0, 0}, + {14, 3, 1, 0, 0, 0}, + {14, 0, 1, 0, 0, 0}, +}; + +const struct dll_setting default_ddr2_667_dq[TOTAL_BYTELANES] = { + {9, 0, 0, 0, 1, 0}, + {9, 1, 0, 0, 1, 0}, + {9, 2, 0, 0, 1, 0}, + {9, 2, 0, 0, 1, 0}, + {9, 1, 0, 0, 1, 0}, + {6, 4, 0, 0, 1, 0}, + {6, 2, 0, 0, 1, 0}, + {5, 4, 0, 0, 1, 0} +}; + +const struct dll_setting default_ddr2_800_ctrl[7] = { + /* tap pi db delay coarse */ + {11, 5, 1, 0, 0, 0}, + {0, 5, 1, 1, 0, 0}, + {11, 5, 1, 0, 0, 0}, + {1, 4, 1, 1, 0, 0}, + {0, 5, 1, 1, 0, 0}, + {0, 5, 1, 1, 0, 0}, + {0, 5, 1, 1, 0, 0}, +}; + +const struct dll_setting default_ddr2_800_dqs[TOTAL_BYTELANES] = { + {2, 5, 1, 1, 1, 0}, + {2, 6, 1, 1, 1, 0}, + {3, 0, 1, 1, 1, 0}, + {3, 0, 1, 1, 1, 0}, + {3, 3, 1, 1, 1, 0}, + {2, 0, 1, 1, 1, 0}, + {1, 3, 1, 1, 1, 0}, + {0, 3, 1, 1, 1, 0}, +}; + +const struct dll_setting default_ddr2_800_dq[TOTAL_BYTELANES] = { + {9, 3, 0, 0, 1, 0}, + {9, 4, 0, 0, 1, 0}, + {9, 5, 0, 0, 1, 0}, + {9, 6, 0, 0, 1, 0}, + {10, 0, 0, 0, 1, 0}, + {8, 1, 0, 0, 1, 0}, + {7, 5, 0, 0, 1, 0}, + {6, 2, 0, 0, 1, 0} +}; + +const struct dll_setting default_ddr3_800_ctrl[2][7] = { + { /* 1N */ + /* tap pi db(2) delay coarse */ + {8, 2, 0, 0, 0, 0}, + {8, 4, 0, 0, 0, 0}, + {9, 5, 0, 0, 0, 0}, + {6, 1, 0, 0, 0, 0}, + {8, 4, 0, 0, 0, 0}, + {10, 0, 0, 0, 0, 0}, + {10, 0, 0, 0, 0, 0}, }, + { /* 2N */ + {2, 2, 1, 1, 0, 0}, + {2, 4, 1, 1, 0, 0}, + {3, 5, 0, 0, 0, 0}, + {3, 2, 1, 1, 0, 0}, + {2, 4, 1, 1, 0, 0}, + {3, 6, 0, 0, 0, 0}, + {3, 6, 0, 0, 0, 0}, } +}; + +const struct dll_setting default_ddr3_800_dqs[2][TOTAL_BYTELANES] = { + { /* 1N */ + {12, 0, 1, 0, 0, 0}, + {1, 1, 1, 1, 1, 0}, + {2, 4, 1, 1, 1, 0}, + {3, 5, 0, 0, 1, 0}, + {4, 3, 0, 0, 1, 0}, + {5, 2, 0, 0, 1, 0}, + {6, 1, 0, 0, 1, 0}, + {6, 4, 0, 0, 1, 0}, }, + { /* 2N */ + {5, 6, 0, 0, 0, 0}, + {8, 0, 0, 0, 0, 0}, + {9, 4, 0, 0, 0, 0}, + {10, 4, 1, 0, 0, 0}, + {11, 3, 1, 0, 0, 0}, + {12, 1, 1, 0, 0, 0}, + {0, 1, 1, 1, 1, 0}, + {0, 3, 1, 1, 1, 0}, } +}; + +const struct dll_setting default_ddr3_800_dq[2][TOTAL_BYTELANES] = { + { /* 1N */ + {4, 1, 0, 0, 1, 0}, + {6, 4, 0, 0, 1, 0}, + {8, 1, 0, 0, 1, 0}, + {8, 6, 0, 0, 1, 0}, + {9, 5, 0, 0, 1, 0}, + {10, 2, 0, 0, 1, 0}, + {10, 6, 1, 0, 1, 0}, + {11, 4, 1, 0, 1, 0} }, + { /* 2N */ + {11, 0, 1, 0, 0, 0}, + {0, 3, 1, 1, 1, 0}, + {2, 1, 1, 1, 1, 0}, + {2, 5, 1, 1, 1, 0}, + {3, 5, 0, 0, 1, 0}, + {4, 2, 0, 0, 1, 0}, + {4, 6, 0, 0, 1, 0}, + {5, 4, 0, 0, 1, 0}, } +}; + +const struct dll_setting default_ddr3_1067_ctrl[2][7] = { + { /* 1N */ + {8, 5, 0, 0, 0, 0}, + {7, 6, 0, 0, 0, 0}, + {10, 2, 1, 0, 0, 0}, + {4, 4, 0, 0, 0, 0}, + {7, 6, 0, 0, 0, 0}, + {9, 2, 1, 0, 0, 0}, + {9, 2, 1, 0, 0, 0}, }, + { /* 2N */ + {1, 5, 1, 1, 0, 0}, + {0, 6, 1, 1, 0, 0}, + {3, 2, 0, 0, 0, 0}, + {2, 6, 1, 1, 0, 0}, + {0, 6, 1, 1, 0, 0}, + {2, 2, 1, 1, 0, 0}, + {2, 2, 1, 1, 0, 0}, } +}; + +const struct dll_setting default_ddr3_1067_dqs[2][TOTAL_BYTELANES] = { + { /* 1N */ + {2, 5, 1, 1, 1, 0}, + {5, 1, 0, 0, 1, 0}, + {6, 6, 0, 0, 1, 0}, + {8, 0, 0, 0, 1, 0}, + {8, 6, 0, 0, 1, 0}, + {9, 6, 1, 0, 1, 0}, + {10, 6, 1, 0, 1, 0}, + {0, 1, 1, 1, 0, 1}, }, + { /* 2N */ + {6, 4, 0, 0, 0, 0}, + {9, 1, 1, 0, 0, 0}, + {10, 6, 1, 0, 0, 0}, + {1, 0, 1, 1, 1, 0}, + {1, 6, 1, 1, 1, 0}, + {2, 5, 1, 1, 1, 0}, + {3, 5, 0, 0, 1, 0}, + {4, 1, 0, 0, 1, 0}, + } +}; + +const struct dll_setting default_ddr3_1067_dq[2][TOTAL_BYTELANES] = { + { /* 1N */ + {6, 5, 0, 0, 1, 0}, + {9, 3, 1, 0, 1, 0}, + {0, 2, 1, 1, 0, 1}, + {1, 0, 1, 1, 0, 1}, + {2, 0, 1, 1, 0, 1}, + {2, 5, 1, 1, 0, 1}, + {3, 2, 0, 0, 0, 1}, + {4, 1, 0, 0, 0, 1}, }, + { /* 2N */ + {10, 5, 1, 0, 0, 0}, + {2, 3, 1, 1, 1, 0}, + {4, 1, 0, 0, 1, 0}, + {5, 0, 0, 0, 1, 0}, + {6, 0, 0, 0, 1, 0}, + {6, 5, 0, 0, 1, 0}, + {7, 2, 0, 0, 1, 0}, + {8, 1, 0, 0, 1, 0}, + } +}; + +const struct dll_setting default_ddr3_1333_ctrl[2][7] = { + { /* 1N */ + {8, 5, 0, 0, 0, 0}, + {9, 0, 1, 0, 0, 0}, + {10, 2, 1, 0, 0, 0}, + {0, 0, 1, 1, 0, 0}, + {9, 0, 1, 0, 0, 0}, + {10, 4, 1, 0, 0, 0}, + {10, 4, 1, 0, 0, 0}, }, + { /* 2N */ + {1, 6, 1, 1, 0, 0}, + {2, 2, 1, 1, 0, 0}, + {4, 2, 0, 0, 0, 0}, + {3, 1, 1, 1, 0, 0}, + {2, 2, 1, 1, 0, 0}, + {4, 5, 0, 0, 0, 0}, + {4, 5, 0, 0, 0, 0}, } +}; + +const struct dll_setting default_ddr3_1333_dqs[2][TOTAL_BYTELANES] = { + { /* 1N */ + {2, 4, 1, 1, 1, 0}, + {5, 1, 0, 0, 1, 0}, + {6, 6, 0, 0, 1, 0}, + {8, 0, 0, 0, 1, 0}, + {8, 6, 0, 0, 1, 0}, + {9, 5, 1, 0, 1, 0}, + {10, 6, 1, 0, 1, 0}, + {0, 1, 1, 1, 0, 1}, }, + { /* 2N */ + {10, 4, 0, 0, 0, 0}, + {0, 3, 1, 1, 1, 0}, + {3, 2, 1, 1, 1, 0}, + {5, 0, 0, 0, 1, 0}, + {6, 1, 0, 0, 1, 0}, + {7, 4, 0, 0, 1, 0}, + {9, 2, 0, 0, 1, 0}, + {9, 6, 0, 0, 1, 0}, } +}; + +const struct dll_setting default_ddr3_1333_dq[2][TOTAL_BYTELANES] = { + { /* 1N */ + {6, 5, 0, 0, 1, 0}, + {9, 3, 1, 0, 1, 0}, + {0, 2, 1, 1, 0, 1}, + {1, 0, 1, 1, 0, 1}, + {2, 0, 1, 1, 0, 1}, + {2, 5, 1, 1, 0, 1}, + {3, 2, 0, 0, 0, 1}, + {4, 1, 0, 0, 0, 1}, }, + { /* 2N */ + {1, 3, 1, 1, 1, 0}, + {5, 6, 0, 0, 1, 0}, + {8, 5, 0, 0, 1, 0}, + {10, 2, 0, 0, 1, 0}, + {11, 1, 0, 0, 1, 0}, + {12, 3, 1, 0, 1, 0}, + {13, 6, 1, 0, 1, 0}, + {0, 3, 1, 1, 0, 1}, } +}; + +const u8 ddr3_emrs1_rtt_nom_config[16][4] = { /* [Config][Rank] */ + {0x00, 0x00, 0x00, 0x00}, /* NC_NC */ + {0x11, 0x00, 0x00, 0x00}, /* 8S_NC */ + {0x11, 0x11, 0x00, 0x00}, /* 8D_NC */ + {0x11, 0x00, 0x00, 0x00}, /* 16S_NC */ + {0x00, 0x00, 0x11, 0x00}, /* NC_8S */ + {0x81, 0x00, 0x81, 0x00}, /* 8S_8S */ + {0x81, 0x81, 0x81, 0x00}, /* 8D_8S */ + {0x81, 0x00, 0x81, 0x00}, /* 16S_8S */ + {0x00, 0x00, 0x11, 0x11}, /* NC_8D */ + {0x81, 0x00, 0x81, 0x81}, /* 8S_8D */ + {0x81, 0x81, 0x81, 0x81}, /* 8D_8D */ + {0x81, 0x00, 0x81, 0x81}, /* 16S_8D */ + {0x00, 0x00, 0x11, 0x00}, /* NC_16S */ + {0x81, 0x00, 0x81, 0x00}, /* 8S_16S */ + {0x81, 0x81, 0x81, 0x00}, /* 8D_16S */ + {0x81, 0x00, 0x81, 0x00}, /* 16S_16S */ +}; + +const u8 post_jedec_tab[3][4][2]= /* [FSB][DDR freq][17:13 or 12:8] */ +{ /* FSB DDR */ + {{0x3, 0x5}, /* 800 667 */ + {0x3, 0x4}, /* 800 800 */ + }, + {{0x4, 0x8}, /* 1067 667 */ + {0x4, 0x6}, /* 1067 800 */ + {0x3, 0x5}, /* 1067 1066 */ + }, + {{0x5, 0x9}, /* 1333 667 */ + {0x4, 0x7}, /* 1333 800 */ + {0x4, 0x7}, /* 1333 1066 */ + {0x4, 0x7} /* 1333 1333 */ + }, +}; + + +const u32 ddr3_c2_tab[2][3][6][2] = { /* [n-mode][ddr3 freq][CAS][reg] */ + /* 115h[15:0] 117h[23:0] */ + { /* 1N mode */ + { /* DDR3 800MHz */ + {0x0189, 0x000aaa}, /* CAS = 5 */ + {0x0189, 0x101aaa}, /* CAS = 6 */ + }, + { /* DDR3 1067MHz */ + {0x0000, 0x000000}, /* CAS = 5 - Not supported */ + {0x0089, 0x000bbb}, /* CAS = 6 */ + {0x0099, 0x101bbb}, /* CAS = 7 */ + {0x0099, 0x202bbb} /* CAS = 8 */ + },{ /* DDR3 1333 */ + {0x0000, 0x000000}, /* CAS = 5 - Not supported */ + {0x0000, 0x000000}, /* CAS = 6 - Not supported */ + {0x0000, 0x000000}, /* CAS = 7 - Not supported */ + {0x129a, 0x0078dc}, /* CAS = 8 */ + {0x028a, 0x0078dc}, /* CAS = 9 */ + {0x028a, 0x1088dc}, /* CAS = 10 */ + }, + }, + { /* 2N mode */ + { /* DDR3 800MHz */ + {0x0189, 0x000aaa}, /* CAS = 5 */ + {0x0189, 0x101aaa}, /* CAS = 6 */ + {0x0000, 0x000000}, /* CAS = 7 - Not supported */ + {0x0000, 0x000000} /* CAS = 8 - Not supported */ + }, + { /* DDR3 1067 */ + {0x0000, 0x000000}, /* CAS = 5 - Not supported */ + {0x0089, 0x000bbb}, /* CAS = 6 */ + {0x0099, 0x101bbb}, /* CAS = 7 */ + {0x0099, 0x202bbb} /* CAS = 8 */ + },{ /* DDR3 1333MHz */ + {0x0000, 0x000000}, /* CAS = 5 - Not supported */ + {0x0000, 0x000000}, /* CAS = 6 - Not supported */ + {0x0000, 0x000000}, /* CAS = 7 - Not supported */ + {0x019a, 0x0078dc}, /* CAS = 8 */ + {0x019a, 0x1088dc}, /* CAS = 9 */ + {0x019a, 0x2098dc}, /* CAS = 10 */ + }, + } +}; + +const u8 ddr3_c2_x264[3][6] = { /* [freq][cas] */ + /* DDR3 800MHz */ + {0x78, /* CAS = 5 */ + 0x89}, /* CAS = 6 */ + /* DDR3 1066 */ + {0x00, /* CAS = 5 - Not supported */ + 0xff, /* CAS = 6 */ + 0x8a, /* CAS = 7 */ + 0x9a}, /* CAS = 8 */ + /* DDR3 1333 */ + {0x00, /* CAS = 5 - Not supported */ + 0x00, /* CAS = 6 - Not supported */ + 0xff, /* CAS = 7 - Not supported */ + 0xff, /* CAS = 8 */ + 0xff, /* CAS = 9 */ + 0xff}, /* CAS = 10 */ +}; + +const u16 ddr3_c2_x23c[3][6]={ /* [freq][cas] */ + /* DDR3 800MHz */ + {0x9bbb, /* CAS = 5 */ + 0x8bbb}, /* CAS = 6 */ + /* DDR3 1066MHz */ + {0x0000, /* CAS = 5 - Not supported */ + 0x9baa, /* CAS = 6 */ + 0x8caa, /* CAS = 7 */ + 0x7daa}, /* CAS = 8 */ + + /* DDR3 1333MHz */ + {0x0000, /* CAS = 5 - Not supported */ + 0x0000, /* CAS = 6 - Not supported */ + 0x0000, /* CAS = 7 - Not supported */ + 0xaecb, /* CAS = 8 */ + 0x9fcb, /* CAS = 9 */ + 0x8fcb}, /* CAS = 10 */ +}; diff --git a/src/northbridge/intel/x3x/rcven.c b/src/northbridge/intel/x3x/rcven.c new file mode 100644 index 0000000..a24d0f0 --- /dev/null +++ b/src/northbridge/intel/x3x/rcven.c @@ -0,0 +1,376 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <device/mmio.h> +#include <console/console.h> +#include <delay.h> +#include "iomap.h" +#include "x3x.h" + +#define MAX_COARSE 15 +#define DQS_HIGH 1 +#define DQS_LOW 0 + +#define RESET_CNTL(channel) (0x5d8 + channel * 0x400) + +struct rec_timing { + u8 medium; + u8 coarse; + u8 pi; + u8 tap; +}; + +static inline void barrier(void) +{ + asm volatile("mfence":::); +} + +static u8 sampledqs(u32 addr, u8 lane, u8 channel) +{ + u32 sample_offset = 0x400 * channel + 0x561 + lane * 4; + + /* Reset the DQS probe */ + MCHBAR8(RESET_CNTL(channel)) &= ~0x2; + udelay(2); + MCHBAR8(RESET_CNTL(channel)) |= 0x2; + udelay(2); + barrier(); + /* Read strobe */ + read32((u32 *)addr); + barrier(); + return (MCHBAR8(sample_offset) >> 6) & 1; +} + +static void program_timing(const struct rec_timing *timing, u8 channel, + u8 lane) +{ + u32 reg32; + u16 reg16; + u8 reg8; + + printk(RAM_SPEW, " Programming timings:" + "Coarse: %d, Medium: %d, TAP: %d, PI: %d\n", + timing->coarse, timing->medium, timing->tap, timing->pi); + + reg32 = MCHBAR32(0x400 * channel + 0x248); + reg32 &= ~0xf0000; + reg32 |= timing->coarse << 16; + MCHBAR32(0x400 * channel + 0x248) = reg32; + + reg16 = MCHBAR16(0x400 * channel + 0x58c); + reg16 &= ~(3 << (lane * 2)); + reg16 |= timing->medium << (lane * 2); + MCHBAR16(0x400 * channel + 0x58c) = reg16; + + reg8 = MCHBAR8(0x400 * channel + 0x560 + lane * 4); + reg8 &= ~0x7f; + reg8 |= timing->tap; + reg8 |= timing->pi << 4; + MCHBAR8(0x400 * channel + 0x560 + lane * 4) = reg8; +} + +static int increase_medium(struct rec_timing *timing) +{ + if (timing->medium < 3) { + timing->medium++; + } else if (timing->coarse < MAX_COARSE) { + timing->medium = 0; + timing->coarse++; + } else { + printk(BIOS_ERR, "Cannot increase medium any further.\n"); + return -1; + } + return 0; +} + +static int decrease_medium(struct rec_timing *timing) +{ + if (timing->medium > 0) { + timing->medium--; + } else if (timing->coarse > 0) { + timing->medium = 3; + timing->coarse--; + } else { + printk(BIOS_ERR, "Cannot lower medium any further.\n"); + return -1; + } + return 0; +} + +static int increase_tap(struct rec_timing *timing) +{ + if (timing->tap == 14) { + if (increase_medium(timing)) + return -1; + timing->tap = 0; + } else { + timing->tap++; + } + return 0; +} + +static int decrease_tap(struct rec_timing *timing) +{ + if (timing->tap > 0) { + timing->tap--; + } else { + if (decrease_medium(timing)) + return -1; + timing->tap = 14; + } + return 0; +} + +static int decr_coarse_low(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + printk(RAM_DEBUG, + " Decreasing coarse until high to low transition is found\n"); + while (sampledqs(addr, lane, channel) != DQS_LOW) { + if (timing->coarse == 0) { + printk(BIOS_CRIT, + "Couldn't find DQS-high 0 indicator, halt\n"); + return -1; + } + timing->coarse--; + program_timing(timing, channel, lane); + } + printk(RAM_DEBUG, " DQS low at coarse=%d medium=%d\n", + timing->coarse, timing->medium); + return 0; +} + +static int fine_search_dqs_high(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + printk(RAM_DEBUG, + " Increasing TAP until low to high transition is found\n"); + /* + * We use a do while loop since it happens that the strobe read + * is inconsistent, with the strobe already high. The current + * code flow results in failure later when finding the preamble, + * at which DQS needs to be high is often not the case if TAP was + * not increased at least once here. Work around this by incrementing + * TAP at least once to guarantee searching for preamble start at + * DQS high. + * This seems to be the result of hysteresis on some settings, where + * the DQS probe is influenced by its previous value. + */ + if (sampledqs(addr, lane, channel) == DQS_HIGH) { + printk(BIOS_WARNING, + "DQS already HIGH... DQS probe is inconsistent!\n" + "Continuing....\n"); + } + do { + if (increase_tap(timing)) { + printk(BIOS_CRIT, + "Could not find DQS-high on fine search.\n"); + return -1; + } + program_timing(timing, channel, lane); + } while (sampledqs(addr, lane, channel) != DQS_HIGH); + + printk(RAM_DEBUG, " DQS high at coarse=%d medium=%d tap:%d\n", + timing->coarse, timing->medium, timing->tap); + return 0; +} + +static int find_dqs_low(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + /* Look for DQS low, using quarter steps. */ + printk(RAM_DEBUG, " Increasing medium until DQS LOW is found\n"); + while (sampledqs(addr, lane, channel) != DQS_LOW) { + if (increase_medium(timing)) { + printk(BIOS_CRIT, + "Coarse > 15: DQS tuning failed, halt\n"); + return -1; + } + program_timing(timing, channel, lane); + } + printk(RAM_DEBUG, " DQS low at coarse=%d medium=%d\n", + timing->coarse, timing->medium); + return 0; +} +static int find_dqs_high(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + /* Look for DQS high, using quarter steps. */ + printk(RAM_DEBUG, " Increasing medium until DQS HIGH is found\n"); + while (sampledqs(addr, lane, channel) != DQS_HIGH) { + if (increase_medium(timing)) { + printk(BIOS_CRIT, + "Coarse > 16: DQS tuning failed, halt\n"); + return -1; + } + program_timing(timing, channel, lane); + } + printk(RAM_DEBUG, " DQS high at coarse=%d medium=%d\n", + timing->coarse, timing->medium); + return 0; +} + +static int find_dqs_edge_lowhigh(u8 channel, u8 lane, + u32 addr, struct rec_timing *timing) +{ + /* Medium search for DQS high. */ + if (find_dqs_high(channel, lane, addr, timing)) + return -1; + + /* Go back and perform finer search. */ + if (decrease_medium(timing)) + return -1; + program_timing(timing, channel, lane); + if (fine_search_dqs_high(channel, lane, addr, timing) < 0) + return -1; + + return 0; +} + +static int find_preamble(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + /* Add a quarter step */ + if (increase_medium(timing)) + return -1; + program_timing(timing, channel, lane); + /* Verify we are at high */ + if (sampledqs(addr, lane, channel) != DQS_HIGH) { + printk(BIOS_CRIT, "Not at DQS high, d'oh\n"); + return -1; + } + + /* Decrease coarse until LOW is found */ + if (decr_coarse_low(channel, lane, addr, timing)) + return -1; + return 0; +} + +static int calibrate_receive_enable(u8 channel, u8 lane, + u32 addr, struct rec_timing *timing) +{ + program_timing(timing, channel, lane); + /* Set receive enable bit */ + MCHBAR16(0x400 * channel + 0x588) = (MCHBAR16(0x400 * channel + 0x588) + & ~(3 << (lane * 2))) | (1 << (lane * 2)); + + if (find_dqs_low(channel, lane, addr, timing)) + return -1; + + /* Advance a little further. */ + if (increase_medium(timing)) { + /* A finer search could be implemented */ + printk(BIOS_WARNING, "Cannot increase medium further"); + return -1; + } + program_timing(timing, channel, lane); + + if (find_dqs_edge_lowhigh(channel, lane, addr, timing)) + return -1; + + /* Go back on fine search */ + if (decrease_tap(timing)) + return -1; + timing->pi = 3; + program_timing(timing, channel, lane); + + if (find_preamble(channel, lane, addr, timing)) + return -1; + + if (find_dqs_edge_lowhigh(channel, lane, addr, timing)) + return -1; + if (decrease_tap(timing)) + return -1; + timing->pi = 7; + program_timing(timing, channel, lane); + + /* Unset receive enable bit */ + MCHBAR16(0x400 * channel + 0x588) = MCHBAR16(0x400 * channel + 0x588) & + ~(3 << (lane * 2)); + return 0; +} + +void rcven(struct sysinfo *s) +{ + int rank; + u8 channel, lane, reg8; + /* + * Using the macros below the compiler warns about this possibly being + * unitialised. + */ + u32 addr = 0; + struct rec_timing timing[TOTAL_BYTELANES]; + u8 mincoarse; + + printk(BIOS_DEBUG, "Starting DQS receiver enable calibration\n"); + + MCHBAR8(0x5d8) = MCHBAR8(0x5d8) & ~0xc; + MCHBAR8(0x9d8) = MCHBAR8(0x9d8) & ~0xc; + MCHBAR8(0x5dc) = MCHBAR8(0x5dc) & ~0x80; + FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) { + mincoarse = 0xff; + /* + * Receive enable calibration happens on the first populated + * rank on each channel. + */ + FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, channel, rank) { + addr = test_address(channel, rank); + break; + } + FOR_EACH_BYTELANE(lane) { + printk(BIOS_DEBUG, "Channel %d, Lane %d addr=0x%08x\n", + channel, lane, addr); + timing[lane].coarse = (s->selected_timings.CAS + 1); + switch (lane) { + default: + case 0: + case 1: + timing[lane].medium = 0; + break; + case 2: + case 3: + timing[lane].medium = 1; + break; + case 4: + case 5: + timing[lane].medium = 2; + break; + case 6: + case 7: + timing[lane].medium = 3; + break; + } + timing[lane].tap = 0; + timing[lane].pi = 0; + + if (calibrate_receive_enable(channel, lane, addr, + &timing[lane])) + die("Receive enable calibration failed\n"); + if (mincoarse > timing[lane].coarse) + mincoarse = timing[lane].coarse; + } + printk(BIOS_DEBUG, "Found min coarse value = %d\n", mincoarse); + s->rcven_t[channel].min_common_coarse = mincoarse; + printk(BIOS_DEBUG, "Receive enable, final timings:\n"); + /* Normalise coarse */ + FOR_EACH_BYTELANE(lane) { + if (timing[lane].coarse == 0) + reg8 = 0; + else + reg8 = timing[lane].coarse - mincoarse; + printk(BIOS_DEBUG, "ch %d lane %d: coarse offset: %d;" + "medium: %d; tap: %d\n", + channel, lane, reg8, timing[lane].medium, + timing[lane].tap); + s->rcven_t[channel].coarse_offset[lane] = reg8; + s->rcven_t[channel].medium[lane] = timing[lane].medium; + s->rcven_t[channel].tap[lane] = timing[lane].tap; + s->rcven_t[channel].pi[lane] = timing[lane].pi; + MCHBAR16(0x400 * channel + 0x5fa) = + (MCHBAR16(0x400 * channel + 0x5fa) & + ~(3 << (lane * 2))) | (reg8 << (lane * 2)); + } + /* simply use timing[0] to program mincoarse */ + timing[0].coarse = mincoarse; + program_timing(&timing[0], channel, 0); + } +} diff --git a/src/northbridge/intel/x3x/romstage.c b/src/northbridge/intel/x3x/romstage.c new file mode 100644 index 0000000..ebf1111 --- /dev/null +++ b/src/northbridge/intel/x3x/romstage.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <console/console.h> +#include <southbridge/intel/common/pmclib.h> +#include <northbridge/intel/x3x/x3x.h> +#include <arch/romstage.h> + +#if CONFIG(SOUTHBRIDGE_INTEL_I82801GX) +#include <southbridge/intel/i82801gx/i82801gx.h> +#elif CONFIG(SOUTHBRIDGE_INTEL_I82801IX) +#include <southbridge/intel/i82801ix/i82801ix.h> +#elif CONFIG(SOUTHBRIDGE_INTEL_I82801JX) +#include <southbridge/intel/i82801jx/i82801jx.h> +#endif + +__weak void mb_pre_raminit_setup(int s3_resume) +{ +} + +void mainboard_romstage_entry(void) +{ + u8 spd_addr_map[4] = {}; + u8 boot_path = 0; + u8 s3_resume; + +#if CONFIG(SOUTHBRIDGE_INTEL_I82801JX) + i82801jx_early_init(); +#elif CONFIG(SOUTHBRIDGE_INTEL_I82801GX) + i82801gx_early_init(); +#elif CONFIG(SOUTHBRIDGE_INTEL_I82801IX) + i82801ix_early_init(); +#endif + + x3x_early_init(); + + s3_resume = southbridge_detect_s3_resume(); + mb_pre_raminit_setup(s3_resume); + + if (s3_resume) + boot_path = BOOT_PATH_RESUME; + if (MCHBAR32(PMSTS_MCHBAR) & PMSTS_WARM_RESET) + boot_path = BOOT_PATH_WARM_RESET; + + mb_get_spd_map(spd_addr_map); + sdram_initialize(boot_path, spd_addr_map); + + x3x_late_init(s3_resume); + + printk(BIOS_DEBUG, "x3x late init complete\n"); +} diff --git a/src/northbridge/intel/x3x/x3x.h b/src/northbridge/intel/x3x/x3x.h new file mode 100644 index 0000000..45b893e --- /dev/null +++ b/src/northbridge/intel/x3x/x3x.h @@ -0,0 +1,408 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __NORTHBRIDGE_INTEL_X3X_H__ +#define __NORTHBRIDGE_INTEL_X3X_H__ + +#include <stdint.h> +#include "iomap.h" + +/* + * D0:F0 + */ +#define D0F0_EPBAR_LO 0x40 +#define D0F0_EPBAR_HI 0x44 +#define D0F0_MCHBAR_LO 0x48 +#define D0F0_MCHBAR_HI 0x4c +#define D0F0_GGC 0x52 +#define D0F0_DEVEN 0x54 +#define D0EN (1 << 0) +#define D1EN (1 << 1) +#define IGD0EN (1 << 3) +#define IGD1EN (1 << 4) +#define D3F0EN (1 << 6) +#define D3F1EN (1 << 7) +#define D3F2EN (1 << 8) +#define D3F3EN (1 << 9) +#define PEG1EN (1 << 13) +#define BOARD_DEVEN (D0EN | D1EN | IGD0EN | IGD1EN | PEG1EN) +#define D0F0_PCIEXBAR_LO 0x60 +#define D0F0_PCIEXBAR_HI 0x64 +#define D0F0_DMIBAR_LO 0x68 +#define D0F0_DMIBAR_HI 0x6c +#define D0F0_PAM(x) (0x90+(x)) /* 0-6*/ +#define D0F0_REMAPBASE 0x98 +#define D0F0_REMAPLIMIT 0x9a +#define D0F0_SMRAM 0x9d +#define D0F0_ESMRAMC 0x9e +#define D0F0_TOM 0xa0 +#define D0F0_TOUUD 0xa2 +#define D0F0_TOLUD 0xb0 +#define D0F0_GBSM 0xa4 +#define D0F0_BGSM 0xa8 +#define D0F0_TSEG 0xac +#define D0F0_SKPD 0xdc /* Scratchpad Data */ +#define D0F0_CAPID0 0xe0 + +/* + * D1:F0 PEG + */ +#define PEG_CAP 0xa2 +#define SLOTCAP 0xb4 +#define PEGLC 0xec +#define D1F0_VCCAP 0x104 +#define D1F0_VC0RCTL 0x114 + +/* + * Graphics frequencies + */ +#define GCFGC_PCIDEV PCI_DEV(0, 2, 0) +#define GCFGC_OFFSET 0xf0 +#define GCFGC_CR_SHIFT 0 +#define GCFGC_CR_MASK (0xf << GCFGC_CR_SHIFT) +#define GCFGC_CS_SHIFT 8 +#define GCFGC_CS_MASK (0xf << GCFGC_CS_SHIFT) +#define GCFGC_CD_SHIFT 12 +#define GCFGC_CD_MASK (0x1 << GCFGC_CD_SHIFT) +#define GCFGC_UPDATE_SHIFT 5 +#define GCFGC_UPDATE (0x1 << GCFGC_UPDATE_SHIFT) + +/* + * MCHBAR + */ + +#define MCHBAR8(x) (*((volatile u8 *)(DEFAULT_MCHBAR + (x)))) +#define MCHBAR16(x) (*((volatile u16 *)(DEFAULT_MCHBAR + (x)))) +#define MCHBAR32(x) (*((volatile u32 *)(DEFAULT_MCHBAR + (x)))) +#define MCHBAR8_AND(x, and) (MCHBAR8(x) = MCHBAR8(x) & (and)) +#define MCHBAR8_OR(x, or) (MCHBAR8(x) = MCHBAR8(x) | (or)) +#define MCHBAR8_AND_OR(x, and, or) \ + (MCHBAR8(x) = (MCHBAR8(x) & (and)) | (or)) +#define MCHBAR16_AND(x, and) (MCHBAR16(x) = MCHBAR16(x) & (and)) +#define MCHBAR16_OR(x, or) (MCHBAR16(x) = MCHBAR16(x) | (or)) +#define MCHBAR16_AND_OR(x, and, or) \ + (MCHBAR16(x) = (MCHBAR16(x) & (and)) | (or)) +#define MCHBAR32_AND(x, and) (MCHBAR32(x) = MCHBAR32(x) & (and)) +#define MCHBAR32_OR(x, or) (MCHBAR32(x) = MCHBAR32(x) | (or)) +#define MCHBAR32_AND_OR(x, and, or) \ + (MCHBAR32(x) = (MCHBAR32(x) & (and)) | (or)) + +#define CHDECMISC 0x111 +#define STACKED_MEM (1 << 1) + +#define C0DRB0 0x200 +#define C0DRB1 0x202 +#define C0DRB2 0x204 +#define C0DRB3 0x206 +#define C0DRA01 0x208 +#define C0DRA23 0x20a +#define C0CKECTRL 0x260 + +#define C1DRB0 0x600 +#define C1DRB1 0x602 +#define C1DRB2 0x604 +#define C1DRB3 0x606 +#define C1DRA01 0x608 +#define C1DRA23 0x60a +#define C1CKECTRL 0x660 + +#define PMSTS_MCHBAR 0x0f14 /* Self refresh channel status */ +#define PMSTS_WARM_RESET (1 << 8) +#define PMSTS_BOTH_SELFREFRESH (3 << 0) + +#define CLKCFG_MCHBAR 0x0c00 +#define CLKCFG_FSBCLK_SHIFT 0 +#define CLKCFG_FSBCLK_MASK (7 << CLKCFG_FSBCLK_SHIFT) +#define CLKCFG_MEMCLK_SHIFT 4 +#define CLKCFG_MEMCLK_MASK (7 << CLKCFG_MEMCLK_SHIFT) +#define CLKCFG_UPDATE (1 << 12) + +#define SSKPD_MCHBAR 0x0c20 /* 64 bit */ + +/* + * DMIBAR + */ + +#define DMIBAR8(x) (*((volatile u8 *)(DEFAULT_DMIBAR + (x)))) +#define DMIBAR16(x) (*((volatile u16 *)(DEFAULT_DMIBAR + (x)))) +#define DMIBAR32(x) (*((volatile u32 *)(DEFAULT_DMIBAR + (x)))) + +#define DMIVC0RCTL 0x14 +#define DMIVC1RCTL 0x20 +#define DMIVC1RSTS 0x26 +#define DMIESD 0x44 +#define DMILE1D 0x50 +#define DMILE1A 0x58 +#define DMILE2D 0x60 +#define DMILE2A 0x68 + +/* + * EPBAR + */ + +#define EPBAR8(x) (*((volatile u8 *)(DEFAULT_EPBAR + (x)))) +#define EPBAR16(x) (*((volatile u16 *)(DEFAULT_EPBAR + (x)))) +#define EPBAR32(x) (*((volatile u32 *)(DEFAULT_EPBAR + (x)))) + +#define EPESD 0x44 +#define EPLE1D 0x50 +#define EPLE1A 0x58 +#define EPLE2D 0x60 + +#define NOP_CMD 0x2 +#define PRECHARGE_CMD 0x4 +#define MRS_CMD 0x6 +#define EMRS_CMD 0x8 +#define EMRS1_CMD (EMRS_CMD | 0x10) +#define EMRS2_CMD (EMRS_CMD | 0x20) +#define EMRS3_CMD (EMRS_CMD | 0x30) +#define ZQCAL_CMD 0xa +#define CBR_CMD 0xc +#define NORMALOP_CMD 0xe + +#define TOTAL_CHANNELS 2 +#define TOTAL_DIMMS 4 +#define TOTAL_BYTELANES 8 +#define DIMMS_PER_CHANNEL (TOTAL_DIMMS / TOTAL_CHANNELS) +#define RAW_CARD_UNPOPULATED 0xff +#define RAW_CARD_POPULATED 0 + +#define DIMM_IS_POPULATED(dimms, idx) (dimms[idx].card_type != RAW_CARD_UNPOPULATED) +#define IF_DIMM_POPULATED(dimms, idx) if (dimms[idx].card_type != RAW_CARD_UNPOPULATED) +#define ONLY_DIMMA_IS_POPULATED(dimms, ch) ( \ + (DIMM_IS_POPULATED(dimms, (ch == 0) ? 0 : 2) && \ + !DIMM_IS_POPULATED(dimms, (ch == 0) ? 1 : 3))) +#define ONLY_DIMMB_IS_POPULATED(dimms, ch) ( \ + (DIMM_IS_POPULATED(dimms, (ch == 0) ? 1 : 3) && \ + !DIMM_IS_POPULATED(dimms, (ch == 0) ? 0 : 2))) +#define BOTH_DIMMS_ARE_POPULATED(dimms, ch) ( \ + (DIMM_IS_POPULATED(dimms, (ch == 0) ? 0 : 2) && \ + (DIMM_IS_POPULATED(dimms, (ch == 0) ? 1 : 3)))) +#define FOR_EACH_DIMM(idx) \ + for (idx = 0; idx < TOTAL_DIMMS; ++idx) +#define FOR_EACH_POPULATED_DIMM(dimms, idx) \ + FOR_EACH_DIMM(idx) IF_DIMM_POPULATED(dimms, idx) +#define FOR_EACH_DIMM_IN_CHANNEL(ch, idx) \ + for (idx = (ch) << 1; idx < ((ch) << 1) + DIMMS_PER_CHANNEL; ++idx) +#define FOR_EACH_POPULATED_DIMM_IN_CHANNEL(dimms, ch, idx) \ + FOR_EACH_DIMM_IN_CHANNEL(ch, idx) IF_DIMM_POPULATED(dimms, idx) +#define CHANNEL_IS_POPULATED(dimms, idx) \ + ((dimms[idx<<1].card_type != RAW_CARD_UNPOPULATED) \ + || (dimms[(idx<<1) + 1].card_type != RAW_CARD_UNPOPULATED)) +#define CHANNEL_IS_CARDF(dimms, idx) \ + ((dimms[idx<<1].card_type == 0xf) \ + || (dimms[(idx<<1) + 1].card_type == 0xf)) +#define IF_CHANNEL_POPULATED(dimms, idx) \ + if ((dimms[idx<<1].card_type != RAW_CARD_UNPOPULATED) \ + || (dimms[(idx<<1) + 1].card_type != RAW_CARD_UNPOPULATED)) +#define FOR_EACH_CHANNEL(idx) \ + for (idx = 0; idx < TOTAL_CHANNELS; ++idx) +#define FOR_EACH_POPULATED_CHANNEL(dimms, idx) \ + FOR_EACH_CHANNEL(idx) IF_CHANNEL_POPULATED(dimms, idx) + +#define RANKS_PER_CHANNEL 4 +#define RANK_IS_POPULATED(dimms, ch, r) \ + (((dimms[ch<<1].card_type != RAW_CARD_UNPOPULATED) && ((r) < dimms[ch<<1].ranks)) || \ + ((dimms[(ch<<1) + 1].card_type != RAW_CARD_UNPOPULATED) && ((r) >= 2) && ((r) < (dimms[(ch<<1) + 1].ranks + 2)))) +#define IF_RANK_POPULATED(dimms, ch, r) \ + if (((dimms[ch<<1].card_type != RAW_CARD_UNPOPULATED) \ + && ((r) < dimms[ch<<1].ranks)) \ + || ((dimms[(ch<<1) + 1].card_type != RAW_CARD_UNPOPULATED) \ + && ((r) >= 2) && ((r) < (dimms[(ch<<1) + 1].ranks + 2)))) +#define FOR_EACH_RANK_IN_CHANNEL(r) \ + for (r = 0; r < RANKS_PER_CHANNEL; ++r) +#define FOR_EACH_POPULATED_RANK_IN_CHANNEL(dimms, ch, r) \ + FOR_EACH_RANK_IN_CHANNEL(r) IF_RANK_POPULATED(dimms, ch, r) +#define FOR_EACH_RANK(ch, r) \ + FOR_EACH_CHANNEL(ch) FOR_EACH_RANK_IN_CHANNEL(r) +#define FOR_EACH_POPULATED_RANK(dimms, ch, r) \ + FOR_EACH_RANK(ch, r) IF_RANK_POPULATED(dimms, ch, r) +#define FOR_EACH_BYTELANE(l) \ + for (l = 0; l < TOTAL_BYTELANES; l++) +#define FOR_EACH_POPULATED_CHANNEL_AND_BYTELANE(dimms, ch, l) \ + FOR_EACH_POPULATED_CHANNEL (dimms, ch) FOR_EACH_BYTELANE(l) + +#define DDR3_MAX_CAS 18 + +enum fsb_clock { + FSB_CLOCK_800MHz = 0, + FSB_CLOCK_1066MHz = 1, + FSB_CLOCK_1333MHz = 2, +}; + +enum mem_clock { + MEM_CLOCK_400MHz = 0, + MEM_CLOCK_533MHz = 1, + MEM_CLOCK_667MHz = 2, + MEM_CLOCK_800MHz = 3, + MEM_CLOCK_1066MHz = 4, + MEM_CLOCK_1333MHz = 5, +}; + +enum ddr { + DDR2 = 2, + DDR3 = 3, +}; + +enum ddrxspd { + DDR2SPD = 0x8, + DDR3SPD = 0xb, +}; + +enum chip_width { /* as in DDR3 spd */ + CHIP_WIDTH_x4 = 0, + CHIP_WIDTH_x8 = 1, + CHIP_WIDTH_x16 = 2, + CHIP_WIDTH_x32 = 3, +}; + +enum chip_cap { /* as in DDR3 spd */ + CHIP_CAP_256M = 0, + CHIP_CAP_512M = 1, + CHIP_CAP_1G = 2, + CHIP_CAP_2G = 3, + CHIP_CAP_4G = 4, + CHIP_CAP_8G = 5, + CHIP_CAP_16G = 6, +}; + +struct dll_setting { + u8 tap; + u8 pi; + u8 db_en; + u8 db_sel; + u8 clk_delay; + u8 coarse; +}; + +struct rt_dqs_setting { + u8 tap; + u8 pi; +}; + +enum n_banks { + N_BANKS_4 = 0, + N_BANKS_8 = 1, +}; + +struct timings { + unsigned int CAS; + unsigned int tclk; + enum fsb_clock fsb_clk; + enum mem_clock mem_clk; + unsigned int tRAS; + unsigned int tRP; + unsigned int tRCD; + unsigned int tWR; + unsigned int tRFC; + unsigned int tWTR; + unsigned int tRRD; + unsigned int tRTP; +}; + +struct dimminfo { + unsigned int card_type; /* 0xff: unpopulated, + 0xa - 0xf: raw card type A - F */ + enum chip_width width; + unsigned int page_size; /* of whole DIMM in Bytes (4096 or 8192) */ + enum n_banks n_banks; + unsigned int ranks; + unsigned int rows; + unsigned int cols; + u16 spd_crc; + u8 mirrored; +}; + +struct rcven_timings { + u8 min_common_coarse; + u8 coarse_offset[TOTAL_BYTELANES]; + u8 medium[TOTAL_BYTELANES]; + u8 tap[TOTAL_BYTELANES]; + u8 pi[TOTAL_BYTELANES]; +}; + +/* The setup is up to two DIMMs per channel */ +struct sysinfo { + int boot_path; + enum fsb_clock max_fsb; + + int dimm_config[2]; + int spd_type; + int channel_capacity[2]; + struct timings selected_timings; + struct dimminfo dimms[4]; + u8 spd_map[4]; + struct rcven_timings rcven_t[TOTAL_CHANNELS]; + /* + * The rt_dqs delay register for rank 0 seems to be used + * for all other ranks on the channel, so only save that + */ + struct rt_dqs_setting rt_dqs[TOTAL_CHANNELS][TOTAL_BYTELANES]; + struct dll_setting dqs_settings[TOTAL_CHANNELS][TOTAL_BYTELANES]; + struct dll_setting dq_settings[TOTAL_CHANNELS][TOTAL_BYTELANES]; + u8 nmode; + u8 stacked_mode; +}; +#define BOOT_PATH_NORMAL 0 +#define BOOT_PATH_WARM_RESET 1 +#define BOOT_PATH_RESUME 2 + +enum ddr2_signals { + CLKSET0 = 0, + CTRL0, + CLKSET1, + CMD, + CTRL1, + CTRL2, + CTRL3, +}; + +void x3x_early_init(void); +void x3x_late_init(int s3resume); +void mb_get_spd_map(u8 spd_map[4]); +void mb_pre_raminit_setup(int s3_resume); +u32 decode_igd_memory_size(u32 gms); +u32 decode_igd_gtt_size(u32 gsm); +u32 decode_tseg_size(const u32 esmramc); +u8 decode_pciebar(u32 *const base, u32 *const len); +void sdram_initialize(int boot_path, const u8 *spd_map); +void do_raminit(struct sysinfo *, int fast_boot); +void rcven(struct sysinfo *s); +u32 fsb_to_mhz(u32 speed); +u32 ddr_to_mhz(u32 speed); +u32 test_address(int channel, int rank); +void dqsset(u8 ch, u8 lane, const struct dll_setting *setting); +void dqset(u8 ch, u8 lane, const struct dll_setting *setting); +void rt_set_dqs(u8 channel, u8 lane, u8 rank, + struct rt_dqs_setting *dqs_setting); +int do_write_training(struct sysinfo *s); +int do_read_training(struct sysinfo *s); +void search_write_leveling(struct sysinfo *s); +void send_jedec_cmd(const struct sysinfo *s, u8 r, u8 ch, u8 cmd, u32 val); + +extern const struct dll_setting default_ddr2_667_ctrl[7]; +extern const struct dll_setting default_ddr2_800_ctrl[7]; +extern const struct dll_setting default_ddr3_800_ctrl[2][7]; +extern const struct dll_setting default_ddr3_1067_ctrl[2][7]; +extern const struct dll_setting default_ddr3_1333_ctrl[2][7]; +extern const struct dll_setting default_ddr2_667_dqs[TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr2_800_dqs[TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr3_800_dqs[2][TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr3_1067_dqs[2][TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr3_1333_dqs[2][TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr2_667_dq[TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr2_800_dq[TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr3_800_dq[2][TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr3_1067_dq[2][TOTAL_BYTELANES]; +extern const struct dll_setting default_ddr3_1333_dq[2][TOTAL_BYTELANES]; +extern const u8 ddr3_emrs1_rtt_nom_config[16][4]; +extern const u8 post_jedec_tab[3][4][2]; +extern const u32 ddr3_c2_tab[2][3][6][2]; +extern const u8 ddr3_c2_x264[3][6]; +extern const u16 ddr3_c2_x23c[3][6]; + +#include <device/device.h> +struct acpi_rsdp; +unsigned long northbridge_write_acpi_tables(const struct device *device, + unsigned long start, struct acpi_rsdp *rsdp); + +#endif /* __NORTHBRIDGE_INTEL_X3X_H__ */