mail.coreboot.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2024
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
2016
December
November
October
September
August
July
June
May
April
March
February
January
2015
December
November
October
September
August
July
June
May
April
March
February
January
2014
December
November
October
September
August
July
June
May
April
March
February
January
2013
December
November
October
September
August
July
June
May
April
March
List overview
Download
coreboot-gerrit
May 2014
----- 2024 -----
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
coreboot-gerrit@coreboot.org
1 participants
1066 discussions
Start a n
N
ew thread
Patch set updated for coreboot: f705375 mainboard/jetway/nf81-t56n-lf: Major ACPI board rework
by Edward O'Callaghan
31 May '14
31 May '14
Edward O'Callaghan (eocallaghan(a)alterapraxis.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5887
-gerrit commit f70537583b1e2dcaa81b5a1e6cfa33a65ba670ff Author: Edward O'Callaghan <eocallaghan(a)alterapraxis.com> Date: Sat May 31 10:09:52 2014 +1000 mainboard/jetway/nf81-t56n-lf: Major ACPI board rework NOTFORMERGE yet Change-Id: I500ebbafd5e9400f9a9dbfe8240fdcdccb6bb900 Signed-off-by: Edward O'Callaghan <eocallaghan(a)alterapraxis.com> --- src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl | 110 ++++++++++++----- .../jetway/nf81-t56n-lf/acpi/mainboard.asl | 6 + src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl | 76 +++++++++++- src/mainboard/jetway/nf81-t56n-lf/dsdt.asl | 133 +++++++++++++++++++++ 4 files changed, 288 insertions(+), 37 deletions(-) diff --git a/src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl b/src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl index 6ad1ad4..42a0494 100644 --- a/src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl +++ b/src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl @@ -2,6 +2,7 @@ * This file is part of the coreboot project. * * Copyright (C) 2011 Advanced Micro Devices, Inc. + * Copyright (C) 2014 Edward O'Callaghan <eocallaghan(a)alterapraxis.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,67 +18,110 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -Scope(\_GPE) { /* Start Scope GPE */ +Scope(\_GPE) +{ + OperationRegion (IP, SystemIO, 0x0225, 0x02) + Field (IP, ByteAcc, NoLock, Preserve) + { + INDX, 8, + DAT0, 8 + } /* General event 3 */ - Method(_L03) { - /* DBGO("\\_GPE\\_L00\n") */ - Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + Method(_L03) + { + Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ } - /* Legacy PM event */ - Method(_L08) { - /* DBGO("\\_GPE\\_L08\n") */ + Method (_L04, 0, NotSerialized) /* _Lxx: Level-Triggered GPE */ + { + Notify (\_SB.PCI0.P0PC, 0x02) + Notify (\_SB.PWRB, 0x02) } /* Temp warning (TWarn) event */ - Method(_L09) { + Method(_L09, 0, NotSerialized) + { /* DBGO("\\_GPE\\_L09\n") */ - /* Notify (\_TZ.TZ00, 0x80) */ + Store (GBYT (0x66), Local0) + If (LNotEqual (And (Local0, 0x02), Zero)) + { + Notify (\_TZ.THRM, 0x80) + } + } + + Method (GBYT, 1, NotSerialized) + { + Store (Arg0, INDX) + Store (DAT0, Local0) + Return (Local0) } /* USB controller PME# */ - Method(_L0B) { - /* DBGO("\\_GPE\\_L0B\n") */ - Notify(\_SB.PCI0.UOH1, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH2, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH3, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH4, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH5, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH6, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UEH1, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + Method(_L0B, 0, NotSerialized) { + Notify (\_SB.PCI0.UOH1, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH2, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH3, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH4, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH5, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH6, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UEH1, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + } + + Method(_L0F) { + Notify (\_SB.PCI0.PE20, 0x02) + Notify (\_SB.PWRB, 0x02) } /* ExtEvent0 SCI event */ Method(_L10) { - /* DBGO("\\_GPE\\_L10\n") */ + Notify (\_SB.PCI0.PE21, 0x02) + Notify (\_SB.PWRB, 0x02) } - /* ExtEvent1 SCI event */ Method(_L11) { - /* DBGO("\\_GPE\\_L11\n") */ + Notify (\_SB.PCI0.PE22, 0x02) + Notify (\_SB.PWRB, 0x02) + } + + Method(_L12) { + Notify (\_SB.PCI0.PE23, 0x02) + Notify (\_SB.PWRB, 0x02) } /* GPIO0 or GEvent8 event */ Method(_L18) { - /* DBGO("\\_GPE\\_L18\n") */ - Notify(\_SB.PCI0.PBR4, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.PBR5, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.PBR6, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.PBR7, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.BR15, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.PCE6, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ } /* Azalia SCI event */ Method(_L1B) { - /* DBGO("\\_GPE\\_L1B\n") */ - Notify(\_SB.PCI0.AZHD, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.AZHD, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + } + + Method (_L1D, 0, NotSerialized) + { + /* call SIO method on PS/2 Mouse/keyboard event */ + /* \_SB.PCI0.SBRG.SIOH () */ + Notify (\_SB.PWRB, 0x02) } -} /* End Scope GPE */ + + Device (PWRB) + { + Name (_HID, EisaId ("PNP0C0C")) // _HID: Hardware ID + Name (_UID, 0xAA) // _UID: Unique ID + Name (_STA, 0x0B) // _STA: Status + Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake + { + Return (GPRW (0x1D, 0x03)) + } + } +} /* End Scope GPE */ /* Contains the GPEs for USB overcurrent */ #include "usb_oc.asl" - diff --git a/src/mainboard/jetway/nf81-t56n-lf/acpi/mainboard.asl b/src/mainboard/jetway/nf81-t56n-lf/acpi/mainboard.asl index 47c17df..af37965 100644 --- a/src/mainboard/jetway/nf81-t56n-lf/acpi/mainboard.asl +++ b/src/mainboard/jetway/nf81-t56n-lf/acpi/mainboard.asl @@ -35,6 +35,12 @@ Name(OSTP, 3) Name(OSV, Ones) /* Assume nothing */ Name(PMOD, One) /* Assume APIC */ +Name (TPCH, Zero) +Name (SS1, Zero) +Name (SS2, Zero) +Name (SS3, One) +Name (SS4, One) + Scope(\_SB) { Method(CkOT, 0, NotSerialized) { diff --git a/src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl b/src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl index 2f50475..b73a7ce 100644 --- a/src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl +++ b/src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl @@ -1,6 +1,8 @@ /* * This file is part of the coreboot project. * + * Copyright (C) 2014 Edward O'Callaghan <eocallaghan(a)alterapraxis.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. @@ -15,7 +17,73 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -/* Thermal Zones have been #if 0 for a long time. - * Removing it for now because it doesn't seem to - * do anything when enabled anyway. - */ +/* Thermal Zones. */ + +Scope (\_TZ) +{ + OperationRegion (IP, SystemIO, 0x0225, 0x02) + Field (IP, ByteAcc, NoLock, Preserve) + { + INDX, 8, + DAT0, 8 + } + + ThermalZone (THRM) + { + Method (KELV, 1, NotSerialized) + { + Store (Arg0, Local1) + Multiply (0x0A, Local1, Local1) + Add (Local1, 0x0AAC, Local1) + Return (Local1) + } + + Method (_TMP, 0, NotSerialized) // _TMP: Temperature + { + If (LEqual (TPCH, One)) + { + While (LGreater (GBYT (0x7A), 0x7E)) + { + Store (GBYT (0x7A), DBG8) + Sleep (0xFA) + Store (One, Local1) + Multiply (0x0A, Local1, Local1) + Add (Local1, 0x0AAC, Local1) + Return (Local1) + } + } + + Return (KELV (CTMP ())) + } + + Method (_CRT, 0, NotSerialized) // _CRT: Critical Temperature + { + Return (KELV (STMP ())) + } + + Method (STMP, 0, NotSerialized) + { + Store (GBYT (0x82), Local0) + Return (Local0) + } + + Method (CTMP, 0, NotSerialized) + { + Store (GBYT (0x7A), Local0) + Store (Local0, DBG8) + If (LGreaterEqual (Local0, 0x65)) + { + Store (0x30, Local0) + } + + Return (Local0) + } + + Method (GBYT, 1, NotSerialized) + { + Store (Arg0, INDX) + Store (DAT0, Local0) + Return (Local0) + } + } +} diff --git a/src/mainboard/jetway/nf81-t56n-lf/dsdt.asl b/src/mainboard/jetway/nf81-t56n-lf/dsdt.asl index a650bca..45d5650 100644 --- a/src/mainboard/jetway/nf81-t56n-lf/dsdt.asl +++ b/src/mainboard/jetway/nf81-t56n-lf/dsdt.asl @@ -2,6 +2,7 @@ * This file is part of the coreboot project. * * Copyright (C) 2011 Advanced Micro Devices, Inc. + * Copyright (C) 2014 Edward O'Callaghan <eocallaghan(a)alterapraxis.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,6 +48,138 @@ DefinitionBlock ( /* Describe the AMD Fusion Controller Hub Southbridge */ #include <southbridge/amd/cimx/sb800/acpi/fch.asl> + Device (BR15) + { + Name (_ADR, 0x00050000) // _ADR: Address + Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake + { + Return (GPRW (0x18, 0x04)) + } + + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PR0) + { + Return (APR0) + } + + Return (PR0) + } + } + + Device (PCE6) + { + Name (_ADR, 0x00060000) // _ADR: Address + Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake + { + Return (GPRW (0x18, 0x04)) + } + + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PR0) + { + Return (APR0) + } + + Return (PR0) + } + } + + Device (PCE7) + { + Name (_ADR, 0x00070000) // _ADR: Address + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PR0) + { + Return (APR0) + } + + Return (PR0) + } + } + + Device (PCE8) + { + Name (_ADR, 0x00080000) // _ADR: Address + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PR0) + { + Return (APR0) + } + + Return (PR0) + } + } + + Device (P0PC) + { + Name (_ADR, 0x00140004) // _ADR: Address + Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake + { + Return (GPRW (0x04, 0x04)) + } + + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PR0) + { + Return (APR0) + } + + Return (PR0) + } + } + + Device (GEC) + { + Name (_ADR, 0x00140006) // _ADR: Address + Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake + { + Return (GPRW (0x13, 0x04)) + } + } + + Name (PRWP, Package (0x02) + { + Zero, + Zero + }) + + Method (GPRW, 2, NotSerialized) + { + Store (Arg0, Index (PRWP, Zero)) + Store (ShiftLeft (SS1, One), Local0) + Or (Local0, ShiftLeft (SS2, 0x02), Local0) + Or (Local0, ShiftLeft (SS3, 0x03), Local0) + Or (Local0, ShiftLeft (SS4, 0x04), Local0) + If (And (ShiftLeft (One, Arg1), Local0)) + { + Store (Arg1, Index (PRWP, One)) + } + Else + { + ShiftRight (Local0, One, Local0) + If (LOr (LEqual (CkOT (), One), LEqual (CkOT (), 0x02))) + { + FindSetLeftBit (Local0, Index (PRWP, One)) + } + Else + { + FindSetRightBit (Local0, Index (PRWP, One)) + } + } + + Return (PRWP) + } + + OperationRegion (DEB0, SystemIO, 0x80, One) + Field (DEB0, ByteAcc, NoLock, Preserve) + { + DBG8, 8 + } } } /* End Scope(_SB) */
1
0
0
0
Patch set updated for coreboot: f5d0df6 sandy/ivybridge: Native raminit.
by Vladimir Serbinenko
31 May '14
31 May '14
Vladimir Serbinenko (phcoder(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5786
-gerrit commit f5d0df67e6ec2be502e5d522ac65a83e80ead938 Author: Vladimir Serbinenko <phcoder(a)gmail.com> Date: Sun May 18 11:05:56 2014 +0200 sandy/ivybridge: Native raminit. Based on damo22 work and my X230 tracing. Works for my X230 in a variety of RAM configs. Also-By: Damien Zammit <damien(a)zamaudio.com> Change-Id: I1aa024c55a8416fc53b25e7123037df0e55a2769 Signed-off-by: Vladimir Serbinenko <phcoder(a)gmail.com> --- 3rdparty | 2 +- src/cpu/intel/Makefile.inc | 1 + src/cpu/x86/smm/smmhandler_tseg.S | 2 +- src/cpu/x86/smm/smmrelocate.S | 4 +- src/device/dram/ddr3.c | 8 +- src/include/device/dram/ddr3.h | 3 + src/mainboard/lenovo/x230/Kconfig | 6 +- src/mainboard/lenovo/x230/romstage.c | 120 +- src/northbridge/intel/Makefile.inc | 1 + src/northbridge/intel/sandybridge/Kconfig | 12 +- src/northbridge/intel/sandybridge/Makefile.inc | 5 +- src/northbridge/intel/sandybridge/gma.c | 4 +- src/northbridge/intel/sandybridge/raminit_native.c | 3766 ++++++++++++++++++++ src/northbridge/intel/sandybridge/raminit_native.h | 29 + src/southbridge/intel/bd82x6x/Makefile.inc | 6 +- src/southbridge/intel/bd82x6x/early_me_native.c | 272 ++ src/southbridge/intel/bd82x6x/early_pch_native.c | 375 ++ src/southbridge/intel/bd82x6x/early_thermal.c | 70 + src/southbridge/intel/bd82x6x/pch.h | 2 + src/southbridge/intel/bd82x6x/smi.c | 3 - src/southbridge/intel/bd82x6x/usb_ehci.c | 33 + src/southbridge/intel/ibexpeak/me.c | 125 - src/southbridge/intel/ibexpeak/smi.c | 6 - src/southbridge/intel/ibexpeak/usb_ehci.c | 17 + 24 files changed, 4648 insertions(+), 224 deletions(-) diff --git a/3rdparty b/3rdparty index 324ec3c..45f0c04 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 324ec3cb642a278d6d97ae809bc6098045bc6e65 +Subproject commit 45f0c04fd788fb29d9e303b2b2d1657ddb03448a diff --git a/src/cpu/intel/Makefile.inc b/src/cpu/intel/Makefile.inc index 0392f69..68ea05d 100644 --- a/src/cpu/intel/Makefile.inc +++ b/src/cpu/intel/Makefile.inc @@ -19,6 +19,7 @@ subdirs-$(CONFIG_CPU_INTEL_SOCKET_RPGA989) += socket_rPGA989 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += model_2065x subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += model_206ax +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_model_206ax diff --git a/src/cpu/x86/smm/smmhandler_tseg.S b/src/cpu/x86/smm/smmhandler_tseg.S index b33fcdf..380935a 100644 --- a/src/cpu/x86/smm/smmhandler_tseg.S +++ b/src/cpu/x86/smm/smmhandler_tseg.S @@ -57,7 +57,7 @@ #define SMI_UNLOCKED 1 #define __PRE_RAM__ -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include <northbridge/intel/sandybridge/sandybridge.h> #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM diff --git a/src/cpu/x86/smm/smmrelocate.S b/src/cpu/x86/smm/smmrelocate.S index bdc9771..bc90fab 100644 --- a/src/cpu/x86/smm/smmrelocate.S +++ b/src/cpu/x86/smm/smmrelocate.S @@ -48,7 +48,7 @@ #if CONFIG_SMM_TSEG -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include <northbridge/intel/sandybridge/sandybridge.h> #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM @@ -195,7 +195,7 @@ smm_relocate: xorl %edx, %edx wrmsr -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE /* * IED base is top 4M of TSEG */ diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c index 9b4f490..69782ab 100644 --- a/src/device/dram/ddr3.c +++ b/src/device/dram/ddr3.c @@ -110,7 +110,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) { int ret; u16 crc, spd_crc; - u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width, sdram_width; + u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width; u8 reg8; u32 mtb; /* medium time base */ unsigned int val, param; @@ -209,8 +209,8 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) printram(" Invalid SDRAM width\n"); ret = SPD_STATUS_INVALID_FIELD; } - sdram_width = (4 << val); - printram(" SDRAM width : %u\n", sdram_width); + dimm->width = (4 << val); + printram(" SDRAM width : %u\n", dimm->width); /* Memory bus width */ reg8 = spd[8]; @@ -236,7 +236,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) * capacity_shift * The rest is the JEDEC formula */ dimm->size_mb = ((1 << (capacity_shift + (25 - 20))) * bus_width - * dimm->ranks) / sdram_width; + * dimm->ranks) / dimm->width; /* Fine Timebase (FTB) Dividend/Divisor */ /* Dividend */ diff --git a/src/include/device/dram/ddr3.h b/src/include/device/dram/ddr3.h index b19c51c..4bf5058 100644 --- a/src/include/device/dram/ddr3.h +++ b/src/include/device/dram/ddr3.h @@ -37,6 +37,7 @@ * @{ */ #define TCK_1066MHZ 240 +#define TCK_933MHZ 275 #define TCK_800MHZ 320 #define TCK_666MHZ 384 #define TCK_533MHZ 480 @@ -137,6 +138,8 @@ typedef struct dimm_attr_st { u16 cas_supported; /* Flags extracted from SPD */ dimm_flags_t flags; + /* SDRAM width */ + u8 width; /* Number of ranks */ u8 ranks; /* Number or row address bits */ diff --git a/src/mainboard/lenovo/x230/Kconfig b/src/mainboard/lenovo/x230/Kconfig index d3aa6e9..6fd309e 100644 --- a/src/mainboard/lenovo/x230/Kconfig +++ b/src/mainboard/lenovo/x230/Kconfig @@ -3,7 +3,7 @@ if BOARD_LENOVO_X230 config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select CPU_INTEL_SOCKET_RPGA989 - select NORTHBRIDGE_INTEL_IVYBRIDGE + select NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE select SOUTHBRIDGE_INTEL_C216 select EC_LENOVO_PMH7 select EC_LENOVO_H8 @@ -47,10 +47,6 @@ config MMCONF_BASE_ADDRESS hex default 0xf0000000 -config CACHE_ROM_SIZE_OVERRIDE - hex - default 0x800000 - config IRQ_SLOT_COUNT int default 18 diff --git a/src/mainboard/lenovo/x230/romstage.c b/src/mainboard/lenovo/x230/romstage.c index 6e4e685..ff319b5 100644 --- a/src/mainboard/lenovo/x230/romstage.c +++ b/src/mainboard/lenovo/x230/romstage.c @@ -32,7 +32,7 @@ #include <cbmem.h> #include <console/console.h> #include "northbridge/intel/sandybridge/sandybridge.h" -#include "northbridge/intel/sandybridge/raminit.h" +#include "northbridge/intel/sandybridge/raminit_native.h" #include "southbridge/intel/bd82x6x/pch.h" #include "southbridge/intel/bd82x6x/gpio.h" #include <arch/cpu.h> @@ -108,66 +108,59 @@ static void rcba_config(void) RCBA32(BUC) = 0; } +static void +init_usb (void) +{ + const u32 rcba_dump[64] = { + /* 3500 */ 0x20000153, 0x20000153, 0x20000f57, 0x20000f57, + /* 3510 */ 0x20000f57, 0x20000f57, 0x20000153, 0x2000055b, + /* 3520 */ 0x20000153, 0x2000055b, 0x20000f57, 0x20000f57, + /* 3530 */ 0x20000f57, 0x20000f57, 0x00000000, 0x00000000, + /* 3540 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 3550 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 3560 */ 0x024c8001, 0x000024a3, 0x00040002, 0x01000050, + /* 3570 */ 0x02000772, 0x16000f9f, 0x1800ff4f, 0x0001d630, + /* 3580 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 3590 */ 0x00000000, 0x00000000, 0x00000000, 0x00000040, + /* 35a0 */ 0x04000201, 0x00000200, 0x00000000, 0x00000000, + /* 35b0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 35c0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 35d0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 35e0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + /* 35f0 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }; + int i; + /* Activate PMBAR. */ + pci_write_config32(PCI_DEV(0, 0x1f, 0), PMBASE, DEFAULT_PMBASE | 1); + pci_write_config32(PCI_DEV(0, 0x1f, 0), PMBASE + 4, 0); + pci_write_config8(PCI_DEV(0, 0x1f, 0), 0x44 /* ACPI_CNTL */ , 0x80); /* Enable ACPI BAR */ + + /* Unlock registers. */ + outw (inw (DEFAULT_PMBASE | 0x003c) | 2, DEFAULT_PMBASE | 0x003c); + + for (i = 0; i < 64; i++) + write32 (DEFAULT_RCBABASE | (0x3500 + 4 * i), rcba_dump[i]); + + pcie_write_config32 (PCI_DEV (0, 0x14, 0), 0xe4, 0x00000000); + + /* Relock registers. */ + outw (0x0000, DEFAULT_PMBASE | 0x003c); +} + + void main(unsigned long bist) { int boot_mode = 0; int cbmem_was_initted; u32 pm1_cnt; u16 pm1_sts; + spd_raw_data spd[4]; if (MCHBAR16(SSKPD) == 0xCAFE) { outb(0x6, 0xcf9); hlt (); } - struct pei_data pei_data = { - .pei_version = PEI_VERSION, - .mchbar = DEFAULT_MCHBAR, - .dmibar = DEFAULT_DMIBAR, - .epbar = DEFAULT_EPBAR, - .pciexbar = CONFIG_MMCONF_BASE_ADDRESS, - .smbusbar = SMBUS_IO_BASE, - .wdbbar = 0x4000000, - .wdbsize = 0x1000, - .hpet_address = CONFIG_HPET_ADDRESS, - .rcba = DEFAULT_RCBABASE, - .pmbase = DEFAULT_PMBASE, - .gpiobase = DEFAULT_GPIOBASE, - .thermalbase = 0xfed08000, - .system_type = 0, // 0 Mobile, 1 Desktop/Server - .tseg_size = CONFIG_SMM_TSEG_SIZE, - .spd_addresses = { 0xA0, 0x00,0xA2,0x00 }, - .ts_addresses = { 0x00, 0x00, 0x00, 0x00 }, - .ec_present = 1, - .gbe_enable = 1, - .ddr3lv_support = 0, - // 0 = leave channel enabled - // 1 = disable dimm 0 on channel - // 2 = disable dimm 1 on channel - // 3 = disable dimm 0+1 on channel - .dimm_channel0_disabled = 2, - .dimm_channel1_disabled = 2, - .max_ddr3_freq = 1600, - .usb_port_config = { - /* enabled usb oc pin length */ - { 1, 0, 0x0080 }, /* P0 (left, fan side), OC 0 */ - { 1, 1, 0x0080 }, /* P1 (left touchpad side), OC 1 */ - { 1, 3, 0x0080 }, /* P2: dock, OC 3 */ - { 1, 0, 0x0040 }, /* P3: wwan, no OC */ - { 1, 0, 0x0080 }, /* P4: Wacom tablet on X230t, otherwise empty */ - { 1, 0, 0x0080 }, /* P5: Expresscard, no OC */ - { 0, 0, 0x0000 }, /* P6: Empty */ - { 1, 0, 0x0080 }, /* P7: dock, no OC */ - { 0, 0, 0x0000 }, /* P8: Empty */ - { 1, 5, 0x0080 }, /* P9: Right (EHCI debug), OC 5 */ - { 1, 0, 0x0040 }, /* P10: fingerprint reader, no OC */ - { 1, 0, 0x0040 }, /* P11: bluetooth, no OC. */ - { 1, 0, 0x0040 }, /* P12: wlan, no OC */ - { 1, 0, 0x0080 }, /* P13: webcam, no OC */ - }, - .ddr_refresh_rate_config = 2, /* Force double refresh rate */ - }; - timestamp_init(get_initial_timestamp()); timestamp_add_now(TS_START_ROMSTAGE); @@ -182,6 +175,8 @@ void main(unsigned long bist) setup_pch_gpios(&x230_gpio_map); + init_usb(); + /* Initialize console device(s) */ console_init(); @@ -217,31 +212,16 @@ void main(unsigned long bist) /* Enable SPD ROMs and DDR-III DRAM */ enable_smbus(); - /* Prepare USB controller early in S3 resume */ - if (boot_mode == 2) - enable_usb_bar(); - post_code(0x39); post_code(0x3a); - pei_data.boot_mode = boot_mode; timestamp_add_now(TS_BEFORE_INITRAM); - /* MRC.bin has a bug and sometimes halts (instead of reboot?). - */ - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) & ~(1 << 5); /* reset */ - outw((0 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* let timer go */ - } - - sdram_initialize(&pei_data); + memset (spd, 0, sizeof (spd)); + read_spd (&spd[0], 0x50); + read_spd (&spd[2], 0x51); - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) | (1 << 5); /* No reset */ - outw((1 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */ - } + init_dram_ddr3 (spd, 1, TCK_800MHZ); timestamp_add_now(TS_AFTER_INITRAM); post_code(0x3c); @@ -254,8 +234,8 @@ void main(unsigned long bist) MCHBAR16(SSKPD) = 0xCAFE; cbmem_was_initted = !cbmem_recovery(boot_mode==2); - if (boot_mode!=2) - save_mrc_data(&pei_data); +// if (boot_mode!=2) +// save_mrc_data(&pei_data); #if CONFIG_HAVE_ACPI_RESUME /* If there is no high memory area, we didn't boot before, so diff --git a/src/northbridge/intel/Makefile.inc b/src/northbridge/intel/Makefile.inc index 808a1b2..f1c2540 100644 --- a/src/northbridge/intel/Makefile.inc +++ b/src/northbridge/intel/Makefile.inc @@ -15,6 +15,7 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I5000) += i5000 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += nehalem subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += sandybridge +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_sandybridge diff --git a/src/northbridge/intel/sandybridge/Kconfig b/src/northbridge/intel/sandybridge/Kconfig index fb92e40..9bac706 100644 --- a/src/northbridge/intel/sandybridge/Kconfig +++ b/src/northbridge/intel/sandybridge/Kconfig @@ -31,7 +31,14 @@ config NORTHBRIDGE_INTEL_IVYBRIDGE select MMCONF_SUPPORT_DEFAULT select CPU_INTEL_MODEL_306AX -if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE +config NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + bool + select CACHE_MRC_BIN + select MMCONF_SUPPORT + select MMCONF_SUPPORT_DEFAULT + select CPU_INTEL_MODEL_306AX + +if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config VGA_BIOS_ID string @@ -52,7 +59,8 @@ config MRC_CACHE_SIZE config DCACHE_RAM_BASE hex - default 0xff7e0000 + default 0xff7e0000 if !NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + default 0xfefe0000 if NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config DCACHE_RAM_SIZE hex diff --git a/src/northbridge/intel/sandybridge/Makefile.inc b/src/northbridge/intel/sandybridge/Makefile.inc index 6655e2a..ae20de5 100644 --- a/src/northbridge/intel/sandybridge/Makefile.inc +++ b/src/northbridge/intel/sandybridge/Makefile.inc @@ -26,7 +26,10 @@ ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c ramstage-y += mrccache.c romstage-y += ram_calc.c -romstage-y += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += raminit_native.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += ../../../device/dram/ddr3.c romstage-y += mrccache.c romstage-y += early_init.c romstage-y += report_platform.c diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index d271a1a..a82a284 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -650,9 +650,7 @@ static void gma_func0_init(struct device *dev) physbase = pci_read_config32(dev, 0x5c) & ~0xf; graphics_base = dev->resource_list[1].base; - int lightup_ok = i915lightup(conf, physbase, iobase, mmiobase, graphics_base); - if (lightup_ok) - gfx_set_init_done(1); + i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif } diff --git a/src/northbridge/intel/sandybridge/raminit_native.c b/src/northbridge/intel/sandybridge/raminit_native.c new file mode 100644 index 0000000..4f37413 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.c @@ -0,0 +1,3766 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Damien Zammit <damien(a)zamaudio.com> + * Copyright (C) 2014 Vladimir Serbinenko <phcoder(a)gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <console/usb.h> +#include <bootmode.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include "raminit_native.h" +#include "sandybridge.h" +#include <delay.h> + +/* Management Engine is in the southbridge */ +#include "southbridge/intel/bd82x6x/me.h" +/* For SPD. */ +#include "southbridge/intel/bd82x6x/smbus.h" +#include "arch/cpu.h" +#include "cpu/x86/msr.h" + +/* FIXME: no ECC support. */ +/* FIXME: no support for 3-channel chipsets. */ +/* FIXME: no S3. */ +/* FIXME: no timing caching. */ + +#define BASEFREQ 133 +#define tDLLK 512 + +#define IS_SANDY_CPU(x) ((x & 0xffff0) == 0x206a0) +#define IS_SANDY_CPU_C(x) ((x & 0xf) == 4) +#define IS_SANDY_CPU_D0(x) ((x & 0xf) == 5) +#define IS_SANDY_CPU_D1(x) ((x & 0xf) == 6) +#define IS_SANDY_CPU_D2(x) ((x & 0xf) == 7) + +#define IS_IVY_CPU(x) ((x & 0xffff0) == 0x306a0) +#define IS_IVY_CPU_C(x) ((x & 0xf) == 4) +#define IS_IVY_CPU_K(x) ((x & 0xf) == 5) +#define IS_IVY_CPU_D(x) ((x & 0xf) == 6) +#define IS_IVY_CPU_E(x) ((x & 0xf) >= 8) + +#define NUM_CHANNELS 2 +#define NUM_SLOTRANKS 4 +#define NUM_SLOTS 2 +#define NUM_LANES 8 + +typedef struct odtmap_st { + u16 rttwr; + u16 rttnom; +} odtmap; + +typedef struct dimm_info_st { + dimm_attr dimm[NUM_CHANNELS][NUM_SLOTS]; +} dimm_info; + +struct ram_rank_timings { + /* Register 4024. One byte per slotrank. */ + u8 val_4024; + /* Register 4028. One nibble per slotrank. */ + u8 val_4028; + + int val_320c; + + struct ram_lane_timings { + /* lane register offset 0x10. */ + u16 timA; /* bits 0 - 5, bits 16 - 18 */ + u8 rising; /* bits 8 - 14 */ + u8 falling; /* bits 20 - 26. */ + + /* lane register offset 0x20. */ + int timC; /* bit 0 - 5, 19. */ + u16 timB; /* bits 8 - 13, 15 - 17. */ + } lanes[NUM_LANES]; +}; + +typedef struct ramctr_timing_st { + int mobile; + + enum spd_memory_type dram_type; + u16 cas_supported; + /* tLatencies are in units of ns, scaled by x256 */ + u32 tCK; + u32 tAA; + u32 tWR; + u32 tRCD; + u32 tRRD; + u32 tRP; + u32 tRAS; + u32 tRC; + u32 tRFC; + u32 tWTR; + u32 tRTP; + u32 tFAW; + /* Latencies in terms of clock cycles + * They are saved separately as they are needed for DRAM MRS commands*/ + u8 CAS; /* CAS read latency */ + u8 CWL; /* CAS write latency */ + /* Number of dimms currently connected */ + u8 n_dimms; + + u32 tREFI; + u32 tMOD; + u32 tXSOffset; + u32 tWLO; + u32 tCKE; + u32 tXPDLL; + u32 tXP; + u32 tAONPD; + + u32 delay1; + u32 delay2; + u16 reg_5064b0; /* bits 0-11. */ + + u8 eccsupport; + u8 dualchannel; + + u8 rankmap[NUM_CHANNELS]; + int ref_card_offset[NUM_CHANNELS]; + + int reg_c14_offset; + + int edge_offset[3]; + int timC_offset[3]; + + int extended_temperature_range; + int auto_self_refresh; + + int rank_mirror[NUM_CHANNELS][NUM_SLOTRANKS]; + + struct ram_rank_timings timings[NUM_CHANNELS][NUM_SLOTRANKS]; +} ramctr_timing; + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) +#define NORTHBRIDGE PCI_DEV(0, 0x0, 0) +#define FOR_ALL_LANES for (lane = 0; lane < NUM_LANES; lane++) +#define FOR_ALL_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) +#define FOR_ALL_POPULATED_RANKS for (slotrank = 0; slotrank < NUM_SLOTRANKS; slotrank++) if (ctrl->rankmap[channel] & (1 << slotrank)) +#define FOR_ALL_POPULATED_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) if (ctrl->rankmap[channel]) +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define MAX_EDGE_TIMING 71 +#define MAX_TIMC 127 +#define MAX_TIMB 511 +#define MAX_TIMA 127 + +static void program_timings(ramctr_timing * ctrl, int channel); + +static const char *ecc_decoder[] = { + "inactive", + "active on IO", + "disabled on IO", + "active" +}; + +static void wait_txt_clear(void) +{ + struct cpuid_result cp; + + cp = cpuid_ext(0x1, 0x0); + /* Check if TXT is supported? */ + if (!(cp.ecx & 0x40)) + return; + /* Some TXT public bit. */ + if (!(read32(0xfed30010) & 1)) + return; + /* Wait for TXT clear. */ + while (!(read8(0xfed40000) & (1 << 7))) ; +} + +static void sfence(void) +{ + asm volatile ("sfence"); +} + +/* + * Dump in the log memory controller configuration as read from the memory + * controller registers. + */ +static void report_memory_config(void) +{ + u32 addr_decoder_common, addr_decode_ch[NUM_CHANNELS]; + int i; + + addr_decoder_common = MCHBAR32(0x5000); + addr_decode_ch[0] = MCHBAR32(0x5004); + addr_decode_ch[1] = MCHBAR32(0x5008); + + printk(BIOS_DEBUG, "memcfg DDR3 clock %d MHz\n", + (MCHBAR32(0x5e04) * 13333 * 2 + 50) / 100); + printk(BIOS_DEBUG, "memcfg channel assignment: A: %d, B % d, C % d\n", + addr_decoder_common & 3, (addr_decoder_common >> 2) & 3, + (addr_decoder_common >> 4) & 3); + + for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) { + u32 ch_conf = addr_decode_ch[i]; + printk(BIOS_DEBUG, "memcfg channel[%d] config (%8.8x):\n", i, + ch_conf); + printk(BIOS_DEBUG, " ECC %s\n", + ecc_decoder[(ch_conf >> 24) & 3]); + printk(BIOS_DEBUG, " enhanced interleave mode %s\n", + ((ch_conf >> 22) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " rank interleave %s\n", + ((ch_conf >> 21) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " DIMMA %d MB width x%d %s rank%s\n", + ((ch_conf >> 0) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? 16 : 8, + ((ch_conf >> 17) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? "" : ", selected"); + printk(BIOS_DEBUG, " DIMMB %d MB width x%d %s rank%s\n", + ((ch_conf >> 8) & 0xff) * 256, + ((ch_conf >> 20) & 1) ? 16 : 8, + ((ch_conf >> 18) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? ", selected" : ""); + } +} + +static void post_system_agent_init(void) +{ + /* If PCIe init is skipped, set the PEG clock gating */ + MCHBAR32(0x7010) = MCHBAR32(0x7010) | 0x01; +} + +void read_spd(spd_raw_data * spd, u8 addr) +{ + int j; + for (j = 0; j < 256; j++) + (*spd)[j] = do_smbus_read_byte(SMBUS_IO_BASE, addr, j); +} + +static void dram_find_spds_ddr3(spd_raw_data * spd, dimm_info * dimm, + ramctr_timing * ctrl) +{ + int dimms = 0; + int channel, slot, spd_slot; + + memset (ctrl->rankmap, 0, sizeof (ctrl->rankmap)); + + ctrl->extended_temperature_range = 1; + ctrl->auto_self_refresh = 1; + + FOR_ALL_CHANNELS { + int ref_card[NUM_SLOTS]; + for (slot = 0; slot < NUM_SLOTS; slot++) { + spd_slot = 2 * channel + slot; + spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + if (dimm->dimm[channel][slot].dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { + // set dimm invalid + dimm->dimm[channel][slot].ranks = 0; + dimm->dimm[channel][slot].size_mb = 0; + continue; + } + + dram_print_spd_ddr3(&dimm->dimm[channel][slot]); + dimms++; + ctrl->rank_mirror[channel][slot * 2] = 0; + ctrl->rank_mirror[channel][slot * 2 + 1] = spd[spd_slot][0x3f] & 1; + + ctrl->auto_self_refresh &= (spd[spd_slot][31] >> 2) & 1; + ctrl->extended_temperature_range &= spd[spd_slot][31] & 1; + + ctrl->rankmap[channel] = ((1 << dimm->dimm[channel][slot].ranks) - 1) << (2 * slot); + ref_card[channel] = spd[spd_slot][62] & 0x1f;; + printk(BIOS_DEBUG, "rankmap[%d] = 0x%x\n", channel, ctrl->rankmap[channel]); + } + if ((ctrl->rankmap[channel] & 3) && (ctrl->rankmap[channel] & 0xc) + && ref_card[0] <= 5 && ref_card[1] <= 5) { + const int ref_card_offset_table[6][6] = { + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 1, 1, }, + { 2, 2, 2, 1, 0, 0, }, + { 2, 2, 2, 1, 0, 0, }, + }; + ctrl->ref_card_offset[channel] = ref_card_offset_table[ref_card[0]][ref_card[1]]; + } else + ctrl->ref_card_offset[channel] = 0; + } + + if (!dimms) + die("No DIMMs were found"); +} + +static void dram_find_common_params(const dimm_info * dimms, + ramctr_timing * ctrl) +{ + size_t valid_dimms; + int channel, slot; + ctrl->cas_supported = 0xff; + valid_dimms = 0; + FOR_ALL_CHANNELS + for (slot = 0; slot < 2; slot++) { + const dimm_attr *dimm = &dimms->dimm[channel][slot]; + if (dimm->dram_type == SPD_MEMORY_TYPE_UNDEFINED) + continue; + valid_dimms++; + + if (valid_dimms == 1) { + /* First DIMM defines the type of DIMM */ + ctrl->dram_type = dimm->dram_type; + } else { + /* Check if we have mismatched DIMMs */ + if (ctrl->dram_type != dimm->dram_type) + die("Mismatched DIMM Types"); + } + /* Find all possible CAS combinations */ + ctrl->cas_supported &= dimm->cas_supported; + + /* Find the smallest common latencies supported by all DIMMs */ + ctrl->tCK = MAX(ctrl->tCK, dimm->tCK); + ctrl->tAA = MAX(ctrl->tAA, dimm->tAA); + ctrl->tWR = MAX(ctrl->tWR, dimm->tWR); + ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD); + ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD); + ctrl->tRP = MAX(ctrl->tRP, dimm->tRP); + ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS); + ctrl->tRC = MAX(ctrl->tRC, dimm->tRC); + ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC); + ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR); + ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP); + ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW); + } + + ctrl->n_dimms = valid_dimms; + if (!ctrl->cas_supported) + die("Unsupported DIMM combination. " + "DIMMS do not support common CAS latency"); + if (!valid_dimms) + die("No valid DIMMs found"); + + ctrl->dualchannel = + (pcie_read_config32(PCI_DEV(0, 0, 0), 0xE4) & 0x4000) >> 14; + if (ctrl->dualchannel) { + printk(BIOS_DEBUG, "Dual channel supported\n"); + } else { + printk(BIOS_DEBUG, "Dual channel not supported\n"); + } +} + +static u8 get_CWL(u8 CAS) +{ + /* Get CWL based on CAS using the following rule: + * _________________________________________ + * CAS: | 4T | 5T | 6T | 7T | 8T | 9T | 10T | 11T | + * CWL: | 5T | 5T | 5T | 6T | 6T | 7T | 7T | 8T | + */ + static const u8 cas_cwl_map[] = { 5, 5, 5, 6, 6, 7, 7, 8 }; + if (CAS > 11) + return 8; + return cas_cwl_map[CAS - 4]; +} + +static u32 get_REFI(u32 tCK) +{ + /* Get REFI based on MCU frequency using the following rule: + * _________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * REFI: | 3120 | 4160 | 5200 | 6240 | 7280 | 8320 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u32 frq_refi_map[] = + { 3120, 4160, 5200, 6240, 7280, 8320 }; + if (FRQ > 8) + return 8320; + return frq_refi_map[FRQ - 3]; +} + +static u8 get_XSOffset(u32 tCK) +{ + /* Get XSOffset based on MCU frequency using the following rule: + * _________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XSOffset : | 4 | 6 | 7 | 8 | 10 | 11 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xs_map[] = { 4, 6, 7, 8, 10, 11 }; + if (FRQ > 8) + return 11; + return frq_xs_map[FRQ - 3]; +} + +static u8 get_MOD(u32 tCK) +{ + /* Get MOD based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * MOD : | 12 | 12 | 12 | 12 | 15 | 16 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_mod_map[] = { 12, 12, 12, 12, 15, 16 }; + if (FRQ > 8) + return 16; + return frq_mod_map[FRQ - 3]; +} + +static u8 get_WLO(u32 tCK) +{ + /* Get WLO based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * WLO : | 4 | 5 | 6 | 6 | 8 | 8 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_wlo_map[] = { 4, 5, 6, 6, 8, 8 }; + if (FRQ > 8) + return 8; + return frq_wlo_map[FRQ - 3]; +} + +static u8 get_CKE(u32 tCK) +{ + /* Get CKE based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * CKE : | 3 | 3 | 4 | 4 | 5 | 6 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_cke_map[] = { 3, 3, 4, 4, 5, 6 }; + if (FRQ > 8) + return 6; + return frq_cke_map[FRQ - 3]; +} + +static u8 get_XPDLL(u32 tCK) +{ + /* Get XPDLL based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XPDLL : | 10 | 13 | 16 | 20 | 23 | 26 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xpdll_map[] = { 10, 13, 16, 20, 23, 26 }; + if (FRQ > 8) + return 26; + return frq_xpdll_map[FRQ - 3]; +} + +static u8 get_XP(u32 tCK) +{ + /* Get XP based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XP : | 3 | 4 | 4 | 5 | 6 | 7 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xp_map[] = { 3, 4, 4, 5, 6, 7 }; + if (FRQ > 8) + return 7; + return frq_xp_map[FRQ - 3]; +} + +static u8 get_AONPD(u32 tCK) +{ + /* Get AONPD based on MCU frequency using the following rule: + * ________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * AONPD : | 4 | 5 | 6 | 8 | 8 | 10 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_aonpd_map[] = { 4, 5, 6, 8, 8, 10 }; + if (FRQ > 8) + return 10; + return frq_aonpd_map[FRQ - 3]; +} + +static u32 get_COMP2(u32 tCK) +{ + /* Get COMP2 based on MCU frequency using the following rule: + * ___________________________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * COMP : | D6BEDCC | CE7C34C | CA57A4C | C6369CC | C42514C | C21410C | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u32 frq_comp2_map[] = { 0xD6BEDCC, 0xCE7C34C, 0xCA57A4C, + 0xC6369CC, 0xC42514C, 0xC21410C + }; + if (FRQ > 8) + return 0xD6BEDCC; + return frq_comp2_map[FRQ - 3]; +} + +static void dram_timing(ramctr_timing * ctrl) +{ + u8 val; + u32 val32; + + /* Maximum supported DDR3 frequency is 1066MHz (DDR3 2133) so make sure + * we cap it if we have faster DIMMs. + * Then, align it to the closest JEDEC standard frequency */ + if (ctrl->tCK <= TCK_1066MHZ) { + ctrl->tCK = TCK_1066MHZ; + ctrl->delay1 = 16; + ctrl->delay2 = 8; + ctrl->edge_offset[0] = 16; + ctrl->edge_offset[1] = 7; + ctrl->edge_offset[2] = 7; + ctrl->timC_offset[0] = 18; + ctrl->timC_offset[1] = 7; + ctrl->timC_offset[2] = 7; + ctrl->reg_c14_offset = 16; + ctrl->reg_5064b0 = 0x218; + } else if (ctrl->tCK <= TCK_933MHZ) { + ctrl->tCK = TCK_933MHZ; + ctrl->delay1 = 15; + ctrl->delay2 = 8; + ctrl->edge_offset[0] = 14; + ctrl->edge_offset[1] = 6; + ctrl->edge_offset[2] = 6; + ctrl->timC_offset[0] = 15; + ctrl->timC_offset[1] = 6; + ctrl->timC_offset[2] = 6; + ctrl->reg_c14_offset = 14; + ctrl->reg_5064b0 = 0x1d5; + } else if (ctrl->tCK <= TCK_800MHZ) { + ctrl->tCK = TCK_800MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 6; + ctrl->edge_offset[0] = 13; + ctrl->edge_offset[1] = 5; + ctrl->edge_offset[2] = 5; + ctrl->timC_offset[0] = 14; + ctrl->timC_offset[1] = 5; + ctrl->timC_offset[2] = 5; + ctrl->reg_c14_offset = 12; + ctrl->reg_5064b0 = 0x193; + } else if (ctrl->tCK <= TCK_666MHZ) { + ctrl->tCK = TCK_666MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 6; + ctrl->edge_offset[0] = 10; + ctrl->edge_offset[1] = 4; + ctrl->edge_offset[2] = 4; + ctrl->timC_offset[0] = 11; + ctrl->timC_offset[1] = 4; + ctrl->timC_offset[2] = 4; + ctrl->reg_c14_offset = 10; + ctrl->reg_5064b0 = 0x150; + } else { + ctrl->tCK = TCK_533MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 5; + ctrl->edge_offset[0] = 8; + ctrl->edge_offset[1] = 3; + ctrl->edge_offset[2] = 3; + ctrl->timC_offset[0] = 9; + ctrl->timC_offset[1] = 3; + ctrl->timC_offset[2] = 3; + ctrl->reg_c14_offset = 8; + ctrl->reg_5064b0 = 0x10d; + } + + val32 = (1000 << 8) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected DRAM frequency: %u MHz\n", val32); + + /* Find CAS and CWL latencies */ + val = (ctrl->tAA + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Minimum CAS latency : %uT\n", val); + /* Find lowest supported CAS latency that satisfies the minimum value */ + while (!((ctrl->cas_supported >> (val - 4)) & 1) + && (ctrl->cas_supported >> (val - 4))) { + val++; + } + /* Is CAS supported */ + if (!(ctrl->cas_supported & (1 << (val - 4)))) + printk(BIOS_DEBUG, "CAS not supported\n"); + printk(BIOS_DEBUG, "Selected CAS latency : %uT\n", val); + ctrl->CAS = val; + ctrl->CWL = get_CWL(ctrl->CAS); + printk(BIOS_DEBUG, "Selected CWL latency : %uT\n", ctrl->CWL); + + /* Find tRCD */ + ctrl->tRCD = (ctrl->tRCD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRCD : %uT\n", ctrl->tRCD); + + ctrl->tRP = (ctrl->tRP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRP : %uT\n", ctrl->tRP); + + /* Find tRAS */ + ctrl->tRAS = (ctrl->tRAS + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRAS : %uT\n", ctrl->tRAS); + + /* Find tWR */ + ctrl->tWR = (ctrl->tWR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWR : %uT\n", ctrl->tWR); + + /* Find tFAW */ + ctrl->tFAW = (ctrl->tFAW + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tFAW : %uT\n", ctrl->tFAW); + + /* Find tRRD */ + ctrl->tRRD = (ctrl->tRRD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRRD : %uT\n", ctrl->tRRD); + + /* Find tRTP */ + ctrl->tRTP = (ctrl->tRTP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRTP : %uT\n", ctrl->tRTP); + + /* Find tWTR */ + ctrl->tWTR = (ctrl->tWTR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWTR : %uT\n", ctrl->tWTR); + + /* Refresh-to-Active or Refresh-to-Refresh (tRFC) */ + ctrl->tRFC = (ctrl->tRFC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRFC : %uT\n", ctrl->tRFC); + + ctrl->tRC = (ctrl->tRC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Required tRC : %uT\n", ctrl->tRC); + + ctrl->tREFI = get_REFI(ctrl->tCK); + ctrl->tMOD = get_MOD(ctrl->tCK); + ctrl->tXSOffset = get_XSOffset(ctrl->tCK); + ctrl->tWLO = get_WLO(ctrl->tCK); + ctrl->tCKE = get_CKE(ctrl->tCK); + ctrl->tXPDLL = get_XPDLL(ctrl->tCK); + ctrl->tXP = get_XP(ctrl->tCK); + ctrl->tAONPD = get_AONPD(ctrl->tCK); +} + +static void dram_freq(ramctr_timing * ctrl) +{ + u8 val1, val2; + u32 reg1 = 0; + + /* Step 1 - Set target PCU frequency */ + + if (ctrl->tCK <= TCK_1066MHZ) { + val1 = 0x08; + ctrl->tCK = TCK_1066MHZ; + } else if (ctrl->tCK <= TCK_933MHZ) { + val1 = 0x07; + ctrl->tCK = TCK_933MHZ; + } else if (ctrl->tCK <= TCK_800MHZ) { + val1 = 0x06; + ctrl->tCK = TCK_800MHZ; + } else if (ctrl->tCK <= TCK_666MHZ) { + val1 = 0x05; + ctrl->tCK = TCK_666MHZ; + } else { + val1 = 0x04; + ctrl->tCK = TCK_533MHZ; + } + + /* Step 2 - Select frequency in the MCU */ + reg1 = val1; + reg1 |= 0x80000000; // set running bit + MCHBAR32(0x5e00) = reg1; + while (reg1 & 0x80000000) { + printk(BIOS_DEBUG, " PLL busy..."); + reg1 = MCHBAR32(0x5e00); + } + printk(BIOS_DEBUG, "done\n"); + + /* Step 3 - Verify lock frequency */ + reg1 = MCHBAR32(0x5e04); + val2 = (u8) reg1; + if (val2 > val1) { + printk(BIOS_DEBUG, "Lock frequency is lower, recalculating\n"); + switch (val2) { + case 8: + ctrl->tCK = TCK_1066MHZ; + break; + case 7: + ctrl->tCK = TCK_933MHZ; + break; + case 6: + ctrl->tCK = TCK_800MHZ; + break; + case 5: + ctrl->tCK = TCK_666MHZ; + break; + case 4: + ctrl->tCK = TCK_533MHZ; + break; + default: + printk(BIOS_DEBUG, "ERROR: PLL is off or unknown\n"); + break; + } + dram_timing(ctrl); // recalculate timings + } + printk(BIOS_DEBUG, "MCU frequency is set at : %d MHz\n", + (1000 << 8) / ctrl->tCK); +} + +static void dram_xover(ramctr_timing * ctrl) +{ + u32 reg; + int channel; + + FOR_ALL_CHANNELS { + // enable xover clk + printk(BIOS_DEBUG, "[%x] = %x\n", channel * 0x100 + 0xc14, + (ctrl->rankmap[channel] << 24)); + MCHBAR32(channel * 0x100 + 0xc14) = (ctrl->rankmap[channel] << 24); + + // enable xover ctl + reg = 0; + if (ctrl->rankmap[channel] & 0x5) { + reg |= 0x20000; + } + if (ctrl->rankmap[channel] & 0xa) { + reg |= 0x4000000; + } + // enable xover cmd + reg |= 0x4000; + printk(BIOS_DEBUG, "[%x] = %x\n", 0x100 * channel + 0x320c, + reg); + MCHBAR32(0x100 * channel + 0x320c) = reg; + } +} + +static void dram_timing_regs(ramctr_timing * ctrl) +{ + u32 reg, addr, val32, cpu, stretch; + struct cpuid_result cpures; + int channel; + + FOR_ALL_CHANNELS { + // DBP + reg = 0; + reg |= ctrl->tRCD; + reg |= (ctrl->tRP << 4); + reg |= (ctrl->CAS << 8); + reg |= (ctrl->CWL << 12); + reg |= (ctrl->tRAS << 16); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4000, + reg); + MCHBAR32(0x400 * channel + 0x4000) = reg; + + // RAP + reg = 0; + reg |= ctrl->tRRD; + reg |= (ctrl->tRTP << 4); + reg |= (ctrl->tCKE << 8); + reg |= (ctrl->tWTR << 12); + reg |= (ctrl->tFAW << 16); + reg |= (ctrl->tWR << 24); + reg |= (3 << 30); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4004, + reg); + MCHBAR32(0x400 * channel + 0x4004) = reg; + + // OTHP + addr = 0x400 * channel + 0x400c; + reg = 0; + reg |= ctrl->tXPDLL; + reg |= (ctrl->tXP << 5); + reg |= (ctrl->tAONPD << 8); + reg |= 0xa0000; + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + + MCHBAR32(0x400 * channel + 0x4014) = 0; + + MCHBAR32(addr) |= 0x00020000; + + // ODT stretch + reg = 0; + + cpures = cpuid(0); + cpu = cpures.eax; + if (IS_IVY_CPU(cpu) + || (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_D2(cpu))) { + stretch = 2; + addr = 0x400 * channel + 0x400c; + printk(BIOS_DEBUG, "[%x] = %x\n", + 0x400 * channel + 0x400c, reg); + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0xc0000) | (stretch << 18); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0x30000) | (stretch << 16); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + + } else if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) { + stretch = 3; + addr = 0x400 * channel + 0x401c; + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0x3000) | (stretch << 12); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0xc00) | (stretch << 10); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + } else { + stretch = 0; + } + + // REFI + reg = 0; + val32 = ctrl->tREFI; + reg = (reg & ~0xffff) | val32; + val32 = ctrl->tRFC; + reg = (reg & ~0x1ff0000) | (val32 << 16); + val32 = (u32) (ctrl->tREFI * 9) / 1024; + reg = (reg & ~0xfe000000) | (val32 << 25); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4298, + reg); + MCHBAR32(0x400 * channel + 0x4298) = reg; + + MCHBAR32(0x400 * channel + 0x4294) |= 0xff; + + // SRFTP + reg = 0; + val32 = tDLLK; + reg = (reg & ~0xfff) | val32; + val32 = ctrl->tXSOffset; + reg = (reg & ~0xf000) | (val32 << 12); + val32 = tDLLK - ctrl->tXSOffset; + reg = (reg & ~0x3ff0000) | (val32 << 16); + val32 = ctrl->tMOD - 8; + reg = (reg & ~0xf0000000) | (val32 << 28); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x42a4, + reg); + MCHBAR32(0x400 * channel + 0x42a4) = reg; + } +} + +static void dram_dimm_mapping(dimm_info * info, ramctr_timing * ctrl) +{ + int t; + u32 reg, val32; + int channel; + + FOR_ALL_CHANNELS { + dimm_attr *dimmA = 0; + dimm_attr *dimmB = 0; + reg = 0; + val32 = 0; + if (info->dimm[channel][0].size_mb >= + info->dimm[channel][1].size_mb) { + // dimm 0 is bigger, set it to dimmA + dimmA = &info->dimm[channel][0]; + dimmB = &info->dimm[channel][1]; + reg |= (0 << 16); + } else { + // dimm 1 is bigger, set it to dimmA + dimmA = &info->dimm[channel][1]; + dimmB = &info->dimm[channel][0]; + reg |= (1 << 16); + // swap dimm info + t = ctrl->rank_mirror[channel][1]; + ctrl->rank_mirror[channel][1] = + ctrl->rank_mirror[channel][3]; + ctrl->rank_mirror[channel][3] = t; + } + // dimmA + if (dimmA && (dimmA->ranks > 0)) { + val32 = dimmA->size_mb / 256; + reg = (reg & ~0xff) | val32; + val32 = dimmA->ranks - 1; + reg = (reg & ~0x20000) | (val32 << 17); + val32 = (dimmA->width / 8) - 1; + reg = (reg & ~0x80000) | (val32 << 19); + } + // dimmB + if (dimmB && (dimmB->ranks > 0)) { + val32 = dimmB->size_mb / 256; + reg = (reg & ~0xff00) | (val32 << 8); + val32 = dimmB->ranks - 1; + reg = (reg & ~0x40000) | (val32 << 18); + val32 = (dimmB->width / 8) - 1; + reg = (reg & ~0x100000) | (val32 << 20); + } + reg = (reg & ~0x200000) | (1 << 21); // rank interleave + reg = (reg & ~0x400000) | (1 << 22); // enhanced interleave + + // Set MAD-DIMM register + if ((dimmA && (dimmA->ranks > 0)) + || (dimmB && (dimmB->ranks > 0))) { + MCHBAR32(0x5004 + channel * 4) = reg; + } + } +} + +static void dram_zones(dimm_info * info, ramctr_timing * ctrl, int training) +{ + u32 reg, ch0size, ch1size; + u8 val; + reg = 0; + val = 0; + if (training) { + ch0size = + info->dimm[0][0].size_mb + info->dimm[0][1].size_mb ? 256 : 0; + ch1size = + info->dimm[1][0].size_mb + info->dimm[1][1].size_mb ? 256 : 0; + } else { + ch0size = info->dimm[0][0].size_mb + info->dimm[0][1].size_mb; + ch1size = info->dimm[1][0].size_mb + info->dimm[1][1].size_mb; + } + + if (ch0size >= ch1size) { + reg = MCHBAR32(0x5014); + val = ch1size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x24; + } else { + reg = MCHBAR32(0x5014); + val = ch0size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x21; + } +} + +/* FIXME: this function bugs. */ +static void dram_memorymap(dimm_info * info, int me_uma_size) +{ + u32 reg, val, reclaim; + u32 tom, gfxstolen, gttsize; + size_t tsegsize, mmiosize, toludbase, touudbase, gfxstolenbase, gttbase, + tsegbase, mestolenbase; + size_t tsegbasedelta, remapbase, remaplimit; + uint16_t ggc; + + mmiosize = 0x400; + + ggc = pci_read_config16(NORTHBRIDGE, GGC); + if (!(ggc & 2)) { + gfxstolen = ((ggc >> 3) & 0x1f) * 32; + gttsize = ((ggc >> 8) & 0x3); + } else { + gfxstolen = 0; + gttsize = 0; + } + + tsegsize = CONFIG_SMM_TSEG_SIZE >> 20; + + tom = + info->dimm[0][0].size_mb + info->dimm[0][1].size_mb + + info->dimm[0][1].size_mb + info->dimm[1][1].size_mb; + + mestolenbase = tom - me_uma_size; + + toludbase = MIN(4096 - mmiosize, tom - me_uma_size); + gfxstolenbase = toludbase - gfxstolen; + gttbase = gfxstolenbase - gttsize; + + tsegbase = gttbase - tsegsize; + + // Round tsegbase down to nearest address aligned to tsegsize + tsegbasedelta = tsegbase & (tsegsize - 1); + tsegbase &= ~(tsegsize - 1); + + gttbase -= tsegbasedelta; + gfxstolenbase -= tsegbasedelta; + toludbase -= tsegbasedelta; + + // Test if it is possible to reclaim a hole in the ram addressing + if (tom - me_uma_size > toludbase) { + // Reclaim is possible + reclaim = 1; + remapbase = MAX(4096, tom - me_uma_size); + remaplimit = + remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1; + touudbase = remaplimit + 1; + } else { + // Reclaim not possible + reclaim = 0; + touudbase = tom - me_uma_size; + } + + // Update memory map in pci-e configuration space + + // TOM (top of memory) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa0); + val = tom & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa0, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa4); + val = tom & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa4, reg); + + // TOLUD (top of low used dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xbc); + val = toludbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xbc, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xbc, reg); + + // TOUUD LSB (top of upper usable dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa8); + val = touudbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa8, reg); + + // TOUUD MSB + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xac); + val = touudbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xac, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xac, reg); + + if (reclaim) { + // REMAP BASE + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x94); + val = remapbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x94, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x94, reg); + + // REMAP LIMIT + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x98); + val = remaplimit & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x98, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x98, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x9c); + val = remaplimit & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x9c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x9c, reg); + } + // TSEG + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb8); + val = tsegbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb8, reg); + + // GFX stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb0); + val = gfxstolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb0, reg); + + // GTT stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb4); + val = gttbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb4, reg); + + if (me_uma_size) { + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x7c); + val = (0x80000 - me_uma_size) & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x7c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x7c, reg); + + // ME base + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x70); + val = mestolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x70, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x70, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x74); + val = mestolenbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x74, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x74, reg); + + // ME mask + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x78); + val = (0x80000 - me_uma_size) & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + reg = (reg & ~0x400) | (1 << 10); // set lockbit on ME mem + + reg = (reg & ~0x800) | (1 << 11); // set ME memory enable + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x78, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x78, reg); + } +} + +static void dram_ioregs(ramctr_timing * ctrl) +{ + u32 reg, comp2; + + int channel; + + // IO clock + FOR_ALL_CHANNELS { + MCHBAR32(0xc00 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO command + FOR_ALL_CHANNELS { + MCHBAR32(0x3200 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO control + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + + // Rcomp + printk(BIOS_DEBUG, "RCOMP..."); + reg = 0; + while (reg == 0) { + reg = MCHBAR32(0x5084) & 0x10000; + } + printk(BIOS_DEBUG, "done\n"); + + // Set comp2 + comp2 = get_COMP2(ctrl->tCK); + MCHBAR32(0x3714) = comp2; + printk(BIOS_DEBUG, "COMP2 done\n"); + + // Set comp1 + FOR_ALL_POPULATED_CHANNELS { + reg = MCHBAR32(0x1810 + channel * 0x100); //ch0 + reg = (reg & ~0xe00) | (1 << 9); //odt + reg = (reg & ~0xe00000) | (1 << 21); //clk drive up + reg = (reg & ~0x38000000) | (1 << 27); //ctl drive up + MCHBAR32(0x1810 + channel * 0x100) = reg; + } + printk(BIOS_DEBUG, "COMP1 done\n"); + + printk(BIOS_DEBUG, "FORCE RCOMP and wait 20us..."); + MCHBAR32(0x5f08) |= 0x100; + udelay(20); + printk(BIOS_DEBUG, "done\n"); +} + +static void wait_428c(int channel) +{ + while (1) { + if (read32(DEFAULT_MCHBAR | 0x428c | (channel << 10)) & 0x50) + return; + } +} + +static void write_reset(ramctr_timing * ctrl) +{ + int channel, slotrank; + + /* choose a populated channel. */ + channel = (ctrl->rankmap[0]) ? 0 : 1; + + wait_428c(channel); + + /* choose a populated rank. */ + slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x80c01); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x400001); + wait_428c(channel); +} + +static void dram_jedecreset(ramctr_timing * ctrl) +{ + u32 reg, addr; + int channel; + + while (!(MCHBAR32(0x5084) & 0x10000)) ; + do { + reg = MCHBAR32(0x428c); + } while ((reg & 0x14) == 0); + + // Set state of memory controller + reg = 0x112; + MCHBAR32(0x5030) = reg; + MCHBAR32(0x4ea0) = 0; + reg |= 2; //ddr reset + MCHBAR32(0x5030) = reg; + + // Assert dimm reset signal + reg = MCHBAR32(0x5030); + reg &= ~0x2; + MCHBAR32(0x5030) = reg; + + // Wait 200us + udelay(200); + + // Deassert dimm reset signal + MCHBAR32(0x5030) |= 2; + + // Wait 500us + udelay(500); + + // Enable DCLK + MCHBAR32(0x5030) |= 4; + + // XXX Wait 20ns + udelay(1); + + FOR_ALL_CHANNELS { + // Set valid rank CKE + reg = 0; + reg = (reg & ~0xf) | ctrl->rankmap[channel]; + addr = 0x400 * channel + 0x42a0; + MCHBAR32(addr) = reg; + + // Wait 10ns for ranks to settle + //udelay(0.01); + + reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4); + MCHBAR32(addr) = reg; + + // Write reset using a NOP + write_reset(ctrl); + } +} + +static odtmap get_ODT(ramctr_timing * ctrl, u8 rank) +{ + /* Get ODT based on rankmap: */ + int dimms_per_ch = 0; + int channel; + + FOR_ALL_CHANNELS { + dimms_per_ch = max ((ctrl->rankmap[channel] & 1) + + ((ctrl->rankmap[channel] >> 2) & 1), + dimms_per_ch); + } + + if (dimms_per_ch == 1) { + return (const odtmap){60, 60}; + } else if (dimms_per_ch == 2) { + return (const odtmap){120, 30}; + } else { + printk(BIOS_DEBUG, + "Huh, no dimms? m0 = %d m1 = %d dpc = %d\n", + ctrl->rankmap[0], + ctrl->rankmap[1], dimms_per_ch); + die(""); + } +} + +static void write_mrreg(ramctr_timing * ctrl, int channel, int slotrank, + int reg, u32 val) +{ + wait_428c(channel); + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + if (ctrl->rank_mirror[channel][slotrank]) { + reg = ((reg >> 1) & 1) | ((reg << 1) & 2); + val = (val & ~0x1f8) | ((val >> 1) & 0xa8) + | ((val & 0xa8) << 1); + } + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); +} + +static u32 make_mr0(ramctr_timing * ctrl, u8 rank) +{ + u16 mr0reg, mch_cas, mch_wr; + static const u8 mch_wr_t[12] = { 1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0 }; + mr0reg = 0x100; + + // Convert CAS to MCH register friendly + if (ctrl->CAS < 12) { + mch_cas = (u16) ((ctrl->CAS - 4) << 1); + } else { + mch_cas = (u16) (ctrl->CAS - 12); + mch_cas = ((mch_cas << 1) | 0x1); + } + + // Convert tWR to MCH register friendly + mch_wr = mch_wr_t[ctrl->tWR - 5]; + + mr0reg = (mr0reg & ~0x4) | (mch_cas & 0x1); + mr0reg = (mr0reg & ~0x70) | ((mch_cas & 0xe) << 3); + mr0reg = (mr0reg & ~0xe00) | (mch_wr << 9); + // Fast (desktop) 0x1 or slow (mobile) 0x0 + mr0reg = (mr0reg & ~0x1000) | (!ctrl->mobile << 12); + return mr0reg; +} + +static void dram_mr0(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS write_mrreg(ctrl, channel, rank, 0, + make_mr0(ctrl, rank)); +} + +static u32 encode_odt(u32 odt) +{ + switch (odt) { + case 30: + return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4 + case 60: + return (1 << 2); // RZQ/4 + case 120: + return (1 << 6); // RZQ/2 + default: + case 0: + return 0; + } +} + +static u32 make_mr1(ramctr_timing * ctrl, u8 rank) +{ + odtmap odt; + u32 mr1reg; + + odt = get_ODT(ctrl, rank); + mr1reg = 0x2; + + mr1reg |= encode_odt(odt.rttnom); + + return mr1reg; +} + +static void dram_mr1(ramctr_timing * ctrl, u8 rank) +{ + u16 mr1reg; + int channel; + + mr1reg = make_mr1(ctrl, rank); + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 1, mr1reg); + } +} + +static void dram_mr2(ramctr_timing * ctrl, u8 rank) +{ + u16 pasr, cwl, mr2reg; + odtmap odt; + int channel; + int srt; + + pasr = 0; + cwl = ctrl->CWL - 5; + odt = get_ODT(ctrl, rank); + + srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh; + + mr2reg = 0; + mr2reg = (mr2reg & ~0x7) | pasr; + mr2reg = (mr2reg & ~0x38) | (cwl << 3); + mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6); + mr2reg = (mr2reg & ~0x80) | (srt << 7); + mr2reg |= (odt.rttwr / 60) << 9; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 2, mr2reg); + } +} + +static void dram_mr3(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 3, 0); + } +} + +static void dram_mrscommands(ramctr_timing * ctrl) +{ + u8 rank; + u32 reg, addr; + int channel; + + for (rank = 0; rank < 4; rank++) { + // MR2 + printk(BIOS_SPEW, "MR2 rank %d...", rank); + dram_mr2(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR3 + printk(BIOS_SPEW, "MR3 rank %d...", rank); + dram_mr3(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR1 + printk(BIOS_SPEW, "MR1 rank %d...", rank); + dram_mr1(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR0 + printk(BIOS_SPEW, "MR0 rank %d...", rank); + dram_mr0(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + } + + write32(DEFAULT_MCHBAR + 0x4e20, 0x7); + write32(DEFAULT_MCHBAR + 0x4e30, 0xf1001); + write32(DEFAULT_MCHBAR + 0x4e00, 0x60002); + write32(DEFAULT_MCHBAR + 0x4e10, 0); + write32(DEFAULT_MCHBAR + 0x4e24, 0x1f003); + write32(DEFAULT_MCHBAR + 0x4e34, 0x1901001); + write32(DEFAULT_MCHBAR + 0x4e04, 0x60400); + write32(DEFAULT_MCHBAR + 0x4e14, 0x288); + write32(DEFAULT_MCHBAR + 0x4e84, 0x40004); + + // Drain + FOR_ALL_CHANNELS { + // Wait for ref drained + wait_428c(channel); + } + + // Refresh enable + MCHBAR32(0x5030) |= 8; + + FOR_ALL_POPULATED_CHANNELS { + addr = 0x400 * channel + 0x4020; + reg = MCHBAR32(addr); + reg &= ~0x200000; + MCHBAR32(addr) = reg; + + wait_428c(channel); + + rank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + // Drain + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (rank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x1); + + // Drain + wait_428c(channel); + } +} + +const u32 lane_registers[] = { + 0x0000, 0x0200, 0x0400, 0x0600, + 0x1000, 0x1200, 0x1400, 0x1600, + 0x0800 +}; + +static int clamp(int val, int low, int up) +{ + if (val < low) + return low; + if (val > up) + return up; + return val; +} + +static void program_timings(ramctr_timing * ctrl, int channel) +{ + u32 reg32, reg_4024, reg_c14, reg_c18, reg_4028; + int lane; + int slotrank, slot; + int full_shift = 0; + u16 slot320c[NUM_SLOTS]; + + FOR_ALL_POPULATED_RANKS if (full_shift < + -ctrl->timings[channel][slotrank].val_320c) + full_shift = -ctrl->timings[channel][slotrank].val_320c; + + for (slot = 0; slot < NUM_SLOTS; slot++) + switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) { + case 0: + default: + slot320c[slot] = 0x7f; + break; + case 1: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 0].val_320c + + full_shift; + break; + case 2: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 1].val_320c + + full_shift; + break; + case 3: + slot320c[slot] = + (ctrl->timings[channel][2 * slot].val_320c + + ctrl->timings[channel][2 * slot + + 1].val_320c) / 2 + + full_shift; + break; + } + + reg32 = (1 << 17) | (1 << 14); + reg32 |= ((slot320c[0] & 0x3f) << 6) | ((slot320c[0] & 0x40) << 9); + reg32 |= (slot320c[1] & 0x7f) << 18; + reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6); + + MCHBAR32(0x320c + 0x100 * channel) = reg32; + + reg_c14 = ctrl->rankmap[channel] << 24; + reg_c18 = 0; + + FOR_ALL_POPULATED_RANKS { + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + int offset_val_c14; + if (shift < 0) + shift = 0; + offset_val_c14 = ctrl->reg_c14_offset + shift; + reg_c14 |= (offset_val_c14 & 0x3f) << (6 * slotrank); + reg_c18 |= ((offset_val_c14 >> 6) & 1) << slotrank; + } + + MCHBAR32(0xc14 + channel * 0x100) = reg_c14; + MCHBAR32(0xc18 + channel * 0x100) = reg_c18; + + reg_4028 = MCHBAR32(0x4028 + 0x400 * channel); + reg_4028 &= 0xffff0000; + + reg_4024 = 0; + + FOR_ALL_POPULATED_RANKS { + int post_timA_min_high = 7, post_timA_max_high = 0; + int pre_timA_min_high = 7, pre_timA_max_high = 0; + int shift_402x = 0; + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + + if (shift < 0) + shift = 0; + + FOR_ALL_LANES { + if (post_timA_min_high > + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_min_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_min_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + if (post_timA_max_high < + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_max_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_max_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + } + + if (pre_timA_max_high - pre_timA_min_high < + post_timA_max_high - post_timA_min_high) + shift_402x = +1; + else if (pre_timA_max_high - pre_timA_min_high > + post_timA_max_high - post_timA_min_high) + shift_402x = -1; + + reg_4028 |= + (ctrl->timings[channel][slotrank].val_4028 + shift_402x - + post_timA_min_high) << (4 * slotrank); + reg_4024 |= + (ctrl->timings[channel][slotrank].val_4024 + + shift_402x) << (8 * slotrank); + + FOR_ALL_LANES { + MCHBAR32(lane_registers[lane] + 0x10 + 0x100 * channel + + 4 * slotrank) + = + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) & 0x3f) + | + ((ctrl->timings[channel][slotrank].lanes[lane]. + rising + shift) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift - + (post_timA_min_high << 6)) & 0x1c0) << 10) + | (ctrl->timings[channel][slotrank].lanes[lane]. + falling << 20)); + + MCHBAR32(lane_registers[lane] + 0x20 + 0x100 * channel + + 4 * slotrank) + = + ((clamp + (ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift, 0, 127) & 0x3f) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x3f) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x1c0) << 9) + | + ((clamp + (ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift, 0, 127) & 0x40) << 13)); + } + } + MCHBAR32(0x4024 + 0x400 * channel) = reg_4024; + MCHBAR32(0x4028 + 0x400 * channel) = reg_4028; +} + +static void test_timA(ramctr_timing * ctrl, int channel, int slotrank) +{ + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x4040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x100f | ((ctrl->CAS + 36) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); +} + +static int does_lane_work(ramctr_timing * ctrl, int channel, int slotrank, + int lane) +{ + u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA; + return ((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + channel * 0x100 + 4 + + ((timA / 32) & 1) * 4) + >> (timA % 32)) & 1); +} + +struct run { + int middle; + int end; + int start; + int all; +}; + +static struct run get_longest_zero_run(int *seq, int sz) +{ + int i, ls; + int bl = 0, bs = 0; + struct run ret; + + ls = 0; + for (i = 0; i < 2 * sz; i++) + if (seq[i % sz]) { + if (i - ls > bl) { + bl = i - ls; + bs = ls; + } + ls = i + 1; + } + if (bl == 0) { + ret.middle = sz / 2; + ret.start = 0; + ret.end = sz; + ret.all = 1; + return ret; + } + + ret.start = bs % sz; + ret.end = (bs + bl - 1) % sz; + ret.middle = (bs + (bl - 1) / 2) % sz; + ret.all = 0; + + return ret; +} + +static void discover_timA_coarse(ramctr_timing * ctrl, int channel, + int slotrank, int *upperA) +{ + int timA; + int statistics[NUM_LANES][128]; + int lane; + + for (timA = 0; timA < 128; timA++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = timA; + program_timings(ctrl, channel); + + test_timA(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timA] = + !does_lane_work(ctrl, channel, slotrank, lane); + printk(BIOS_SPEW, "Astat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timA, + statistics[lane][timA]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle; + upperA[lane] = rn.end; + if (upperA[lane] < rn.middle) + upperA[lane] += 128; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + printk(BIOS_SPEW, "Aend: %d, %d, %d, %x\n", channel, slotrank, + lane, upperA[lane]); + } +} + +static void discover_timA_fine(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int timA_delta; + int statistics[NUM_LANES][51]; + int lane, i; + + memset(statistics, 0, sizeof(statistics)); + + for (timA_delta = -25; timA_delta <= 25; timA_delta++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = upperA[lane] + timA_delta + 0x40; + program_timings(ctrl, channel); + + for (i = 0; i < 100; i++) { + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + statistics[lane][timA_delta + 25] += + does_lane_work(ctrl, channel, slotrank, + lane); + } + } + FOR_ALL_LANES { + printk(BIOS_SPEW, "A+stat: %d, %d, %d, %d (%x), %x\n", + channel, slotrank, lane, timA_delta, + upperA[lane] + timA_delta + 0x40, + statistics[lane][timA_delta + 25]); + } + } + FOR_ALL_LANES { + int last_zero, first_all; + + for (last_zero = -25; last_zero <= 25; last_zero++) + if (statistics[lane][last_zero + 25]) + break; + last_zero--; + for (first_all = -25; first_all <= 25; first_all++) + if (statistics[lane][first_all + 25] == 100) + break; + + printk(BIOS_SPEW, "lane %d: %d, %d\n", lane, last_zero, + first_all); + + ctrl->timings[channel][slotrank].lanes[lane].timA = + (last_zero + first_all) / 2 + upperA[lane]; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + } +} + +static void discover_402x(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int works[NUM_LANES]; + int lane; + while (1) { + int all_works = 1, some_works = 0; + program_timings(ctrl, channel); + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + works[lane] = + !does_lane_work(ctrl, channel, slotrank, lane); + if (works[lane]) + some_works = 1; + else + all_works = 0; + } + if (all_works) + return; + if (!some_works) { + if (ctrl->timings[channel][slotrank].val_4024 < 2) + die("402x discovery failed"); + ctrl->timings[channel][slotrank].val_4024 -= 2; + printk(BIOS_SPEW, "4024 -= 2;\n"); + continue; + } + ctrl->timings[channel][slotrank].val_4028 += 2; + printk(BIOS_SPEW, "4028 += 2;\n"); + if (ctrl->timings[channel][slotrank].val_4028 >= 0x10) + die("402x discovery failed"); + FOR_ALL_LANES if (works[lane]) { + ctrl->timings[channel][slotrank].lanes[lane].timA += + 128; + upperA[lane] += 128; + printk(BIOS_SPEW, "increment %d, %d, %d\n", channel, + slotrank, lane); + } + } +} + +struct timA_minmax { + int timA_min_high, timA_max_high; +}; + +static void pre_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + int lane; + mnmx->timA_min_high = 7; + mnmx->timA_max_high = 0; + + FOR_ALL_LANES { + if (mnmx->timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_min_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + if (mnmx->timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_max_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + } +} + +static void post_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + struct timA_minmax post; + int shift_402x = 0; + + /* Get changed maxima. */ + pre_timA_change(ctrl, channel, slotrank, &post); + + if (mnmx->timA_max_high - mnmx->timA_min_high < + post.timA_max_high - post.timA_min_high) + shift_402x = +1; + else if (mnmx->timA_max_high - mnmx->timA_min_high > + post.timA_max_high - post.timA_min_high) + shift_402x = -1; + else + shift_402x = 0; + + ctrl->timings[channel][slotrank].val_4028 += shift_402x; + ctrl->timings[channel][slotrank].val_4024 += shift_402x; + printk(BIOS_SPEW, "4024 += %d;\n", shift_402x); + printk(BIOS_SPEW, "4028 += %d;\n", shift_402x); +} + +static void read_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + u32 r32; + int all_high, some_high; + int upperA[NUM_LANES]; + struct timA_minmax mnmx; + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + write32(DEFAULT_MCHBAR + 0x3400, (slotrank << 2) | 0x8001); + + ctrl->timings[channel][slotrank].val_4028 = 4; + ctrl->timings[channel][slotrank].val_4024 = 55; + program_timings(ctrl, channel); + + discover_timA_coarse(ctrl, channel, slotrank, upperA); + + all_high = 1; + some_high = 0; + FOR_ALL_LANES if (ctrl->timings[channel][slotrank].lanes[lane]. + timA >= 0x40) + some_high = 1; + else + all_high = 0; + if (all_high) { + ctrl->timings[channel][slotrank].val_4028--; + printk(BIOS_SPEW, "4028--;\n"); + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + timA -= 0x40; + upperA[lane] -= 0x40; + + } + } else if (some_high) { + ctrl->timings[channel][slotrank].val_4024++; + ctrl->timings[channel][slotrank].val_4028++; + printk(BIOS_SPEW, "4024++;\n"); + printk(BIOS_SPEW, "4028++;\n"); + } + + program_timings(ctrl, channel); + + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_402x(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_timA_fine(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA -= mnmx.timA_min_high * 0x40; + ctrl->timings[channel][slotrank].val_4028 -= mnmx.timA_min_high; + printk(BIOS_SPEW, "4028 -= %d;\n", mnmx.timA_min_high); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + + printk(BIOS_SPEW, "4/8: %d, %d, %x, %x\n", channel, slotrank, + ctrl->timings[channel][slotrank].val_4024, + ctrl->timings[channel][slotrank].val_4028); + + FOR_ALL_LANES + printk(BIOS_SPEW, "%d, %d, %d, %x\n", channel, slotrank, + lane, + ctrl->timings[channel][slotrank].lanes[lane].timA); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + } + + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + 4 * lane, 0); +} + +static void test_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int lane; + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x4140 + 0x400 * channel + 4 * lane); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) + | 4 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (6 << 16)); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8041001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x80411f4); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) + | 8 | (ctrl->CAS << 16)); + + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x40011f4 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timC; + int statistics[NUM_LANES][MAX_TIMC + 1]; + int lane; + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + for (timC = 0; timC <= MAX_TIMC; timC++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timC = timC; + program_timings(ctrl, channel); + + test_timC(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timC] = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + printk(BIOS_SPEW, "Cstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timC, + statistics[lane][timC]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_TIMC + 1); + ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle; + if (rn.all) + die("timC discovery failed"); + printk(BIOS_SPEW, "Cval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timC); + } +} + +static int get_precedening_channels(ramctr_timing * ctrl, int target_channel) +{ + int channel, ret = 0; + FOR_ALL_POPULATED_CHANNELS if (channel < target_channel) + ret++; + return ret; +} + +static void fill_pattern0(ramctr_timing * ctrl, int channel, u32 a, u32 b) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + printk(BIOS_SPEW, "channel_offset=%x\n", channel_offset); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + 4 * j, j & 2 ? b : a); + sfence(); +} + +static int num_of_channels(const ramctr_timing * ctrl) +{ + int ret = 0; + int channel; + FOR_ALL_POPULATED_CHANNELS ret++; + return ret; +} + +static void fill_pattern1(ramctr_timing * ctrl, int channel) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + j * 4, 0xffffffff); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + channel_step + j * 4, 0); + sfence(); +} + +static void precharge(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + } +} + +static void test_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + write_mrreg(ctrl, channel, slotrank, 1, + 0x80 | make_mr1(ctrl, slotrank)); + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->delay2) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + 8 | (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f107); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4000c01 | ((ctrl->CAS + 38) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 4); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x400 * channel + 0x4284, 0x40001); + wait_428c(channel); + + write_mrreg(ctrl, channel, slotrank, 1, + 0x1080 | make_mr1(ctrl, slotrank)); +} + +static void discover_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timB; + int statistics[NUM_LANES][128]; + int lane; + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052 | (slotrank << 2)); + + for (timB = 0; timB < 128; timB++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timB = timB; + program_timings(ctrl, channel); + + test_timB(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timB] = + !((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + + channel * 0x100 + 4 + ((timB / 32) & 1) * 4) + >> (timB % 32)) & 1); + printk(BIOS_SPEW, "Bstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timB, + statistics[lane][timB]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timB = rn.end; + if (rn.all) + die("timB discovery failed"); + printk(BIOS_SPEW, "Bval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timB); + } +} + +static int get_timB_high_adjust(u64 val) +{ + int i; + if (val >= 0xfffffffffff00000LL) + return -1; + if (val >= 0xfffffff000000000LL) + return -2; + if (val >= 0xfff0000000000000LL) + return -3; + + for (i = 0; i < 8; i++) + if (val >> (8 * (7 - i) + 4)) + return i; + return 8; +} + +static void adjust_high_timB(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + write32(DEFAULT_MCHBAR + 0x3400, 0x200); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern1(ctrl, channel); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 1); + } + FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS { + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x10001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRCD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x8041003); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x3e2); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | ((ctrl->tRP) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0xc01 | ((ctrl->tRCD) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x3f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4000c01 | + ((ctrl->tRP + + ctrl->timings[channel][slotrank].val_4024 + + ctrl->timings[channel][slotrank].val_4028) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60008); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); + wait_428c(channel); + FOR_ALL_LANES { + u64 res = + read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 4); + res |= + ((u64) read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 8)) << 32; + ctrl->timings[channel][slotrank].lanes[lane].timB += + get_timB_high_adjust(res) * 64; + printk(BIOS_SPEW, "Bval+: %d, %d, %d, %x\n", channel, + slotrank, lane, + ctrl->timings[channel][slotrank].lanes[lane]. + timB); + } + } + write32(DEFAULT_MCHBAR + 0x3400, 0); +} + +static void write_op(ramctr_timing * ctrl, int channel) +{ + int slotrank; + + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); +} + +static void write_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS + write32(DEFAULT_MCHBAR + 0x4008 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4008 + + 0x400 * channel) | 0x8000000); + + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + } + + FOR_ALL_CHANNELS + FOR_ALL_POPULATED_RANKS write_mrreg(ctrl, channel, slotrank, 1, + make_mr1(ctrl, + slotrank) | 0x1080); + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS discover_timB(ctrl, channel, + slotrank); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS write_mrreg(ctrl, channel, + slotrank, 1, + make_mr1(ctrl, + slotrank)); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + FOR_ALL_POPULATED_CHANNELS wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) | 8); + + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + ~0x00200000 & read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel)); + read32(DEFAULT_MCHBAR + 0x428c + 0x400 * channel); + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + printk(BIOS_SPEW, "CPE\n"); + precharge(ctrl); + printk(BIOS_SPEW, "CPF\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + discover_timC(ctrl, channel, slotrank); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + adjust_high_timB(ctrl); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static int test_320c(ramctr_timing * ctrl, int channel, int slotrank) +{ + struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank]; + int timC_delta; + int lanes_ok = 0; + int ctr = 0; + int lane; + + for (timC_delta = -5; timC_delta <= 5; timC_delta++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].timC = + saved_rt.lanes[lane].timC + timC_delta; + } + program_timings(ctrl, channel); + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 4 * lane + 0x4f40, 0); + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + ((max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10) + | 8 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | ctr | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4244 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4001020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4248 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, 0xf1001); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + u32 r32 = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + + if (r32 == 0) + lanes_ok |= 1 << lane; + } + ctr++; + if (lanes_ok == ((1 << NUM_LANES) - 1)) + break; + } + + ctrl->timings[channel][slotrank] = saved_rt; + return lanes_ok != ((1 << NUM_LANES) - 1); +} + +const u32 pattern[][16] = { + {0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff}, + {0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000}, + {0xe62d6424, 0x9277e09e, 0x8f43dc3f, 0x76eae589, + 0x0010fdc6, 0xdc55e01c, 0x5effb0ab, 0x6cba5d29, + 0xa43d1e64, 0xab5c2e0f, 0x7796ed16, 0x96023bf4, + 0xa74c831d, 0x90f138c0, 0x17830a8a, 0x5ac17c47}, + {0x359ebbeb, 0x2b9b4512, 0xef584d98, 0x106bf7cb, + 0x363525ad, 0xb3a4dfdc, 0xa6b9fcd8, 0xd21689ec, + 0x84a3695b, 0xbd9c2e27, 0xdb3d0f44, 0x988158f1, + 0xcca91d3f, 0xb62a6d12, 0xe905e4cf, 0x7f1fa626}, + {0xe58efeae, 0xcd006081, 0xa9119403, 0xbcfbd35f, + 0x213b3bf7, 0x7bfcb773, 0xc85143f9, 0x0bdbff50, + 0xa3053c90, 0x51d66cb7, 0x296f4387, 0xb715f99e, + 0xfaddc989, 0xbb1de8a7, 0x39206b4d, 0x80174a57}, + {0xa1622ac1, 0xb4f4a5f0, 0x16dc2bc3, 0x50fb0954, + 0x2e261721, 0x52b82c3c, 0x821902b8, 0x0d4b6c38, + 0x1f618631, 0x047956f3, 0xd4337f5a, 0x591f8002, + 0x27f28db2, 0xfae37369, 0xb3f27580, 0x3cdb6397}, + {0x3dee23be, 0x19f36408, 0x227f4a6a, 0x024603c5, + 0xd5e062db, 0x6d8d4c5c, 0x7ff693b0, 0x76641be9, + 0x9e74f41c, 0xe7bc7f33, 0x2636f2e9, 0x70279750, + 0xce2355aa, 0x32d230ef, 0x22f9b468, 0xadd4e7a2}, + {0x936c0fed, 0xba0612d5, 0xa97c1ea7, 0x10e29d67, + 0x1c4c5dc8, 0x83645621, 0xcd8b521c, 0xb8301817, + 0xac7d6571, 0xcc41d200, 0x4ebdefdd, 0xd2917bde, + 0x60f75acc, 0x7791534b, 0x26ea2a83, 0x6b74513a}, + {0xd1957b85, 0xc6f8f9ca, 0xf04fb4be, 0xfeb786fb, + 0xa1dea3aa, 0x67fe7db6, 0x25d49c87, 0xe3d54870, + 0x93dc1f86, 0x7d0c1a18, 0x9272e128, 0x68e1b876, + 0xce284c9e, 0x8fa18792, 0x5785a340, 0xb6fcf198}, + {0xff7d8e4a, 0x0c21ee43, 0xe820b388, 0xb4443c0e, + 0xa1e6e498, 0x5c426110, 0x1b434ef3, 0xbef05b91, + 0xa6907968, 0x53662ac3, 0x6defac32, 0x2c11c29c, + 0x6175cced, 0xb17dd3ad, 0x6e6a1076, 0x1372b1fa}, + {0x4408ed06, 0x49460ffd, 0xb49d26cb, 0x6a3662a5, + 0x5e857047, 0xa387cd4a, 0x04edc81e, 0xfd94d8d4, + 0x2fe48d91, 0x9d2356bc, 0x96131878, 0xaca3fce4, + 0xbb312c6c, 0x5023b090, 0x3614be70, 0xa14dfabb}, + {0xd4cc1e83, 0x757a1930, 0xc3d16a61, 0x9e0d6681, + 0x8a081fa9, 0xbd11c888, 0x1672f010, 0xa083f71c, + 0x1ec02eef, 0xc4586ca8, 0x6d322b35, 0x56054679, + 0x1552a0ff, 0x5cb7707e, 0xdfb55d4a, 0xcc76cc07}, + {0x507cf71f, 0x2166421a, 0x54be4af0, 0xfd42158c, + 0x417b1f7f, 0x9466860b, 0x3a0075bf, 0x2055575c, + 0xcedfe7ab, 0xbe85aa5f, 0x39d0c2e3, 0x851c19df, + 0x39a35a3f, 0x3fb10d7d, 0x20b14899, 0x703b7f08}, + {0x8a7d9dd1, 0x33235565, 0xbd3d2e57, 0xa48c2726, + 0x0d5e2e13, 0xae421ff9, 0x8784a224, 0xf66c1510, + 0x057627aa, 0x8fb0cb41, 0x4289975a, 0xb181adfa, + 0x59f2059a, 0xe86feb05, 0x84222fc1, 0x319b3ce9}, + {0xe1e243b8, 0x3b0bcc1a, 0x70396f00, 0x5caff44d, + 0xe96961b3, 0xad73f692, 0x8b841a2d, 0xf5838839, + 0xec9c9d04, 0xcc2b5562, 0xf8ca2549, 0xa9c52ff8, + 0x3b2fde68, 0x3d4dc7f0, 0xa57387d0, 0x051199ad}, + {0x5f0ce4fc, 0xd830fbb7, 0x90abeb8f, 0x96d9cdbb, + 0x58f80a80, 0x0baaca36, 0x81a23623, 0x77127614, + 0xaa8382cd, 0x0922fbca, 0xd84d37e1, 0x721297df, + 0x160f3b3a, 0x10a1ecdc, 0x151c92f4, 0xc1fdcdab}, + {0x261c45cc, 0xfeddd2da, 0xfc3cb1c1, 0x6639641f, + 0x2c011892, 0x7108bee2, 0x8545e0b9, 0x7dd36dab, + 0x07d91950, 0x1520adcb, 0xf84aa939, 0x07d9bb2d, + 0xdf1ed826, 0xaee3c814, 0x1dca1e81, 0xc8e9f486}, + {0x933d306a, 0xaab7103d, 0xa8be37be, 0x49612f3a, + 0xb0cf28e5, 0xf9648902, 0x106d7c11, 0xf32e1813, + 0x21af36ef, 0xe695e4c4, 0x7ee1831d, 0x2aeda467, + 0x99d0c655, 0x3f0691ab, 0xcd68f7c1, 0xb469a20e}, + {0x8557aef0, 0x3eb0e373, 0x0853ac31, 0xe5bded62, + 0x3eddb0dd, 0x6bbf1caf, 0x2119c3d9, 0xe1732350, + 0x55456c75, 0xf6119375, 0x498dd1ad, 0x13f80916, + 0xb97f9f5e, 0x921d9f4c, 0xabdee367, 0x1d6bb8bf}, + {0xd165a3be, 0xd8b41598, 0xa20e1809, 0xefd5c8ce, + 0x18935c80, 0xdf1911f9, 0xc9e449eb, 0xb887a4d7, + 0x4a324f6f, 0x533e8031, 0x1c21c074, 0xa95f1ea5, + 0x765b320a, 0x839d7dfb, 0xc7d3aa93, 0xe534ae3d}, + {0xbe8592c8, 0x068457e6, 0x89b94fa3, 0xd522ad02, + 0x7e7db0b7, 0x2c5b896f, 0x9f8ecb37, 0x05b983ff, + 0x3fe9b25f, 0x34a6215b, 0x0592ba34, 0xd564f85a, + 0x156c426d, 0x25ad5460, 0xe7b5e8b7, 0xa73285c6}, + {0x5ad8d838, 0x27b42d36, 0xcc806ad1, 0x157a058a, + 0x7297735a, 0xffd6df8d, 0xff96f7a2, 0x155b27ea, + 0x84708101, 0x979fd78b, 0x49797d0c, 0x0dc93e3c, + 0x20287332, 0xed759f88, 0xe5068529, 0xb83aa781}, + {0xc38b302c, 0x57b54075, 0xac810692, 0xb0d493e7, + 0x4adda486, 0x0665ce2e, 0xb2a9c003, 0xafacc4ce, + 0x4d5e906d, 0xb3d52fab, 0xe6962c6b, 0x850f4dd1, + 0x5021656c, 0x5df6c06b, 0x9255125b, 0x2363c478}, + {0x188b715c, 0xe8b884b0, 0x5e6d0b9a, 0x1f0051e1, + 0xd2d35d4c, 0xbfeaecbe, 0xc84bb0ad, 0x67a232d6, + 0x99001587, 0xbf4313e1, 0x74f64061, 0x2c1fc562, + 0xb6fe8ca6, 0x5226a239, 0xf5198574, 0x61b51dca}, + {0x51dcecd3, 0xbadbe596, 0xebe3e84a, 0x772bfdfc, + 0x03656ac5, 0xa7c36e91, 0x6cd32cf0, 0xc3f699dd, + 0x7d5aba01, 0x51e38e82, 0x23103a98, 0x20298b9d, + 0x19436510, 0x63ad7e6c, 0x8bc2b33f, 0x27079917}, + {0x8bd5be78, 0xf2403bfa, 0x780ebdb6, 0x94c53b64, + 0x6241c2e2, 0x5bfb081e, 0x6799e88f, 0xc997b7d1, + 0x466ac8b1, 0xbf5909da, 0x497ea39f, 0x402ffb48, + 0xd7470c2d, 0x8510aba9, 0x6c52a1c9, 0x812ca967}, + {0x031f7ab4, 0xd32fe890, 0x36ae6de5, 0x083dcde4, + 0x99a7f12f, 0xe44864a7, 0x02b75fff, 0xf25dda35, + 0x7679ff4f, 0xed421e01, 0xd9c2cfa1, 0xd36b4e82, + 0x5315d908, 0xc7ebcb2a, 0xb6f3e4c1, 0xf5bfbae9}, + {0x3f4a2a96, 0x64d8bd5a, 0x19acd70d, 0xf62fcdd9, + 0x5de99cdf, 0x32f3b7cb, 0x2c020578, 0x4e9bafb8, + 0x74919a08, 0xaba33e91, 0xa6bd2254, 0x2435a9b9, + 0x47e2a1b4, 0xe837a28e, 0xe113f1b0, 0x7654bd79}, + {0x05537a6c, 0x77be1a5c, 0x4c7492c9, 0x9086bfb0, + 0x257adc18, 0xf4787fc1, 0xe3fb6d53, 0x9525e589, + 0x445a65bc, 0x833f7d08, 0x69cf1f7e, 0x9a6372e1, + 0xceedb52e, 0x31032997, 0xd1c36828, 0x132772d6}, + {0x0a166972, 0x89beaf3b, 0x8d780fbc, 0x8aea5392, + 0x58347a41, 0x1e381ec2, 0xcc6280c8, 0xee0863e1, + 0x976e2dd2, 0x8c6ee6e2, 0xa0ca57cd, 0x95114a7d, + 0x3c096704, 0xa941769d, 0x2de20c05, 0x0bf8f812}, + {0x22779d6c, 0x94e12e8f, 0x5ce40299, 0xea1b55b0, + 0x9ebec05d, 0xe076cd2b, 0x8fef5648, 0x6a284c65, + 0xa790b705, 0xf0b19997, 0x0d8ca8af, 0x17440419, + 0xef4f702f, 0x33cbcbb1, 0x83d60f26, 0x48988397}, + {0x0fed7f53, 0xb5acbb67, 0xc031c73f, 0x5364d9ef, + 0xa6dbd12d, 0x82174a6c, 0xccf8e7ab, 0xc473c036, + 0xcff493d8, 0xad9afc3b, 0x316a24e8, 0x1842bea4, + 0x4cc0c82e, 0x28ccd91e, 0xd7311b5d, 0x50a89860}, +}; + +static void fill_pattern5(ramctr_timing * ctrl, int channel) +{ + unsigned i, j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (i = 0; i < sizeof(pattern) / sizeof(pattern[0]); i++) { + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + i * channel_step + + j * 4, pattern[i][j]); + } + sfence(); +} + +static void reprogram_320c(ramctr_timing * ctrl) +{ + int channel, slotrank; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + /* jedec reset */ + dram_jedecreset(ctrl); + /* mrs commands. */ + dram_mrscommands(ctrl); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); +} + +static void command_training(ramctr_timing * ctrl) +{ + int channel; + int slotrank; + u32 reg_4004_b30; + int delta = 0; + int c320c; + int stat[NUM_SLOTRANKS][256]; + + /* FIXME: vendor BIOS discovers this by trying 0 and 2. Apparently 2 should work for + all systems but 0 is slightly more efficient for the systems that can tolerate it. + */ + reg_4004_b30 = 2; + + FOR_ALL_POPULATED_CHANNELS + MCHBAR32(0x4004 + 0x400 * channel) = + ctrl->tRRD + | (ctrl->tRTP << 4) + | (ctrl->tCKE << 8) + | (ctrl->tWTR << 12) + | (ctrl->tFAW << 16) + | (ctrl->tWR << 24) + | (reg_4004_b30 << 30); + + if (reg_4004_b30 == 2) + delta = 2; + else if (reg_4004_b30 == 0) + delta = 4; + + FOR_ALL_CHANNELS { + FOR_ALL_POPULATED_RANKS ctrl->timings[channel][slotrank]. + val_4024 -= delta; + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + FOR_ALL_POPULATED_CHANNELS { + for (c320c = -127; c320c <= 127; c320c++) { + FOR_ALL_POPULATED_RANKS ctrl-> + timings[channel][slotrank].val_320c = c320c; + program_timings(ctrl, channel); + reprogram_320c(ctrl); + FOR_ALL_POPULATED_RANKS { + stat[slotrank][c320c + 127] = + test_320c(ctrl, channel, slotrank); + printk(BIOS_SPEW, "3stat: %d, %d, %d: %d\n", + channel, slotrank, c320c, + stat[slotrank][c320c + 127]); + } + } + FOR_ALL_POPULATED_RANKS { + struct run rn = + get_longest_zero_run(stat[slotrank], 255); + ctrl->timings[channel][slotrank].val_320c = + rn.middle - 127; + printk(BIOS_SPEW, "3val: %d, %d: %d\n", channel, + slotrank, + ctrl->timings[channel][slotrank].val_320c); + if (rn.all) + die("c320c discovery failed"); + } + } + + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + + reprogram_320c(ctrl); +} + +static void discover_edges_real(ramctr_timing * ctrl, int channel, int slotrank, + int *edges) +{ + int edge; + int statistics[NUM_LANES][MAX_EDGE_TIMING + 1]; + int lane; + + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].rising = + edge; + ctrl->timings[channel][slotrank].lanes[lane].falling = + edge; + } + printk(BIOS_SPEW, "edge %02x\n", edge); + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + 4 * lane + + 0x4140); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x40411f4); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + FOR_ALL_LANES { + statistics[lane][edge] = + read32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + lane * 4); + printk(BIOS_SPEW, "estat %d, %d, %d, %d %02x\n", + channel, slotrank, lane, edge, + statistics[lane][edge]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_EDGE_TIMING + 1); + edges[lane] = rn.middle; + if (rn.all) + die("edge discovery failed"); + printk(BIOS_SPEW, "eval %d, %d, %d, %02x\n", channel, slotrank, + lane, edges[lane]); + } +} + +static void discover_edges(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + u32 r32; + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 4 * lane + + 0x400 * channel + 0x4080, 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0, 0); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + FOR_ALL_LANES read32(DEFAULT_MCHBAR + 0x400 * channel + + lane * 4 + 0x4140); + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } + + program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + lane * 4, + ~read32(DEFAULT_MCHBAR + 0x4040 + + 0x400 * channel + lane * 4) & 0xff); + } + + fill_pattern0(ctrl, channel, 0, 0xffffffff); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void discover_edges_write_real(ramctr_timing * ctrl, int channel, + int slotrank, int *edges) +{ + int edge; + u32 raw_statistics[MAX_EDGE_TIMING + 1]; + int statistics[MAX_EDGE_TIMING + 1]; + const int reg3000b24[] = { 0, 0xc, 0x2c }; + int lane, i; + int lower[NUM_LANES]; + int upper[NUM_LANES]; + + FOR_ALL_LANES { + lower[lane] = 0; + upper[lane] = MAX_EDGE_TIMING; + } + + for (i = 0; i < 3; i++) { + /* FIXME: trace shows that vendor BIOS also tests with other patterns. + I'm not sure whether it's really needed. + */ + write32(DEFAULT_MCHBAR + 0x3000 + 0x100 * channel, + reg3000b24[i] << 24); + printk(BIOS_SPEW, "patterned\n"); + printk(BIOS_SPEW, "[%x] = 0x%08x\n(%d, %d)\n", + 0x3000 + 0x100 * channel, reg3000b24[i] << 24, channel, + slotrank); + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + rising = edge; + ctrl->timings[channel][slotrank].lanes[lane]. + falling = edge; + } + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + + 4 * lane + 0x4140); + } + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x4 | (ctrl->tRCD << 16) + | (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << + 10)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, + 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8005020 | ((ctrl->tWTR + ctrl->CWL + 8) << + 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4005020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + lane * 4); + } + + raw_statistics[edge] = + MCHBAR32(0x436c + 0x400 * channel); + } + FOR_ALL_LANES { + struct run rn; + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) + statistics[edge] = + ! !(raw_statistics[edge] & (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_EDGE_TIMING + 1); + printk(BIOS_SPEW, + "edges: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, rn.middle, + rn.end, rn.start + ctrl->edge_offset[i], + rn.end - ctrl->edge_offset[i]); + lower[lane] = + max(rn.start + ctrl->edge_offset[i], lower[lane]); + upper[lane] = + min(rn.end - ctrl->edge_offset[i], upper[lane]); + edges[lane] = (lower[lane] + upper[lane]) / 2; + + } + } + + write32(DEFAULT_MCHBAR + 0x3000, 0); + printk(BIOS_SPEW, "CPA\n"); +} + +static void discover_edges_write(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank) +{ + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) + << 10) | (ctrl->tRCD << 16) | 4); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x80011e0 | + ((ctrl->tWTR + ctrl->CWL + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + + 0x400 * channel, + 0x40011e0 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + + 0x400 * channel, + 0x1001 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC_write(ramctr_timing * ctrl) +{ + const u8 rege3c_b24[3] = { 0, 0xf, 0x2f }; + int i; + + int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + lower[channel][slotrank][lane] = 0; + upper[channel][slotrank][lane] = MAX_TIMC; + } + + write32(DEFAULT_MCHBAR + 0x4ea8, 1); + + for (i = 0; i < 3; i++) + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100), + (rege3c_b24[i] << 24) + | (read32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100)) + & ~0x3f000000)); + udelay(2); + FOR_ALL_POPULATED_RANKS { + int timC; + u32 raw_statistics[MAX_TIMC + 1]; + int statistics[MAX_TIMC + 1]; + + /* FIXME: trace shows that vendor BIOS also tests with other patterns. + I'm not sure whether it's really needed. + */ + fill_pattern5(ctrl, channel); + for (timC = 0; timC < MAX_TIMC + 1; timC++) { + FOR_ALL_LANES + ctrl->timings[channel][slotrank].lanes[lane].timC = timC; + program_timings(ctrl, channel); + + test_timC_write (ctrl, channel, slotrank); + + raw_statistics[timC] = + MCHBAR32(0x436c + 0x400 * channel); + printk(BIOS_SPEW, "Cstat %02x %02x\n", timC, + raw_statistics[timC]); + } + FOR_ALL_LANES { + struct run rn; + for (timC = 0; timC <= MAX_TIMC; timC++) + statistics[timC] = + !!(raw_statistics[timC] & + (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_TIMC + 1); + if (rn.all) + die("timC write discovery failed"); + printk(BIOS_SPEW, + "timC: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, + rn.middle, rn.end, + rn.start + ctrl->timC_offset[i], + rn.end - ctrl->timC_offset[i]); + lower[channel][slotrank][lane] = + max(rn.start + ctrl->timC_offset[i], + lower[channel][slotrank][lane]); + upper[channel][slotrank][lane] = + min(rn.end - ctrl->timC_offset[i], + upper[channel][slotrank][lane]); + + } + } + } + + write32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c, + 0 | (read32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c) & + ~0x3f000000)); + udelay(2); + + write32(DEFAULT_MCHBAR + 0x4ea8, 0); + + printk(BIOS_SPEW, "CPB\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + printk(BIOS_SPEW, "timC [%d, %d, %d] = 0x%x\n", channel, + slotrank, lane, + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2); + ctrl->timings[channel][slotrank].lanes[lane].timC = + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2; + } + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); +} + +static void normalize_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + int mat = 0; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + int delta; + FOR_ALL_LANES mat = + max(ctrl->timings[channel][slotrank].lanes[lane].timA, mat); + delta = (mat >> 6) - ctrl->timings[channel][slotrank].val_4028; + ctrl->timings[channel][slotrank].val_4024 += delta; + ctrl->timings[channel][slotrank].val_4028 += delta; + } FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); +} + +static void write_controller_mr(ramctr_timing * ctrl) +{ + int channel, slotrank; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + write32(DEFAULT_MCHBAR | 0x0004 | (channel << 8) | + lane_registers[slotrank], make_mr0(ctrl, slotrank)); + write32(DEFAULT_MCHBAR | 0x0008 | (channel << 8) | + lane_registers[slotrank], make_mr1(ctrl, slotrank)); + } +} + +static void channel_test(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS + if (read32(DEFAULT_MCHBAR | 0x42a0 | (channel << 10)) & 0xa000) + die("Mini channel test failed (1)\n"); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0x12345678, 0x98765432); + + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + for (slotrank = 0; slotrank < 4; slotrank++) + FOR_ALL_CHANNELS + if (ctrl->rankmap[channel] & (1 << slotrank)) { + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR | (0x4f40 + 4 * lane), 0); + write32(DEFAULT_MCHBAR | (0x4d40 + 4 * lane), 0); + } + wait_428c(channel); + write32(DEFAULT_MCHBAR | 0x4220 | (channel << 10), 0x0001f006); + write32(DEFAULT_MCHBAR | 0x4230 | (channel << 10), 0x0028a004); + write32(DEFAULT_MCHBAR | 0x4200 | (channel << 10), + 0x00060000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4210 | (channel << 10), 0x00000244); + write32(DEFAULT_MCHBAR | 0x4224 | (channel << 10), 0x0001f201); + write32(DEFAULT_MCHBAR | 0x4234 | (channel << 10), 0x08281064); + write32(DEFAULT_MCHBAR | 0x4204 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4214 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x4228 | (channel << 10), 0x0001f105); + write32(DEFAULT_MCHBAR | 0x4238 | (channel << 10), 0x04281064); + write32(DEFAULT_MCHBAR | 0x4208 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4218 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x422c | (channel << 10), 0x0001f002); + write32(DEFAULT_MCHBAR | 0x423c | (channel << 10), 0x00280c01); + write32(DEFAULT_MCHBAR | 0x420c | (channel << 10), + 0x00060400 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x421c | (channel << 10), 0x00000240); + write32(DEFAULT_MCHBAR | 0x4284 | (channel << 10), 0x000c0001); + wait_428c(channel); + FOR_ALL_LANES + if (read32(DEFAULT_MCHBAR | 0x4340 | (channel << 10))) + die("Mini channel test failed (2)\n"); + } +} + +static void set_scrambling_seed(ramctr_timing * ctrl) +{ + int channel; + + /* FIXME: we hardcode seeds. Do we need to use some PRNG for them? + I don't think so. */ + static u32 seeds[NUM_CHANNELS][3] = { + {0x00009a36, 0xbafcfdcf, 0x46d1ab68}, + {0x00028bfa, 0x53fe4b49, 0x19ed5483} + }; + FOR_ALL_POPULATED_CHANNELS { + MCHBAR32(0x4020 + 0x400 * channel) &= ~0x10000000; + write32(DEFAULT_MCHBAR | 0x4034, seeds[channel][0]); + write32(DEFAULT_MCHBAR | 0x403c, seeds[channel][1]); + write32(DEFAULT_MCHBAR | 0x4038, seeds[channel][2]); + } +} + +static void set_4f8c(void) +{ + struct cpuid_result cpures; + u32 cpu; + + cpures = cpuid(0); + cpu = (cpures.eax); + if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) { + MCHBAR32(0x4f8c) = 0x141D1519; + } else { + MCHBAR32(0x4f8c) = 0x551D1519; + } +} + +static void prepare_training(ramctr_timing * ctrl) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS { + // Always drive command bus + MCHBAR32(0x4004 + 0x400 * channel) |= 0x20000000; + } + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS wait_428c(channel); +} + +static void hardcode1(ramctr_timing * ctrl) +{ + int channel; + u32 reg; + FOR_ALL_POPULATED_CHANNELS { + reg = read32(DEFAULT_MCHBAR | 0x400c | (channel << 10)); + write32(DEFAULT_MCHBAR | 0x400c | (channel << 10), + (reg & 0xFFF0FFFF) + | (ctrl->ref_card_offset[channel] << 16) + | (ctrl->ref_card_offset[channel] << 18)); + write32(DEFAULT_MCHBAR | 0x4008 | (channel << 10), 0x0a042220); // FIXME: hardcoded + } +} + +static void set_42a0(ramctr_timing * ctrl) +{ + int channel; + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR | (0x42a0 + 0x400 * channel), + 0x00001000 | ctrl->rankmap[channel]); + MCHBAR32(0x4004 + 0x400 * channel) &= ~0x20000000; // OK + } +} + +void init_dram_ddr3(spd_raw_data * spds, int mobile, int min_tck) +{ + int me_uma_size; + + MCHBAR32(0x5f00) |= 1; + + report_platform_info(); + + /* Wait for ME to be ready */ + intel_early_me_init(); + me_uma_size = intel_early_me_uma_size(); + + printk(BIOS_DEBUG, "Starting native Platform init\n"); + + u32 reg_5d10; + + wait_txt_clear(); + + wrmsr(0x000002e6, (msr_t) { + .lo = 0,.hi = 0}); + + reg_5d10 = read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + if ((pcie_read_config16(SOUTHBRIDGE, 0xa2) & 0xa0) == 0x20 /* 0x0004 */ + && reg_5d10) { + /* Need reset. */ + outb(0x6, 0xcf9); + + while (1) ; + } + + ramctr_timing ctrl; + + dimm_info info; + + early_pch_init_native(); + early_thermal_init(); + + ctrl.mobile = mobile; + ctrl.tCK = min_tck; + + /* Get DDR3 SPD data */ + dram_find_spds_ddr3(spds, &info, &ctrl); + + /* Find fastest common supported parameters */ + dram_find_common_params(&info, &ctrl); + + /* Calculate timings */ + dram_timing(&ctrl); + + /* Set MCU frequency */ + dram_freq(&ctrl); + + /* Set version register */ + MCHBAR32(0x5034) = 0xC04EB002; + + /* Enable crossover */ + dram_xover(&ctrl); + + /* Set timing and refresh registers */ + dram_timing_regs(&ctrl); + + /* Power mode preset */ + MCHBAR32(0x4e80) = 0x5500; + + /* Set scheduler parameters */ + MCHBAR32(0x4c20) = 0x10100005; + + /* Set cpu specific register */ + set_4f8c(); + + /* Clear IO reset bit */ + MCHBAR32(0x5030) &= ~0x20; + + /* Set MAD-DIMM registers */ + dram_dimm_mapping(&info, &ctrl); + printk(BIOS_DEBUG, "Done dimm mapping\n"); + + /* Zone config */ + dram_zones(&info, &ctrl, 1); + + /* Set memory map */ + dram_memorymap(&info, me_uma_size); + printk(BIOS_DEBUG, "Done memory map\n"); + + /* Set IO registers */ + dram_ioregs(&ctrl); + printk(BIOS_DEBUG, "Done io registers\n"); + + udelay(1); + + /* Do jedec ddr3 reset sequence */ + dram_jedecreset(&ctrl); + printk(BIOS_DEBUG, "Done jedec reset\n"); + + /* MRS commands */ + dram_mrscommands(&ctrl); + printk(BIOS_DEBUG, "Done MRS commands\n"); + dram_mrscommands(&ctrl); + + /* Prepare for memory training */ + prepare_training(&ctrl); + + read_training(&ctrl); + write_training(&ctrl); + + printk(BIOS_SPEW, "CP5a\n"); + + discover_edges(&ctrl); + + printk(BIOS_SPEW, "CP5b\n"); + + command_training(&ctrl); + + printk(BIOS_SPEW, "CP5c\n"); + + discover_edges_write(&ctrl); + + discover_timC_write(&ctrl); + + normalize_training(&ctrl); + + hardcode1(&ctrl); + + write_controller_mr(&ctrl); + + channel_test(&ctrl); + + write32(DEFAULT_MCHBAR | 0x5024, 0x00a030ce); // FIXME: hardcoded + + set_scrambling_seed(&ctrl); + + set_42a0(&ctrl); + + write32(DEFAULT_MCHBAR | 0x4cd4, 0x00000046); // FIXME: hardcoded + + write32(DEFAULT_MCHBAR | 0x400c, (read32(DEFAULT_MCHBAR | 0x400c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x440c, (read32(DEFAULT_MCHBAR | 0x440c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x4cb0, 0x00000740); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x4380, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4780, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4f88, 0x5f7003ff); // OK + write32(DEFAULT_MCHBAR | 0x5064, 0x00073000 | ctrl.reg_5064b0); // OK + write32(DEFAULT_MCHBAR | 0x4384, 0x009b6ea1); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x4784, 0x009b6ea1); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x5880, 0xca9171e5); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x5888); // !!! = 0x00e4d5d0 + write32(DEFAULT_MCHBAR | 0x5888, 0x00e4d5d0); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x58a8); // !!! = 0x00000000 + write32(DEFAULT_MCHBAR | 0x58a8, 0x00000000); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x4294); // !!! = 0x000098ff + write32(DEFAULT_MCHBAR | 0x4294, 0x000198ff); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x4694); // !!! = 0x000098ff + write32(DEFAULT_MCHBAR | 0x4694, 0x000198ff); // FIXME: hardcoded + + MCHBAR32(0x5030) |= 1; // OK + MCHBAR32(0x5030) |= 0x80; // OK + MCHBAR32(0x5f18) = 0xfa; // OK + read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + write32(DEFAULT_MCHBAR | 0x5d10, 0x2010040c); // FIXME: hardcoded + + /* Zone config */ + dram_zones(&info, &ctrl, 0); + + intel_early_me_status(); + intel_early_me_init_done(ME_INIT_STATUS_SUCCESS); + intel_early_me_status(); + + post_system_agent_init(); + report_memory_config(); +} diff --git a/src/northbridge/intel/sandybridge/raminit_native.h b/src/northbridge/intel/sandybridge/raminit_native.h new file mode 100644 index 0000000..ff7d7c1 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RAMINIT_H +#define RAMINIT_H + +#include <device/dram/ddr3.h> + +/* The order is ch0dimmA, ch0dimmB, ch1dimmA, ch1dimmB. */ +void init_dram_ddr3(spd_raw_data *spds, int mobile, int min_tck); +void read_spd(spd_raw_data *spd, u8 addr); + +#endif /* RAMINIT_H */ diff --git a/src/southbridge/intel/bd82x6x/Makefile.inc b/src/southbridge/intel/bd82x6x/Makefile.inc index 64038e5..fa21277 100644 --- a/src/southbridge/intel/bd82x6x/Makefile.inc +++ b/src/southbridge/intel/bd82x6x/Makefile.inc @@ -45,10 +45,14 @@ smm-$(CONFIG_SPI_FLASH_SMM) += ../common/spi.c ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c me.c me_8.x.c finalize.c pch.c -romstage-y += early_usb.c early_smbus.c early_me.c me_status.c gpio.c +romstage-y += early_usb.c early_smbus.c me_status.c gpio.c romstage-y += reset.c romstage-y += early_spi.c early_pch.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += early_thermal.c early_pch_native.c early_me_native.c + ifeq ($(CONFIG_BUILD_WITH_FAKE_IFD),y) IFD_BIN_PATH := $(objgenerated)/ifdfake.bin IFD_SECTIONS := $(addprefix -b ,$(CONFIG_IFD_BIOS_SECTION:"%"=%)) \ diff --git a/src/southbridge/intel/bd82x6x/early_me_native.c b/src/southbridge/intel/bd82x6x/early_me_native.c new file mode 100644 index 0000000..ab54ffd --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_me_native.c @@ -0,0 +1,272 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <arch/hlt.h> +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> +#include <device/pci_ids.h> +#include <string.h> +#include "me.h" +#include "pch.h" + +static const char *me_ack_values[] = { + [ME_HFS_ACK_NO_DID] = "No DID Ack received", + [ME_HFS_ACK_RESET] = "Non-power cycle reset", + [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset", + [ME_HFS_ACK_S3] = "Go to S3", + [ME_HFS_ACK_S4] = "Go to S4", + [ME_HFS_ACK_S5] = "Go to S5", + [ME_HFS_ACK_GBL_RESET] = "Global Reset", + [ME_HFS_ACK_CONTINUE] = "Continue to boot" +}; + +static inline void pci_read_dword_ptr(void *ptr, int offset) +{ + u32 dword = pci_read_config32(PCH_ME_DEV, offset); + memcpy(ptr, &dword, sizeof(dword)); +} + +static inline void pci_write_dword_ptr(void *ptr, int offset) +{ + u32 dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + pci_write_config32(PCH_ME_DEV, offset, dword); +} + +void intel_early_me_status(void) +{ + struct me_hfs hfs; + struct me_gmes gmes; + + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + pci_read_dword_ptr(&gmes, PCI_ME_GMES); + + intel_me_status(&hfs, &gmes); +} + +int intel_early_me_init(void) +{ + int count; + struct me_uma uma; + struct me_hfs hfs; + + printk(BIOS_INFO, "Intel ME early init\n"); + + /* Wait for ME UMA SIZE VALID bit to be set */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) + break; + udelay(ME_DELAY); + } + if (!count) { + printk(BIOS_ERR, "ERROR: ME is not ready!\n"); + return -1; + } + + /* Check for valid firmware */ + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + if (hfs.fpt_bad) { + printk(BIOS_WARNING, "WARNING: ME has bad firmware\n"); + return -1; + } + + printk(BIOS_INFO, "Intel ME firmware is ready\n"); + return 0; +} + +int intel_early_me_uma_size(void) +{ + struct me_uma uma; + + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) { + printk(BIOS_DEBUG, "ME: Requested %uMB UMA\n", uma.size); + return uma.size; + } + + printk(BIOS_DEBUG, "ME: Invalid UMA size\n"); + return 0; +} + +static inline void set_global_reset(int enable) +{ + u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3); + + /* Clear CF9 Without Resume Well Reset Enable */ + etr3 &= ~ETR3_CWORWRE; + + /* CF9GR indicates a Global Reset */ + if (enable) + etr3 |= ETR3_CF9GR; + else + etr3 &= ~ETR3_CF9GR; + + pci_write_config32(PCH_LPC_DEV, ETR3, etr3); +} + +int intel_early_me_init_done(u8 status) +{ + u8 reset, errorcode, opmode; + u16 reg16; + u32 mebase_l, mebase_h; + u32 millisec; + u32 hfs, me_fws2; + struct me_did did = { + .init_done = ME_INIT_DONE, + .status = status + }; + u32 meDID; + + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xff000) >> 12; + + opmode = (hfs & 0xf0) >> 4; + errorcode = hfs & 0xf; + + if (opmode != ME_HFS_MODE_NORMAL) { + printk(BIOS_NOTICE, "ME: Wrong mode : %d\n", opmode); + //return 0; + } + if (errorcode) { + printk(BIOS_NOTICE, "ME: HFS error : %d\n", errorcode); + //return 0; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + // Poll cpu replaced for 50ms + millisec = 0; + while ((((me_fws2 & 0x100) >> 8) == 0) && millisec < 50) { + udelay(1000); + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + millisec++; + } + if (millisec >= 50 || ((me_fws2 & 0x100) >> 8) == 0x0) { + printk(BIOS_NOTICE, "Waited long enough, or CPU was not replaced, continue...\n"); + } else if ((me_fws2 & 0x100) == 0x100) { + if ((me_fws2 & 0x80) == 0x80) { + printk(BIOS_NOTICE, "CPU was replaced & warm reset required...\n"); + reg16 = pcie_read_config16(PCI_DEV(0, 31, 0), 0xa2) & ~0x80; + pcie_write_config16(PCI_DEV(0, 31, 0), 0xa2, reg16); + set_global_reset(0); + outb(0x6, 0xcf9); + hlt(); + } + + if (((me_fws2 & 0x10) == 0x10) && (me_fws2 & 0x80) == 0x00) { + printk(BIOS_NOTICE, "Full training required\n"); + } + } + + printk(BIOS_NOTICE, "PASSED! Tell ME that DRAM is ready\n"); + + /* MEBASE from MESEG_BASE[35:20] */ + mebase_l = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_L); + mebase_h = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_H) & 0xf; + did.uma_base = (mebase_l >> 20) | (mebase_h << 12); + + meDID = did.uma_base | (1 << 28);// | (1 << 23); + pci_write_config32(PCI_DEV(0, 0x16, 0), PCI_ME_H_GS, meDID); + + udelay(1100); + + /* Must wait for ME acknowledgement */ + millisec = 0; + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + while ((((hfs & 0xf0) >> 4) != ME_HFS_BIOS_DRAM_ACK) && (millisec < 5000)) { + udelay(1000); + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + millisec++; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + + /* Return the requested BIOS action */ + printk(BIOS_NOTICE, "ME: Requested BIOS Action: %s\n", + me_ack_values[(hfs & 0xe) >> 1]); + + reset = inb(0xcf9); + reset &= 0xf1; + switch ((hfs & 0xe) >> 1) { + case ME_HFS_ACK_NO_DID: + case ME_HFS_ACK_CONTINUE: + /* Continue to boot */ + return 0; + case ME_HFS_ACK_RESET: + /* Non-power cycle reset */ + set_global_reset(0); + reset |= 0x06; + break; + case ME_HFS_ACK_PWR_CYCLE: + /* Power cycle reset */ + set_global_reset(0); + reset |= 0x0e; + break; + case ME_HFS_ACK_GBL_RESET: + /* Global reset */ + set_global_reset(1); + reset |= 0x0e; + break; + case ME_HFS_ACK_S3: + case ME_HFS_ACK_S4: + case ME_HFS_ACK_S5: + break; + } + + /* Perform the requested reset */ + if (reset) { + outb(reset, 0xcf9); + hlt(); + } + return -1; +} diff --git a/src/southbridge/intel/bd82x6x/early_pch_native.c b/src/southbridge/intel/bd82x6x/early_pch_native.c new file mode 100644 index 0000000..eda97a5 --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_pch_native.c @@ -0,0 +1,375 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko <phcoder(a)gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include <delay.h> + +#include "pch.h" +/* For DMI bar. */ +#include "northbridge/intel/sandybridge/sandybridge.h" + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) + +static void +wait_2338 (void) +{ + while (read8 (DEFAULT_RCBA | 0x2338) & 1); +} + +static u32 +read_2338 (u32 edx) +{ + u32 ret; + + write32 (DEFAULT_RCBA | 0x2330, edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + ret = read32 (DEFAULT_RCBA | 0x2334); + wait_2338 (); + read8 (DEFAULT_RCBA | 0x2338); + return ret; +} + +static void +write_2338 (u32 edx, u32 val) +{ + read_2338 (edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + + write32 (DEFAULT_RCBA | 0x2334, val); + wait_2338 (); + write16 (DEFAULT_RCBA | 0x2338, + (read16 (DEFAULT_RCBA | 0x2338) & 0x1ff) | 0x600); + read8 (DEFAULT_RCBA | 0x2338); +} + + +static void +init_dmi (void) +{ + int i; + + write32 (DEFAULT_DMIBAR | 0x0914, + read32 (DEFAULT_DMIBAR | 0x0914) | 0x80000000); + write32 (DEFAULT_DMIBAR | 0x0934, + read32 (DEFAULT_DMIBAR | 0x0934) | 0x80000000); + for (i = 0; i < 4; i++) + { + write32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4)) & 0xf3ffffff); + write32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4)) | 0x800); + } + write32 (DEFAULT_DMIBAR | 0x0c30, (read32 (DEFAULT_DMIBAR | 0x0c30) + & 0xfffffff) | 0x40000000); + for (i = 0; i < 2; i++) + { + write32 (DEFAULT_DMIBAR | 0x0904 | (i << 5), + read32 (DEFAULT_DMIBAR | 0x0904 | (i << 5)) & 0xfe3fffff); + write32 (DEFAULT_DMIBAR | 0x090c | (i << 5), + read32 (DEFAULT_DMIBAR | 0x090c | (i << 5)) & 0xfff1ffff); + } + write32 (DEFAULT_DMIBAR | 0x090c, + read32 (DEFAULT_DMIBAR | 0x090c) & 0xfe1fffff); + write32 (DEFAULT_DMIBAR | 0x092c, + read32 (DEFAULT_DMIBAR | 0x092c) & 0xfe1fffff); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x090c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x090c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x092c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x092c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0c04); // !!! = 0x2e680008 + write32 (DEFAULT_DMIBAR | 0x0c04, 0x2e680008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0910); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0910, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0930); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0930, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0a04); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a04, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a14); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a14, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a24); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a24, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a34); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a34, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0900); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0900, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0920); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0920, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0c0c); // !!! = 0x16063400 + write32 (DEFAULT_DMIBAR | 0x0c0c, 0x00063400); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0258); // !!! = 0x40000600 + write32 (DEFAULT_DMIBAR | 0x0258, 0x60000600); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x022c); // !!! = 0x00c26460 + write32 (DEFAULT_DMIBAR | 0x022c, 0x00c2403c); + read8 (DEFAULT_RCBA | 0x21a4); // !!! = 0x42 + + read32 (DEFAULT_RCBA | 0x21a4); // !!! = 0x00012c42 + read32 (DEFAULT_RCBA | 0x2340); // !!! = 0x0013001b + write32 (DEFAULT_RCBA | 0x2340, 0x003a001b); + read8 (DEFAULT_RCBA | 0x21b0); // !!! = 0x01 + write8 (DEFAULT_RCBA | 0x21b0, 0x02); + read32 (DEFAULT_DMIBAR | 0x0084); // !!! = 0x0041ac41 + write32 (DEFAULT_DMIBAR | 0x0084, 0x0041ac42); + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0041 + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + + read32 (DEFAULT_DMIBAR | 0x0014); // !!! = 0x8000007f + write32 (DEFAULT_DMIBAR | 0x0014, 0x80000019); + read32 (DEFAULT_DMIBAR | 0x0020); // !!! = 0x01000000 + write32 (DEFAULT_DMIBAR | 0x0020, 0x81000022); + read32 (DEFAULT_DMIBAR | 0x002c); // !!! = 0x02000000 + write32 (DEFAULT_DMIBAR | 0x002c, 0x82000044); + read32 (DEFAULT_DMIBAR | 0x0038); // !!! = 0x07000080 + write32 (DEFAULT_DMIBAR | 0x0038, 0x87000080); + read8 (DEFAULT_DMIBAR | 0x0004); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0004, 0x01); + + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x01200654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x012a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + read8 (DEFAULT_RCBA | 0x1114); // !!! = 0x00 + write8 (DEFAULT_RCBA | 0x1114, 0x05); + read32 (DEFAULT_RCBA | 0x2014); // !!! = 0x80000011 + write32 (DEFAULT_RCBA | 0x2014, 0x80000019); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2020, 0x81000022); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x81000022 + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2030, 0x82000044); + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x82000044 + read32 (DEFAULT_RCBA | 0x2040); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2040, 0x87000080); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + write32 (DEFAULT_RCBA | 0x0050, 0x812a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x812a0654 + read16 (DEFAULT_RCBA | 0x201a); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2026); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2036); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2046); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x001a); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0026); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0032); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x003e); // !!! = 0x0000 +} + +void +early_pch_init_native (void) +{ + pcie_write_config8 (SOUTHBRIDGE, 0xa6, + pcie_read_config8 (SOUTHBRIDGE, 0xa6) | 2); + + write32 (DEFAULT_RCBA | 0x2088, 0x00109000); + read32 (DEFAULT_RCBA | 0x20ac); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x20ac, 0x40000000); + write32 (DEFAULT_RCBA | 0x100c, 0x01110000); + write8 (DEFAULT_RCBA | 0x2340, 0x1b); + read32 (DEFAULT_RCBA | 0x2314); // !!! = 0x0a080000 + write32 (DEFAULT_RCBA | 0x2314, 0x0a280000); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xc809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + write32 (DEFAULT_RCBA | 0x2324, 0x00854c74); + read8 (DEFAULT_RCBA | 0x0400); // !!! = 0x00 + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + + write_2338 (0xea007f62, 0x00590133); + write_2338 (0xec007f62, 0x00590133); + write_2338 (0xec007f64, 0x59555588); + write_2338 (0xea0040b9, 0x0001051c); + write_2338 (0xeb0040a1, 0x800084ff); + write_2338 (0xec0040a1, 0x800084ff); + write_2338 (0xea004001, 0x00008400); + write_2338 (0xeb004002, 0x40201758); + write_2338 (0xec004002, 0x40201758); + write_2338 (0xea004002, 0x00601758); + write_2338 (0xea0040a1, 0x810084ff); + write_2338 (0xeb0040b1, 0x0001c598); + write_2338 (0xec0040b1, 0x0001c598); + write_2338 (0xeb0040b6, 0x0001c598); + write_2338 (0xea0000a9, 0x80ff969f); + write_2338 (0xea0001a9, 0x80ff969f); + write_2338 (0xeb0040b2, 0x0001c396); + write_2338 (0xeb0040b3, 0x0001c396); + write_2338 (0xec0040b2, 0x0001c396); + write_2338 (0xea0001a9, 0x80ff94ff); + write_2338 (0xea000151, 0x0088037f); + write_2338 (0xea0000a9, 0x80ff94ff); + write_2338 (0xea000051, 0x0088037f); + + write_2338 (0xea007f05, 0x00010642); + write_2338 (0xea0040b7, 0x0001c91c); + write_2338 (0xea0040b8, 0x0001c91c); + write_2338 (0xeb0040a1, 0x820084ff); + write_2338 (0xec0040a1, 0x820084ff); + write_2338 (0xea007f0a, 0xc2480000); + + write_2338 (0xec00404d, 0x1ff177f); + write_2338 (0xec000084, 0x5a600000); + write_2338 (0xec000184, 0x5a600000); + write_2338 (0xec000284, 0x5a600000); + write_2338 (0xec000384, 0x5a600000); + write_2338 (0xec000094, 0x000f0501); + write_2338 (0xec000194, 0x000f0501); + write_2338 (0xec000294, 0x000f0501); + write_2338 (0xec000394, 0x000f0501); + write_2338 (0xec000096, 0x00000001); + write_2338 (0xec000196, 0x00000001); + write_2338 (0xec000296, 0x00000001); + write_2338 (0xec000396, 0x00000001); + write_2338 (0xec000001, 0x00008c08); + write_2338 (0xec000101, 0x00008c08); + write_2338 (0xec000201, 0x00008c08); + write_2338 (0xec000301, 0x00008c08); + write_2338 (0xec0040b5, 0x0001c518); + write_2338 (0xec000087, 0x06077597); + write_2338 (0xec000187, 0x06077597); + write_2338 (0xec000287, 0x06077597); + write_2338 (0xec000387, 0x06077597); + write_2338 (0xea000050, 0x00bb0157); + write_2338 (0xea000150, 0x00bb0157); + write_2338 (0xec007f60, 0x77777d77); + write_2338 (0xea00008d, 0x01320000); + write_2338 (0xea00018d, 0x01320000); + write_2338 (0xec0007b2, 0x04514b5e); + write_2338 (0xec00078c, 0x40000200); + write_2338 (0xec000780, 0x02000020); + + init_dmi(); +} diff --git a/src/southbridge/intel/bd82x6x/early_thermal.c b/src/southbridge/intel/bd82x6x/early_thermal.c new file mode 100644 index 0000000..02ec9a7 --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_thermal.c @@ -0,0 +1,70 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include "pch.h" +#include "cpu/intel/model_206ax/model_206ax.h" +#include <cpu/x86/msr.h> + +/* Early thermal init, must be done prior to giving ME its memory + which is done at the end of raminit. */ +void early_thermal_init(void) +{ + device_t dev; + msr_t msr; + + dev = PCI_DEV(0x0, 0x1f, 0x6); + + /* Program address for temporary BAR. */ + pci_write_config32(dev, 0x40, 0x40000000); + pci_write_config32(dev, 0x44, 0x0); + + /* Activate temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) | 5); + + + write16 (0x40000004, 0x3a2b); + write8 (0x4000000c, 0xff); + write8 (0x4000000d, 0x00); + write8 (0x4000000e, 0x40); + write8 (0x40000082, 0x00); + write8 (0x40000001, 0xba); + + /* Perform init. */ + /* Configure TJmax. */ + msr = rdmsr(MSR_TEMPERATURE_TARGET); + write16(0x40000012, ((msr.lo >> 16) & 0xff) << 6); + /* Northbridge temperature slope and offset. */ + write16(0x40000016, 0x808c); + + write16 (0x40000014, 0xde87); + + /* Enable thermal data reporting, processor, PCH and northbridge. */ + write16(0x4000001a, (read16(0x4000001a) & ~0xf) | 0x10f0); + + /* Disable temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) & ~1); + pci_write_config32(dev, 0x40, 0); + + write32 (DEFAULT_RCBA | 0x38b0, + (read32 (DEFAULT_RCBA | 0x38b0) & 0xffff8003) | 0x403c); +} diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h index 90de855..83128e2 100644 --- a/src/southbridge/intel/bd82x6x/pch.h +++ b/src/southbridge/intel/bd82x6x/pch.h @@ -74,6 +74,8 @@ void enable_smbus(void); void enable_usb_bar(void); int smbus_read_byte(unsigned device, unsigned address); int early_spi_read(u32 offset, u32 size, u8 *buffer); +void early_thermal_init(void); +void early_pch_init_native(void); #endif #endif diff --git a/src/southbridge/intel/bd82x6x/smi.c b/src/southbridge/intel/bd82x6x/smi.c index 0166edf..a20232e 100644 --- a/src/southbridge/intel/bd82x6x/smi.c +++ b/src/southbridge/intel/bd82x6x/smi.c @@ -29,10 +29,7 @@ #include <cpu/x86/smm.h> #include <string.h> #include "pch.h" - -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE #include "northbridge/intel/sandybridge/sandybridge.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/bd82x6x/usb_ehci.c b/src/southbridge/intel/bd82x6x/usb_ehci.c index 78f92d9..97f20bd 100644 --- a/src/southbridge/intel/bd82x6x/usb_ehci.c +++ b/src/southbridge/intel/bd82x6x/usb_ehci.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <kconfig.h> #include <console/console.h> #include <device/device.h> #include <device/pci.h> @@ -36,11 +37,43 @@ static void usb_ehci_init(struct device *dev) RCBA32(0x35b0) = reg32; printk(BIOS_DEBUG, "EHCI: Setting up controller.. "); + + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + pci_write_config32(dev, 0x84, 0x930c8811); + pci_write_config32(dev, 0x88, 0x24000d30); + pci_write_config32(dev, 0xf4, 0x80408588); + pci_write_config32(dev, 0xf4, 0x80808588); + pci_write_config32(dev, 0xf4, 0x00808588); + pci_write_config32(dev, 0xfc, 0x205b1708); +#endif + reg32 = pci_read_config32(dev, PCI_COMMAND); reg32 |= PCI_COMMAND_MASTER; //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + struct resource *res; + u8 access_cntl; + + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); +#endif + printk(BIOS_DEBUG, "done.\n"); } diff --git a/src/southbridge/intel/ibexpeak/me.c b/src/southbridge/intel/ibexpeak/me.c index bc56012..0e1b5b8 100644 --- a/src/southbridge/intel/ibexpeak/me.c +++ b/src/southbridge/intel/ibexpeak/me.c @@ -377,125 +377,7 @@ static int mkhi_end_of_post(void) printk(BIOS_INFO, "ME: END OF POST message successful\n"); return 0; } -#endif - -#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && !defined(__SMM__) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) -/* Get ME firmware version */ -static int mkhi_get_fw_version(void) -{ - struct me_fw_version version; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_GEN, - .command = MKHI_GET_FW_VERSION, - }; - struct mei_header mei = { - .is_complete = 1, - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - .length = sizeof(mkhi), - }; - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) { - printk(BIOS_ERR, "ME: GET FW VERSION message failed\n"); - return -1; - } - - printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) " - "%u.%u.%u.%u (recovery)\n", - version.code_major, version.code_minor, - version.code_build_number, version.code_hot_fix, - version.recovery_major, version.recovery_minor, - version.recovery_build_number, version.recovery_hot_fix); - - return 0; -} - -static inline void print_cap(const char *name, int state) -{ - printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n", - name, state ? "en" : "dis"); -} - -/* Get ME Firmware Capabilities */ -static int mkhi_get_fwcaps(void) -{ - u32 rule_id = 0; - struct me_fwcaps cap; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_FWCAPS, - .command = MKHI_FWCAPS_GET_RULE, - }; - struct mei_header mei = { - .is_complete = 1, - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - .length = sizeof(mkhi) + sizeof(rule_id), - }; - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) { - printk(BIOS_ERR, "ME: GET FWCAPS message failed\n"); - return -1; - } - - print_cap("Full Network manageability", cap.caps_sku.full_net); - print_cap("Regular Network manageability", cap.caps_sku.std_net); - print_cap("Manageability", cap.caps_sku.manageability); - print_cap("Small business technology", cap.caps_sku.small_business); - print_cap("Level III manageability", cap.caps_sku.l3manageability); - print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at); - print_cap("IntelR Capability Licensing Service (CLS)", - cap.caps_sku.intel_cls); - print_cap("IntelR Power Sharing Technology (MPC)", - cap.caps_sku.intel_mpc); - print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking); - print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp); - print_cap("IPV6", cap.caps_sku.ipv6); - print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm); - print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och); - print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan); - print_cap("TLS", cap.caps_sku.tls); - print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan); - - return 0; -} -#endif -#if CONFIG_CHROMEOS && 0 /* DISABLED */ -/* Tell ME to issue a global reset */ -int mkhi_global_reset(void) -{ - struct me_global_reset reset = { - .request_origin = GLOBAL_RESET_BIOS_POST, - .reset_type = CBM_RR_GLOBAL_RESET, - }; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_CBM, - .command = MKHI_GLOBAL_RESET, - }; - struct mei_header mei = { - .is_complete = 1, - .length = sizeof(mkhi) + sizeof(reset), - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - }; - - printk(BIOS_NOTICE, "ME: Requesting global reset\n"); - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, &reset, NULL, 0) < 0) { - /* No response means reset will happen shortly... */ - hlt(); - } - - /* If the ME responded it rejected the reset request */ - printk(BIOS_ERR, "ME: Global Reset failed\n"); - return -1; -} -#endif - -#ifdef __SMM__ static void intel_me7_finalize_smm(void) { struct me_hfs hfs; @@ -723,13 +605,6 @@ static void intel_me_init(device_t dev) if (intel_mei_setup(dev) < 0) break; -#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) - /* Print ME firmware version */ - mkhi_get_fw_version(); - /* Print ME firmware capabilities */ - mkhi_get_fwcaps(); -#endif - /* * Leave the ME unlocked in this path. * It will be locked via SMI command later. diff --git a/src/southbridge/intel/ibexpeak/smi.c b/src/southbridge/intel/ibexpeak/smi.c index 981be3b..2ce9072 100644 --- a/src/southbridge/intel/ibexpeak/smi.c +++ b/src/southbridge/intel/ibexpeak/smi.c @@ -31,13 +31,7 @@ #include <string.h> #include "pch.h" -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE -#include "northbridge/intel/sandybridge/sandybridge.h" -#endif - -#if CONFIG_NORTHBRIDGE_INTEL_NEHALEM #include "northbridge/intel/nehalem/nehalem.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/ibexpeak/usb_ehci.c b/src/southbridge/intel/ibexpeak/usb_ehci.c index 21a257f..ea767c4 100644 --- a/src/southbridge/intel/ibexpeak/usb_ehci.c +++ b/src/southbridge/intel/ibexpeak/usb_ehci.c @@ -30,6 +30,8 @@ static void usb_ehci_init(struct device *dev) { u32 reg32; + struct resource *res; + u8 access_cntl; /* Disable Wake on Disconnect in RMH */ reg32 = RCBA32(0x35b0); @@ -50,6 +52,21 @@ static void usb_ehci_init(struct device *dev) //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); + printk(BIOS_DEBUG, "done.\n"); }
1
0
0
0
Patch set updated for coreboot: 420ff4c x230: Deploy VBT
by Vladimir Serbinenko
31 May '14
31 May '14
Vladimir Serbinenko (phcoder(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5396
-gerrit commit 420ff4ce6e22750198e48516f198436a948374b2 Author: Vladimir Serbinenko <phcoder(a)gmail.com> Date: Sun Feb 23 00:13:56 2014 +0100 x230: Deploy VBT Change-Id: Ide31a56bfdbc31cd3b87993dfb4ed8ef0107cdba Signed-off-by: Vladimir Serbinenko <phcoder(a)gmail.com> --- src/mainboard/lenovo/x230/Makefile.inc | 1 + src/mainboard/lenovo/x230/gma.c | 297 ++++++++++++++++++++++++++++++++ src/northbridge/intel/sandybridge/gma.c | 30 ++++ src/northbridge/intel/sandybridge/gma.h | 2 + 4 files changed, 330 insertions(+) diff --git a/src/mainboard/lenovo/x230/Makefile.inc b/src/mainboard/lenovo/x230/Makefile.inc index d514d4b..4ce3d61 100644 --- a/src/mainboard/lenovo/x230/Makefile.inc +++ b/src/mainboard/lenovo/x230/Makefile.inc @@ -18,3 +18,4 @@ ## smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c +ramstage-y += gma.c diff --git a/src/mainboard/lenovo/x230/gma.c b/src/mainboard/lenovo/x230/gma.c new file mode 100644 index 0000000..20a461b --- /dev/null +++ b/src/mainboard/lenovo/x230/gma.c @@ -0,0 +1,297 @@ +#include <arch/io.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> + +#include "northbridge/intel/sandybridge/sandybridge.h" + +/* This array contains the information on flat panel. When using native + graphics init coreboot copies it to where VGA Option ROM would be so + that OS can find it and able to use internal display. This contains no + executable code and is just information on the panel. + */ + +unsigned char fake_vbt[8192] = +{ +0x24, 0x56, 0x42, 0x54, 0x20, 0x53, 0x4e, 0x42, 0x2f, 0x49, 0x56, 0x42, 0x2d, 0x4d, 0x4f, 0x42, +0x49, 0x4c, 0x45, 0x20, 0x64, 0x00, 0x30, 0x00, 0x6b, 0x11, 0xc6, 0x00, 0x30, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x42, 0x49, 0x4f, 0x53, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x20, +0xa8, 0x00, 0x16, 0x00, 0x3b, 0x11, 0xfe, 0xea, 0x00, 0x00, 0x64, 0x01, 0x01, 0x14, 0x0d, 0x32, +0x31, 0x36, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x28, 0x52, 0x29, 0x20, 0x53, 0x61, 0x6e, 0x64, +0x79, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2f, 0x49, 0x76, 0x79, 0x62, 0x72, 0x69, 0x64, 0x67, +0x65, 0x20, 0x50, 0x43, 0x49, 0x20, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x65, +0x64, 0x20, 0x53, 0x56, 0x47, 0x41, 0x20, 0x42, 0x49, 0x4f, 0x53, 0x0d, 0x0a, 0x42, 0x75, 0x69, +0x6c, 0x64, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x32, 0x31, 0x36, 0x31, 0x5f, +0x52, 0x59, 0x61, 0x6e, 0x20, 0x50, 0x43, 0x20, 0x31, 0x34, 0x2e, 0x33, 0x34, 0x20, 0x20, 0x31, +0x32, 0x2f, 0x30, 0x37, 0x2f, 0x32, 0x30, 0x31, 0x32, 0x20, 0x20, 0x30, 0x38, 0x3a, 0x31, 0x38, +0x3a, 0x33, 0x31, 0x0d, 0x0a, 0x44, 0x45, 0x43, 0x4f, 0x4d, 0x50, 0x49, 0x4c, 0x41, 0x54, 0x49, +0x4f, 0x4e, 0x20, 0x4f, 0x52, 0x20, 0x44, 0x49, 0x53, 0x41, 0x53, 0x53, 0x45, 0x4d, 0x42, 0x4c, +0x59, 0x20, 0x50, 0x52, 0x4f, 0x48, 0x49, 0x42, 0x49, 0x54, 0x45, 0x44, 0x0d, 0x0a, 0x43, 0x6f, +0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, 0x32, 0x30, 0x30, 0x30, +0x2d, 0x32, 0x30, 0x31, 0x31, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, +0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65, 0x73, +0x65, 0x72, 0x76, 0x65, 0x64, 0x2e, 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x00, 0xc0, 0x03, 0x08, 0x04, +0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x07, 0x03, 0x40, 0x01, 0x09, 0xfd, 0x32, 0x00, 0x44, 0x04, +0x40, 0x06, 0x04, 0x02, 0x09, 0x01, 0x00, 0x0a, 0x02, 0x08, 0x0c, 0x04, 0x08, 0x03, 0x01, 0x02, +0x05, 0x01, 0x04, 0x0d, 0x01, 0x04, 0x0b, 0x01, 0x02, 0x07, 0x01, 0x04, 0x15, 0x01, 0x04, 0x45, +0x01, 0x04, 0x0e, 0x04, 0x08, 0x46, 0x04, 0x40, 0x28, 0x20, 0x08, 0x48, 0x40, 0x08, 0x10, 0x00, +0x02, 0x0d, 0x01, 0x02, 0x04, 0x00, 0x00, 0x21, 0x08, 0x00, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xcb, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd6, 0x60, 0x00, 0x10, 0x10, +0x01, 0xb6, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, +0x00, 0x07, 0x10, 0x01, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xd6, 0x60, 0x00, 0x10, +0x10, 0x01, 0xc8, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, +0x00, 0x00, 0x07, 0x20, 0x01, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0xd6, 0x60, 0x00, +0x10, 0x10, 0x01, 0xda, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x09, 0x00, 0x00, 0x06, 0x00, +0x00, 0x00, 0x00, 0x07, 0x30, 0x01, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x01, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x40, 0x42, +0x44, 0x46, 0x48, 0x4a, 0x4c, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x80, 0x81, 0x82, 0x83, +0x84, 0x1a, 0x00, 0xfc, 0xc2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x30, 0x00, 0x51, 0x07, 0x1e, 0x00, +0x48, 0x0a, 0x0f, 0x00, 0x86, 0x03, 0x28, 0x00, 0xb3, 0x03, 0x02, 0x00, 0xbd, 0x03, 0x38, 0x00, +0x61, 0x04, 0xc8, 0x00, 0xb1, 0x06, 0x30, 0x00, 0xe4, 0x06, 0x18, 0x00, 0xff, 0x06, 0x18, 0x00, +0x1a, 0x07, 0x18, 0x00, 0x12, 0x0a, 0x10, 0x00, 0x25, 0x0a, 0x08, 0x00, 0x30, 0x0a, 0x08, 0x00, +0x3b, 0x0a, 0x08, 0x00, 0x1d, 0x08, 0x08, 0x00, 0x61, 0x08, 0x12, 0x00, 0x73, 0x08, 0x12, 0x00, +0x85, 0x08, 0x12, 0x00, 0x97, 0x08, 0x12, 0x00, 0xac, 0x08, 0x0a, 0x00, 0xb6, 0x08, 0x0a, 0x00, +0xc0, 0x08, 0x0a, 0x00, 0xca, 0x08, 0x0a, 0x00, 0xd7, 0x08, 0x0a, 0x00, 0xe1, 0x08, 0x0a, 0x00, +0xeb, 0x08, 0x0a, 0x00, 0xf5, 0x08, 0x0a, 0x00, 0x07, 0x09, 0x0a, 0x00, 0x11, 0x09, 0x0a, 0x00, +0x1b, 0x09, 0x0a, 0x00, 0x25, 0x09, 0x0a, 0x00, 0x2f, 0x09, 0x0a, 0x00, 0x39, 0x09, 0x0a, 0x00, +0x43, 0x09, 0x0a, 0x00, 0x4d, 0x09, 0x0a, 0x00, 0x57, 0x09, 0x0a, 0x00, 0x61, 0x09, 0x0a, 0x00, +0x6b, 0x09, 0x0a, 0x00, 0x75, 0x09, 0x0a, 0x00, 0x7f, 0x09, 0x0a, 0x00, 0x89, 0x09, 0x0a, 0x00, +0x93, 0x09, 0x0a, 0x00, 0x9d, 0x09, 0x0a, 0x00, 0x06, 0x75, 0x00, 0xfc, 0xff, 0x02, 0x80, 0x00, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x8e, 0x29, 0x00, 0x80, 0x9c, 0x01, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x11, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xf0, +0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xf0, +0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, +0x07, 0x07, 0x00, 0xfe, 0xff, 0xce, 0x18, 0x00, 0xff, 0xff, 0x08, 0x3d, 0x00, 0xfc, 0xff, 0x02, +0x40, 0xf0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x01, 0x44, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, +0x48, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x03, 0x03, +0x50, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xf0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, +0x58, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x09, 0x60, 0x00, 0x00, 0x00, 0x32, +0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, +0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, +0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, +0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, +0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, +0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x0a, 0xcb, 0x00, +0x0a, 0x80, 0x04, 0x60, 0x03, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x05, 0x58, 0x02, 0xff, +0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x05, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, +0x07, 0x40, 0x05, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x40, 0x07, 0x70, 0x05, 0xff, 0xff, 0xff, +0xbf, 0xff, 0xff, 0x80, 0x07, 0xa0, 0x05, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x0b, 0xc7, 0x00, 0x21, 0x80, +0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, +0x05, 0x70, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x02, 0x05, 0x72, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x13, 0x00, 0x4a, 0x00, 0x04, 0x00, 0x03, 0x08, 0x3c, 0x84, +0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0xef, 0x87, 0x0d, 0x03, 0x00, 0xf7, 0x03, +0xc8, 0x0e, 0x09, 0x00, 0x01, 0x22, 0x06, 0x5a, 0x00, 0x7e, 0x06, 0x2d, 0x00, 0x0f, 0x8b, 0x00, +0x09, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x84, 0x00, 0x10, 0x00, +0x03, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, +0x40, 0x00, 0x00, 0x40, 0x08, 0x00, 0x20, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x08, 0x00, 0x03, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, +0x04, 0x08, 0x00, 0x40, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, +0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, +0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, +0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, +0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0c, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x20, 0x00, 0x0f, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, +0x9e, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x16, 0x4b, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x84, 0x0e, 0x00, 0x00, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, +0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, +0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, +0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x17, 0x48, +0x00, 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18, 0x88, 0x36, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00, 0x30, 0x40, 0x30, 0x70, 0x13, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1e, 0xa8, 0x2f, 0x78, 0xe0, 0x51, 0x1a, 0x26, 0x40, 0x58, 0x98, 0x13, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x48, 0x3f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, +0xc0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x18, 0x28, 0x00, 0x36, 0x7f, 0x03, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x36, 0x7f, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, +0x36, 0x7f, 0x01, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x36, 0x7f, 0x06, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x19, 0x28, 0x00, 0x19, 0x00, 0xfa, 0x00, 0xfa, 0x00, 0x19, 0x00, 0x90, +0x01, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xc8, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, +0x00, 0x40, 0x00, 0x2c, 0x01, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x2c, 0x01, 0x1a, +0x02, 0x00, 0x00, 0x40, 0x1b, 0xcc, 0x00, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, +0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, +0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, +0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, +0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, +0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, +0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, +0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, +0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, +0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, +0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0xff, 0x1c, 0x36, 0x00, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, 0x08, 0x60, +0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, +0x08, 0x60, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, +0x1d, 0x10, 0x08, 0x60, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1d, 0x34, 0x00, 0x10, +0x00, 0x01, 0x08, 0x01, 0x09, 0x04, 0x0c, 0x40, 0x48, 0x20, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x08, 0x00, 0x01, 0x08, 0x01, 0x09, 0x04, 0x0c, 0x40, 0x48, 0x00, 0x08, 0x00, 0x01, +0x01, 0x09, 0x08, 0x02, 0x05, 0x04, 0x0c, 0x00, 0x08, 0x00, 0x01, 0x01, 0x09, 0x08, 0x02, 0x05, +0x04, 0x0c, 0x00, 0x1e, 0x11, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x18, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, +0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, +0x00, 0x00, 0x29, 0x94, 0x00, 0x03, 0x0c, 0x0b, 0x20, 0x32, 0x0b, 0x12, 0x44, 0x0b, 0x0a, 0x4e, +0x0b, 0x20, 0x74, 0x0b, 0x12, 0x86, 0x0b, 0x0a, 0x90, 0x0b, 0x20, 0xb6, 0x0b, 0x12, 0xc8, 0x0b, +0x0a, 0xd2, 0x0b, 0x20, 0xf8, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x14, 0x0c, 0x20, 0x3a, 0x0c, 0x12, +0x4c, 0x0c, 0x0a, 0x56, 0x0c, 0x20, 0x7c, 0x0c, 0x12, 0x8e, 0x0c, 0x0a, 0x98, 0x0c, 0x20, 0xbe, +0x0c, 0x12, 0xd0, 0x0c, 0x0a, 0xda, 0x0c, 0x20, 0x00, 0x0d, 0x12, 0x12, 0x0d, 0x0a, 0x1c, 0x0d, +0x20, 0x42, 0x0d, 0x12, 0x54, 0x0d, 0x0a, 0x5e, 0x0d, 0x20, 0x84, 0x0d, 0x12, 0x96, 0x0d, 0x0a, +0xa0, 0x0d, 0x20, 0xc6, 0x0d, 0x12, 0xd8, 0x0d, 0x0a, 0xe2, 0x0d, 0x20, 0x08, 0x0e, 0x12, 0x1a, +0x0e, 0x0a, 0x24, 0x0e, 0x20, 0x4a, 0x0e, 0x12, 0x5c, 0x0e, 0x0a, 0x66, 0x0e, 0x20, 0x8c, 0x0e, +0x12, 0x9e, 0x0e, 0x0a, 0xa8, 0x0e, 0x20, 0xce, 0x0e, 0x12, 0xe0, 0x0e, 0x0a, 0xea, 0x0e, 0x20, +0x10, 0x0f, 0x12, 0x22, 0x0f, 0x0a, 0x2c, 0x0f, 0x0d, 0x2a, 0xf0, 0x04, 0x80, 0x02, 0xe0, 0x01, +0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, +0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, +0xff, 0xff, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, 0x08, 0x60, 0x22, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x03, +0x58, 0x02, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, +0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, +0x27, 0x00, 0xff, 0xff, 0xa0, 0x0f, 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, 0x14, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, +0x56, 0x05, 0x00, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x20, 0x00, 0x08, 0x72, 0x0c, 0x00, +0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, +0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x60, 0x1d, 0x56, 0xd8, 0x50, 0x00, 0x18, 0x30, 0x30, 0x40, +0x47, 0x00, 0x15, 0x9c, 0x10, 0x00, 0x00, 0x1b, 0x36, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, +0x00, 0x0c, 0x00, 0x05, 0x00, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, +0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, +0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00, 0x30, 0x40, +0x30, 0x70, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x05, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x78, 0x05, 0x1a, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, +0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, +0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x78, 0x20, 0x51, 0x1a, +0x10, 0x40, 0x10, 0x70, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x01, 0x90, +0x05, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x78, 0x05, 0x1a, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, +0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, +0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0xa8, 0x2f, 0x78, 0xe0, +0x51, 0x1a, 0x26, 0x40, 0x58, 0x98, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, +0x01, 0x90, 0x06, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x06, 0xb0, 0x04, 0x80, 0x11, 0x0e, 0x00, +0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, +0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x48, 0x3f, +0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, +0x36, 0x7f, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x56, 0x05, 0x00, 0x03, 0x80, 0x11, +0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, +0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, +0x66, 0x21, 0x56, 0xaa, 0x51, 0x00, 0x1e, 0x30, 0x46, 0x90, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x36, 0x7f, 0x03, 0x90, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x90, 0x06, 0x1a, 0x04, +0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, +0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, +0xff, 0xff, 0x7c, 0x2e, 0x90, 0xa0, 0x60, 0x1a, 0x1e, 0x40, 0x30, 0x20, 0x36, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x04, 0x90, 0x09, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, +0xb0, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, +0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, +0x27, 0x00, 0xff, 0xff, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, 0x23, 0x40, 0x30, 0x20, 0x2a, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x05, 0x90, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x0c, +0xa0, 0x05, 0x84, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, +0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, +0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x9a, 0x29, 0xa0, 0xd0, 0x51, 0x84, 0x22, 0x30, 0x50, 0x99, +0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, +0x00, 0x0c, 0x40, 0x06, 0x84, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, +0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, +0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x40, 0xc8, 0x60, 0x84, 0x64, 0x30, +0x18, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x0c, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, +0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, +0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, +0x26, 0x30, 0x18, 0x88, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, +0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x20, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, +0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, +0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0xea, 0x1a, 0x00, 0xa0, +0x50, 0x20, 0x17, 0x30, 0x0c, 0x30, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, +0x03, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, 0x38, 0x04, 0x80, 0x11, 0x0e, 0x00, +0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, +0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x02, 0x3a, +0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2d, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, +0x36, 0x7f, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x06, 0x80, 0x11, +0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, +0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, +0x29, 0x40, 0x00, 0x60, 0x80, 0x00, 0x13, 0x60, 0x10, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x4c, 0x46, 0x50, 0x5f, +0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, +0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, +0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, +0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, +0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, +0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, +0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, +0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, +0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, +0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, +0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, +0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, +0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x2b, 0x71, 0x00, 0x06, +0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4a, 0xdc, 0x00, 0x03, +0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, +0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, +0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, +0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, +0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0x2c, 0x15, 0x00, 0x89, 0x46, 0x00, 0x00, 0x00, 0x49, 0x00, 0x0a, 0x00, 0x55, 0x00, 0x50, 0x00, +0x64, 0x00, 0x2c, 0x01, 0x96, 0x00, 0xe8, 0x03, 0x2e, 0xb0, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00 +}; diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index a82a284..b7b4da5 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -21,6 +21,7 @@ #include <console/console.h> #include <bootmode.h> #include <delay.h> +#include <string.h> #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> @@ -652,6 +653,35 @@ static void gma_func0_init(struct device *dev) i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif + + /* Linux relies on VBT for panel info. */ + if (read16(0xc0000) != 0xaa55) { + optionrom_header_t *oh = (void *)0xc0000; + optionrom_pcir_t *pcir; + int sz; + + memset(oh->reserved, 0, 8192); + + sz = (0x80 + sizeof(fake_vbt) + 511) / 512; + oh->signature = 0xaa55; + oh->size = sz; + oh->pcir_offset = 0x40; + oh->vbt_offset = 0x80; + + pcir = (void *)0xc0040; + pcir->signature = 0x52494350; // PCIR + pcir->vendor = dev->vendor; + pcir->device = dev->device; + pcir->length = sizeof(*pcir); + pcir->revision = dev->class; + pcir->classcode[0] = dev->class >> 8; + pcir->classcode[1] = dev->class >> 16; + pcir->classcode[2] = dev->class >> 24; + pcir->imagelength = sz; + pcir->indicator = 0x80; + + memcpy((void *)0xc0080, fake_vbt, sizeof(fake_vbt)); + } } static void gma_set_subsystem(device_t dev, unsigned vendor, unsigned device) diff --git a/src/northbridge/intel/sandybridge/gma.h b/src/northbridge/intel/sandybridge/gma.h index f128412..0248473 100644 --- a/src/northbridge/intel/sandybridge/gma.h +++ b/src/northbridge/intel/sandybridge/gma.h @@ -166,6 +166,8 @@ typedef struct { #define VBT_SIGNATURE 0x54425624 +extern u8 fake_vbt[8192]; + struct northbridge_intel_sandybridge_config; void i915lightup(const struct northbridge_intel_sandybridge_config *info,
1
0
0
0
New patch to review for coreboot: 9c0325d mainboard/jetway/nf81-t56n-lf: Major ACPI board rework
by Edward O'Callaghan
31 May '14
31 May '14
Edward O'Callaghan (eocallaghan(a)alterapraxis.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5887
-gerrit commit 9c0325d813012b06a55403094f371fc905802806 Author: Edward O'Callaghan <eocallaghan(a)alterapraxis.com> Date: Sat May 31 10:09:52 2014 +1000 mainboard/jetway/nf81-t56n-lf: Major ACPI board rework NOTFORMERGE yet Change-Id: I500ebbafd5e9400f9a9dbfe8240fdcdccb6bb900 Signed-off-by: Edward O'Callaghan <eocallaghan(a)alterapraxis.com> --- src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl | 117 +++++++++++++++------ src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl | 76 ++++++++++++- src/mainboard/jetway/nf81-t56n-lf/dsdt.asl | 67 ++++++++++++ 3 files changed, 223 insertions(+), 37 deletions(-) diff --git a/src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl b/src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl index 6ad1ad4..2b83b0a 100644 --- a/src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl +++ b/src/mainboard/jetway/nf81-t56n-lf/acpi/gpe.asl @@ -2,6 +2,7 @@ * This file is part of the coreboot project. * * Copyright (C) 2011 Advanced Micro Devices, Inc. + * Copyright (C) 2014 Edward O'Callaghan <eocallaghan(a)alterapraxis.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,67 +18,117 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -Scope(\_GPE) { /* Start Scope GPE */ +Scope(\_GPE) +{ + OperationRegion (IP, SystemIO, 0x0225, 0x02) + Field (IP, ByteAcc, NoLock, Preserve) + { + INDX, 8, + DAT0, 8 + } /* General event 3 */ - Method(_L03) { - /* DBGO("\\_GPE\\_L00\n") */ - Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + Method(_L03) + { + Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ } - /* Legacy PM event */ - Method(_L08) { - /* DBGO("\\_GPE\\_L08\n") */ + Method (_L04, 0, NotSerialized) /* _Lxx: Level-Triggered GPE */ + { + Notify (\_SB.PCI0.P0PC, 0x02) + Notify (\_SB.PWRB, 0x02) } /* Temp warning (TWarn) event */ - Method(_L09) { + Method(_L09, 0, NotSerialized) + { /* DBGO("\\_GPE\\_L09\n") */ - /* Notify (\_TZ.TZ00, 0x80) */ + Store (GBYT (0x66), Local0) + If (LNotEqual (And (Local0, 0x02), Zero)) + { + Notify (\_TZ.THRM, 0x80) + } + } + + Method (GBYT, 1, NotSerialized) + { + Store (Arg0, INDX) + Store (DAT0, Local0) + Return (Local0) + } + + Method (_L13, 0, NotSerialized) + { + Notify (\_SB.PCI0.GEC, 0x02) + Notify (\_SB.PWRB, 0x02) } /* USB controller PME# */ - Method(_L0B) { - /* DBGO("\\_GPE\\_L0B\n") */ - Notify(\_SB.PCI0.UOH1, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH2, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH3, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH4, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH5, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UOH6, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.UEH1, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + Method(_L0B, 0, NotSerialized) { + Notify (\_SB.PCI0.UOH1, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH2, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH3, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH4, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH5, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UOH6, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.UEH1, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + } + + Method(_L0F) { + Notify (\_SB.PCI0.PE20, 0x02) + Notify (\_SB.PWRB, 0x02) } /* ExtEvent0 SCI event */ Method(_L10) { - /* DBGO("\\_GPE\\_L10\n") */ + Notify (\_SB.PCI0.PE21, 0x02) + Notify (\_SB.PWRB, 0x02) } - /* ExtEvent1 SCI event */ Method(_L11) { - /* DBGO("\\_GPE\\_L11\n") */ + Notify (\_SB.PCI0.PE22, 0x02) + Notify (\_SB.PWRB, 0x02) + } + + Method(_L12) { + Notify (\_SB.PCI0.PE23, 0x02) + Notify (\_SB.PWRB, 0x02) } /* GPIO0 or GEvent8 event */ Method(_L18) { - /* DBGO("\\_GPE\\_L18\n") */ - Notify(\_SB.PCI0.PBR4, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.PBR5, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.PBR6, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PCI0.PBR7, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.BR15, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.PCE6, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ } /* Azalia SCI event */ Method(_L1B) { - /* DBGO("\\_GPE\\_L1B\n") */ - Notify(\_SB.PCI0.AZHD, 0x02) /* NOTIFY_DEVICE_WAKE */ - Notify(\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PCI0.AZHD, 0x02) /* NOTIFY_DEVICE_WAKE */ + Notify (\_SB.PWRB, 0x02) /* NOTIFY_DEVICE_WAKE */ + } + + Method (_L1D, 0, NotSerialized) + { + /* call SIO method on PS/2 Mouse/keyboard event */ + /* \_SB.PCI0.SBRG.SIOH () */ + DBGO("\\_GPE\\_L1D\n") + Notify (\_SB.PWRB, 0x02) } -} /* End Scope GPE */ + + Device (PWRB) + { + Name (_HID, EisaId ("PNP0C0C")) // _HID: Hardware ID + Name (_UID, 0xAA) // _UID: Unique ID + Name (_STA, 0x0B) // _STA: Status + Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake + { + Return (GPRW (0x1D, 0x03)) + } + } +} /* End Scope GPE */ /* Contains the GPEs for USB overcurrent */ #include "usb_oc.asl" - diff --git a/src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl b/src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl index 2f50475..b73a7ce 100644 --- a/src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl +++ b/src/mainboard/jetway/nf81-t56n-lf/acpi/thermal.asl @@ -1,6 +1,8 @@ /* * This file is part of the coreboot project. * + * Copyright (C) 2014 Edward O'Callaghan <eocallaghan(a)alterapraxis.com> + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. @@ -15,7 +17,73 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -/* Thermal Zones have been #if 0 for a long time. - * Removing it for now because it doesn't seem to - * do anything when enabled anyway. - */ +/* Thermal Zones. */ + +Scope (\_TZ) +{ + OperationRegion (IP, SystemIO, 0x0225, 0x02) + Field (IP, ByteAcc, NoLock, Preserve) + { + INDX, 8, + DAT0, 8 + } + + ThermalZone (THRM) + { + Method (KELV, 1, NotSerialized) + { + Store (Arg0, Local1) + Multiply (0x0A, Local1, Local1) + Add (Local1, 0x0AAC, Local1) + Return (Local1) + } + + Method (_TMP, 0, NotSerialized) // _TMP: Temperature + { + If (LEqual (TPCH, One)) + { + While (LGreater (GBYT (0x7A), 0x7E)) + { + Store (GBYT (0x7A), DBG8) + Sleep (0xFA) + Store (One, Local1) + Multiply (0x0A, Local1, Local1) + Add (Local1, 0x0AAC, Local1) + Return (Local1) + } + } + + Return (KELV (CTMP ())) + } + + Method (_CRT, 0, NotSerialized) // _CRT: Critical Temperature + { + Return (KELV (STMP ())) + } + + Method (STMP, 0, NotSerialized) + { + Store (GBYT (0x82), Local0) + Return (Local0) + } + + Method (CTMP, 0, NotSerialized) + { + Store (GBYT (0x7A), Local0) + Store (Local0, DBG8) + If (LGreaterEqual (Local0, 0x65)) + { + Store (0x30, Local0) + } + + Return (Local0) + } + + Method (GBYT, 1, NotSerialized) + { + Store (Arg0, INDX) + Store (DAT0, Local0) + Return (Local0) + } + } +} diff --git a/src/mainboard/jetway/nf81-t56n-lf/dsdt.asl b/src/mainboard/jetway/nf81-t56n-lf/dsdt.asl index a650bca..815e5e2 100644 --- a/src/mainboard/jetway/nf81-t56n-lf/dsdt.asl +++ b/src/mainboard/jetway/nf81-t56n-lf/dsdt.asl @@ -2,6 +2,7 @@ * This file is part of the coreboot project. * * Copyright (C) 2011 Advanced Micro Devices, Inc. + * Copyright (C) 2014 Edward O'Callaghan <eocallaghan(a)alterapraxis.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,7 +48,73 @@ DefinitionBlock ( /* Describe the AMD Fusion Controller Hub Southbridge */ #include <southbridge/amd/cimx/sb800/acpi/fch.asl> + Device (BR15) + { + Name (_ADR, 0x00050000) // _ADR: Address + Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake + { + Return (GPRW (0x18, 0x04)) + } + + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PICM) + { + Return (AR15) + } + + Return (PR15) + } + } + + Device (PCE6) + { + Name (_ADR, 0x00060000) // _ADR: Address + Method (_PRW, 0, NotSerialized) // _PRW: Power Resources for Wake + { + Return (GPRW (0x18, 0x04)) + } + + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PICM) + { + Return (AR16) + } + + Return (PR16) + } + } + + Device (PCE7) + { + Name (_ADR, 0x00070000) // _ADR: Address + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PICM) + { + Return (AR17) + } + + Return (PR17) + } + } + + Device (PCE8) + { + Name (_ADR, 0x00080000) // _ADR: Address + Method (_PRT, 0, NotSerialized) // _PRT: PCI Routing Table + { + If (PICM) + { + Return (AR18) + } + + Return (PR18) + } + } } + } /* End Scope(_SB) */ /* Contains the supported sleep states for this chipset */
1
0
0
0
Patch set updated for coreboot: bbacc46 sandy/ivybridge: Native raminit.
by Vladimir Serbinenko
31 May '14
31 May '14
Vladimir Serbinenko (phcoder(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5786
-gerrit commit bbacc4652ef0ab60a5c2466a41aba66c5b94796d Author: Vladimir Serbinenko <phcoder(a)gmail.com> Date: Sun May 18 11:05:56 2014 +0200 sandy/ivybridge: Native raminit. Based on damo22 work and my X230 tracing. Works for my X230 in a variety of RAM configs. Also-By: Damien Zammit <damien(a)zamaudio.com> Change-Id: I1aa024c55a8416fc53b25e7123037df0e55a2769 Signed-off-by: Vladimir Serbinenko <phcoder(a)gmail.com> --- 3rdparty | 2 +- src/cpu/intel/Makefile.inc | 1 + src/cpu/x86/smm/smmhandler_tseg.S | 2 +- src/cpu/x86/smm/smmrelocate.S | 4 +- src/device/dram/ddr3.c | 8 +- src/include/device/dram/ddr3.h | 3 + src/mainboard/lenovo/x230/Kconfig | 6 +- src/mainboard/lenovo/x230/romstage.c | 100 +- src/northbridge/intel/Makefile.inc | 1 + src/northbridge/intel/sandybridge/Kconfig | 12 +- src/northbridge/intel/sandybridge/Makefile.inc | 5 +- src/northbridge/intel/sandybridge/gma.c | 4 +- src/northbridge/intel/sandybridge/raminit_native.c | 3766 ++++++++++++++++++++ src/northbridge/intel/sandybridge/raminit_native.h | 29 + src/southbridge/intel/bd82x6x/Makefile.inc | 6 +- src/southbridge/intel/bd82x6x/early_me_native.c | 272 ++ src/southbridge/intel/bd82x6x/early_pch_native.c | 375 ++ src/southbridge/intel/bd82x6x/early_thermal.c | 70 + src/southbridge/intel/bd82x6x/pch.h | 2 + src/southbridge/intel/bd82x6x/smi.c | 3 - src/southbridge/intel/bd82x6x/usb_ehci.c | 33 + src/southbridge/intel/ibexpeak/me.c | 125 - src/southbridge/intel/ibexpeak/smi.c | 6 - src/southbridge/intel/ibexpeak/usb_ehci.c | 17 + 24 files changed, 4628 insertions(+), 224 deletions(-) diff --git a/3rdparty b/3rdparty index 324ec3c..45f0c04 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 324ec3cb642a278d6d97ae809bc6098045bc6e65 +Subproject commit 45f0c04fd788fb29d9e303b2b2d1657ddb03448a diff --git a/src/cpu/intel/Makefile.inc b/src/cpu/intel/Makefile.inc index 0392f69..68ea05d 100644 --- a/src/cpu/intel/Makefile.inc +++ b/src/cpu/intel/Makefile.inc @@ -19,6 +19,7 @@ subdirs-$(CONFIG_CPU_INTEL_SOCKET_RPGA989) += socket_rPGA989 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += model_2065x subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += model_206ax +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_model_206ax diff --git a/src/cpu/x86/smm/smmhandler_tseg.S b/src/cpu/x86/smm/smmhandler_tseg.S index b33fcdf..380935a 100644 --- a/src/cpu/x86/smm/smmhandler_tseg.S +++ b/src/cpu/x86/smm/smmhandler_tseg.S @@ -57,7 +57,7 @@ #define SMI_UNLOCKED 1 #define __PRE_RAM__ -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include <northbridge/intel/sandybridge/sandybridge.h> #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM diff --git a/src/cpu/x86/smm/smmrelocate.S b/src/cpu/x86/smm/smmrelocate.S index bdc9771..bc90fab 100644 --- a/src/cpu/x86/smm/smmrelocate.S +++ b/src/cpu/x86/smm/smmrelocate.S @@ -48,7 +48,7 @@ #if CONFIG_SMM_TSEG -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include <northbridge/intel/sandybridge/sandybridge.h> #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM @@ -195,7 +195,7 @@ smm_relocate: xorl %edx, %edx wrmsr -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE /* * IED base is top 4M of TSEG */ diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c index 9b4f490..69782ab 100644 --- a/src/device/dram/ddr3.c +++ b/src/device/dram/ddr3.c @@ -110,7 +110,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) { int ret; u16 crc, spd_crc; - u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width, sdram_width; + u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width; u8 reg8; u32 mtb; /* medium time base */ unsigned int val, param; @@ -209,8 +209,8 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) printram(" Invalid SDRAM width\n"); ret = SPD_STATUS_INVALID_FIELD; } - sdram_width = (4 << val); - printram(" SDRAM width : %u\n", sdram_width); + dimm->width = (4 << val); + printram(" SDRAM width : %u\n", dimm->width); /* Memory bus width */ reg8 = spd[8]; @@ -236,7 +236,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) * capacity_shift * The rest is the JEDEC formula */ dimm->size_mb = ((1 << (capacity_shift + (25 - 20))) * bus_width - * dimm->ranks) / sdram_width; + * dimm->ranks) / dimm->width; /* Fine Timebase (FTB) Dividend/Divisor */ /* Dividend */ diff --git a/src/include/device/dram/ddr3.h b/src/include/device/dram/ddr3.h index b19c51c..4bf5058 100644 --- a/src/include/device/dram/ddr3.h +++ b/src/include/device/dram/ddr3.h @@ -37,6 +37,7 @@ * @{ */ #define TCK_1066MHZ 240 +#define TCK_933MHZ 275 #define TCK_800MHZ 320 #define TCK_666MHZ 384 #define TCK_533MHZ 480 @@ -137,6 +138,8 @@ typedef struct dimm_attr_st { u16 cas_supported; /* Flags extracted from SPD */ dimm_flags_t flags; + /* SDRAM width */ + u8 width; /* Number of ranks */ u8 ranks; /* Number or row address bits */ diff --git a/src/mainboard/lenovo/x230/Kconfig b/src/mainboard/lenovo/x230/Kconfig index d3aa6e9..6fd309e 100644 --- a/src/mainboard/lenovo/x230/Kconfig +++ b/src/mainboard/lenovo/x230/Kconfig @@ -3,7 +3,7 @@ if BOARD_LENOVO_X230 config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select CPU_INTEL_SOCKET_RPGA989 - select NORTHBRIDGE_INTEL_IVYBRIDGE + select NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE select SOUTHBRIDGE_INTEL_C216 select EC_LENOVO_PMH7 select EC_LENOVO_H8 @@ -47,10 +47,6 @@ config MMCONF_BASE_ADDRESS hex default 0xf0000000 -config CACHE_ROM_SIZE_OVERRIDE - hex - default 0x800000 - config IRQ_SLOT_COUNT int default 18 diff --git a/src/mainboard/lenovo/x230/romstage.c b/src/mainboard/lenovo/x230/romstage.c index 6e4e685..ff664f4 100644 --- a/src/mainboard/lenovo/x230/romstage.c +++ b/src/mainboard/lenovo/x230/romstage.c @@ -32,7 +32,7 @@ #include <cbmem.h> #include <console/console.h> #include "northbridge/intel/sandybridge/sandybridge.h" -#include "northbridge/intel/sandybridge/raminit.h" +#include "northbridge/intel/sandybridge/raminit_native.h" #include "southbridge/intel/bd82x6x/pch.h" #include "southbridge/intel/bd82x6x/gpio.h" #include <arch/cpu.h> @@ -108,66 +108,39 @@ static void rcba_config(void) RCBA32(BUC) = 0; } +static void +init_usb (void) +{ + write32 (DEFAULT_RCBABASE | 0x3598, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35a0, 0x04000201); + write32 (DEFAULT_RCBABASE | 0x35a4, 0x00000200); + write32 (DEFAULT_RCBABASE | 0x35a8, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35ac, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35b0, 0x00000000); + + write32 (DEFAULT_RCBABASE | 0x3560, 0x024c8001); + + outw (inw (DEFAULT_PMBASE | 0x003c) | 2, DEFAULT_PMBASE | 0x003c); + + write32 (DEFAULT_RCBABASE | 0x359c, 0x00000040); + pcie_write_config32 (PCI_DEV (0, 0x14, 0), 0xe4, 0x00000000); + outw (0x0000, DEFAULT_PMBASE | 0x003c); +} + + void main(unsigned long bist) { int boot_mode = 0; int cbmem_was_initted; u32 pm1_cnt; u16 pm1_sts; + spd_raw_data spd[4]; if (MCHBAR16(SSKPD) == 0xCAFE) { outb(0x6, 0xcf9); hlt (); } - struct pei_data pei_data = { - .pei_version = PEI_VERSION, - .mchbar = DEFAULT_MCHBAR, - .dmibar = DEFAULT_DMIBAR, - .epbar = DEFAULT_EPBAR, - .pciexbar = CONFIG_MMCONF_BASE_ADDRESS, - .smbusbar = SMBUS_IO_BASE, - .wdbbar = 0x4000000, - .wdbsize = 0x1000, - .hpet_address = CONFIG_HPET_ADDRESS, - .rcba = DEFAULT_RCBABASE, - .pmbase = DEFAULT_PMBASE, - .gpiobase = DEFAULT_GPIOBASE, - .thermalbase = 0xfed08000, - .system_type = 0, // 0 Mobile, 1 Desktop/Server - .tseg_size = CONFIG_SMM_TSEG_SIZE, - .spd_addresses = { 0xA0, 0x00,0xA2,0x00 }, - .ts_addresses = { 0x00, 0x00, 0x00, 0x00 }, - .ec_present = 1, - .gbe_enable = 1, - .ddr3lv_support = 0, - // 0 = leave channel enabled - // 1 = disable dimm 0 on channel - // 2 = disable dimm 1 on channel - // 3 = disable dimm 0+1 on channel - .dimm_channel0_disabled = 2, - .dimm_channel1_disabled = 2, - .max_ddr3_freq = 1600, - .usb_port_config = { - /* enabled usb oc pin length */ - { 1, 0, 0x0080 }, /* P0 (left, fan side), OC 0 */ - { 1, 1, 0x0080 }, /* P1 (left touchpad side), OC 1 */ - { 1, 3, 0x0080 }, /* P2: dock, OC 3 */ - { 1, 0, 0x0040 }, /* P3: wwan, no OC */ - { 1, 0, 0x0080 }, /* P4: Wacom tablet on X230t, otherwise empty */ - { 1, 0, 0x0080 }, /* P5: Expresscard, no OC */ - { 0, 0, 0x0000 }, /* P6: Empty */ - { 1, 0, 0x0080 }, /* P7: dock, no OC */ - { 0, 0, 0x0000 }, /* P8: Empty */ - { 1, 5, 0x0080 }, /* P9: Right (EHCI debug), OC 5 */ - { 1, 0, 0x0040 }, /* P10: fingerprint reader, no OC */ - { 1, 0, 0x0040 }, /* P11: bluetooth, no OC. */ - { 1, 0, 0x0040 }, /* P12: wlan, no OC */ - { 1, 0, 0x0080 }, /* P13: webcam, no OC */ - }, - .ddr_refresh_rate_config = 2, /* Force double refresh rate */ - }; - timestamp_init(get_initial_timestamp()); timestamp_add_now(TS_START_ROMSTAGE); @@ -182,6 +155,8 @@ void main(unsigned long bist) setup_pch_gpios(&x230_gpio_map); + init_usb(); + /* Initialize console device(s) */ console_init(); @@ -217,31 +192,16 @@ void main(unsigned long bist) /* Enable SPD ROMs and DDR-III DRAM */ enable_smbus(); - /* Prepare USB controller early in S3 resume */ - if (boot_mode == 2) - enable_usb_bar(); - post_code(0x39); post_code(0x3a); - pei_data.boot_mode = boot_mode; timestamp_add_now(TS_BEFORE_INITRAM); - /* MRC.bin has a bug and sometimes halts (instead of reboot?). - */ - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) & ~(1 << 5); /* reset */ - outw((0 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* let timer go */ - } - - sdram_initialize(&pei_data); + memset (spd, 0, sizeof (spd)); + read_spd (&spd[0], 0x50); + read_spd (&spd[2], 0x51); - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) | (1 << 5); /* No reset */ - outw((1 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */ - } + init_dram_ddr3 (spd, 1, TCK_800MHZ); timestamp_add_now(TS_AFTER_INITRAM); post_code(0x3c); @@ -254,8 +214,8 @@ void main(unsigned long bist) MCHBAR16(SSKPD) = 0xCAFE; cbmem_was_initted = !cbmem_recovery(boot_mode==2); - if (boot_mode!=2) - save_mrc_data(&pei_data); +// if (boot_mode!=2) +// save_mrc_data(&pei_data); #if CONFIG_HAVE_ACPI_RESUME /* If there is no high memory area, we didn't boot before, so diff --git a/src/northbridge/intel/Makefile.inc b/src/northbridge/intel/Makefile.inc index 808a1b2..f1c2540 100644 --- a/src/northbridge/intel/Makefile.inc +++ b/src/northbridge/intel/Makefile.inc @@ -15,6 +15,7 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I5000) += i5000 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += nehalem subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += sandybridge +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_sandybridge diff --git a/src/northbridge/intel/sandybridge/Kconfig b/src/northbridge/intel/sandybridge/Kconfig index fb92e40..9bac706 100644 --- a/src/northbridge/intel/sandybridge/Kconfig +++ b/src/northbridge/intel/sandybridge/Kconfig @@ -31,7 +31,14 @@ config NORTHBRIDGE_INTEL_IVYBRIDGE select MMCONF_SUPPORT_DEFAULT select CPU_INTEL_MODEL_306AX -if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE +config NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + bool + select CACHE_MRC_BIN + select MMCONF_SUPPORT + select MMCONF_SUPPORT_DEFAULT + select CPU_INTEL_MODEL_306AX + +if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config VGA_BIOS_ID string @@ -52,7 +59,8 @@ config MRC_CACHE_SIZE config DCACHE_RAM_BASE hex - default 0xff7e0000 + default 0xff7e0000 if !NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + default 0xfefe0000 if NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config DCACHE_RAM_SIZE hex diff --git a/src/northbridge/intel/sandybridge/Makefile.inc b/src/northbridge/intel/sandybridge/Makefile.inc index 6655e2a..ae20de5 100644 --- a/src/northbridge/intel/sandybridge/Makefile.inc +++ b/src/northbridge/intel/sandybridge/Makefile.inc @@ -26,7 +26,10 @@ ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c ramstage-y += mrccache.c romstage-y += ram_calc.c -romstage-y += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += raminit_native.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += ../../../device/dram/ddr3.c romstage-y += mrccache.c romstage-y += early_init.c romstage-y += report_platform.c diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index d271a1a..a82a284 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -650,9 +650,7 @@ static void gma_func0_init(struct device *dev) physbase = pci_read_config32(dev, 0x5c) & ~0xf; graphics_base = dev->resource_list[1].base; - int lightup_ok = i915lightup(conf, physbase, iobase, mmiobase, graphics_base); - if (lightup_ok) - gfx_set_init_done(1); + i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif } diff --git a/src/northbridge/intel/sandybridge/raminit_native.c b/src/northbridge/intel/sandybridge/raminit_native.c new file mode 100644 index 0000000..4f37413 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.c @@ -0,0 +1,3766 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Damien Zammit <damien(a)zamaudio.com> + * Copyright (C) 2014 Vladimir Serbinenko <phcoder(a)gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <console/usb.h> +#include <bootmode.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include "raminit_native.h" +#include "sandybridge.h" +#include <delay.h> + +/* Management Engine is in the southbridge */ +#include "southbridge/intel/bd82x6x/me.h" +/* For SPD. */ +#include "southbridge/intel/bd82x6x/smbus.h" +#include "arch/cpu.h" +#include "cpu/x86/msr.h" + +/* FIXME: no ECC support. */ +/* FIXME: no support for 3-channel chipsets. */ +/* FIXME: no S3. */ +/* FIXME: no timing caching. */ + +#define BASEFREQ 133 +#define tDLLK 512 + +#define IS_SANDY_CPU(x) ((x & 0xffff0) == 0x206a0) +#define IS_SANDY_CPU_C(x) ((x & 0xf) == 4) +#define IS_SANDY_CPU_D0(x) ((x & 0xf) == 5) +#define IS_SANDY_CPU_D1(x) ((x & 0xf) == 6) +#define IS_SANDY_CPU_D2(x) ((x & 0xf) == 7) + +#define IS_IVY_CPU(x) ((x & 0xffff0) == 0x306a0) +#define IS_IVY_CPU_C(x) ((x & 0xf) == 4) +#define IS_IVY_CPU_K(x) ((x & 0xf) == 5) +#define IS_IVY_CPU_D(x) ((x & 0xf) == 6) +#define IS_IVY_CPU_E(x) ((x & 0xf) >= 8) + +#define NUM_CHANNELS 2 +#define NUM_SLOTRANKS 4 +#define NUM_SLOTS 2 +#define NUM_LANES 8 + +typedef struct odtmap_st { + u16 rttwr; + u16 rttnom; +} odtmap; + +typedef struct dimm_info_st { + dimm_attr dimm[NUM_CHANNELS][NUM_SLOTS]; +} dimm_info; + +struct ram_rank_timings { + /* Register 4024. One byte per slotrank. */ + u8 val_4024; + /* Register 4028. One nibble per slotrank. */ + u8 val_4028; + + int val_320c; + + struct ram_lane_timings { + /* lane register offset 0x10. */ + u16 timA; /* bits 0 - 5, bits 16 - 18 */ + u8 rising; /* bits 8 - 14 */ + u8 falling; /* bits 20 - 26. */ + + /* lane register offset 0x20. */ + int timC; /* bit 0 - 5, 19. */ + u16 timB; /* bits 8 - 13, 15 - 17. */ + } lanes[NUM_LANES]; +}; + +typedef struct ramctr_timing_st { + int mobile; + + enum spd_memory_type dram_type; + u16 cas_supported; + /* tLatencies are in units of ns, scaled by x256 */ + u32 tCK; + u32 tAA; + u32 tWR; + u32 tRCD; + u32 tRRD; + u32 tRP; + u32 tRAS; + u32 tRC; + u32 tRFC; + u32 tWTR; + u32 tRTP; + u32 tFAW; + /* Latencies in terms of clock cycles + * They are saved separately as they are needed for DRAM MRS commands*/ + u8 CAS; /* CAS read latency */ + u8 CWL; /* CAS write latency */ + /* Number of dimms currently connected */ + u8 n_dimms; + + u32 tREFI; + u32 tMOD; + u32 tXSOffset; + u32 tWLO; + u32 tCKE; + u32 tXPDLL; + u32 tXP; + u32 tAONPD; + + u32 delay1; + u32 delay2; + u16 reg_5064b0; /* bits 0-11. */ + + u8 eccsupport; + u8 dualchannel; + + u8 rankmap[NUM_CHANNELS]; + int ref_card_offset[NUM_CHANNELS]; + + int reg_c14_offset; + + int edge_offset[3]; + int timC_offset[3]; + + int extended_temperature_range; + int auto_self_refresh; + + int rank_mirror[NUM_CHANNELS][NUM_SLOTRANKS]; + + struct ram_rank_timings timings[NUM_CHANNELS][NUM_SLOTRANKS]; +} ramctr_timing; + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) +#define NORTHBRIDGE PCI_DEV(0, 0x0, 0) +#define FOR_ALL_LANES for (lane = 0; lane < NUM_LANES; lane++) +#define FOR_ALL_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) +#define FOR_ALL_POPULATED_RANKS for (slotrank = 0; slotrank < NUM_SLOTRANKS; slotrank++) if (ctrl->rankmap[channel] & (1 << slotrank)) +#define FOR_ALL_POPULATED_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) if (ctrl->rankmap[channel]) +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define MAX_EDGE_TIMING 71 +#define MAX_TIMC 127 +#define MAX_TIMB 511 +#define MAX_TIMA 127 + +static void program_timings(ramctr_timing * ctrl, int channel); + +static const char *ecc_decoder[] = { + "inactive", + "active on IO", + "disabled on IO", + "active" +}; + +static void wait_txt_clear(void) +{ + struct cpuid_result cp; + + cp = cpuid_ext(0x1, 0x0); + /* Check if TXT is supported? */ + if (!(cp.ecx & 0x40)) + return; + /* Some TXT public bit. */ + if (!(read32(0xfed30010) & 1)) + return; + /* Wait for TXT clear. */ + while (!(read8(0xfed40000) & (1 << 7))) ; +} + +static void sfence(void) +{ + asm volatile ("sfence"); +} + +/* + * Dump in the log memory controller configuration as read from the memory + * controller registers. + */ +static void report_memory_config(void) +{ + u32 addr_decoder_common, addr_decode_ch[NUM_CHANNELS]; + int i; + + addr_decoder_common = MCHBAR32(0x5000); + addr_decode_ch[0] = MCHBAR32(0x5004); + addr_decode_ch[1] = MCHBAR32(0x5008); + + printk(BIOS_DEBUG, "memcfg DDR3 clock %d MHz\n", + (MCHBAR32(0x5e04) * 13333 * 2 + 50) / 100); + printk(BIOS_DEBUG, "memcfg channel assignment: A: %d, B % d, C % d\n", + addr_decoder_common & 3, (addr_decoder_common >> 2) & 3, + (addr_decoder_common >> 4) & 3); + + for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) { + u32 ch_conf = addr_decode_ch[i]; + printk(BIOS_DEBUG, "memcfg channel[%d] config (%8.8x):\n", i, + ch_conf); + printk(BIOS_DEBUG, " ECC %s\n", + ecc_decoder[(ch_conf >> 24) & 3]); + printk(BIOS_DEBUG, " enhanced interleave mode %s\n", + ((ch_conf >> 22) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " rank interleave %s\n", + ((ch_conf >> 21) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " DIMMA %d MB width x%d %s rank%s\n", + ((ch_conf >> 0) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? 16 : 8, + ((ch_conf >> 17) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? "" : ", selected"); + printk(BIOS_DEBUG, " DIMMB %d MB width x%d %s rank%s\n", + ((ch_conf >> 8) & 0xff) * 256, + ((ch_conf >> 20) & 1) ? 16 : 8, + ((ch_conf >> 18) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? ", selected" : ""); + } +} + +static void post_system_agent_init(void) +{ + /* If PCIe init is skipped, set the PEG clock gating */ + MCHBAR32(0x7010) = MCHBAR32(0x7010) | 0x01; +} + +void read_spd(spd_raw_data * spd, u8 addr) +{ + int j; + for (j = 0; j < 256; j++) + (*spd)[j] = do_smbus_read_byte(SMBUS_IO_BASE, addr, j); +} + +static void dram_find_spds_ddr3(spd_raw_data * spd, dimm_info * dimm, + ramctr_timing * ctrl) +{ + int dimms = 0; + int channel, slot, spd_slot; + + memset (ctrl->rankmap, 0, sizeof (ctrl->rankmap)); + + ctrl->extended_temperature_range = 1; + ctrl->auto_self_refresh = 1; + + FOR_ALL_CHANNELS { + int ref_card[NUM_SLOTS]; + for (slot = 0; slot < NUM_SLOTS; slot++) { + spd_slot = 2 * channel + slot; + spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + if (dimm->dimm[channel][slot].dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { + // set dimm invalid + dimm->dimm[channel][slot].ranks = 0; + dimm->dimm[channel][slot].size_mb = 0; + continue; + } + + dram_print_spd_ddr3(&dimm->dimm[channel][slot]); + dimms++; + ctrl->rank_mirror[channel][slot * 2] = 0; + ctrl->rank_mirror[channel][slot * 2 + 1] = spd[spd_slot][0x3f] & 1; + + ctrl->auto_self_refresh &= (spd[spd_slot][31] >> 2) & 1; + ctrl->extended_temperature_range &= spd[spd_slot][31] & 1; + + ctrl->rankmap[channel] = ((1 << dimm->dimm[channel][slot].ranks) - 1) << (2 * slot); + ref_card[channel] = spd[spd_slot][62] & 0x1f;; + printk(BIOS_DEBUG, "rankmap[%d] = 0x%x\n", channel, ctrl->rankmap[channel]); + } + if ((ctrl->rankmap[channel] & 3) && (ctrl->rankmap[channel] & 0xc) + && ref_card[0] <= 5 && ref_card[1] <= 5) { + const int ref_card_offset_table[6][6] = { + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 1, 1, }, + { 2, 2, 2, 1, 0, 0, }, + { 2, 2, 2, 1, 0, 0, }, + }; + ctrl->ref_card_offset[channel] = ref_card_offset_table[ref_card[0]][ref_card[1]]; + } else + ctrl->ref_card_offset[channel] = 0; + } + + if (!dimms) + die("No DIMMs were found"); +} + +static void dram_find_common_params(const dimm_info * dimms, + ramctr_timing * ctrl) +{ + size_t valid_dimms; + int channel, slot; + ctrl->cas_supported = 0xff; + valid_dimms = 0; + FOR_ALL_CHANNELS + for (slot = 0; slot < 2; slot++) { + const dimm_attr *dimm = &dimms->dimm[channel][slot]; + if (dimm->dram_type == SPD_MEMORY_TYPE_UNDEFINED) + continue; + valid_dimms++; + + if (valid_dimms == 1) { + /* First DIMM defines the type of DIMM */ + ctrl->dram_type = dimm->dram_type; + } else { + /* Check if we have mismatched DIMMs */ + if (ctrl->dram_type != dimm->dram_type) + die("Mismatched DIMM Types"); + } + /* Find all possible CAS combinations */ + ctrl->cas_supported &= dimm->cas_supported; + + /* Find the smallest common latencies supported by all DIMMs */ + ctrl->tCK = MAX(ctrl->tCK, dimm->tCK); + ctrl->tAA = MAX(ctrl->tAA, dimm->tAA); + ctrl->tWR = MAX(ctrl->tWR, dimm->tWR); + ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD); + ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD); + ctrl->tRP = MAX(ctrl->tRP, dimm->tRP); + ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS); + ctrl->tRC = MAX(ctrl->tRC, dimm->tRC); + ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC); + ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR); + ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP); + ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW); + } + + ctrl->n_dimms = valid_dimms; + if (!ctrl->cas_supported) + die("Unsupported DIMM combination. " + "DIMMS do not support common CAS latency"); + if (!valid_dimms) + die("No valid DIMMs found"); + + ctrl->dualchannel = + (pcie_read_config32(PCI_DEV(0, 0, 0), 0xE4) & 0x4000) >> 14; + if (ctrl->dualchannel) { + printk(BIOS_DEBUG, "Dual channel supported\n"); + } else { + printk(BIOS_DEBUG, "Dual channel not supported\n"); + } +} + +static u8 get_CWL(u8 CAS) +{ + /* Get CWL based on CAS using the following rule: + * _________________________________________ + * CAS: | 4T | 5T | 6T | 7T | 8T | 9T | 10T | 11T | + * CWL: | 5T | 5T | 5T | 6T | 6T | 7T | 7T | 8T | + */ + static const u8 cas_cwl_map[] = { 5, 5, 5, 6, 6, 7, 7, 8 }; + if (CAS > 11) + return 8; + return cas_cwl_map[CAS - 4]; +} + +static u32 get_REFI(u32 tCK) +{ + /* Get REFI based on MCU frequency using the following rule: + * _________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * REFI: | 3120 | 4160 | 5200 | 6240 | 7280 | 8320 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u32 frq_refi_map[] = + { 3120, 4160, 5200, 6240, 7280, 8320 }; + if (FRQ > 8) + return 8320; + return frq_refi_map[FRQ - 3]; +} + +static u8 get_XSOffset(u32 tCK) +{ + /* Get XSOffset based on MCU frequency using the following rule: + * _________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XSOffset : | 4 | 6 | 7 | 8 | 10 | 11 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xs_map[] = { 4, 6, 7, 8, 10, 11 }; + if (FRQ > 8) + return 11; + return frq_xs_map[FRQ - 3]; +} + +static u8 get_MOD(u32 tCK) +{ + /* Get MOD based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * MOD : | 12 | 12 | 12 | 12 | 15 | 16 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_mod_map[] = { 12, 12, 12, 12, 15, 16 }; + if (FRQ > 8) + return 16; + return frq_mod_map[FRQ - 3]; +} + +static u8 get_WLO(u32 tCK) +{ + /* Get WLO based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * WLO : | 4 | 5 | 6 | 6 | 8 | 8 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_wlo_map[] = { 4, 5, 6, 6, 8, 8 }; + if (FRQ > 8) + return 8; + return frq_wlo_map[FRQ - 3]; +} + +static u8 get_CKE(u32 tCK) +{ + /* Get CKE based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * CKE : | 3 | 3 | 4 | 4 | 5 | 6 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_cke_map[] = { 3, 3, 4, 4, 5, 6 }; + if (FRQ > 8) + return 6; + return frq_cke_map[FRQ - 3]; +} + +static u8 get_XPDLL(u32 tCK) +{ + /* Get XPDLL based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XPDLL : | 10 | 13 | 16 | 20 | 23 | 26 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xpdll_map[] = { 10, 13, 16, 20, 23, 26 }; + if (FRQ > 8) + return 26; + return frq_xpdll_map[FRQ - 3]; +} + +static u8 get_XP(u32 tCK) +{ + /* Get XP based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XP : | 3 | 4 | 4 | 5 | 6 | 7 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xp_map[] = { 3, 4, 4, 5, 6, 7 }; + if (FRQ > 8) + return 7; + return frq_xp_map[FRQ - 3]; +} + +static u8 get_AONPD(u32 tCK) +{ + /* Get AONPD based on MCU frequency using the following rule: + * ________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * AONPD : | 4 | 5 | 6 | 8 | 8 | 10 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_aonpd_map[] = { 4, 5, 6, 8, 8, 10 }; + if (FRQ > 8) + return 10; + return frq_aonpd_map[FRQ - 3]; +} + +static u32 get_COMP2(u32 tCK) +{ + /* Get COMP2 based on MCU frequency using the following rule: + * ___________________________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * COMP : | D6BEDCC | CE7C34C | CA57A4C | C6369CC | C42514C | C21410C | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u32 frq_comp2_map[] = { 0xD6BEDCC, 0xCE7C34C, 0xCA57A4C, + 0xC6369CC, 0xC42514C, 0xC21410C + }; + if (FRQ > 8) + return 0xD6BEDCC; + return frq_comp2_map[FRQ - 3]; +} + +static void dram_timing(ramctr_timing * ctrl) +{ + u8 val; + u32 val32; + + /* Maximum supported DDR3 frequency is 1066MHz (DDR3 2133) so make sure + * we cap it if we have faster DIMMs. + * Then, align it to the closest JEDEC standard frequency */ + if (ctrl->tCK <= TCK_1066MHZ) { + ctrl->tCK = TCK_1066MHZ; + ctrl->delay1 = 16; + ctrl->delay2 = 8; + ctrl->edge_offset[0] = 16; + ctrl->edge_offset[1] = 7; + ctrl->edge_offset[2] = 7; + ctrl->timC_offset[0] = 18; + ctrl->timC_offset[1] = 7; + ctrl->timC_offset[2] = 7; + ctrl->reg_c14_offset = 16; + ctrl->reg_5064b0 = 0x218; + } else if (ctrl->tCK <= TCK_933MHZ) { + ctrl->tCK = TCK_933MHZ; + ctrl->delay1 = 15; + ctrl->delay2 = 8; + ctrl->edge_offset[0] = 14; + ctrl->edge_offset[1] = 6; + ctrl->edge_offset[2] = 6; + ctrl->timC_offset[0] = 15; + ctrl->timC_offset[1] = 6; + ctrl->timC_offset[2] = 6; + ctrl->reg_c14_offset = 14; + ctrl->reg_5064b0 = 0x1d5; + } else if (ctrl->tCK <= TCK_800MHZ) { + ctrl->tCK = TCK_800MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 6; + ctrl->edge_offset[0] = 13; + ctrl->edge_offset[1] = 5; + ctrl->edge_offset[2] = 5; + ctrl->timC_offset[0] = 14; + ctrl->timC_offset[1] = 5; + ctrl->timC_offset[2] = 5; + ctrl->reg_c14_offset = 12; + ctrl->reg_5064b0 = 0x193; + } else if (ctrl->tCK <= TCK_666MHZ) { + ctrl->tCK = TCK_666MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 6; + ctrl->edge_offset[0] = 10; + ctrl->edge_offset[1] = 4; + ctrl->edge_offset[2] = 4; + ctrl->timC_offset[0] = 11; + ctrl->timC_offset[1] = 4; + ctrl->timC_offset[2] = 4; + ctrl->reg_c14_offset = 10; + ctrl->reg_5064b0 = 0x150; + } else { + ctrl->tCK = TCK_533MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 5; + ctrl->edge_offset[0] = 8; + ctrl->edge_offset[1] = 3; + ctrl->edge_offset[2] = 3; + ctrl->timC_offset[0] = 9; + ctrl->timC_offset[1] = 3; + ctrl->timC_offset[2] = 3; + ctrl->reg_c14_offset = 8; + ctrl->reg_5064b0 = 0x10d; + } + + val32 = (1000 << 8) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected DRAM frequency: %u MHz\n", val32); + + /* Find CAS and CWL latencies */ + val = (ctrl->tAA + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Minimum CAS latency : %uT\n", val); + /* Find lowest supported CAS latency that satisfies the minimum value */ + while (!((ctrl->cas_supported >> (val - 4)) & 1) + && (ctrl->cas_supported >> (val - 4))) { + val++; + } + /* Is CAS supported */ + if (!(ctrl->cas_supported & (1 << (val - 4)))) + printk(BIOS_DEBUG, "CAS not supported\n"); + printk(BIOS_DEBUG, "Selected CAS latency : %uT\n", val); + ctrl->CAS = val; + ctrl->CWL = get_CWL(ctrl->CAS); + printk(BIOS_DEBUG, "Selected CWL latency : %uT\n", ctrl->CWL); + + /* Find tRCD */ + ctrl->tRCD = (ctrl->tRCD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRCD : %uT\n", ctrl->tRCD); + + ctrl->tRP = (ctrl->tRP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRP : %uT\n", ctrl->tRP); + + /* Find tRAS */ + ctrl->tRAS = (ctrl->tRAS + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRAS : %uT\n", ctrl->tRAS); + + /* Find tWR */ + ctrl->tWR = (ctrl->tWR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWR : %uT\n", ctrl->tWR); + + /* Find tFAW */ + ctrl->tFAW = (ctrl->tFAW + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tFAW : %uT\n", ctrl->tFAW); + + /* Find tRRD */ + ctrl->tRRD = (ctrl->tRRD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRRD : %uT\n", ctrl->tRRD); + + /* Find tRTP */ + ctrl->tRTP = (ctrl->tRTP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRTP : %uT\n", ctrl->tRTP); + + /* Find tWTR */ + ctrl->tWTR = (ctrl->tWTR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWTR : %uT\n", ctrl->tWTR); + + /* Refresh-to-Active or Refresh-to-Refresh (tRFC) */ + ctrl->tRFC = (ctrl->tRFC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRFC : %uT\n", ctrl->tRFC); + + ctrl->tRC = (ctrl->tRC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Required tRC : %uT\n", ctrl->tRC); + + ctrl->tREFI = get_REFI(ctrl->tCK); + ctrl->tMOD = get_MOD(ctrl->tCK); + ctrl->tXSOffset = get_XSOffset(ctrl->tCK); + ctrl->tWLO = get_WLO(ctrl->tCK); + ctrl->tCKE = get_CKE(ctrl->tCK); + ctrl->tXPDLL = get_XPDLL(ctrl->tCK); + ctrl->tXP = get_XP(ctrl->tCK); + ctrl->tAONPD = get_AONPD(ctrl->tCK); +} + +static void dram_freq(ramctr_timing * ctrl) +{ + u8 val1, val2; + u32 reg1 = 0; + + /* Step 1 - Set target PCU frequency */ + + if (ctrl->tCK <= TCK_1066MHZ) { + val1 = 0x08; + ctrl->tCK = TCK_1066MHZ; + } else if (ctrl->tCK <= TCK_933MHZ) { + val1 = 0x07; + ctrl->tCK = TCK_933MHZ; + } else if (ctrl->tCK <= TCK_800MHZ) { + val1 = 0x06; + ctrl->tCK = TCK_800MHZ; + } else if (ctrl->tCK <= TCK_666MHZ) { + val1 = 0x05; + ctrl->tCK = TCK_666MHZ; + } else { + val1 = 0x04; + ctrl->tCK = TCK_533MHZ; + } + + /* Step 2 - Select frequency in the MCU */ + reg1 = val1; + reg1 |= 0x80000000; // set running bit + MCHBAR32(0x5e00) = reg1; + while (reg1 & 0x80000000) { + printk(BIOS_DEBUG, " PLL busy..."); + reg1 = MCHBAR32(0x5e00); + } + printk(BIOS_DEBUG, "done\n"); + + /* Step 3 - Verify lock frequency */ + reg1 = MCHBAR32(0x5e04); + val2 = (u8) reg1; + if (val2 > val1) { + printk(BIOS_DEBUG, "Lock frequency is lower, recalculating\n"); + switch (val2) { + case 8: + ctrl->tCK = TCK_1066MHZ; + break; + case 7: + ctrl->tCK = TCK_933MHZ; + break; + case 6: + ctrl->tCK = TCK_800MHZ; + break; + case 5: + ctrl->tCK = TCK_666MHZ; + break; + case 4: + ctrl->tCK = TCK_533MHZ; + break; + default: + printk(BIOS_DEBUG, "ERROR: PLL is off or unknown\n"); + break; + } + dram_timing(ctrl); // recalculate timings + } + printk(BIOS_DEBUG, "MCU frequency is set at : %d MHz\n", + (1000 << 8) / ctrl->tCK); +} + +static void dram_xover(ramctr_timing * ctrl) +{ + u32 reg; + int channel; + + FOR_ALL_CHANNELS { + // enable xover clk + printk(BIOS_DEBUG, "[%x] = %x\n", channel * 0x100 + 0xc14, + (ctrl->rankmap[channel] << 24)); + MCHBAR32(channel * 0x100 + 0xc14) = (ctrl->rankmap[channel] << 24); + + // enable xover ctl + reg = 0; + if (ctrl->rankmap[channel] & 0x5) { + reg |= 0x20000; + } + if (ctrl->rankmap[channel] & 0xa) { + reg |= 0x4000000; + } + // enable xover cmd + reg |= 0x4000; + printk(BIOS_DEBUG, "[%x] = %x\n", 0x100 * channel + 0x320c, + reg); + MCHBAR32(0x100 * channel + 0x320c) = reg; + } +} + +static void dram_timing_regs(ramctr_timing * ctrl) +{ + u32 reg, addr, val32, cpu, stretch; + struct cpuid_result cpures; + int channel; + + FOR_ALL_CHANNELS { + // DBP + reg = 0; + reg |= ctrl->tRCD; + reg |= (ctrl->tRP << 4); + reg |= (ctrl->CAS << 8); + reg |= (ctrl->CWL << 12); + reg |= (ctrl->tRAS << 16); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4000, + reg); + MCHBAR32(0x400 * channel + 0x4000) = reg; + + // RAP + reg = 0; + reg |= ctrl->tRRD; + reg |= (ctrl->tRTP << 4); + reg |= (ctrl->tCKE << 8); + reg |= (ctrl->tWTR << 12); + reg |= (ctrl->tFAW << 16); + reg |= (ctrl->tWR << 24); + reg |= (3 << 30); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4004, + reg); + MCHBAR32(0x400 * channel + 0x4004) = reg; + + // OTHP + addr = 0x400 * channel + 0x400c; + reg = 0; + reg |= ctrl->tXPDLL; + reg |= (ctrl->tXP << 5); + reg |= (ctrl->tAONPD << 8); + reg |= 0xa0000; + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + + MCHBAR32(0x400 * channel + 0x4014) = 0; + + MCHBAR32(addr) |= 0x00020000; + + // ODT stretch + reg = 0; + + cpures = cpuid(0); + cpu = cpures.eax; + if (IS_IVY_CPU(cpu) + || (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_D2(cpu))) { + stretch = 2; + addr = 0x400 * channel + 0x400c; + printk(BIOS_DEBUG, "[%x] = %x\n", + 0x400 * channel + 0x400c, reg); + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0xc0000) | (stretch << 18); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0x30000) | (stretch << 16); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + + } else if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) { + stretch = 3; + addr = 0x400 * channel + 0x401c; + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0x3000) | (stretch << 12); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0xc00) | (stretch << 10); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + } else { + stretch = 0; + } + + // REFI + reg = 0; + val32 = ctrl->tREFI; + reg = (reg & ~0xffff) | val32; + val32 = ctrl->tRFC; + reg = (reg & ~0x1ff0000) | (val32 << 16); + val32 = (u32) (ctrl->tREFI * 9) / 1024; + reg = (reg & ~0xfe000000) | (val32 << 25); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4298, + reg); + MCHBAR32(0x400 * channel + 0x4298) = reg; + + MCHBAR32(0x400 * channel + 0x4294) |= 0xff; + + // SRFTP + reg = 0; + val32 = tDLLK; + reg = (reg & ~0xfff) | val32; + val32 = ctrl->tXSOffset; + reg = (reg & ~0xf000) | (val32 << 12); + val32 = tDLLK - ctrl->tXSOffset; + reg = (reg & ~0x3ff0000) | (val32 << 16); + val32 = ctrl->tMOD - 8; + reg = (reg & ~0xf0000000) | (val32 << 28); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x42a4, + reg); + MCHBAR32(0x400 * channel + 0x42a4) = reg; + } +} + +static void dram_dimm_mapping(dimm_info * info, ramctr_timing * ctrl) +{ + int t; + u32 reg, val32; + int channel; + + FOR_ALL_CHANNELS { + dimm_attr *dimmA = 0; + dimm_attr *dimmB = 0; + reg = 0; + val32 = 0; + if (info->dimm[channel][0].size_mb >= + info->dimm[channel][1].size_mb) { + // dimm 0 is bigger, set it to dimmA + dimmA = &info->dimm[channel][0]; + dimmB = &info->dimm[channel][1]; + reg |= (0 << 16); + } else { + // dimm 1 is bigger, set it to dimmA + dimmA = &info->dimm[channel][1]; + dimmB = &info->dimm[channel][0]; + reg |= (1 << 16); + // swap dimm info + t = ctrl->rank_mirror[channel][1]; + ctrl->rank_mirror[channel][1] = + ctrl->rank_mirror[channel][3]; + ctrl->rank_mirror[channel][3] = t; + } + // dimmA + if (dimmA && (dimmA->ranks > 0)) { + val32 = dimmA->size_mb / 256; + reg = (reg & ~0xff) | val32; + val32 = dimmA->ranks - 1; + reg = (reg & ~0x20000) | (val32 << 17); + val32 = (dimmA->width / 8) - 1; + reg = (reg & ~0x80000) | (val32 << 19); + } + // dimmB + if (dimmB && (dimmB->ranks > 0)) { + val32 = dimmB->size_mb / 256; + reg = (reg & ~0xff00) | (val32 << 8); + val32 = dimmB->ranks - 1; + reg = (reg & ~0x40000) | (val32 << 18); + val32 = (dimmB->width / 8) - 1; + reg = (reg & ~0x100000) | (val32 << 20); + } + reg = (reg & ~0x200000) | (1 << 21); // rank interleave + reg = (reg & ~0x400000) | (1 << 22); // enhanced interleave + + // Set MAD-DIMM register + if ((dimmA && (dimmA->ranks > 0)) + || (dimmB && (dimmB->ranks > 0))) { + MCHBAR32(0x5004 + channel * 4) = reg; + } + } +} + +static void dram_zones(dimm_info * info, ramctr_timing * ctrl, int training) +{ + u32 reg, ch0size, ch1size; + u8 val; + reg = 0; + val = 0; + if (training) { + ch0size = + info->dimm[0][0].size_mb + info->dimm[0][1].size_mb ? 256 : 0; + ch1size = + info->dimm[1][0].size_mb + info->dimm[1][1].size_mb ? 256 : 0; + } else { + ch0size = info->dimm[0][0].size_mb + info->dimm[0][1].size_mb; + ch1size = info->dimm[1][0].size_mb + info->dimm[1][1].size_mb; + } + + if (ch0size >= ch1size) { + reg = MCHBAR32(0x5014); + val = ch1size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x24; + } else { + reg = MCHBAR32(0x5014); + val = ch0size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x21; + } +} + +/* FIXME: this function bugs. */ +static void dram_memorymap(dimm_info * info, int me_uma_size) +{ + u32 reg, val, reclaim; + u32 tom, gfxstolen, gttsize; + size_t tsegsize, mmiosize, toludbase, touudbase, gfxstolenbase, gttbase, + tsegbase, mestolenbase; + size_t tsegbasedelta, remapbase, remaplimit; + uint16_t ggc; + + mmiosize = 0x400; + + ggc = pci_read_config16(NORTHBRIDGE, GGC); + if (!(ggc & 2)) { + gfxstolen = ((ggc >> 3) & 0x1f) * 32; + gttsize = ((ggc >> 8) & 0x3); + } else { + gfxstolen = 0; + gttsize = 0; + } + + tsegsize = CONFIG_SMM_TSEG_SIZE >> 20; + + tom = + info->dimm[0][0].size_mb + info->dimm[0][1].size_mb + + info->dimm[0][1].size_mb + info->dimm[1][1].size_mb; + + mestolenbase = tom - me_uma_size; + + toludbase = MIN(4096 - mmiosize, tom - me_uma_size); + gfxstolenbase = toludbase - gfxstolen; + gttbase = gfxstolenbase - gttsize; + + tsegbase = gttbase - tsegsize; + + // Round tsegbase down to nearest address aligned to tsegsize + tsegbasedelta = tsegbase & (tsegsize - 1); + tsegbase &= ~(tsegsize - 1); + + gttbase -= tsegbasedelta; + gfxstolenbase -= tsegbasedelta; + toludbase -= tsegbasedelta; + + // Test if it is possible to reclaim a hole in the ram addressing + if (tom - me_uma_size > toludbase) { + // Reclaim is possible + reclaim = 1; + remapbase = MAX(4096, tom - me_uma_size); + remaplimit = + remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1; + touudbase = remaplimit + 1; + } else { + // Reclaim not possible + reclaim = 0; + touudbase = tom - me_uma_size; + } + + // Update memory map in pci-e configuration space + + // TOM (top of memory) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa0); + val = tom & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa0, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa4); + val = tom & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa4, reg); + + // TOLUD (top of low used dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xbc); + val = toludbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xbc, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xbc, reg); + + // TOUUD LSB (top of upper usable dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa8); + val = touudbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa8, reg); + + // TOUUD MSB + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xac); + val = touudbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xac, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xac, reg); + + if (reclaim) { + // REMAP BASE + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x94); + val = remapbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x94, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x94, reg); + + // REMAP LIMIT + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x98); + val = remaplimit & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x98, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x98, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x9c); + val = remaplimit & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x9c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x9c, reg); + } + // TSEG + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb8); + val = tsegbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb8, reg); + + // GFX stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb0); + val = gfxstolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb0, reg); + + // GTT stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb4); + val = gttbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb4, reg); + + if (me_uma_size) { + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x7c); + val = (0x80000 - me_uma_size) & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x7c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x7c, reg); + + // ME base + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x70); + val = mestolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x70, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x70, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x74); + val = mestolenbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x74, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x74, reg); + + // ME mask + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x78); + val = (0x80000 - me_uma_size) & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + reg = (reg & ~0x400) | (1 << 10); // set lockbit on ME mem + + reg = (reg & ~0x800) | (1 << 11); // set ME memory enable + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x78, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x78, reg); + } +} + +static void dram_ioregs(ramctr_timing * ctrl) +{ + u32 reg, comp2; + + int channel; + + // IO clock + FOR_ALL_CHANNELS { + MCHBAR32(0xc00 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO command + FOR_ALL_CHANNELS { + MCHBAR32(0x3200 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO control + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + + // Rcomp + printk(BIOS_DEBUG, "RCOMP..."); + reg = 0; + while (reg == 0) { + reg = MCHBAR32(0x5084) & 0x10000; + } + printk(BIOS_DEBUG, "done\n"); + + // Set comp2 + comp2 = get_COMP2(ctrl->tCK); + MCHBAR32(0x3714) = comp2; + printk(BIOS_DEBUG, "COMP2 done\n"); + + // Set comp1 + FOR_ALL_POPULATED_CHANNELS { + reg = MCHBAR32(0x1810 + channel * 0x100); //ch0 + reg = (reg & ~0xe00) | (1 << 9); //odt + reg = (reg & ~0xe00000) | (1 << 21); //clk drive up + reg = (reg & ~0x38000000) | (1 << 27); //ctl drive up + MCHBAR32(0x1810 + channel * 0x100) = reg; + } + printk(BIOS_DEBUG, "COMP1 done\n"); + + printk(BIOS_DEBUG, "FORCE RCOMP and wait 20us..."); + MCHBAR32(0x5f08) |= 0x100; + udelay(20); + printk(BIOS_DEBUG, "done\n"); +} + +static void wait_428c(int channel) +{ + while (1) { + if (read32(DEFAULT_MCHBAR | 0x428c | (channel << 10)) & 0x50) + return; + } +} + +static void write_reset(ramctr_timing * ctrl) +{ + int channel, slotrank; + + /* choose a populated channel. */ + channel = (ctrl->rankmap[0]) ? 0 : 1; + + wait_428c(channel); + + /* choose a populated rank. */ + slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x80c01); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x400001); + wait_428c(channel); +} + +static void dram_jedecreset(ramctr_timing * ctrl) +{ + u32 reg, addr; + int channel; + + while (!(MCHBAR32(0x5084) & 0x10000)) ; + do { + reg = MCHBAR32(0x428c); + } while ((reg & 0x14) == 0); + + // Set state of memory controller + reg = 0x112; + MCHBAR32(0x5030) = reg; + MCHBAR32(0x4ea0) = 0; + reg |= 2; //ddr reset + MCHBAR32(0x5030) = reg; + + // Assert dimm reset signal + reg = MCHBAR32(0x5030); + reg &= ~0x2; + MCHBAR32(0x5030) = reg; + + // Wait 200us + udelay(200); + + // Deassert dimm reset signal + MCHBAR32(0x5030) |= 2; + + // Wait 500us + udelay(500); + + // Enable DCLK + MCHBAR32(0x5030) |= 4; + + // XXX Wait 20ns + udelay(1); + + FOR_ALL_CHANNELS { + // Set valid rank CKE + reg = 0; + reg = (reg & ~0xf) | ctrl->rankmap[channel]; + addr = 0x400 * channel + 0x42a0; + MCHBAR32(addr) = reg; + + // Wait 10ns for ranks to settle + //udelay(0.01); + + reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4); + MCHBAR32(addr) = reg; + + // Write reset using a NOP + write_reset(ctrl); + } +} + +static odtmap get_ODT(ramctr_timing * ctrl, u8 rank) +{ + /* Get ODT based on rankmap: */ + int dimms_per_ch = 0; + int channel; + + FOR_ALL_CHANNELS { + dimms_per_ch = max ((ctrl->rankmap[channel] & 1) + + ((ctrl->rankmap[channel] >> 2) & 1), + dimms_per_ch); + } + + if (dimms_per_ch == 1) { + return (const odtmap){60, 60}; + } else if (dimms_per_ch == 2) { + return (const odtmap){120, 30}; + } else { + printk(BIOS_DEBUG, + "Huh, no dimms? m0 = %d m1 = %d dpc = %d\n", + ctrl->rankmap[0], + ctrl->rankmap[1], dimms_per_ch); + die(""); + } +} + +static void write_mrreg(ramctr_timing * ctrl, int channel, int slotrank, + int reg, u32 val) +{ + wait_428c(channel); + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + if (ctrl->rank_mirror[channel][slotrank]) { + reg = ((reg >> 1) & 1) | ((reg << 1) & 2); + val = (val & ~0x1f8) | ((val >> 1) & 0xa8) + | ((val & 0xa8) << 1); + } + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); +} + +static u32 make_mr0(ramctr_timing * ctrl, u8 rank) +{ + u16 mr0reg, mch_cas, mch_wr; + static const u8 mch_wr_t[12] = { 1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0 }; + mr0reg = 0x100; + + // Convert CAS to MCH register friendly + if (ctrl->CAS < 12) { + mch_cas = (u16) ((ctrl->CAS - 4) << 1); + } else { + mch_cas = (u16) (ctrl->CAS - 12); + mch_cas = ((mch_cas << 1) | 0x1); + } + + // Convert tWR to MCH register friendly + mch_wr = mch_wr_t[ctrl->tWR - 5]; + + mr0reg = (mr0reg & ~0x4) | (mch_cas & 0x1); + mr0reg = (mr0reg & ~0x70) | ((mch_cas & 0xe) << 3); + mr0reg = (mr0reg & ~0xe00) | (mch_wr << 9); + // Fast (desktop) 0x1 or slow (mobile) 0x0 + mr0reg = (mr0reg & ~0x1000) | (!ctrl->mobile << 12); + return mr0reg; +} + +static void dram_mr0(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS write_mrreg(ctrl, channel, rank, 0, + make_mr0(ctrl, rank)); +} + +static u32 encode_odt(u32 odt) +{ + switch (odt) { + case 30: + return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4 + case 60: + return (1 << 2); // RZQ/4 + case 120: + return (1 << 6); // RZQ/2 + default: + case 0: + return 0; + } +} + +static u32 make_mr1(ramctr_timing * ctrl, u8 rank) +{ + odtmap odt; + u32 mr1reg; + + odt = get_ODT(ctrl, rank); + mr1reg = 0x2; + + mr1reg |= encode_odt(odt.rttnom); + + return mr1reg; +} + +static void dram_mr1(ramctr_timing * ctrl, u8 rank) +{ + u16 mr1reg; + int channel; + + mr1reg = make_mr1(ctrl, rank); + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 1, mr1reg); + } +} + +static void dram_mr2(ramctr_timing * ctrl, u8 rank) +{ + u16 pasr, cwl, mr2reg; + odtmap odt; + int channel; + int srt; + + pasr = 0; + cwl = ctrl->CWL - 5; + odt = get_ODT(ctrl, rank); + + srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh; + + mr2reg = 0; + mr2reg = (mr2reg & ~0x7) | pasr; + mr2reg = (mr2reg & ~0x38) | (cwl << 3); + mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6); + mr2reg = (mr2reg & ~0x80) | (srt << 7); + mr2reg |= (odt.rttwr / 60) << 9; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 2, mr2reg); + } +} + +static void dram_mr3(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 3, 0); + } +} + +static void dram_mrscommands(ramctr_timing * ctrl) +{ + u8 rank; + u32 reg, addr; + int channel; + + for (rank = 0; rank < 4; rank++) { + // MR2 + printk(BIOS_SPEW, "MR2 rank %d...", rank); + dram_mr2(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR3 + printk(BIOS_SPEW, "MR3 rank %d...", rank); + dram_mr3(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR1 + printk(BIOS_SPEW, "MR1 rank %d...", rank); + dram_mr1(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR0 + printk(BIOS_SPEW, "MR0 rank %d...", rank); + dram_mr0(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + } + + write32(DEFAULT_MCHBAR + 0x4e20, 0x7); + write32(DEFAULT_MCHBAR + 0x4e30, 0xf1001); + write32(DEFAULT_MCHBAR + 0x4e00, 0x60002); + write32(DEFAULT_MCHBAR + 0x4e10, 0); + write32(DEFAULT_MCHBAR + 0x4e24, 0x1f003); + write32(DEFAULT_MCHBAR + 0x4e34, 0x1901001); + write32(DEFAULT_MCHBAR + 0x4e04, 0x60400); + write32(DEFAULT_MCHBAR + 0x4e14, 0x288); + write32(DEFAULT_MCHBAR + 0x4e84, 0x40004); + + // Drain + FOR_ALL_CHANNELS { + // Wait for ref drained + wait_428c(channel); + } + + // Refresh enable + MCHBAR32(0x5030) |= 8; + + FOR_ALL_POPULATED_CHANNELS { + addr = 0x400 * channel + 0x4020; + reg = MCHBAR32(addr); + reg &= ~0x200000; + MCHBAR32(addr) = reg; + + wait_428c(channel); + + rank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + // Drain + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (rank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x1); + + // Drain + wait_428c(channel); + } +} + +const u32 lane_registers[] = { + 0x0000, 0x0200, 0x0400, 0x0600, + 0x1000, 0x1200, 0x1400, 0x1600, + 0x0800 +}; + +static int clamp(int val, int low, int up) +{ + if (val < low) + return low; + if (val > up) + return up; + return val; +} + +static void program_timings(ramctr_timing * ctrl, int channel) +{ + u32 reg32, reg_4024, reg_c14, reg_c18, reg_4028; + int lane; + int slotrank, slot; + int full_shift = 0; + u16 slot320c[NUM_SLOTS]; + + FOR_ALL_POPULATED_RANKS if (full_shift < + -ctrl->timings[channel][slotrank].val_320c) + full_shift = -ctrl->timings[channel][slotrank].val_320c; + + for (slot = 0; slot < NUM_SLOTS; slot++) + switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) { + case 0: + default: + slot320c[slot] = 0x7f; + break; + case 1: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 0].val_320c + + full_shift; + break; + case 2: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 1].val_320c + + full_shift; + break; + case 3: + slot320c[slot] = + (ctrl->timings[channel][2 * slot].val_320c + + ctrl->timings[channel][2 * slot + + 1].val_320c) / 2 + + full_shift; + break; + } + + reg32 = (1 << 17) | (1 << 14); + reg32 |= ((slot320c[0] & 0x3f) << 6) | ((slot320c[0] & 0x40) << 9); + reg32 |= (slot320c[1] & 0x7f) << 18; + reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6); + + MCHBAR32(0x320c + 0x100 * channel) = reg32; + + reg_c14 = ctrl->rankmap[channel] << 24; + reg_c18 = 0; + + FOR_ALL_POPULATED_RANKS { + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + int offset_val_c14; + if (shift < 0) + shift = 0; + offset_val_c14 = ctrl->reg_c14_offset + shift; + reg_c14 |= (offset_val_c14 & 0x3f) << (6 * slotrank); + reg_c18 |= ((offset_val_c14 >> 6) & 1) << slotrank; + } + + MCHBAR32(0xc14 + channel * 0x100) = reg_c14; + MCHBAR32(0xc18 + channel * 0x100) = reg_c18; + + reg_4028 = MCHBAR32(0x4028 + 0x400 * channel); + reg_4028 &= 0xffff0000; + + reg_4024 = 0; + + FOR_ALL_POPULATED_RANKS { + int post_timA_min_high = 7, post_timA_max_high = 0; + int pre_timA_min_high = 7, pre_timA_max_high = 0; + int shift_402x = 0; + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + + if (shift < 0) + shift = 0; + + FOR_ALL_LANES { + if (post_timA_min_high > + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_min_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_min_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + if (post_timA_max_high < + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_max_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_max_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + } + + if (pre_timA_max_high - pre_timA_min_high < + post_timA_max_high - post_timA_min_high) + shift_402x = +1; + else if (pre_timA_max_high - pre_timA_min_high > + post_timA_max_high - post_timA_min_high) + shift_402x = -1; + + reg_4028 |= + (ctrl->timings[channel][slotrank].val_4028 + shift_402x - + post_timA_min_high) << (4 * slotrank); + reg_4024 |= + (ctrl->timings[channel][slotrank].val_4024 + + shift_402x) << (8 * slotrank); + + FOR_ALL_LANES { + MCHBAR32(lane_registers[lane] + 0x10 + 0x100 * channel + + 4 * slotrank) + = + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) & 0x3f) + | + ((ctrl->timings[channel][slotrank].lanes[lane]. + rising + shift) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift - + (post_timA_min_high << 6)) & 0x1c0) << 10) + | (ctrl->timings[channel][slotrank].lanes[lane]. + falling << 20)); + + MCHBAR32(lane_registers[lane] + 0x20 + 0x100 * channel + + 4 * slotrank) + = + ((clamp + (ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift, 0, 127) & 0x3f) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x3f) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x1c0) << 9) + | + ((clamp + (ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift, 0, 127) & 0x40) << 13)); + } + } + MCHBAR32(0x4024 + 0x400 * channel) = reg_4024; + MCHBAR32(0x4028 + 0x400 * channel) = reg_4028; +} + +static void test_timA(ramctr_timing * ctrl, int channel, int slotrank) +{ + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x4040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x100f | ((ctrl->CAS + 36) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); +} + +static int does_lane_work(ramctr_timing * ctrl, int channel, int slotrank, + int lane) +{ + u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA; + return ((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + channel * 0x100 + 4 + + ((timA / 32) & 1) * 4) + >> (timA % 32)) & 1); +} + +struct run { + int middle; + int end; + int start; + int all; +}; + +static struct run get_longest_zero_run(int *seq, int sz) +{ + int i, ls; + int bl = 0, bs = 0; + struct run ret; + + ls = 0; + for (i = 0; i < 2 * sz; i++) + if (seq[i % sz]) { + if (i - ls > bl) { + bl = i - ls; + bs = ls; + } + ls = i + 1; + } + if (bl == 0) { + ret.middle = sz / 2; + ret.start = 0; + ret.end = sz; + ret.all = 1; + return ret; + } + + ret.start = bs % sz; + ret.end = (bs + bl - 1) % sz; + ret.middle = (bs + (bl - 1) / 2) % sz; + ret.all = 0; + + return ret; +} + +static void discover_timA_coarse(ramctr_timing * ctrl, int channel, + int slotrank, int *upperA) +{ + int timA; + int statistics[NUM_LANES][128]; + int lane; + + for (timA = 0; timA < 128; timA++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = timA; + program_timings(ctrl, channel); + + test_timA(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timA] = + !does_lane_work(ctrl, channel, slotrank, lane); + printk(BIOS_SPEW, "Astat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timA, + statistics[lane][timA]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle; + upperA[lane] = rn.end; + if (upperA[lane] < rn.middle) + upperA[lane] += 128; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + printk(BIOS_SPEW, "Aend: %d, %d, %d, %x\n", channel, slotrank, + lane, upperA[lane]); + } +} + +static void discover_timA_fine(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int timA_delta; + int statistics[NUM_LANES][51]; + int lane, i; + + memset(statistics, 0, sizeof(statistics)); + + for (timA_delta = -25; timA_delta <= 25; timA_delta++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = upperA[lane] + timA_delta + 0x40; + program_timings(ctrl, channel); + + for (i = 0; i < 100; i++) { + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + statistics[lane][timA_delta + 25] += + does_lane_work(ctrl, channel, slotrank, + lane); + } + } + FOR_ALL_LANES { + printk(BIOS_SPEW, "A+stat: %d, %d, %d, %d (%x), %x\n", + channel, slotrank, lane, timA_delta, + upperA[lane] + timA_delta + 0x40, + statistics[lane][timA_delta + 25]); + } + } + FOR_ALL_LANES { + int last_zero, first_all; + + for (last_zero = -25; last_zero <= 25; last_zero++) + if (statistics[lane][last_zero + 25]) + break; + last_zero--; + for (first_all = -25; first_all <= 25; first_all++) + if (statistics[lane][first_all + 25] == 100) + break; + + printk(BIOS_SPEW, "lane %d: %d, %d\n", lane, last_zero, + first_all); + + ctrl->timings[channel][slotrank].lanes[lane].timA = + (last_zero + first_all) / 2 + upperA[lane]; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + } +} + +static void discover_402x(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int works[NUM_LANES]; + int lane; + while (1) { + int all_works = 1, some_works = 0; + program_timings(ctrl, channel); + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + works[lane] = + !does_lane_work(ctrl, channel, slotrank, lane); + if (works[lane]) + some_works = 1; + else + all_works = 0; + } + if (all_works) + return; + if (!some_works) { + if (ctrl->timings[channel][slotrank].val_4024 < 2) + die("402x discovery failed"); + ctrl->timings[channel][slotrank].val_4024 -= 2; + printk(BIOS_SPEW, "4024 -= 2;\n"); + continue; + } + ctrl->timings[channel][slotrank].val_4028 += 2; + printk(BIOS_SPEW, "4028 += 2;\n"); + if (ctrl->timings[channel][slotrank].val_4028 >= 0x10) + die("402x discovery failed"); + FOR_ALL_LANES if (works[lane]) { + ctrl->timings[channel][slotrank].lanes[lane].timA += + 128; + upperA[lane] += 128; + printk(BIOS_SPEW, "increment %d, %d, %d\n", channel, + slotrank, lane); + } + } +} + +struct timA_minmax { + int timA_min_high, timA_max_high; +}; + +static void pre_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + int lane; + mnmx->timA_min_high = 7; + mnmx->timA_max_high = 0; + + FOR_ALL_LANES { + if (mnmx->timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_min_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + if (mnmx->timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_max_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + } +} + +static void post_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + struct timA_minmax post; + int shift_402x = 0; + + /* Get changed maxima. */ + pre_timA_change(ctrl, channel, slotrank, &post); + + if (mnmx->timA_max_high - mnmx->timA_min_high < + post.timA_max_high - post.timA_min_high) + shift_402x = +1; + else if (mnmx->timA_max_high - mnmx->timA_min_high > + post.timA_max_high - post.timA_min_high) + shift_402x = -1; + else + shift_402x = 0; + + ctrl->timings[channel][slotrank].val_4028 += shift_402x; + ctrl->timings[channel][slotrank].val_4024 += shift_402x; + printk(BIOS_SPEW, "4024 += %d;\n", shift_402x); + printk(BIOS_SPEW, "4028 += %d;\n", shift_402x); +} + +static void read_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + u32 r32; + int all_high, some_high; + int upperA[NUM_LANES]; + struct timA_minmax mnmx; + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + write32(DEFAULT_MCHBAR + 0x3400, (slotrank << 2) | 0x8001); + + ctrl->timings[channel][slotrank].val_4028 = 4; + ctrl->timings[channel][slotrank].val_4024 = 55; + program_timings(ctrl, channel); + + discover_timA_coarse(ctrl, channel, slotrank, upperA); + + all_high = 1; + some_high = 0; + FOR_ALL_LANES if (ctrl->timings[channel][slotrank].lanes[lane]. + timA >= 0x40) + some_high = 1; + else + all_high = 0; + if (all_high) { + ctrl->timings[channel][slotrank].val_4028--; + printk(BIOS_SPEW, "4028--;\n"); + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + timA -= 0x40; + upperA[lane] -= 0x40; + + } + } else if (some_high) { + ctrl->timings[channel][slotrank].val_4024++; + ctrl->timings[channel][slotrank].val_4028++; + printk(BIOS_SPEW, "4024++;\n"); + printk(BIOS_SPEW, "4028++;\n"); + } + + program_timings(ctrl, channel); + + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_402x(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_timA_fine(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA -= mnmx.timA_min_high * 0x40; + ctrl->timings[channel][slotrank].val_4028 -= mnmx.timA_min_high; + printk(BIOS_SPEW, "4028 -= %d;\n", mnmx.timA_min_high); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + + printk(BIOS_SPEW, "4/8: %d, %d, %x, %x\n", channel, slotrank, + ctrl->timings[channel][slotrank].val_4024, + ctrl->timings[channel][slotrank].val_4028); + + FOR_ALL_LANES + printk(BIOS_SPEW, "%d, %d, %d, %x\n", channel, slotrank, + lane, + ctrl->timings[channel][slotrank].lanes[lane].timA); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + } + + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + 4 * lane, 0); +} + +static void test_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int lane; + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x4140 + 0x400 * channel + 4 * lane); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) + | 4 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (6 << 16)); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8041001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x80411f4); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) + | 8 | (ctrl->CAS << 16)); + + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x40011f4 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timC; + int statistics[NUM_LANES][MAX_TIMC + 1]; + int lane; + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + for (timC = 0; timC <= MAX_TIMC; timC++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timC = timC; + program_timings(ctrl, channel); + + test_timC(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timC] = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + printk(BIOS_SPEW, "Cstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timC, + statistics[lane][timC]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_TIMC + 1); + ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle; + if (rn.all) + die("timC discovery failed"); + printk(BIOS_SPEW, "Cval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timC); + } +} + +static int get_precedening_channels(ramctr_timing * ctrl, int target_channel) +{ + int channel, ret = 0; + FOR_ALL_POPULATED_CHANNELS if (channel < target_channel) + ret++; + return ret; +} + +static void fill_pattern0(ramctr_timing * ctrl, int channel, u32 a, u32 b) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + printk(BIOS_SPEW, "channel_offset=%x\n", channel_offset); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + 4 * j, j & 2 ? b : a); + sfence(); +} + +static int num_of_channels(const ramctr_timing * ctrl) +{ + int ret = 0; + int channel; + FOR_ALL_POPULATED_CHANNELS ret++; + return ret; +} + +static void fill_pattern1(ramctr_timing * ctrl, int channel) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + j * 4, 0xffffffff); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + channel_step + j * 4, 0); + sfence(); +} + +static void precharge(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + } +} + +static void test_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + write_mrreg(ctrl, channel, slotrank, 1, + 0x80 | make_mr1(ctrl, slotrank)); + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->delay2) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + 8 | (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f107); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4000c01 | ((ctrl->CAS + 38) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 4); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x400 * channel + 0x4284, 0x40001); + wait_428c(channel); + + write_mrreg(ctrl, channel, slotrank, 1, + 0x1080 | make_mr1(ctrl, slotrank)); +} + +static void discover_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timB; + int statistics[NUM_LANES][128]; + int lane; + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052 | (slotrank << 2)); + + for (timB = 0; timB < 128; timB++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timB = timB; + program_timings(ctrl, channel); + + test_timB(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timB] = + !((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + + channel * 0x100 + 4 + ((timB / 32) & 1) * 4) + >> (timB % 32)) & 1); + printk(BIOS_SPEW, "Bstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timB, + statistics[lane][timB]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timB = rn.end; + if (rn.all) + die("timB discovery failed"); + printk(BIOS_SPEW, "Bval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timB); + } +} + +static int get_timB_high_adjust(u64 val) +{ + int i; + if (val >= 0xfffffffffff00000LL) + return -1; + if (val >= 0xfffffff000000000LL) + return -2; + if (val >= 0xfff0000000000000LL) + return -3; + + for (i = 0; i < 8; i++) + if (val >> (8 * (7 - i) + 4)) + return i; + return 8; +} + +static void adjust_high_timB(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + write32(DEFAULT_MCHBAR + 0x3400, 0x200); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern1(ctrl, channel); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 1); + } + FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS { + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x10001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRCD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x8041003); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x3e2); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | ((ctrl->tRP) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0xc01 | ((ctrl->tRCD) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x3f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4000c01 | + ((ctrl->tRP + + ctrl->timings[channel][slotrank].val_4024 + + ctrl->timings[channel][slotrank].val_4028) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60008); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); + wait_428c(channel); + FOR_ALL_LANES { + u64 res = + read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 4); + res |= + ((u64) read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 8)) << 32; + ctrl->timings[channel][slotrank].lanes[lane].timB += + get_timB_high_adjust(res) * 64; + printk(BIOS_SPEW, "Bval+: %d, %d, %d, %x\n", channel, + slotrank, lane, + ctrl->timings[channel][slotrank].lanes[lane]. + timB); + } + } + write32(DEFAULT_MCHBAR + 0x3400, 0); +} + +static void write_op(ramctr_timing * ctrl, int channel) +{ + int slotrank; + + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); +} + +static void write_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS + write32(DEFAULT_MCHBAR + 0x4008 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4008 + + 0x400 * channel) | 0x8000000); + + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + } + + FOR_ALL_CHANNELS + FOR_ALL_POPULATED_RANKS write_mrreg(ctrl, channel, slotrank, 1, + make_mr1(ctrl, + slotrank) | 0x1080); + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS discover_timB(ctrl, channel, + slotrank); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS write_mrreg(ctrl, channel, + slotrank, 1, + make_mr1(ctrl, + slotrank)); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + FOR_ALL_POPULATED_CHANNELS wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) | 8); + + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + ~0x00200000 & read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel)); + read32(DEFAULT_MCHBAR + 0x428c + 0x400 * channel); + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + printk(BIOS_SPEW, "CPE\n"); + precharge(ctrl); + printk(BIOS_SPEW, "CPF\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + discover_timC(ctrl, channel, slotrank); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + adjust_high_timB(ctrl); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static int test_320c(ramctr_timing * ctrl, int channel, int slotrank) +{ + struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank]; + int timC_delta; + int lanes_ok = 0; + int ctr = 0; + int lane; + + for (timC_delta = -5; timC_delta <= 5; timC_delta++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].timC = + saved_rt.lanes[lane].timC + timC_delta; + } + program_timings(ctrl, channel); + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 4 * lane + 0x4f40, 0); + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + ((max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10) + | 8 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | ctr | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4244 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4001020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4248 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, 0xf1001); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + u32 r32 = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + + if (r32 == 0) + lanes_ok |= 1 << lane; + } + ctr++; + if (lanes_ok == ((1 << NUM_LANES) - 1)) + break; + } + + ctrl->timings[channel][slotrank] = saved_rt; + return lanes_ok != ((1 << NUM_LANES) - 1); +} + +const u32 pattern[][16] = { + {0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff}, + {0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000}, + {0xe62d6424, 0x9277e09e, 0x8f43dc3f, 0x76eae589, + 0x0010fdc6, 0xdc55e01c, 0x5effb0ab, 0x6cba5d29, + 0xa43d1e64, 0xab5c2e0f, 0x7796ed16, 0x96023bf4, + 0xa74c831d, 0x90f138c0, 0x17830a8a, 0x5ac17c47}, + {0x359ebbeb, 0x2b9b4512, 0xef584d98, 0x106bf7cb, + 0x363525ad, 0xb3a4dfdc, 0xa6b9fcd8, 0xd21689ec, + 0x84a3695b, 0xbd9c2e27, 0xdb3d0f44, 0x988158f1, + 0xcca91d3f, 0xb62a6d12, 0xe905e4cf, 0x7f1fa626}, + {0xe58efeae, 0xcd006081, 0xa9119403, 0xbcfbd35f, + 0x213b3bf7, 0x7bfcb773, 0xc85143f9, 0x0bdbff50, + 0xa3053c90, 0x51d66cb7, 0x296f4387, 0xb715f99e, + 0xfaddc989, 0xbb1de8a7, 0x39206b4d, 0x80174a57}, + {0xa1622ac1, 0xb4f4a5f0, 0x16dc2bc3, 0x50fb0954, + 0x2e261721, 0x52b82c3c, 0x821902b8, 0x0d4b6c38, + 0x1f618631, 0x047956f3, 0xd4337f5a, 0x591f8002, + 0x27f28db2, 0xfae37369, 0xb3f27580, 0x3cdb6397}, + {0x3dee23be, 0x19f36408, 0x227f4a6a, 0x024603c5, + 0xd5e062db, 0x6d8d4c5c, 0x7ff693b0, 0x76641be9, + 0x9e74f41c, 0xe7bc7f33, 0x2636f2e9, 0x70279750, + 0xce2355aa, 0x32d230ef, 0x22f9b468, 0xadd4e7a2}, + {0x936c0fed, 0xba0612d5, 0xa97c1ea7, 0x10e29d67, + 0x1c4c5dc8, 0x83645621, 0xcd8b521c, 0xb8301817, + 0xac7d6571, 0xcc41d200, 0x4ebdefdd, 0xd2917bde, + 0x60f75acc, 0x7791534b, 0x26ea2a83, 0x6b74513a}, + {0xd1957b85, 0xc6f8f9ca, 0xf04fb4be, 0xfeb786fb, + 0xa1dea3aa, 0x67fe7db6, 0x25d49c87, 0xe3d54870, + 0x93dc1f86, 0x7d0c1a18, 0x9272e128, 0x68e1b876, + 0xce284c9e, 0x8fa18792, 0x5785a340, 0xb6fcf198}, + {0xff7d8e4a, 0x0c21ee43, 0xe820b388, 0xb4443c0e, + 0xa1e6e498, 0x5c426110, 0x1b434ef3, 0xbef05b91, + 0xa6907968, 0x53662ac3, 0x6defac32, 0x2c11c29c, + 0x6175cced, 0xb17dd3ad, 0x6e6a1076, 0x1372b1fa}, + {0x4408ed06, 0x49460ffd, 0xb49d26cb, 0x6a3662a5, + 0x5e857047, 0xa387cd4a, 0x04edc81e, 0xfd94d8d4, + 0x2fe48d91, 0x9d2356bc, 0x96131878, 0xaca3fce4, + 0xbb312c6c, 0x5023b090, 0x3614be70, 0xa14dfabb}, + {0xd4cc1e83, 0x757a1930, 0xc3d16a61, 0x9e0d6681, + 0x8a081fa9, 0xbd11c888, 0x1672f010, 0xa083f71c, + 0x1ec02eef, 0xc4586ca8, 0x6d322b35, 0x56054679, + 0x1552a0ff, 0x5cb7707e, 0xdfb55d4a, 0xcc76cc07}, + {0x507cf71f, 0x2166421a, 0x54be4af0, 0xfd42158c, + 0x417b1f7f, 0x9466860b, 0x3a0075bf, 0x2055575c, + 0xcedfe7ab, 0xbe85aa5f, 0x39d0c2e3, 0x851c19df, + 0x39a35a3f, 0x3fb10d7d, 0x20b14899, 0x703b7f08}, + {0x8a7d9dd1, 0x33235565, 0xbd3d2e57, 0xa48c2726, + 0x0d5e2e13, 0xae421ff9, 0x8784a224, 0xf66c1510, + 0x057627aa, 0x8fb0cb41, 0x4289975a, 0xb181adfa, + 0x59f2059a, 0xe86feb05, 0x84222fc1, 0x319b3ce9}, + {0xe1e243b8, 0x3b0bcc1a, 0x70396f00, 0x5caff44d, + 0xe96961b3, 0xad73f692, 0x8b841a2d, 0xf5838839, + 0xec9c9d04, 0xcc2b5562, 0xf8ca2549, 0xa9c52ff8, + 0x3b2fde68, 0x3d4dc7f0, 0xa57387d0, 0x051199ad}, + {0x5f0ce4fc, 0xd830fbb7, 0x90abeb8f, 0x96d9cdbb, + 0x58f80a80, 0x0baaca36, 0x81a23623, 0x77127614, + 0xaa8382cd, 0x0922fbca, 0xd84d37e1, 0x721297df, + 0x160f3b3a, 0x10a1ecdc, 0x151c92f4, 0xc1fdcdab}, + {0x261c45cc, 0xfeddd2da, 0xfc3cb1c1, 0x6639641f, + 0x2c011892, 0x7108bee2, 0x8545e0b9, 0x7dd36dab, + 0x07d91950, 0x1520adcb, 0xf84aa939, 0x07d9bb2d, + 0xdf1ed826, 0xaee3c814, 0x1dca1e81, 0xc8e9f486}, + {0x933d306a, 0xaab7103d, 0xa8be37be, 0x49612f3a, + 0xb0cf28e5, 0xf9648902, 0x106d7c11, 0xf32e1813, + 0x21af36ef, 0xe695e4c4, 0x7ee1831d, 0x2aeda467, + 0x99d0c655, 0x3f0691ab, 0xcd68f7c1, 0xb469a20e}, + {0x8557aef0, 0x3eb0e373, 0x0853ac31, 0xe5bded62, + 0x3eddb0dd, 0x6bbf1caf, 0x2119c3d9, 0xe1732350, + 0x55456c75, 0xf6119375, 0x498dd1ad, 0x13f80916, + 0xb97f9f5e, 0x921d9f4c, 0xabdee367, 0x1d6bb8bf}, + {0xd165a3be, 0xd8b41598, 0xa20e1809, 0xefd5c8ce, + 0x18935c80, 0xdf1911f9, 0xc9e449eb, 0xb887a4d7, + 0x4a324f6f, 0x533e8031, 0x1c21c074, 0xa95f1ea5, + 0x765b320a, 0x839d7dfb, 0xc7d3aa93, 0xe534ae3d}, + {0xbe8592c8, 0x068457e6, 0x89b94fa3, 0xd522ad02, + 0x7e7db0b7, 0x2c5b896f, 0x9f8ecb37, 0x05b983ff, + 0x3fe9b25f, 0x34a6215b, 0x0592ba34, 0xd564f85a, + 0x156c426d, 0x25ad5460, 0xe7b5e8b7, 0xa73285c6}, + {0x5ad8d838, 0x27b42d36, 0xcc806ad1, 0x157a058a, + 0x7297735a, 0xffd6df8d, 0xff96f7a2, 0x155b27ea, + 0x84708101, 0x979fd78b, 0x49797d0c, 0x0dc93e3c, + 0x20287332, 0xed759f88, 0xe5068529, 0xb83aa781}, + {0xc38b302c, 0x57b54075, 0xac810692, 0xb0d493e7, + 0x4adda486, 0x0665ce2e, 0xb2a9c003, 0xafacc4ce, + 0x4d5e906d, 0xb3d52fab, 0xe6962c6b, 0x850f4dd1, + 0x5021656c, 0x5df6c06b, 0x9255125b, 0x2363c478}, + {0x188b715c, 0xe8b884b0, 0x5e6d0b9a, 0x1f0051e1, + 0xd2d35d4c, 0xbfeaecbe, 0xc84bb0ad, 0x67a232d6, + 0x99001587, 0xbf4313e1, 0x74f64061, 0x2c1fc562, + 0xb6fe8ca6, 0x5226a239, 0xf5198574, 0x61b51dca}, + {0x51dcecd3, 0xbadbe596, 0xebe3e84a, 0x772bfdfc, + 0x03656ac5, 0xa7c36e91, 0x6cd32cf0, 0xc3f699dd, + 0x7d5aba01, 0x51e38e82, 0x23103a98, 0x20298b9d, + 0x19436510, 0x63ad7e6c, 0x8bc2b33f, 0x27079917}, + {0x8bd5be78, 0xf2403bfa, 0x780ebdb6, 0x94c53b64, + 0x6241c2e2, 0x5bfb081e, 0x6799e88f, 0xc997b7d1, + 0x466ac8b1, 0xbf5909da, 0x497ea39f, 0x402ffb48, + 0xd7470c2d, 0x8510aba9, 0x6c52a1c9, 0x812ca967}, + {0x031f7ab4, 0xd32fe890, 0x36ae6de5, 0x083dcde4, + 0x99a7f12f, 0xe44864a7, 0x02b75fff, 0xf25dda35, + 0x7679ff4f, 0xed421e01, 0xd9c2cfa1, 0xd36b4e82, + 0x5315d908, 0xc7ebcb2a, 0xb6f3e4c1, 0xf5bfbae9}, + {0x3f4a2a96, 0x64d8bd5a, 0x19acd70d, 0xf62fcdd9, + 0x5de99cdf, 0x32f3b7cb, 0x2c020578, 0x4e9bafb8, + 0x74919a08, 0xaba33e91, 0xa6bd2254, 0x2435a9b9, + 0x47e2a1b4, 0xe837a28e, 0xe113f1b0, 0x7654bd79}, + {0x05537a6c, 0x77be1a5c, 0x4c7492c9, 0x9086bfb0, + 0x257adc18, 0xf4787fc1, 0xe3fb6d53, 0x9525e589, + 0x445a65bc, 0x833f7d08, 0x69cf1f7e, 0x9a6372e1, + 0xceedb52e, 0x31032997, 0xd1c36828, 0x132772d6}, + {0x0a166972, 0x89beaf3b, 0x8d780fbc, 0x8aea5392, + 0x58347a41, 0x1e381ec2, 0xcc6280c8, 0xee0863e1, + 0x976e2dd2, 0x8c6ee6e2, 0xa0ca57cd, 0x95114a7d, + 0x3c096704, 0xa941769d, 0x2de20c05, 0x0bf8f812}, + {0x22779d6c, 0x94e12e8f, 0x5ce40299, 0xea1b55b0, + 0x9ebec05d, 0xe076cd2b, 0x8fef5648, 0x6a284c65, + 0xa790b705, 0xf0b19997, 0x0d8ca8af, 0x17440419, + 0xef4f702f, 0x33cbcbb1, 0x83d60f26, 0x48988397}, + {0x0fed7f53, 0xb5acbb67, 0xc031c73f, 0x5364d9ef, + 0xa6dbd12d, 0x82174a6c, 0xccf8e7ab, 0xc473c036, + 0xcff493d8, 0xad9afc3b, 0x316a24e8, 0x1842bea4, + 0x4cc0c82e, 0x28ccd91e, 0xd7311b5d, 0x50a89860}, +}; + +static void fill_pattern5(ramctr_timing * ctrl, int channel) +{ + unsigned i, j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (i = 0; i < sizeof(pattern) / sizeof(pattern[0]); i++) { + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + i * channel_step + + j * 4, pattern[i][j]); + } + sfence(); +} + +static void reprogram_320c(ramctr_timing * ctrl) +{ + int channel, slotrank; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + /* jedec reset */ + dram_jedecreset(ctrl); + /* mrs commands. */ + dram_mrscommands(ctrl); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); +} + +static void command_training(ramctr_timing * ctrl) +{ + int channel; + int slotrank; + u32 reg_4004_b30; + int delta = 0; + int c320c; + int stat[NUM_SLOTRANKS][256]; + + /* FIXME: vendor BIOS discovers this by trying 0 and 2. Apparently 2 should work for + all systems but 0 is slightly more efficient for the systems that can tolerate it. + */ + reg_4004_b30 = 2; + + FOR_ALL_POPULATED_CHANNELS + MCHBAR32(0x4004 + 0x400 * channel) = + ctrl->tRRD + | (ctrl->tRTP << 4) + | (ctrl->tCKE << 8) + | (ctrl->tWTR << 12) + | (ctrl->tFAW << 16) + | (ctrl->tWR << 24) + | (reg_4004_b30 << 30); + + if (reg_4004_b30 == 2) + delta = 2; + else if (reg_4004_b30 == 0) + delta = 4; + + FOR_ALL_CHANNELS { + FOR_ALL_POPULATED_RANKS ctrl->timings[channel][slotrank]. + val_4024 -= delta; + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + FOR_ALL_POPULATED_CHANNELS { + for (c320c = -127; c320c <= 127; c320c++) { + FOR_ALL_POPULATED_RANKS ctrl-> + timings[channel][slotrank].val_320c = c320c; + program_timings(ctrl, channel); + reprogram_320c(ctrl); + FOR_ALL_POPULATED_RANKS { + stat[slotrank][c320c + 127] = + test_320c(ctrl, channel, slotrank); + printk(BIOS_SPEW, "3stat: %d, %d, %d: %d\n", + channel, slotrank, c320c, + stat[slotrank][c320c + 127]); + } + } + FOR_ALL_POPULATED_RANKS { + struct run rn = + get_longest_zero_run(stat[slotrank], 255); + ctrl->timings[channel][slotrank].val_320c = + rn.middle - 127; + printk(BIOS_SPEW, "3val: %d, %d: %d\n", channel, + slotrank, + ctrl->timings[channel][slotrank].val_320c); + if (rn.all) + die("c320c discovery failed"); + } + } + + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + + reprogram_320c(ctrl); +} + +static void discover_edges_real(ramctr_timing * ctrl, int channel, int slotrank, + int *edges) +{ + int edge; + int statistics[NUM_LANES][MAX_EDGE_TIMING + 1]; + int lane; + + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].rising = + edge; + ctrl->timings[channel][slotrank].lanes[lane].falling = + edge; + } + printk(BIOS_SPEW, "edge %02x\n", edge); + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + 4 * lane + + 0x4140); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x40411f4); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + FOR_ALL_LANES { + statistics[lane][edge] = + read32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + lane * 4); + printk(BIOS_SPEW, "estat %d, %d, %d, %d %02x\n", + channel, slotrank, lane, edge, + statistics[lane][edge]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_EDGE_TIMING + 1); + edges[lane] = rn.middle; + if (rn.all) + die("edge discovery failed"); + printk(BIOS_SPEW, "eval %d, %d, %d, %02x\n", channel, slotrank, + lane, edges[lane]); + } +} + +static void discover_edges(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + u32 r32; + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 4 * lane + + 0x400 * channel + 0x4080, 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0, 0); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + FOR_ALL_LANES read32(DEFAULT_MCHBAR + 0x400 * channel + + lane * 4 + 0x4140); + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } + + program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + lane * 4, + ~read32(DEFAULT_MCHBAR + 0x4040 + + 0x400 * channel + lane * 4) & 0xff); + } + + fill_pattern0(ctrl, channel, 0, 0xffffffff); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void discover_edges_write_real(ramctr_timing * ctrl, int channel, + int slotrank, int *edges) +{ + int edge; + u32 raw_statistics[MAX_EDGE_TIMING + 1]; + int statistics[MAX_EDGE_TIMING + 1]; + const int reg3000b24[] = { 0, 0xc, 0x2c }; + int lane, i; + int lower[NUM_LANES]; + int upper[NUM_LANES]; + + FOR_ALL_LANES { + lower[lane] = 0; + upper[lane] = MAX_EDGE_TIMING; + } + + for (i = 0; i < 3; i++) { + /* FIXME: trace shows that vendor BIOS also tests with other patterns. + I'm not sure whether it's really needed. + */ + write32(DEFAULT_MCHBAR + 0x3000 + 0x100 * channel, + reg3000b24[i] << 24); + printk(BIOS_SPEW, "patterned\n"); + printk(BIOS_SPEW, "[%x] = 0x%08x\n(%d, %d)\n", + 0x3000 + 0x100 * channel, reg3000b24[i] << 24, channel, + slotrank); + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + rising = edge; + ctrl->timings[channel][slotrank].lanes[lane]. + falling = edge; + } + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + + 4 * lane + 0x4140); + } + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x4 | (ctrl->tRCD << 16) + | (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << + 10)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, + 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8005020 | ((ctrl->tWTR + ctrl->CWL + 8) << + 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4005020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + lane * 4); + } + + raw_statistics[edge] = + MCHBAR32(0x436c + 0x400 * channel); + } + FOR_ALL_LANES { + struct run rn; + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) + statistics[edge] = + ! !(raw_statistics[edge] & (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_EDGE_TIMING + 1); + printk(BIOS_SPEW, + "edges: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, rn.middle, + rn.end, rn.start + ctrl->edge_offset[i], + rn.end - ctrl->edge_offset[i]); + lower[lane] = + max(rn.start + ctrl->edge_offset[i], lower[lane]); + upper[lane] = + min(rn.end - ctrl->edge_offset[i], upper[lane]); + edges[lane] = (lower[lane] + upper[lane]) / 2; + + } + } + + write32(DEFAULT_MCHBAR + 0x3000, 0); + printk(BIOS_SPEW, "CPA\n"); +} + +static void discover_edges_write(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank) +{ + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) + << 10) | (ctrl->tRCD << 16) | 4); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x80011e0 | + ((ctrl->tWTR + ctrl->CWL + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + + 0x400 * channel, + 0x40011e0 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + + 0x400 * channel, + 0x1001 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC_write(ramctr_timing * ctrl) +{ + const u8 rege3c_b24[3] = { 0, 0xf, 0x2f }; + int i; + + int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + lower[channel][slotrank][lane] = 0; + upper[channel][slotrank][lane] = MAX_TIMC; + } + + write32(DEFAULT_MCHBAR + 0x4ea8, 1); + + for (i = 0; i < 3; i++) + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100), + (rege3c_b24[i] << 24) + | (read32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100)) + & ~0x3f000000)); + udelay(2); + FOR_ALL_POPULATED_RANKS { + int timC; + u32 raw_statistics[MAX_TIMC + 1]; + int statistics[MAX_TIMC + 1]; + + /* FIXME: trace shows that vendor BIOS also tests with other patterns. + I'm not sure whether it's really needed. + */ + fill_pattern5(ctrl, channel); + for (timC = 0; timC < MAX_TIMC + 1; timC++) { + FOR_ALL_LANES + ctrl->timings[channel][slotrank].lanes[lane].timC = timC; + program_timings(ctrl, channel); + + test_timC_write (ctrl, channel, slotrank); + + raw_statistics[timC] = + MCHBAR32(0x436c + 0x400 * channel); + printk(BIOS_SPEW, "Cstat %02x %02x\n", timC, + raw_statistics[timC]); + } + FOR_ALL_LANES { + struct run rn; + for (timC = 0; timC <= MAX_TIMC; timC++) + statistics[timC] = + !!(raw_statistics[timC] & + (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_TIMC + 1); + if (rn.all) + die("timC write discovery failed"); + printk(BIOS_SPEW, + "timC: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, + rn.middle, rn.end, + rn.start + ctrl->timC_offset[i], + rn.end - ctrl->timC_offset[i]); + lower[channel][slotrank][lane] = + max(rn.start + ctrl->timC_offset[i], + lower[channel][slotrank][lane]); + upper[channel][slotrank][lane] = + min(rn.end - ctrl->timC_offset[i], + upper[channel][slotrank][lane]); + + } + } + } + + write32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c, + 0 | (read32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c) & + ~0x3f000000)); + udelay(2); + + write32(DEFAULT_MCHBAR + 0x4ea8, 0); + + printk(BIOS_SPEW, "CPB\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + printk(BIOS_SPEW, "timC [%d, %d, %d] = 0x%x\n", channel, + slotrank, lane, + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2); + ctrl->timings[channel][slotrank].lanes[lane].timC = + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2; + } + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); +} + +static void normalize_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + int mat = 0; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + int delta; + FOR_ALL_LANES mat = + max(ctrl->timings[channel][slotrank].lanes[lane].timA, mat); + delta = (mat >> 6) - ctrl->timings[channel][slotrank].val_4028; + ctrl->timings[channel][slotrank].val_4024 += delta; + ctrl->timings[channel][slotrank].val_4028 += delta; + } FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); +} + +static void write_controller_mr(ramctr_timing * ctrl) +{ + int channel, slotrank; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + write32(DEFAULT_MCHBAR | 0x0004 | (channel << 8) | + lane_registers[slotrank], make_mr0(ctrl, slotrank)); + write32(DEFAULT_MCHBAR | 0x0008 | (channel << 8) | + lane_registers[slotrank], make_mr1(ctrl, slotrank)); + } +} + +static void channel_test(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS + if (read32(DEFAULT_MCHBAR | 0x42a0 | (channel << 10)) & 0xa000) + die("Mini channel test failed (1)\n"); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0x12345678, 0x98765432); + + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + for (slotrank = 0; slotrank < 4; slotrank++) + FOR_ALL_CHANNELS + if (ctrl->rankmap[channel] & (1 << slotrank)) { + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR | (0x4f40 + 4 * lane), 0); + write32(DEFAULT_MCHBAR | (0x4d40 + 4 * lane), 0); + } + wait_428c(channel); + write32(DEFAULT_MCHBAR | 0x4220 | (channel << 10), 0x0001f006); + write32(DEFAULT_MCHBAR | 0x4230 | (channel << 10), 0x0028a004); + write32(DEFAULT_MCHBAR | 0x4200 | (channel << 10), + 0x00060000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4210 | (channel << 10), 0x00000244); + write32(DEFAULT_MCHBAR | 0x4224 | (channel << 10), 0x0001f201); + write32(DEFAULT_MCHBAR | 0x4234 | (channel << 10), 0x08281064); + write32(DEFAULT_MCHBAR | 0x4204 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4214 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x4228 | (channel << 10), 0x0001f105); + write32(DEFAULT_MCHBAR | 0x4238 | (channel << 10), 0x04281064); + write32(DEFAULT_MCHBAR | 0x4208 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4218 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x422c | (channel << 10), 0x0001f002); + write32(DEFAULT_MCHBAR | 0x423c | (channel << 10), 0x00280c01); + write32(DEFAULT_MCHBAR | 0x420c | (channel << 10), + 0x00060400 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x421c | (channel << 10), 0x00000240); + write32(DEFAULT_MCHBAR | 0x4284 | (channel << 10), 0x000c0001); + wait_428c(channel); + FOR_ALL_LANES + if (read32(DEFAULT_MCHBAR | 0x4340 | (channel << 10))) + die("Mini channel test failed (2)\n"); + } +} + +static void set_scrambling_seed(ramctr_timing * ctrl) +{ + int channel; + + /* FIXME: we hardcode seeds. Do we need to use some PRNG for them? + I don't think so. */ + static u32 seeds[NUM_CHANNELS][3] = { + {0x00009a36, 0xbafcfdcf, 0x46d1ab68}, + {0x00028bfa, 0x53fe4b49, 0x19ed5483} + }; + FOR_ALL_POPULATED_CHANNELS { + MCHBAR32(0x4020 + 0x400 * channel) &= ~0x10000000; + write32(DEFAULT_MCHBAR | 0x4034, seeds[channel][0]); + write32(DEFAULT_MCHBAR | 0x403c, seeds[channel][1]); + write32(DEFAULT_MCHBAR | 0x4038, seeds[channel][2]); + } +} + +static void set_4f8c(void) +{ + struct cpuid_result cpures; + u32 cpu; + + cpures = cpuid(0); + cpu = (cpures.eax); + if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) { + MCHBAR32(0x4f8c) = 0x141D1519; + } else { + MCHBAR32(0x4f8c) = 0x551D1519; + } +} + +static void prepare_training(ramctr_timing * ctrl) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS { + // Always drive command bus + MCHBAR32(0x4004 + 0x400 * channel) |= 0x20000000; + } + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS wait_428c(channel); +} + +static void hardcode1(ramctr_timing * ctrl) +{ + int channel; + u32 reg; + FOR_ALL_POPULATED_CHANNELS { + reg = read32(DEFAULT_MCHBAR | 0x400c | (channel << 10)); + write32(DEFAULT_MCHBAR | 0x400c | (channel << 10), + (reg & 0xFFF0FFFF) + | (ctrl->ref_card_offset[channel] << 16) + | (ctrl->ref_card_offset[channel] << 18)); + write32(DEFAULT_MCHBAR | 0x4008 | (channel << 10), 0x0a042220); // FIXME: hardcoded + } +} + +static void set_42a0(ramctr_timing * ctrl) +{ + int channel; + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR | (0x42a0 + 0x400 * channel), + 0x00001000 | ctrl->rankmap[channel]); + MCHBAR32(0x4004 + 0x400 * channel) &= ~0x20000000; // OK + } +} + +void init_dram_ddr3(spd_raw_data * spds, int mobile, int min_tck) +{ + int me_uma_size; + + MCHBAR32(0x5f00) |= 1; + + report_platform_info(); + + /* Wait for ME to be ready */ + intel_early_me_init(); + me_uma_size = intel_early_me_uma_size(); + + printk(BIOS_DEBUG, "Starting native Platform init\n"); + + u32 reg_5d10; + + wait_txt_clear(); + + wrmsr(0x000002e6, (msr_t) { + .lo = 0,.hi = 0}); + + reg_5d10 = read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + if ((pcie_read_config16(SOUTHBRIDGE, 0xa2) & 0xa0) == 0x20 /* 0x0004 */ + && reg_5d10) { + /* Need reset. */ + outb(0x6, 0xcf9); + + while (1) ; + } + + ramctr_timing ctrl; + + dimm_info info; + + early_pch_init_native(); + early_thermal_init(); + + ctrl.mobile = mobile; + ctrl.tCK = min_tck; + + /* Get DDR3 SPD data */ + dram_find_spds_ddr3(spds, &info, &ctrl); + + /* Find fastest common supported parameters */ + dram_find_common_params(&info, &ctrl); + + /* Calculate timings */ + dram_timing(&ctrl); + + /* Set MCU frequency */ + dram_freq(&ctrl); + + /* Set version register */ + MCHBAR32(0x5034) = 0xC04EB002; + + /* Enable crossover */ + dram_xover(&ctrl); + + /* Set timing and refresh registers */ + dram_timing_regs(&ctrl); + + /* Power mode preset */ + MCHBAR32(0x4e80) = 0x5500; + + /* Set scheduler parameters */ + MCHBAR32(0x4c20) = 0x10100005; + + /* Set cpu specific register */ + set_4f8c(); + + /* Clear IO reset bit */ + MCHBAR32(0x5030) &= ~0x20; + + /* Set MAD-DIMM registers */ + dram_dimm_mapping(&info, &ctrl); + printk(BIOS_DEBUG, "Done dimm mapping\n"); + + /* Zone config */ + dram_zones(&info, &ctrl, 1); + + /* Set memory map */ + dram_memorymap(&info, me_uma_size); + printk(BIOS_DEBUG, "Done memory map\n"); + + /* Set IO registers */ + dram_ioregs(&ctrl); + printk(BIOS_DEBUG, "Done io registers\n"); + + udelay(1); + + /* Do jedec ddr3 reset sequence */ + dram_jedecreset(&ctrl); + printk(BIOS_DEBUG, "Done jedec reset\n"); + + /* MRS commands */ + dram_mrscommands(&ctrl); + printk(BIOS_DEBUG, "Done MRS commands\n"); + dram_mrscommands(&ctrl); + + /* Prepare for memory training */ + prepare_training(&ctrl); + + read_training(&ctrl); + write_training(&ctrl); + + printk(BIOS_SPEW, "CP5a\n"); + + discover_edges(&ctrl); + + printk(BIOS_SPEW, "CP5b\n"); + + command_training(&ctrl); + + printk(BIOS_SPEW, "CP5c\n"); + + discover_edges_write(&ctrl); + + discover_timC_write(&ctrl); + + normalize_training(&ctrl); + + hardcode1(&ctrl); + + write_controller_mr(&ctrl); + + channel_test(&ctrl); + + write32(DEFAULT_MCHBAR | 0x5024, 0x00a030ce); // FIXME: hardcoded + + set_scrambling_seed(&ctrl); + + set_42a0(&ctrl); + + write32(DEFAULT_MCHBAR | 0x4cd4, 0x00000046); // FIXME: hardcoded + + write32(DEFAULT_MCHBAR | 0x400c, (read32(DEFAULT_MCHBAR | 0x400c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x440c, (read32(DEFAULT_MCHBAR | 0x440c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x4cb0, 0x00000740); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x4380, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4780, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4f88, 0x5f7003ff); // OK + write32(DEFAULT_MCHBAR | 0x5064, 0x00073000 | ctrl.reg_5064b0); // OK + write32(DEFAULT_MCHBAR | 0x4384, 0x009b6ea1); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x4784, 0x009b6ea1); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x5880, 0xca9171e5); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x5888); // !!! = 0x00e4d5d0 + write32(DEFAULT_MCHBAR | 0x5888, 0x00e4d5d0); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x58a8); // !!! = 0x00000000 + write32(DEFAULT_MCHBAR | 0x58a8, 0x00000000); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x4294); // !!! = 0x000098ff + write32(DEFAULT_MCHBAR | 0x4294, 0x000198ff); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x4694); // !!! = 0x000098ff + write32(DEFAULT_MCHBAR | 0x4694, 0x000198ff); // FIXME: hardcoded + + MCHBAR32(0x5030) |= 1; // OK + MCHBAR32(0x5030) |= 0x80; // OK + MCHBAR32(0x5f18) = 0xfa; // OK + read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + write32(DEFAULT_MCHBAR | 0x5d10, 0x2010040c); // FIXME: hardcoded + + /* Zone config */ + dram_zones(&info, &ctrl, 0); + + intel_early_me_status(); + intel_early_me_init_done(ME_INIT_STATUS_SUCCESS); + intel_early_me_status(); + + post_system_agent_init(); + report_memory_config(); +} diff --git a/src/northbridge/intel/sandybridge/raminit_native.h b/src/northbridge/intel/sandybridge/raminit_native.h new file mode 100644 index 0000000..ff7d7c1 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RAMINIT_H +#define RAMINIT_H + +#include <device/dram/ddr3.h> + +/* The order is ch0dimmA, ch0dimmB, ch1dimmA, ch1dimmB. */ +void init_dram_ddr3(spd_raw_data *spds, int mobile, int min_tck); +void read_spd(spd_raw_data *spd, u8 addr); + +#endif /* RAMINIT_H */ diff --git a/src/southbridge/intel/bd82x6x/Makefile.inc b/src/southbridge/intel/bd82x6x/Makefile.inc index 64038e5..fa21277 100644 --- a/src/southbridge/intel/bd82x6x/Makefile.inc +++ b/src/southbridge/intel/bd82x6x/Makefile.inc @@ -45,10 +45,14 @@ smm-$(CONFIG_SPI_FLASH_SMM) += ../common/spi.c ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c me.c me_8.x.c finalize.c pch.c -romstage-y += early_usb.c early_smbus.c early_me.c me_status.c gpio.c +romstage-y += early_usb.c early_smbus.c me_status.c gpio.c romstage-y += reset.c romstage-y += early_spi.c early_pch.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += early_thermal.c early_pch_native.c early_me_native.c + ifeq ($(CONFIG_BUILD_WITH_FAKE_IFD),y) IFD_BIN_PATH := $(objgenerated)/ifdfake.bin IFD_SECTIONS := $(addprefix -b ,$(CONFIG_IFD_BIOS_SECTION:"%"=%)) \ diff --git a/src/southbridge/intel/bd82x6x/early_me_native.c b/src/southbridge/intel/bd82x6x/early_me_native.c new file mode 100644 index 0000000..ab54ffd --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_me_native.c @@ -0,0 +1,272 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <arch/hlt.h> +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> +#include <device/pci_ids.h> +#include <string.h> +#include "me.h" +#include "pch.h" + +static const char *me_ack_values[] = { + [ME_HFS_ACK_NO_DID] = "No DID Ack received", + [ME_HFS_ACK_RESET] = "Non-power cycle reset", + [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset", + [ME_HFS_ACK_S3] = "Go to S3", + [ME_HFS_ACK_S4] = "Go to S4", + [ME_HFS_ACK_S5] = "Go to S5", + [ME_HFS_ACK_GBL_RESET] = "Global Reset", + [ME_HFS_ACK_CONTINUE] = "Continue to boot" +}; + +static inline void pci_read_dword_ptr(void *ptr, int offset) +{ + u32 dword = pci_read_config32(PCH_ME_DEV, offset); + memcpy(ptr, &dword, sizeof(dword)); +} + +static inline void pci_write_dword_ptr(void *ptr, int offset) +{ + u32 dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + pci_write_config32(PCH_ME_DEV, offset, dword); +} + +void intel_early_me_status(void) +{ + struct me_hfs hfs; + struct me_gmes gmes; + + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + pci_read_dword_ptr(&gmes, PCI_ME_GMES); + + intel_me_status(&hfs, &gmes); +} + +int intel_early_me_init(void) +{ + int count; + struct me_uma uma; + struct me_hfs hfs; + + printk(BIOS_INFO, "Intel ME early init\n"); + + /* Wait for ME UMA SIZE VALID bit to be set */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) + break; + udelay(ME_DELAY); + } + if (!count) { + printk(BIOS_ERR, "ERROR: ME is not ready!\n"); + return -1; + } + + /* Check for valid firmware */ + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + if (hfs.fpt_bad) { + printk(BIOS_WARNING, "WARNING: ME has bad firmware\n"); + return -1; + } + + printk(BIOS_INFO, "Intel ME firmware is ready\n"); + return 0; +} + +int intel_early_me_uma_size(void) +{ + struct me_uma uma; + + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) { + printk(BIOS_DEBUG, "ME: Requested %uMB UMA\n", uma.size); + return uma.size; + } + + printk(BIOS_DEBUG, "ME: Invalid UMA size\n"); + return 0; +} + +static inline void set_global_reset(int enable) +{ + u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3); + + /* Clear CF9 Without Resume Well Reset Enable */ + etr3 &= ~ETR3_CWORWRE; + + /* CF9GR indicates a Global Reset */ + if (enable) + etr3 |= ETR3_CF9GR; + else + etr3 &= ~ETR3_CF9GR; + + pci_write_config32(PCH_LPC_DEV, ETR3, etr3); +} + +int intel_early_me_init_done(u8 status) +{ + u8 reset, errorcode, opmode; + u16 reg16; + u32 mebase_l, mebase_h; + u32 millisec; + u32 hfs, me_fws2; + struct me_did did = { + .init_done = ME_INIT_DONE, + .status = status + }; + u32 meDID; + + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xff000) >> 12; + + opmode = (hfs & 0xf0) >> 4; + errorcode = hfs & 0xf; + + if (opmode != ME_HFS_MODE_NORMAL) { + printk(BIOS_NOTICE, "ME: Wrong mode : %d\n", opmode); + //return 0; + } + if (errorcode) { + printk(BIOS_NOTICE, "ME: HFS error : %d\n", errorcode); + //return 0; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + // Poll cpu replaced for 50ms + millisec = 0; + while ((((me_fws2 & 0x100) >> 8) == 0) && millisec < 50) { + udelay(1000); + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + millisec++; + } + if (millisec >= 50 || ((me_fws2 & 0x100) >> 8) == 0x0) { + printk(BIOS_NOTICE, "Waited long enough, or CPU was not replaced, continue...\n"); + } else if ((me_fws2 & 0x100) == 0x100) { + if ((me_fws2 & 0x80) == 0x80) { + printk(BIOS_NOTICE, "CPU was replaced & warm reset required...\n"); + reg16 = pcie_read_config16(PCI_DEV(0, 31, 0), 0xa2) & ~0x80; + pcie_write_config16(PCI_DEV(0, 31, 0), 0xa2, reg16); + set_global_reset(0); + outb(0x6, 0xcf9); + hlt(); + } + + if (((me_fws2 & 0x10) == 0x10) && (me_fws2 & 0x80) == 0x00) { + printk(BIOS_NOTICE, "Full training required\n"); + } + } + + printk(BIOS_NOTICE, "PASSED! Tell ME that DRAM is ready\n"); + + /* MEBASE from MESEG_BASE[35:20] */ + mebase_l = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_L); + mebase_h = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_H) & 0xf; + did.uma_base = (mebase_l >> 20) | (mebase_h << 12); + + meDID = did.uma_base | (1 << 28);// | (1 << 23); + pci_write_config32(PCI_DEV(0, 0x16, 0), PCI_ME_H_GS, meDID); + + udelay(1100); + + /* Must wait for ME acknowledgement */ + millisec = 0; + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + while ((((hfs & 0xf0) >> 4) != ME_HFS_BIOS_DRAM_ACK) && (millisec < 5000)) { + udelay(1000); + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + millisec++; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + + /* Return the requested BIOS action */ + printk(BIOS_NOTICE, "ME: Requested BIOS Action: %s\n", + me_ack_values[(hfs & 0xe) >> 1]); + + reset = inb(0xcf9); + reset &= 0xf1; + switch ((hfs & 0xe) >> 1) { + case ME_HFS_ACK_NO_DID: + case ME_HFS_ACK_CONTINUE: + /* Continue to boot */ + return 0; + case ME_HFS_ACK_RESET: + /* Non-power cycle reset */ + set_global_reset(0); + reset |= 0x06; + break; + case ME_HFS_ACK_PWR_CYCLE: + /* Power cycle reset */ + set_global_reset(0); + reset |= 0x0e; + break; + case ME_HFS_ACK_GBL_RESET: + /* Global reset */ + set_global_reset(1); + reset |= 0x0e; + break; + case ME_HFS_ACK_S3: + case ME_HFS_ACK_S4: + case ME_HFS_ACK_S5: + break; + } + + /* Perform the requested reset */ + if (reset) { + outb(reset, 0xcf9); + hlt(); + } + return -1; +} diff --git a/src/southbridge/intel/bd82x6x/early_pch_native.c b/src/southbridge/intel/bd82x6x/early_pch_native.c new file mode 100644 index 0000000..eda97a5 --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_pch_native.c @@ -0,0 +1,375 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko <phcoder(a)gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include <delay.h> + +#include "pch.h" +/* For DMI bar. */ +#include "northbridge/intel/sandybridge/sandybridge.h" + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) + +static void +wait_2338 (void) +{ + while (read8 (DEFAULT_RCBA | 0x2338) & 1); +} + +static u32 +read_2338 (u32 edx) +{ + u32 ret; + + write32 (DEFAULT_RCBA | 0x2330, edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + ret = read32 (DEFAULT_RCBA | 0x2334); + wait_2338 (); + read8 (DEFAULT_RCBA | 0x2338); + return ret; +} + +static void +write_2338 (u32 edx, u32 val) +{ + read_2338 (edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + + write32 (DEFAULT_RCBA | 0x2334, val); + wait_2338 (); + write16 (DEFAULT_RCBA | 0x2338, + (read16 (DEFAULT_RCBA | 0x2338) & 0x1ff) | 0x600); + read8 (DEFAULT_RCBA | 0x2338); +} + + +static void +init_dmi (void) +{ + int i; + + write32 (DEFAULT_DMIBAR | 0x0914, + read32 (DEFAULT_DMIBAR | 0x0914) | 0x80000000); + write32 (DEFAULT_DMIBAR | 0x0934, + read32 (DEFAULT_DMIBAR | 0x0934) | 0x80000000); + for (i = 0; i < 4; i++) + { + write32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4)) & 0xf3ffffff); + write32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4)) | 0x800); + } + write32 (DEFAULT_DMIBAR | 0x0c30, (read32 (DEFAULT_DMIBAR | 0x0c30) + & 0xfffffff) | 0x40000000); + for (i = 0; i < 2; i++) + { + write32 (DEFAULT_DMIBAR | 0x0904 | (i << 5), + read32 (DEFAULT_DMIBAR | 0x0904 | (i << 5)) & 0xfe3fffff); + write32 (DEFAULT_DMIBAR | 0x090c | (i << 5), + read32 (DEFAULT_DMIBAR | 0x090c | (i << 5)) & 0xfff1ffff); + } + write32 (DEFAULT_DMIBAR | 0x090c, + read32 (DEFAULT_DMIBAR | 0x090c) & 0xfe1fffff); + write32 (DEFAULT_DMIBAR | 0x092c, + read32 (DEFAULT_DMIBAR | 0x092c) & 0xfe1fffff); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x090c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x090c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x092c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x092c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0c04); // !!! = 0x2e680008 + write32 (DEFAULT_DMIBAR | 0x0c04, 0x2e680008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0910); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0910, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0930); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0930, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0a04); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a04, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a14); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a14, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a24); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a24, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a34); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a34, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0900); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0900, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0920); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0920, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0c0c); // !!! = 0x16063400 + write32 (DEFAULT_DMIBAR | 0x0c0c, 0x00063400); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0258); // !!! = 0x40000600 + write32 (DEFAULT_DMIBAR | 0x0258, 0x60000600); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x022c); // !!! = 0x00c26460 + write32 (DEFAULT_DMIBAR | 0x022c, 0x00c2403c); + read8 (DEFAULT_RCBA | 0x21a4); // !!! = 0x42 + + read32 (DEFAULT_RCBA | 0x21a4); // !!! = 0x00012c42 + read32 (DEFAULT_RCBA | 0x2340); // !!! = 0x0013001b + write32 (DEFAULT_RCBA | 0x2340, 0x003a001b); + read8 (DEFAULT_RCBA | 0x21b0); // !!! = 0x01 + write8 (DEFAULT_RCBA | 0x21b0, 0x02); + read32 (DEFAULT_DMIBAR | 0x0084); // !!! = 0x0041ac41 + write32 (DEFAULT_DMIBAR | 0x0084, 0x0041ac42); + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0041 + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + + read32 (DEFAULT_DMIBAR | 0x0014); // !!! = 0x8000007f + write32 (DEFAULT_DMIBAR | 0x0014, 0x80000019); + read32 (DEFAULT_DMIBAR | 0x0020); // !!! = 0x01000000 + write32 (DEFAULT_DMIBAR | 0x0020, 0x81000022); + read32 (DEFAULT_DMIBAR | 0x002c); // !!! = 0x02000000 + write32 (DEFAULT_DMIBAR | 0x002c, 0x82000044); + read32 (DEFAULT_DMIBAR | 0x0038); // !!! = 0x07000080 + write32 (DEFAULT_DMIBAR | 0x0038, 0x87000080); + read8 (DEFAULT_DMIBAR | 0x0004); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0004, 0x01); + + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x01200654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x012a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + read8 (DEFAULT_RCBA | 0x1114); // !!! = 0x00 + write8 (DEFAULT_RCBA | 0x1114, 0x05); + read32 (DEFAULT_RCBA | 0x2014); // !!! = 0x80000011 + write32 (DEFAULT_RCBA | 0x2014, 0x80000019); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2020, 0x81000022); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x81000022 + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2030, 0x82000044); + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x82000044 + read32 (DEFAULT_RCBA | 0x2040); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2040, 0x87000080); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + write32 (DEFAULT_RCBA | 0x0050, 0x812a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x812a0654 + read16 (DEFAULT_RCBA | 0x201a); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2026); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2036); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2046); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x001a); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0026); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0032); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x003e); // !!! = 0x0000 +} + +void +early_pch_init_native (void) +{ + pcie_write_config8 (SOUTHBRIDGE, 0xa6, + pcie_read_config8 (SOUTHBRIDGE, 0xa6) | 2); + + write32 (DEFAULT_RCBA | 0x2088, 0x00109000); + read32 (DEFAULT_RCBA | 0x20ac); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x20ac, 0x40000000); + write32 (DEFAULT_RCBA | 0x100c, 0x01110000); + write8 (DEFAULT_RCBA | 0x2340, 0x1b); + read32 (DEFAULT_RCBA | 0x2314); // !!! = 0x0a080000 + write32 (DEFAULT_RCBA | 0x2314, 0x0a280000); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xc809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + write32 (DEFAULT_RCBA | 0x2324, 0x00854c74); + read8 (DEFAULT_RCBA | 0x0400); // !!! = 0x00 + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + + write_2338 (0xea007f62, 0x00590133); + write_2338 (0xec007f62, 0x00590133); + write_2338 (0xec007f64, 0x59555588); + write_2338 (0xea0040b9, 0x0001051c); + write_2338 (0xeb0040a1, 0x800084ff); + write_2338 (0xec0040a1, 0x800084ff); + write_2338 (0xea004001, 0x00008400); + write_2338 (0xeb004002, 0x40201758); + write_2338 (0xec004002, 0x40201758); + write_2338 (0xea004002, 0x00601758); + write_2338 (0xea0040a1, 0x810084ff); + write_2338 (0xeb0040b1, 0x0001c598); + write_2338 (0xec0040b1, 0x0001c598); + write_2338 (0xeb0040b6, 0x0001c598); + write_2338 (0xea0000a9, 0x80ff969f); + write_2338 (0xea0001a9, 0x80ff969f); + write_2338 (0xeb0040b2, 0x0001c396); + write_2338 (0xeb0040b3, 0x0001c396); + write_2338 (0xec0040b2, 0x0001c396); + write_2338 (0xea0001a9, 0x80ff94ff); + write_2338 (0xea000151, 0x0088037f); + write_2338 (0xea0000a9, 0x80ff94ff); + write_2338 (0xea000051, 0x0088037f); + + write_2338 (0xea007f05, 0x00010642); + write_2338 (0xea0040b7, 0x0001c91c); + write_2338 (0xea0040b8, 0x0001c91c); + write_2338 (0xeb0040a1, 0x820084ff); + write_2338 (0xec0040a1, 0x820084ff); + write_2338 (0xea007f0a, 0xc2480000); + + write_2338 (0xec00404d, 0x1ff177f); + write_2338 (0xec000084, 0x5a600000); + write_2338 (0xec000184, 0x5a600000); + write_2338 (0xec000284, 0x5a600000); + write_2338 (0xec000384, 0x5a600000); + write_2338 (0xec000094, 0x000f0501); + write_2338 (0xec000194, 0x000f0501); + write_2338 (0xec000294, 0x000f0501); + write_2338 (0xec000394, 0x000f0501); + write_2338 (0xec000096, 0x00000001); + write_2338 (0xec000196, 0x00000001); + write_2338 (0xec000296, 0x00000001); + write_2338 (0xec000396, 0x00000001); + write_2338 (0xec000001, 0x00008c08); + write_2338 (0xec000101, 0x00008c08); + write_2338 (0xec000201, 0x00008c08); + write_2338 (0xec000301, 0x00008c08); + write_2338 (0xec0040b5, 0x0001c518); + write_2338 (0xec000087, 0x06077597); + write_2338 (0xec000187, 0x06077597); + write_2338 (0xec000287, 0x06077597); + write_2338 (0xec000387, 0x06077597); + write_2338 (0xea000050, 0x00bb0157); + write_2338 (0xea000150, 0x00bb0157); + write_2338 (0xec007f60, 0x77777d77); + write_2338 (0xea00008d, 0x01320000); + write_2338 (0xea00018d, 0x01320000); + write_2338 (0xec0007b2, 0x04514b5e); + write_2338 (0xec00078c, 0x40000200); + write_2338 (0xec000780, 0x02000020); + + init_dmi(); +} diff --git a/src/southbridge/intel/bd82x6x/early_thermal.c b/src/southbridge/intel/bd82x6x/early_thermal.c new file mode 100644 index 0000000..02ec9a7 --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_thermal.c @@ -0,0 +1,70 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include "pch.h" +#include "cpu/intel/model_206ax/model_206ax.h" +#include <cpu/x86/msr.h> + +/* Early thermal init, must be done prior to giving ME its memory + which is done at the end of raminit. */ +void early_thermal_init(void) +{ + device_t dev; + msr_t msr; + + dev = PCI_DEV(0x0, 0x1f, 0x6); + + /* Program address for temporary BAR. */ + pci_write_config32(dev, 0x40, 0x40000000); + pci_write_config32(dev, 0x44, 0x0); + + /* Activate temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) | 5); + + + write16 (0x40000004, 0x3a2b); + write8 (0x4000000c, 0xff); + write8 (0x4000000d, 0x00); + write8 (0x4000000e, 0x40); + write8 (0x40000082, 0x00); + write8 (0x40000001, 0xba); + + /* Perform init. */ + /* Configure TJmax. */ + msr = rdmsr(MSR_TEMPERATURE_TARGET); + write16(0x40000012, ((msr.lo >> 16) & 0xff) << 6); + /* Northbridge temperature slope and offset. */ + write16(0x40000016, 0x808c); + + write16 (0x40000014, 0xde87); + + /* Enable thermal data reporting, processor, PCH and northbridge. */ + write16(0x4000001a, (read16(0x4000001a) & ~0xf) | 0x10f0); + + /* Disable temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) & ~1); + pci_write_config32(dev, 0x40, 0); + + write32 (DEFAULT_RCBA | 0x38b0, + (read32 (DEFAULT_RCBA | 0x38b0) & 0xffff8003) | 0x403c); +} diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h index 90de855..83128e2 100644 --- a/src/southbridge/intel/bd82x6x/pch.h +++ b/src/southbridge/intel/bd82x6x/pch.h @@ -74,6 +74,8 @@ void enable_smbus(void); void enable_usb_bar(void); int smbus_read_byte(unsigned device, unsigned address); int early_spi_read(u32 offset, u32 size, u8 *buffer); +void early_thermal_init(void); +void early_pch_init_native(void); #endif #endif diff --git a/src/southbridge/intel/bd82x6x/smi.c b/src/southbridge/intel/bd82x6x/smi.c index 0166edf..a20232e 100644 --- a/src/southbridge/intel/bd82x6x/smi.c +++ b/src/southbridge/intel/bd82x6x/smi.c @@ -29,10 +29,7 @@ #include <cpu/x86/smm.h> #include <string.h> #include "pch.h" - -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE #include "northbridge/intel/sandybridge/sandybridge.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/bd82x6x/usb_ehci.c b/src/southbridge/intel/bd82x6x/usb_ehci.c index 78f92d9..97f20bd 100644 --- a/src/southbridge/intel/bd82x6x/usb_ehci.c +++ b/src/southbridge/intel/bd82x6x/usb_ehci.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <kconfig.h> #include <console/console.h> #include <device/device.h> #include <device/pci.h> @@ -36,11 +37,43 @@ static void usb_ehci_init(struct device *dev) RCBA32(0x35b0) = reg32; printk(BIOS_DEBUG, "EHCI: Setting up controller.. "); + + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + pci_write_config32(dev, 0x84, 0x930c8811); + pci_write_config32(dev, 0x88, 0x24000d30); + pci_write_config32(dev, 0xf4, 0x80408588); + pci_write_config32(dev, 0xf4, 0x80808588); + pci_write_config32(dev, 0xf4, 0x00808588); + pci_write_config32(dev, 0xfc, 0x205b1708); +#endif + reg32 = pci_read_config32(dev, PCI_COMMAND); reg32 |= PCI_COMMAND_MASTER; //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + struct resource *res; + u8 access_cntl; + + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); +#endif + printk(BIOS_DEBUG, "done.\n"); } diff --git a/src/southbridge/intel/ibexpeak/me.c b/src/southbridge/intel/ibexpeak/me.c index bc56012..0e1b5b8 100644 --- a/src/southbridge/intel/ibexpeak/me.c +++ b/src/southbridge/intel/ibexpeak/me.c @@ -377,125 +377,7 @@ static int mkhi_end_of_post(void) printk(BIOS_INFO, "ME: END OF POST message successful\n"); return 0; } -#endif - -#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && !defined(__SMM__) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) -/* Get ME firmware version */ -static int mkhi_get_fw_version(void) -{ - struct me_fw_version version; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_GEN, - .command = MKHI_GET_FW_VERSION, - }; - struct mei_header mei = { - .is_complete = 1, - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - .length = sizeof(mkhi), - }; - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) { - printk(BIOS_ERR, "ME: GET FW VERSION message failed\n"); - return -1; - } - - printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) " - "%u.%u.%u.%u (recovery)\n", - version.code_major, version.code_minor, - version.code_build_number, version.code_hot_fix, - version.recovery_major, version.recovery_minor, - version.recovery_build_number, version.recovery_hot_fix); - - return 0; -} - -static inline void print_cap(const char *name, int state) -{ - printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n", - name, state ? "en" : "dis"); -} - -/* Get ME Firmware Capabilities */ -static int mkhi_get_fwcaps(void) -{ - u32 rule_id = 0; - struct me_fwcaps cap; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_FWCAPS, - .command = MKHI_FWCAPS_GET_RULE, - }; - struct mei_header mei = { - .is_complete = 1, - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - .length = sizeof(mkhi) + sizeof(rule_id), - }; - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) { - printk(BIOS_ERR, "ME: GET FWCAPS message failed\n"); - return -1; - } - - print_cap("Full Network manageability", cap.caps_sku.full_net); - print_cap("Regular Network manageability", cap.caps_sku.std_net); - print_cap("Manageability", cap.caps_sku.manageability); - print_cap("Small business technology", cap.caps_sku.small_business); - print_cap("Level III manageability", cap.caps_sku.l3manageability); - print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at); - print_cap("IntelR Capability Licensing Service (CLS)", - cap.caps_sku.intel_cls); - print_cap("IntelR Power Sharing Technology (MPC)", - cap.caps_sku.intel_mpc); - print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking); - print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp); - print_cap("IPV6", cap.caps_sku.ipv6); - print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm); - print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och); - print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan); - print_cap("TLS", cap.caps_sku.tls); - print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan); - - return 0; -} -#endif -#if CONFIG_CHROMEOS && 0 /* DISABLED */ -/* Tell ME to issue a global reset */ -int mkhi_global_reset(void) -{ - struct me_global_reset reset = { - .request_origin = GLOBAL_RESET_BIOS_POST, - .reset_type = CBM_RR_GLOBAL_RESET, - }; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_CBM, - .command = MKHI_GLOBAL_RESET, - }; - struct mei_header mei = { - .is_complete = 1, - .length = sizeof(mkhi) + sizeof(reset), - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - }; - - printk(BIOS_NOTICE, "ME: Requesting global reset\n"); - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, &reset, NULL, 0) < 0) { - /* No response means reset will happen shortly... */ - hlt(); - } - - /* If the ME responded it rejected the reset request */ - printk(BIOS_ERR, "ME: Global Reset failed\n"); - return -1; -} -#endif - -#ifdef __SMM__ static void intel_me7_finalize_smm(void) { struct me_hfs hfs; @@ -723,13 +605,6 @@ static void intel_me_init(device_t dev) if (intel_mei_setup(dev) < 0) break; -#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) - /* Print ME firmware version */ - mkhi_get_fw_version(); - /* Print ME firmware capabilities */ - mkhi_get_fwcaps(); -#endif - /* * Leave the ME unlocked in this path. * It will be locked via SMI command later. diff --git a/src/southbridge/intel/ibexpeak/smi.c b/src/southbridge/intel/ibexpeak/smi.c index 981be3b..2ce9072 100644 --- a/src/southbridge/intel/ibexpeak/smi.c +++ b/src/southbridge/intel/ibexpeak/smi.c @@ -31,13 +31,7 @@ #include <string.h> #include "pch.h" -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE -#include "northbridge/intel/sandybridge/sandybridge.h" -#endif - -#if CONFIG_NORTHBRIDGE_INTEL_NEHALEM #include "northbridge/intel/nehalem/nehalem.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/ibexpeak/usb_ehci.c b/src/southbridge/intel/ibexpeak/usb_ehci.c index 21a257f..ea767c4 100644 --- a/src/southbridge/intel/ibexpeak/usb_ehci.c +++ b/src/southbridge/intel/ibexpeak/usb_ehci.c @@ -30,6 +30,8 @@ static void usb_ehci_init(struct device *dev) { u32 reg32; + struct resource *res; + u8 access_cntl; /* Disable Wake on Disconnect in RMH */ reg32 = RCBA32(0x35b0); @@ -50,6 +52,21 @@ static void usb_ehci_init(struct device *dev) //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); + printk(BIOS_DEBUG, "done.\n"); }
1
0
0
0
Patch set updated for coreboot: 27b7381 x230: Deploy VBT
by Vladimir Serbinenko
31 May '14
31 May '14
Vladimir Serbinenko (phcoder(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5396
-gerrit commit 27b73819772554f371144cb43e5b5dd31e54a07c Author: Vladimir Serbinenko <phcoder(a)gmail.com> Date: Sun Feb 23 00:13:56 2014 +0100 x230: Deploy VBT Change-Id: Ide31a56bfdbc31cd3b87993dfb4ed8ef0107cdba Signed-off-by: Vladimir Serbinenko <phcoder(a)gmail.com> --- src/mainboard/lenovo/x230/Makefile.inc | 1 + src/mainboard/lenovo/x230/gma.c | 297 ++++++++++++++++++++++++++++++++ src/northbridge/intel/sandybridge/gma.c | 30 ++++ src/northbridge/intel/sandybridge/gma.h | 2 + 4 files changed, 330 insertions(+) diff --git a/src/mainboard/lenovo/x230/Makefile.inc b/src/mainboard/lenovo/x230/Makefile.inc index d514d4b..4ce3d61 100644 --- a/src/mainboard/lenovo/x230/Makefile.inc +++ b/src/mainboard/lenovo/x230/Makefile.inc @@ -18,3 +18,4 @@ ## smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c +ramstage-y += gma.c diff --git a/src/mainboard/lenovo/x230/gma.c b/src/mainboard/lenovo/x230/gma.c new file mode 100644 index 0000000..20a461b --- /dev/null +++ b/src/mainboard/lenovo/x230/gma.c @@ -0,0 +1,297 @@ +#include <arch/io.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> + +#include "northbridge/intel/sandybridge/sandybridge.h" + +/* This array contains the information on flat panel. When using native + graphics init coreboot copies it to where VGA Option ROM would be so + that OS can find it and able to use internal display. This contains no + executable code and is just information on the panel. + */ + +unsigned char fake_vbt[8192] = +{ +0x24, 0x56, 0x42, 0x54, 0x20, 0x53, 0x4e, 0x42, 0x2f, 0x49, 0x56, 0x42, 0x2d, 0x4d, 0x4f, 0x42, +0x49, 0x4c, 0x45, 0x20, 0x64, 0x00, 0x30, 0x00, 0x6b, 0x11, 0xc6, 0x00, 0x30, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x42, 0x49, 0x4f, 0x53, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x20, +0xa8, 0x00, 0x16, 0x00, 0x3b, 0x11, 0xfe, 0xea, 0x00, 0x00, 0x64, 0x01, 0x01, 0x14, 0x0d, 0x32, +0x31, 0x36, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x28, 0x52, 0x29, 0x20, 0x53, 0x61, 0x6e, 0x64, +0x79, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2f, 0x49, 0x76, 0x79, 0x62, 0x72, 0x69, 0x64, 0x67, +0x65, 0x20, 0x50, 0x43, 0x49, 0x20, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x65, +0x64, 0x20, 0x53, 0x56, 0x47, 0x41, 0x20, 0x42, 0x49, 0x4f, 0x53, 0x0d, 0x0a, 0x42, 0x75, 0x69, +0x6c, 0x64, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x32, 0x31, 0x36, 0x31, 0x5f, +0x52, 0x59, 0x61, 0x6e, 0x20, 0x50, 0x43, 0x20, 0x31, 0x34, 0x2e, 0x33, 0x34, 0x20, 0x20, 0x31, +0x32, 0x2f, 0x30, 0x37, 0x2f, 0x32, 0x30, 0x31, 0x32, 0x20, 0x20, 0x30, 0x38, 0x3a, 0x31, 0x38, +0x3a, 0x33, 0x31, 0x0d, 0x0a, 0x44, 0x45, 0x43, 0x4f, 0x4d, 0x50, 0x49, 0x4c, 0x41, 0x54, 0x49, +0x4f, 0x4e, 0x20, 0x4f, 0x52, 0x20, 0x44, 0x49, 0x53, 0x41, 0x53, 0x53, 0x45, 0x4d, 0x42, 0x4c, +0x59, 0x20, 0x50, 0x52, 0x4f, 0x48, 0x49, 0x42, 0x49, 0x54, 0x45, 0x44, 0x0d, 0x0a, 0x43, 0x6f, +0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, 0x32, 0x30, 0x30, 0x30, +0x2d, 0x32, 0x30, 0x31, 0x31, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, +0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65, 0x73, +0x65, 0x72, 0x76, 0x65, 0x64, 0x2e, 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x00, 0xc0, 0x03, 0x08, 0x04, +0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x07, 0x03, 0x40, 0x01, 0x09, 0xfd, 0x32, 0x00, 0x44, 0x04, +0x40, 0x06, 0x04, 0x02, 0x09, 0x01, 0x00, 0x0a, 0x02, 0x08, 0x0c, 0x04, 0x08, 0x03, 0x01, 0x02, +0x05, 0x01, 0x04, 0x0d, 0x01, 0x04, 0x0b, 0x01, 0x02, 0x07, 0x01, 0x04, 0x15, 0x01, 0x04, 0x45, +0x01, 0x04, 0x0e, 0x04, 0x08, 0x46, 0x04, 0x40, 0x28, 0x20, 0x08, 0x48, 0x40, 0x08, 0x10, 0x00, +0x02, 0x0d, 0x01, 0x02, 0x04, 0x00, 0x00, 0x21, 0x08, 0x00, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xcb, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd6, 0x60, 0x00, 0x10, 0x10, +0x01, 0xb6, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, +0x00, 0x07, 0x10, 0x01, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xd6, 0x60, 0x00, 0x10, +0x10, 0x01, 0xc8, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, +0x00, 0x00, 0x07, 0x20, 0x01, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0xd6, 0x60, 0x00, +0x10, 0x10, 0x01, 0xda, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x09, 0x00, 0x00, 0x06, 0x00, +0x00, 0x00, 0x00, 0x07, 0x30, 0x01, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x01, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x40, 0x42, +0x44, 0x46, 0x48, 0x4a, 0x4c, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x80, 0x81, 0x82, 0x83, +0x84, 0x1a, 0x00, 0xfc, 0xc2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x30, 0x00, 0x51, 0x07, 0x1e, 0x00, +0x48, 0x0a, 0x0f, 0x00, 0x86, 0x03, 0x28, 0x00, 0xb3, 0x03, 0x02, 0x00, 0xbd, 0x03, 0x38, 0x00, +0x61, 0x04, 0xc8, 0x00, 0xb1, 0x06, 0x30, 0x00, 0xe4, 0x06, 0x18, 0x00, 0xff, 0x06, 0x18, 0x00, +0x1a, 0x07, 0x18, 0x00, 0x12, 0x0a, 0x10, 0x00, 0x25, 0x0a, 0x08, 0x00, 0x30, 0x0a, 0x08, 0x00, +0x3b, 0x0a, 0x08, 0x00, 0x1d, 0x08, 0x08, 0x00, 0x61, 0x08, 0x12, 0x00, 0x73, 0x08, 0x12, 0x00, +0x85, 0x08, 0x12, 0x00, 0x97, 0x08, 0x12, 0x00, 0xac, 0x08, 0x0a, 0x00, 0xb6, 0x08, 0x0a, 0x00, +0xc0, 0x08, 0x0a, 0x00, 0xca, 0x08, 0x0a, 0x00, 0xd7, 0x08, 0x0a, 0x00, 0xe1, 0x08, 0x0a, 0x00, +0xeb, 0x08, 0x0a, 0x00, 0xf5, 0x08, 0x0a, 0x00, 0x07, 0x09, 0x0a, 0x00, 0x11, 0x09, 0x0a, 0x00, +0x1b, 0x09, 0x0a, 0x00, 0x25, 0x09, 0x0a, 0x00, 0x2f, 0x09, 0x0a, 0x00, 0x39, 0x09, 0x0a, 0x00, +0x43, 0x09, 0x0a, 0x00, 0x4d, 0x09, 0x0a, 0x00, 0x57, 0x09, 0x0a, 0x00, 0x61, 0x09, 0x0a, 0x00, +0x6b, 0x09, 0x0a, 0x00, 0x75, 0x09, 0x0a, 0x00, 0x7f, 0x09, 0x0a, 0x00, 0x89, 0x09, 0x0a, 0x00, +0x93, 0x09, 0x0a, 0x00, 0x9d, 0x09, 0x0a, 0x00, 0x06, 0x75, 0x00, 0xfc, 0xff, 0x02, 0x80, 0x00, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x8e, 0x29, 0x00, 0x80, 0x9c, 0x01, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x11, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xf0, +0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xf0, +0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, +0x07, 0x07, 0x00, 0xfe, 0xff, 0xce, 0x18, 0x00, 0xff, 0xff, 0x08, 0x3d, 0x00, 0xfc, 0xff, 0x02, +0x40, 0xf0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x01, 0x44, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, +0x48, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x03, 0x03, +0x50, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xf0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, +0x58, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x09, 0x60, 0x00, 0x00, 0x00, 0x32, +0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, +0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, +0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, +0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, +0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, +0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x0a, 0xcb, 0x00, +0x0a, 0x80, 0x04, 0x60, 0x03, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x05, 0x58, 0x02, 0xff, +0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x05, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, +0x07, 0x40, 0x05, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x40, 0x07, 0x70, 0x05, 0xff, 0xff, 0xff, +0xbf, 0xff, 0xff, 0x80, 0x07, 0xa0, 0x05, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x0b, 0xc7, 0x00, 0x21, 0x80, +0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, +0x05, 0x70, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x02, 0x05, 0x72, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x13, 0x00, 0x4a, 0x00, 0x04, 0x00, 0x03, 0x08, 0x3c, 0x84, +0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0xef, 0x87, 0x0d, 0x03, 0x00, 0xf7, 0x03, +0xc8, 0x0e, 0x09, 0x00, 0x01, 0x22, 0x06, 0x5a, 0x00, 0x7e, 0x06, 0x2d, 0x00, 0x0f, 0x8b, 0x00, +0x09, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x84, 0x00, 0x10, 0x00, +0x03, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, +0x40, 0x00, 0x00, 0x40, 0x08, 0x00, 0x20, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x08, 0x00, 0x03, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, +0x04, 0x08, 0x00, 0x40, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, +0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, +0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, +0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, +0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0c, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x20, 0x00, 0x0f, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, +0x9e, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x16, 0x4b, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x84, 0x0e, 0x00, 0x00, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, +0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, +0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, +0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x17, 0x48, +0x00, 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18, 0x88, 0x36, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00, 0x30, 0x40, 0x30, 0x70, 0x13, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1e, 0xa8, 0x2f, 0x78, 0xe0, 0x51, 0x1a, 0x26, 0x40, 0x58, 0x98, 0x13, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x48, 0x3f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, +0xc0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x18, 0x28, 0x00, 0x36, 0x7f, 0x03, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x36, 0x7f, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, +0x36, 0x7f, 0x01, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x36, 0x7f, 0x06, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x19, 0x28, 0x00, 0x19, 0x00, 0xfa, 0x00, 0xfa, 0x00, 0x19, 0x00, 0x90, +0x01, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xc8, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, +0x00, 0x40, 0x00, 0x2c, 0x01, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x2c, 0x01, 0x1a, +0x02, 0x00, 0x00, 0x40, 0x1b, 0xcc, 0x00, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, +0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, +0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, +0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, +0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, +0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, +0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, +0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, +0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, +0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, +0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0xff, 0x1c, 0x36, 0x00, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, 0x08, 0x60, +0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, +0x08, 0x60, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, +0x1d, 0x10, 0x08, 0x60, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1d, 0x34, 0x00, 0x10, +0x00, 0x01, 0x08, 0x01, 0x09, 0x04, 0x0c, 0x40, 0x48, 0x20, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x08, 0x00, 0x01, 0x08, 0x01, 0x09, 0x04, 0x0c, 0x40, 0x48, 0x00, 0x08, 0x00, 0x01, +0x01, 0x09, 0x08, 0x02, 0x05, 0x04, 0x0c, 0x00, 0x08, 0x00, 0x01, 0x01, 0x09, 0x08, 0x02, 0x05, +0x04, 0x0c, 0x00, 0x1e, 0x11, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x18, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, +0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, +0x00, 0x00, 0x29, 0x94, 0x00, 0x03, 0x0c, 0x0b, 0x20, 0x32, 0x0b, 0x12, 0x44, 0x0b, 0x0a, 0x4e, +0x0b, 0x20, 0x74, 0x0b, 0x12, 0x86, 0x0b, 0x0a, 0x90, 0x0b, 0x20, 0xb6, 0x0b, 0x12, 0xc8, 0x0b, +0x0a, 0xd2, 0x0b, 0x20, 0xf8, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x14, 0x0c, 0x20, 0x3a, 0x0c, 0x12, +0x4c, 0x0c, 0x0a, 0x56, 0x0c, 0x20, 0x7c, 0x0c, 0x12, 0x8e, 0x0c, 0x0a, 0x98, 0x0c, 0x20, 0xbe, +0x0c, 0x12, 0xd0, 0x0c, 0x0a, 0xda, 0x0c, 0x20, 0x00, 0x0d, 0x12, 0x12, 0x0d, 0x0a, 0x1c, 0x0d, +0x20, 0x42, 0x0d, 0x12, 0x54, 0x0d, 0x0a, 0x5e, 0x0d, 0x20, 0x84, 0x0d, 0x12, 0x96, 0x0d, 0x0a, +0xa0, 0x0d, 0x20, 0xc6, 0x0d, 0x12, 0xd8, 0x0d, 0x0a, 0xe2, 0x0d, 0x20, 0x08, 0x0e, 0x12, 0x1a, +0x0e, 0x0a, 0x24, 0x0e, 0x20, 0x4a, 0x0e, 0x12, 0x5c, 0x0e, 0x0a, 0x66, 0x0e, 0x20, 0x8c, 0x0e, +0x12, 0x9e, 0x0e, 0x0a, 0xa8, 0x0e, 0x20, 0xce, 0x0e, 0x12, 0xe0, 0x0e, 0x0a, 0xea, 0x0e, 0x20, +0x10, 0x0f, 0x12, 0x22, 0x0f, 0x0a, 0x2c, 0x0f, 0x0d, 0x2a, 0xf0, 0x04, 0x80, 0x02, 0xe0, 0x01, +0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, +0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, +0xff, 0xff, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, 0x08, 0x60, 0x22, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x03, +0x58, 0x02, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, +0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, +0x27, 0x00, 0xff, 0xff, 0xa0, 0x0f, 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, 0x14, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, +0x56, 0x05, 0x00, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x20, 0x00, 0x08, 0x72, 0x0c, 0x00, +0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, +0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x60, 0x1d, 0x56, 0xd8, 0x50, 0x00, 0x18, 0x30, 0x30, 0x40, +0x47, 0x00, 0x15, 0x9c, 0x10, 0x00, 0x00, 0x1b, 0x36, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, +0x00, 0x0c, 0x00, 0x05, 0x00, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, +0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, +0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00, 0x30, 0x40, +0x30, 0x70, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x05, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x78, 0x05, 0x1a, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, +0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, +0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x78, 0x20, 0x51, 0x1a, +0x10, 0x40, 0x10, 0x70, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x01, 0x90, +0x05, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x78, 0x05, 0x1a, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, +0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, +0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0xa8, 0x2f, 0x78, 0xe0, +0x51, 0x1a, 0x26, 0x40, 0x58, 0x98, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, +0x01, 0x90, 0x06, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x06, 0xb0, 0x04, 0x80, 0x11, 0x0e, 0x00, +0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, +0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x48, 0x3f, +0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, +0x36, 0x7f, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x56, 0x05, 0x00, 0x03, 0x80, 0x11, +0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, +0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, +0x66, 0x21, 0x56, 0xaa, 0x51, 0x00, 0x1e, 0x30, 0x46, 0x90, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x36, 0x7f, 0x03, 0x90, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x90, 0x06, 0x1a, 0x04, +0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, +0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, +0xff, 0xff, 0x7c, 0x2e, 0x90, 0xa0, 0x60, 0x1a, 0x1e, 0x40, 0x30, 0x20, 0x36, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x04, 0x90, 0x09, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, +0xb0, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, +0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, +0x27, 0x00, 0xff, 0xff, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, 0x23, 0x40, 0x30, 0x20, 0x2a, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x05, 0x90, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x0c, +0xa0, 0x05, 0x84, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, +0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, +0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x9a, 0x29, 0xa0, 0xd0, 0x51, 0x84, 0x22, 0x30, 0x50, 0x99, +0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, +0x00, 0x0c, 0x40, 0x06, 0x84, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, +0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, +0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x40, 0xc8, 0x60, 0x84, 0x64, 0x30, +0x18, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x0c, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, +0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, +0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, +0x26, 0x30, 0x18, 0x88, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, +0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x20, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, +0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, +0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0xea, 0x1a, 0x00, 0xa0, +0x50, 0x20, 0x17, 0x30, 0x0c, 0x30, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, +0x03, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, 0x38, 0x04, 0x80, 0x11, 0x0e, 0x00, +0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, +0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x02, 0x3a, +0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2d, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, +0x36, 0x7f, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x06, 0x80, 0x11, +0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, +0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, +0x29, 0x40, 0x00, 0x60, 0x80, 0x00, 0x13, 0x60, 0x10, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x4c, 0x46, 0x50, 0x5f, +0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, +0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, +0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, +0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, +0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, +0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, +0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, +0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, +0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, +0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, +0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, +0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, +0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x2b, 0x71, 0x00, 0x06, +0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4a, 0xdc, 0x00, 0x03, +0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, +0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, +0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, +0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, +0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0x2c, 0x15, 0x00, 0x89, 0x46, 0x00, 0x00, 0x00, 0x49, 0x00, 0x0a, 0x00, 0x55, 0x00, 0x50, 0x00, +0x64, 0x00, 0x2c, 0x01, 0x96, 0x00, 0xe8, 0x03, 0x2e, 0xb0, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00 +}; diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index a82a284..b7b4da5 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -21,6 +21,7 @@ #include <console/console.h> #include <bootmode.h> #include <delay.h> +#include <string.h> #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> @@ -652,6 +653,35 @@ static void gma_func0_init(struct device *dev) i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif + + /* Linux relies on VBT for panel info. */ + if (read16(0xc0000) != 0xaa55) { + optionrom_header_t *oh = (void *)0xc0000; + optionrom_pcir_t *pcir; + int sz; + + memset(oh->reserved, 0, 8192); + + sz = (0x80 + sizeof(fake_vbt) + 511) / 512; + oh->signature = 0xaa55; + oh->size = sz; + oh->pcir_offset = 0x40; + oh->vbt_offset = 0x80; + + pcir = (void *)0xc0040; + pcir->signature = 0x52494350; // PCIR + pcir->vendor = dev->vendor; + pcir->device = dev->device; + pcir->length = sizeof(*pcir); + pcir->revision = dev->class; + pcir->classcode[0] = dev->class >> 8; + pcir->classcode[1] = dev->class >> 16; + pcir->classcode[2] = dev->class >> 24; + pcir->imagelength = sz; + pcir->indicator = 0x80; + + memcpy((void *)0xc0080, fake_vbt, sizeof(fake_vbt)); + } } static void gma_set_subsystem(device_t dev, unsigned vendor, unsigned device) diff --git a/src/northbridge/intel/sandybridge/gma.h b/src/northbridge/intel/sandybridge/gma.h index f128412..0248473 100644 --- a/src/northbridge/intel/sandybridge/gma.h +++ b/src/northbridge/intel/sandybridge/gma.h @@ -166,6 +166,8 @@ typedef struct { #define VBT_SIGNATURE 0x54425624 +extern u8 fake_vbt[8192]; + struct northbridge_intel_sandybridge_config; void i915lightup(const struct northbridge_intel_sandybridge_config *info,
1
0
0
0
Patch set updated for coreboot: 419e505 sandy/ivybridge: Native raminit.
by Vladimir Serbinenko
31 May '14
31 May '14
Vladimir Serbinenko (phcoder(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5786
-gerrit commit 419e505e1041c9a9fa2366232553bf3d21c95b3f Author: Vladimir Serbinenko <phcoder(a)gmail.com> Date: Sun May 18 11:05:56 2014 +0200 sandy/ivybridge: Native raminit. Based on damo22 work and my X230 tracing. Works for my X230 in a variety of RAM configs. Also-By: Damien Zammit <damien(a)zamaudio.com> Change-Id: I1aa024c55a8416fc53b25e7123037df0e55a2769 Signed-off-by: Vladimir Serbinenko <phcoder(a)gmail.com> --- 3rdparty | 2 +- src/cpu/intel/Makefile.inc | 1 + src/cpu/x86/smm/smmhandler_tseg.S | 2 +- src/cpu/x86/smm/smmrelocate.S | 4 +- src/device/dram/ddr3.c | 8 +- src/include/device/dram/ddr3.h | 3 + src/mainboard/lenovo/x230/Kconfig | 6 +- src/mainboard/lenovo/x230/romstage.c | 100 +- src/northbridge/intel/Makefile.inc | 1 + src/northbridge/intel/sandybridge/Kconfig | 12 +- src/northbridge/intel/sandybridge/Makefile.inc | 5 +- src/northbridge/intel/sandybridge/gma.c | 4 +- src/northbridge/intel/sandybridge/raminit_native.c | 3766 ++++++++++++++++++++ src/northbridge/intel/sandybridge/raminit_native.h | 29 + src/southbridge/intel/bd82x6x/Makefile.inc | 6 +- src/southbridge/intel/bd82x6x/early_me_native.c | 272 ++ src/southbridge/intel/bd82x6x/early_pch_native.c | 375 ++ src/southbridge/intel/bd82x6x/early_thermal.c | 70 + src/southbridge/intel/bd82x6x/pch.h | 2 + src/southbridge/intel/bd82x6x/smi.c | 3 - src/southbridge/intel/bd82x6x/usb_ehci.c | 33 + src/southbridge/intel/ibexpeak/me.c | 125 - src/southbridge/intel/ibexpeak/smi.c | 6 - src/southbridge/intel/ibexpeak/usb_ehci.c | 17 + 24 files changed, 4628 insertions(+), 224 deletions(-) diff --git a/3rdparty b/3rdparty index 324ec3c..45f0c04 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 324ec3cb642a278d6d97ae809bc6098045bc6e65 +Subproject commit 45f0c04fd788fb29d9e303b2b2d1657ddb03448a diff --git a/src/cpu/intel/Makefile.inc b/src/cpu/intel/Makefile.inc index 0392f69..68ea05d 100644 --- a/src/cpu/intel/Makefile.inc +++ b/src/cpu/intel/Makefile.inc @@ -19,6 +19,7 @@ subdirs-$(CONFIG_CPU_INTEL_SOCKET_RPGA989) += socket_rPGA989 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += model_2065x subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += model_206ax +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_model_206ax diff --git a/src/cpu/x86/smm/smmhandler_tseg.S b/src/cpu/x86/smm/smmhandler_tseg.S index b33fcdf..380935a 100644 --- a/src/cpu/x86/smm/smmhandler_tseg.S +++ b/src/cpu/x86/smm/smmhandler_tseg.S @@ -57,7 +57,7 @@ #define SMI_UNLOCKED 1 #define __PRE_RAM__ -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include <northbridge/intel/sandybridge/sandybridge.h> #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM diff --git a/src/cpu/x86/smm/smmrelocate.S b/src/cpu/x86/smm/smmrelocate.S index bdc9771..bc90fab 100644 --- a/src/cpu/x86/smm/smmrelocate.S +++ b/src/cpu/x86/smm/smmrelocate.S @@ -48,7 +48,7 @@ #if CONFIG_SMM_TSEG -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include <northbridge/intel/sandybridge/sandybridge.h> #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM @@ -195,7 +195,7 @@ smm_relocate: xorl %edx, %edx wrmsr -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE /* * IED base is top 4M of TSEG */ diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c index 9b4f490..69782ab 100644 --- a/src/device/dram/ddr3.c +++ b/src/device/dram/ddr3.c @@ -110,7 +110,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) { int ret; u16 crc, spd_crc; - u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width, sdram_width; + u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width; u8 reg8; u32 mtb; /* medium time base */ unsigned int val, param; @@ -209,8 +209,8 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) printram(" Invalid SDRAM width\n"); ret = SPD_STATUS_INVALID_FIELD; } - sdram_width = (4 << val); - printram(" SDRAM width : %u\n", sdram_width); + dimm->width = (4 << val); + printram(" SDRAM width : %u\n", dimm->width); /* Memory bus width */ reg8 = spd[8]; @@ -236,7 +236,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) * capacity_shift * The rest is the JEDEC formula */ dimm->size_mb = ((1 << (capacity_shift + (25 - 20))) * bus_width - * dimm->ranks) / sdram_width; + * dimm->ranks) / dimm->width; /* Fine Timebase (FTB) Dividend/Divisor */ /* Dividend */ diff --git a/src/include/device/dram/ddr3.h b/src/include/device/dram/ddr3.h index b19c51c..4bf5058 100644 --- a/src/include/device/dram/ddr3.h +++ b/src/include/device/dram/ddr3.h @@ -37,6 +37,7 @@ * @{ */ #define TCK_1066MHZ 240 +#define TCK_933MHZ 275 #define TCK_800MHZ 320 #define TCK_666MHZ 384 #define TCK_533MHZ 480 @@ -137,6 +138,8 @@ typedef struct dimm_attr_st { u16 cas_supported; /* Flags extracted from SPD */ dimm_flags_t flags; + /* SDRAM width */ + u8 width; /* Number of ranks */ u8 ranks; /* Number or row address bits */ diff --git a/src/mainboard/lenovo/x230/Kconfig b/src/mainboard/lenovo/x230/Kconfig index d3aa6e9..6fd309e 100644 --- a/src/mainboard/lenovo/x230/Kconfig +++ b/src/mainboard/lenovo/x230/Kconfig @@ -3,7 +3,7 @@ if BOARD_LENOVO_X230 config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select CPU_INTEL_SOCKET_RPGA989 - select NORTHBRIDGE_INTEL_IVYBRIDGE + select NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE select SOUTHBRIDGE_INTEL_C216 select EC_LENOVO_PMH7 select EC_LENOVO_H8 @@ -47,10 +47,6 @@ config MMCONF_BASE_ADDRESS hex default 0xf0000000 -config CACHE_ROM_SIZE_OVERRIDE - hex - default 0x800000 - config IRQ_SLOT_COUNT int default 18 diff --git a/src/mainboard/lenovo/x230/romstage.c b/src/mainboard/lenovo/x230/romstage.c index 6e4e685..ff664f4 100644 --- a/src/mainboard/lenovo/x230/romstage.c +++ b/src/mainboard/lenovo/x230/romstage.c @@ -32,7 +32,7 @@ #include <cbmem.h> #include <console/console.h> #include "northbridge/intel/sandybridge/sandybridge.h" -#include "northbridge/intel/sandybridge/raminit.h" +#include "northbridge/intel/sandybridge/raminit_native.h" #include "southbridge/intel/bd82x6x/pch.h" #include "southbridge/intel/bd82x6x/gpio.h" #include <arch/cpu.h> @@ -108,66 +108,39 @@ static void rcba_config(void) RCBA32(BUC) = 0; } +static void +init_usb (void) +{ + write32 (DEFAULT_RCBABASE | 0x3598, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35a0, 0x04000201); + write32 (DEFAULT_RCBABASE | 0x35a4, 0x00000200); + write32 (DEFAULT_RCBABASE | 0x35a8, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35ac, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35b0, 0x00000000); + + write32 (DEFAULT_RCBABASE | 0x3560, 0x024c8001); + + outw (inw (DEFAULT_PMBASE | 0x003c) | 2, DEFAULT_PMBASE | 0x003c); + + write32 (DEFAULT_RCBABASE | 0x359c, 0x00000040); + pcie_write_config32 (PCI_DEV (0, 0x14, 0), 0xe4, 0x00000000); + outw (0x0000, DEFAULT_PMBASE | 0x003c); +} + + void main(unsigned long bist) { int boot_mode = 0; int cbmem_was_initted; u32 pm1_cnt; u16 pm1_sts; + spd_raw_data spd[4]; if (MCHBAR16(SSKPD) == 0xCAFE) { outb(0x6, 0xcf9); hlt (); } - struct pei_data pei_data = { - .pei_version = PEI_VERSION, - .mchbar = DEFAULT_MCHBAR, - .dmibar = DEFAULT_DMIBAR, - .epbar = DEFAULT_EPBAR, - .pciexbar = CONFIG_MMCONF_BASE_ADDRESS, - .smbusbar = SMBUS_IO_BASE, - .wdbbar = 0x4000000, - .wdbsize = 0x1000, - .hpet_address = CONFIG_HPET_ADDRESS, - .rcba = DEFAULT_RCBABASE, - .pmbase = DEFAULT_PMBASE, - .gpiobase = DEFAULT_GPIOBASE, - .thermalbase = 0xfed08000, - .system_type = 0, // 0 Mobile, 1 Desktop/Server - .tseg_size = CONFIG_SMM_TSEG_SIZE, - .spd_addresses = { 0xA0, 0x00,0xA2,0x00 }, - .ts_addresses = { 0x00, 0x00, 0x00, 0x00 }, - .ec_present = 1, - .gbe_enable = 1, - .ddr3lv_support = 0, - // 0 = leave channel enabled - // 1 = disable dimm 0 on channel - // 2 = disable dimm 1 on channel - // 3 = disable dimm 0+1 on channel - .dimm_channel0_disabled = 2, - .dimm_channel1_disabled = 2, - .max_ddr3_freq = 1600, - .usb_port_config = { - /* enabled usb oc pin length */ - { 1, 0, 0x0080 }, /* P0 (left, fan side), OC 0 */ - { 1, 1, 0x0080 }, /* P1 (left touchpad side), OC 1 */ - { 1, 3, 0x0080 }, /* P2: dock, OC 3 */ - { 1, 0, 0x0040 }, /* P3: wwan, no OC */ - { 1, 0, 0x0080 }, /* P4: Wacom tablet on X230t, otherwise empty */ - { 1, 0, 0x0080 }, /* P5: Expresscard, no OC */ - { 0, 0, 0x0000 }, /* P6: Empty */ - { 1, 0, 0x0080 }, /* P7: dock, no OC */ - { 0, 0, 0x0000 }, /* P8: Empty */ - { 1, 5, 0x0080 }, /* P9: Right (EHCI debug), OC 5 */ - { 1, 0, 0x0040 }, /* P10: fingerprint reader, no OC */ - { 1, 0, 0x0040 }, /* P11: bluetooth, no OC. */ - { 1, 0, 0x0040 }, /* P12: wlan, no OC */ - { 1, 0, 0x0080 }, /* P13: webcam, no OC */ - }, - .ddr_refresh_rate_config = 2, /* Force double refresh rate */ - }; - timestamp_init(get_initial_timestamp()); timestamp_add_now(TS_START_ROMSTAGE); @@ -182,6 +155,8 @@ void main(unsigned long bist) setup_pch_gpios(&x230_gpio_map); + init_usb(); + /* Initialize console device(s) */ console_init(); @@ -217,31 +192,16 @@ void main(unsigned long bist) /* Enable SPD ROMs and DDR-III DRAM */ enable_smbus(); - /* Prepare USB controller early in S3 resume */ - if (boot_mode == 2) - enable_usb_bar(); - post_code(0x39); post_code(0x3a); - pei_data.boot_mode = boot_mode; timestamp_add_now(TS_BEFORE_INITRAM); - /* MRC.bin has a bug and sometimes halts (instead of reboot?). - */ - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) & ~(1 << 5); /* reset */ - outw((0 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* let timer go */ - } - - sdram_initialize(&pei_data); + memset (spd, 0, sizeof (spd)); + read_spd (&spd[0], 0x50); + read_spd (&spd[2], 0x51); - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) | (1 << 5); /* No reset */ - outw((1 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */ - } + init_dram_ddr3 (spd, 1, TCK_800MHZ); timestamp_add_now(TS_AFTER_INITRAM); post_code(0x3c); @@ -254,8 +214,8 @@ void main(unsigned long bist) MCHBAR16(SSKPD) = 0xCAFE; cbmem_was_initted = !cbmem_recovery(boot_mode==2); - if (boot_mode!=2) - save_mrc_data(&pei_data); +// if (boot_mode!=2) +// save_mrc_data(&pei_data); #if CONFIG_HAVE_ACPI_RESUME /* If there is no high memory area, we didn't boot before, so diff --git a/src/northbridge/intel/Makefile.inc b/src/northbridge/intel/Makefile.inc index 808a1b2..f1c2540 100644 --- a/src/northbridge/intel/Makefile.inc +++ b/src/northbridge/intel/Makefile.inc @@ -15,6 +15,7 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I5000) += i5000 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += nehalem subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += sandybridge +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_sandybridge diff --git a/src/northbridge/intel/sandybridge/Kconfig b/src/northbridge/intel/sandybridge/Kconfig index fb92e40..9bac706 100644 --- a/src/northbridge/intel/sandybridge/Kconfig +++ b/src/northbridge/intel/sandybridge/Kconfig @@ -31,7 +31,14 @@ config NORTHBRIDGE_INTEL_IVYBRIDGE select MMCONF_SUPPORT_DEFAULT select CPU_INTEL_MODEL_306AX -if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE +config NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + bool + select CACHE_MRC_BIN + select MMCONF_SUPPORT + select MMCONF_SUPPORT_DEFAULT + select CPU_INTEL_MODEL_306AX + +if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config VGA_BIOS_ID string @@ -52,7 +59,8 @@ config MRC_CACHE_SIZE config DCACHE_RAM_BASE hex - default 0xff7e0000 + default 0xff7e0000 if !NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + default 0xfefe0000 if NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config DCACHE_RAM_SIZE hex diff --git a/src/northbridge/intel/sandybridge/Makefile.inc b/src/northbridge/intel/sandybridge/Makefile.inc index 6655e2a..ae20de5 100644 --- a/src/northbridge/intel/sandybridge/Makefile.inc +++ b/src/northbridge/intel/sandybridge/Makefile.inc @@ -26,7 +26,10 @@ ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c ramstage-y += mrccache.c romstage-y += ram_calc.c -romstage-y += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += raminit_native.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += ../../../device/dram/ddr3.c romstage-y += mrccache.c romstage-y += early_init.c romstage-y += report_platform.c diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index d271a1a..a82a284 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -650,9 +650,7 @@ static void gma_func0_init(struct device *dev) physbase = pci_read_config32(dev, 0x5c) & ~0xf; graphics_base = dev->resource_list[1].base; - int lightup_ok = i915lightup(conf, physbase, iobase, mmiobase, graphics_base); - if (lightup_ok) - gfx_set_init_done(1); + i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif } diff --git a/src/northbridge/intel/sandybridge/raminit_native.c b/src/northbridge/intel/sandybridge/raminit_native.c new file mode 100644 index 0000000..4f37413 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.c @@ -0,0 +1,3766 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Damien Zammit <damien(a)zamaudio.com> + * Copyright (C) 2014 Vladimir Serbinenko <phcoder(a)gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <console/usb.h> +#include <bootmode.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include "raminit_native.h" +#include "sandybridge.h" +#include <delay.h> + +/* Management Engine is in the southbridge */ +#include "southbridge/intel/bd82x6x/me.h" +/* For SPD. */ +#include "southbridge/intel/bd82x6x/smbus.h" +#include "arch/cpu.h" +#include "cpu/x86/msr.h" + +/* FIXME: no ECC support. */ +/* FIXME: no support for 3-channel chipsets. */ +/* FIXME: no S3. */ +/* FIXME: no timing caching. */ + +#define BASEFREQ 133 +#define tDLLK 512 + +#define IS_SANDY_CPU(x) ((x & 0xffff0) == 0x206a0) +#define IS_SANDY_CPU_C(x) ((x & 0xf) == 4) +#define IS_SANDY_CPU_D0(x) ((x & 0xf) == 5) +#define IS_SANDY_CPU_D1(x) ((x & 0xf) == 6) +#define IS_SANDY_CPU_D2(x) ((x & 0xf) == 7) + +#define IS_IVY_CPU(x) ((x & 0xffff0) == 0x306a0) +#define IS_IVY_CPU_C(x) ((x & 0xf) == 4) +#define IS_IVY_CPU_K(x) ((x & 0xf) == 5) +#define IS_IVY_CPU_D(x) ((x & 0xf) == 6) +#define IS_IVY_CPU_E(x) ((x & 0xf) >= 8) + +#define NUM_CHANNELS 2 +#define NUM_SLOTRANKS 4 +#define NUM_SLOTS 2 +#define NUM_LANES 8 + +typedef struct odtmap_st { + u16 rttwr; + u16 rttnom; +} odtmap; + +typedef struct dimm_info_st { + dimm_attr dimm[NUM_CHANNELS][NUM_SLOTS]; +} dimm_info; + +struct ram_rank_timings { + /* Register 4024. One byte per slotrank. */ + u8 val_4024; + /* Register 4028. One nibble per slotrank. */ + u8 val_4028; + + int val_320c; + + struct ram_lane_timings { + /* lane register offset 0x10. */ + u16 timA; /* bits 0 - 5, bits 16 - 18 */ + u8 rising; /* bits 8 - 14 */ + u8 falling; /* bits 20 - 26. */ + + /* lane register offset 0x20. */ + int timC; /* bit 0 - 5, 19. */ + u16 timB; /* bits 8 - 13, 15 - 17. */ + } lanes[NUM_LANES]; +}; + +typedef struct ramctr_timing_st { + int mobile; + + enum spd_memory_type dram_type; + u16 cas_supported; + /* tLatencies are in units of ns, scaled by x256 */ + u32 tCK; + u32 tAA; + u32 tWR; + u32 tRCD; + u32 tRRD; + u32 tRP; + u32 tRAS; + u32 tRC; + u32 tRFC; + u32 tWTR; + u32 tRTP; + u32 tFAW; + /* Latencies in terms of clock cycles + * They are saved separately as they are needed for DRAM MRS commands*/ + u8 CAS; /* CAS read latency */ + u8 CWL; /* CAS write latency */ + /* Number of dimms currently connected */ + u8 n_dimms; + + u32 tREFI; + u32 tMOD; + u32 tXSOffset; + u32 tWLO; + u32 tCKE; + u32 tXPDLL; + u32 tXP; + u32 tAONPD; + + u32 delay1; + u32 delay2; + u16 reg_5064b0; /* bits 0-11. */ + + u8 eccsupport; + u8 dualchannel; + + u8 rankmap[NUM_CHANNELS]; + int ref_card_offset[NUM_CHANNELS]; + + int reg_c14_offset; + + int edge_offset[3]; + int timC_offset[3]; + + int extended_temperature_range; + int auto_self_refresh; + + int rank_mirror[NUM_CHANNELS][NUM_SLOTRANKS]; + + struct ram_rank_timings timings[NUM_CHANNELS][NUM_SLOTRANKS]; +} ramctr_timing; + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) +#define NORTHBRIDGE PCI_DEV(0, 0x0, 0) +#define FOR_ALL_LANES for (lane = 0; lane < NUM_LANES; lane++) +#define FOR_ALL_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) +#define FOR_ALL_POPULATED_RANKS for (slotrank = 0; slotrank < NUM_SLOTRANKS; slotrank++) if (ctrl->rankmap[channel] & (1 << slotrank)) +#define FOR_ALL_POPULATED_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) if (ctrl->rankmap[channel]) +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define MAX_EDGE_TIMING 71 +#define MAX_TIMC 127 +#define MAX_TIMB 511 +#define MAX_TIMA 127 + +static void program_timings(ramctr_timing * ctrl, int channel); + +static const char *ecc_decoder[] = { + "inactive", + "active on IO", + "disabled on IO", + "active" +}; + +static void wait_txt_clear(void) +{ + struct cpuid_result cp; + + cp = cpuid_ext(0x1, 0x0); + /* Check if TXT is supported? */ + if (!(cp.ecx & 0x40)) + return; + /* Some TXT public bit. */ + if (!(read32(0xfed30010) & 1)) + return; + /* Wait for TXT clear. */ + while (!(read8(0xfed40000) & (1 << 7))) ; +} + +static void sfence(void) +{ + asm volatile ("sfence"); +} + +/* + * Dump in the log memory controller configuration as read from the memory + * controller registers. + */ +static void report_memory_config(void) +{ + u32 addr_decoder_common, addr_decode_ch[NUM_CHANNELS]; + int i; + + addr_decoder_common = MCHBAR32(0x5000); + addr_decode_ch[0] = MCHBAR32(0x5004); + addr_decode_ch[1] = MCHBAR32(0x5008); + + printk(BIOS_DEBUG, "memcfg DDR3 clock %d MHz\n", + (MCHBAR32(0x5e04) * 13333 * 2 + 50) / 100); + printk(BIOS_DEBUG, "memcfg channel assignment: A: %d, B % d, C % d\n", + addr_decoder_common & 3, (addr_decoder_common >> 2) & 3, + (addr_decoder_common >> 4) & 3); + + for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) { + u32 ch_conf = addr_decode_ch[i]; + printk(BIOS_DEBUG, "memcfg channel[%d] config (%8.8x):\n", i, + ch_conf); + printk(BIOS_DEBUG, " ECC %s\n", + ecc_decoder[(ch_conf >> 24) & 3]); + printk(BIOS_DEBUG, " enhanced interleave mode %s\n", + ((ch_conf >> 22) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " rank interleave %s\n", + ((ch_conf >> 21) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " DIMMA %d MB width x%d %s rank%s\n", + ((ch_conf >> 0) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? 16 : 8, + ((ch_conf >> 17) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? "" : ", selected"); + printk(BIOS_DEBUG, " DIMMB %d MB width x%d %s rank%s\n", + ((ch_conf >> 8) & 0xff) * 256, + ((ch_conf >> 20) & 1) ? 16 : 8, + ((ch_conf >> 18) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? ", selected" : ""); + } +} + +static void post_system_agent_init(void) +{ + /* If PCIe init is skipped, set the PEG clock gating */ + MCHBAR32(0x7010) = MCHBAR32(0x7010) | 0x01; +} + +void read_spd(spd_raw_data * spd, u8 addr) +{ + int j; + for (j = 0; j < 256; j++) + (*spd)[j] = do_smbus_read_byte(SMBUS_IO_BASE, addr, j); +} + +static void dram_find_spds_ddr3(spd_raw_data * spd, dimm_info * dimm, + ramctr_timing * ctrl) +{ + int dimms = 0; + int channel, slot, spd_slot; + + memset (ctrl->rankmap, 0, sizeof (ctrl->rankmap)); + + ctrl->extended_temperature_range = 1; + ctrl->auto_self_refresh = 1; + + FOR_ALL_CHANNELS { + int ref_card[NUM_SLOTS]; + for (slot = 0; slot < NUM_SLOTS; slot++) { + spd_slot = 2 * channel + slot; + spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + if (dimm->dimm[channel][slot].dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { + // set dimm invalid + dimm->dimm[channel][slot].ranks = 0; + dimm->dimm[channel][slot].size_mb = 0; + continue; + } + + dram_print_spd_ddr3(&dimm->dimm[channel][slot]); + dimms++; + ctrl->rank_mirror[channel][slot * 2] = 0; + ctrl->rank_mirror[channel][slot * 2 + 1] = spd[spd_slot][0x3f] & 1; + + ctrl->auto_self_refresh &= (spd[spd_slot][31] >> 2) & 1; + ctrl->extended_temperature_range &= spd[spd_slot][31] & 1; + + ctrl->rankmap[channel] = ((1 << dimm->dimm[channel][slot].ranks) - 1) << (2 * slot); + ref_card[channel] = spd[spd_slot][62] & 0x1f;; + printk(BIOS_DEBUG, "rankmap[%d] = 0x%x\n", channel, ctrl->rankmap[channel]); + } + if ((ctrl->rankmap[channel] & 3) && (ctrl->rankmap[channel] & 0xc) + && ref_card[0] <= 5 && ref_card[1] <= 5) { + const int ref_card_offset_table[6][6] = { + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 1, 1, }, + { 2, 2, 2, 1, 0, 0, }, + { 2, 2, 2, 1, 0, 0, }, + }; + ctrl->ref_card_offset[channel] = ref_card_offset_table[ref_card[0]][ref_card[1]]; + } else + ctrl->ref_card_offset[channel] = 0; + } + + if (!dimms) + die("No DIMMs were found"); +} + +static void dram_find_common_params(const dimm_info * dimms, + ramctr_timing * ctrl) +{ + size_t valid_dimms; + int channel, slot; + ctrl->cas_supported = 0xff; + valid_dimms = 0; + FOR_ALL_CHANNELS + for (slot = 0; slot < 2; slot++) { + const dimm_attr *dimm = &dimms->dimm[channel][slot]; + if (dimm->dram_type == SPD_MEMORY_TYPE_UNDEFINED) + continue; + valid_dimms++; + + if (valid_dimms == 1) { + /* First DIMM defines the type of DIMM */ + ctrl->dram_type = dimm->dram_type; + } else { + /* Check if we have mismatched DIMMs */ + if (ctrl->dram_type != dimm->dram_type) + die("Mismatched DIMM Types"); + } + /* Find all possible CAS combinations */ + ctrl->cas_supported &= dimm->cas_supported; + + /* Find the smallest common latencies supported by all DIMMs */ + ctrl->tCK = MAX(ctrl->tCK, dimm->tCK); + ctrl->tAA = MAX(ctrl->tAA, dimm->tAA); + ctrl->tWR = MAX(ctrl->tWR, dimm->tWR); + ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD); + ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD); + ctrl->tRP = MAX(ctrl->tRP, dimm->tRP); + ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS); + ctrl->tRC = MAX(ctrl->tRC, dimm->tRC); + ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC); + ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR); + ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP); + ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW); + } + + ctrl->n_dimms = valid_dimms; + if (!ctrl->cas_supported) + die("Unsupported DIMM combination. " + "DIMMS do not support common CAS latency"); + if (!valid_dimms) + die("No valid DIMMs found"); + + ctrl->dualchannel = + (pcie_read_config32(PCI_DEV(0, 0, 0), 0xE4) & 0x4000) >> 14; + if (ctrl->dualchannel) { + printk(BIOS_DEBUG, "Dual channel supported\n"); + } else { + printk(BIOS_DEBUG, "Dual channel not supported\n"); + } +} + +static u8 get_CWL(u8 CAS) +{ + /* Get CWL based on CAS using the following rule: + * _________________________________________ + * CAS: | 4T | 5T | 6T | 7T | 8T | 9T | 10T | 11T | + * CWL: | 5T | 5T | 5T | 6T | 6T | 7T | 7T | 8T | + */ + static const u8 cas_cwl_map[] = { 5, 5, 5, 6, 6, 7, 7, 8 }; + if (CAS > 11) + return 8; + return cas_cwl_map[CAS - 4]; +} + +static u32 get_REFI(u32 tCK) +{ + /* Get REFI based on MCU frequency using the following rule: + * _________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * REFI: | 3120 | 4160 | 5200 | 6240 | 7280 | 8320 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u32 frq_refi_map[] = + { 3120, 4160, 5200, 6240, 7280, 8320 }; + if (FRQ > 8) + return 8320; + return frq_refi_map[FRQ - 3]; +} + +static u8 get_XSOffset(u32 tCK) +{ + /* Get XSOffset based on MCU frequency using the following rule: + * _________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XSOffset : | 4 | 6 | 7 | 8 | 10 | 11 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xs_map[] = { 4, 6, 7, 8, 10, 11 }; + if (FRQ > 8) + return 11; + return frq_xs_map[FRQ - 3]; +} + +static u8 get_MOD(u32 tCK) +{ + /* Get MOD based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * MOD : | 12 | 12 | 12 | 12 | 15 | 16 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_mod_map[] = { 12, 12, 12, 12, 15, 16 }; + if (FRQ > 8) + return 16; + return frq_mod_map[FRQ - 3]; +} + +static u8 get_WLO(u32 tCK) +{ + /* Get WLO based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * WLO : | 4 | 5 | 6 | 6 | 8 | 8 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_wlo_map[] = { 4, 5, 6, 6, 8, 8 }; + if (FRQ > 8) + return 8; + return frq_wlo_map[FRQ - 3]; +} + +static u8 get_CKE(u32 tCK) +{ + /* Get CKE based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * CKE : | 3 | 3 | 4 | 4 | 5 | 6 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_cke_map[] = { 3, 3, 4, 4, 5, 6 }; + if (FRQ > 8) + return 6; + return frq_cke_map[FRQ - 3]; +} + +static u8 get_XPDLL(u32 tCK) +{ + /* Get XPDLL based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XPDLL : | 10 | 13 | 16 | 20 | 23 | 26 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xpdll_map[] = { 10, 13, 16, 20, 23, 26 }; + if (FRQ > 8) + return 26; + return frq_xpdll_map[FRQ - 3]; +} + +static u8 get_XP(u32 tCK) +{ + /* Get XP based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XP : | 3 | 4 | 4 | 5 | 6 | 7 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xp_map[] = { 3, 4, 4, 5, 6, 7 }; + if (FRQ > 8) + return 7; + return frq_xp_map[FRQ - 3]; +} + +static u8 get_AONPD(u32 tCK) +{ + /* Get AONPD based on MCU frequency using the following rule: + * ________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * AONPD : | 4 | 5 | 6 | 8 | 8 | 10 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_aonpd_map[] = { 4, 5, 6, 8, 8, 10 }; + if (FRQ > 8) + return 10; + return frq_aonpd_map[FRQ - 3]; +} + +static u32 get_COMP2(u32 tCK) +{ + /* Get COMP2 based on MCU frequency using the following rule: + * ___________________________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * COMP : | D6BEDCC | CE7C34C | CA57A4C | C6369CC | C42514C | C21410C | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u32 frq_comp2_map[] = { 0xD6BEDCC, 0xCE7C34C, 0xCA57A4C, + 0xC6369CC, 0xC42514C, 0xC21410C + }; + if (FRQ > 8) + return 0xD6BEDCC; + return frq_comp2_map[FRQ - 3]; +} + +static void dram_timing(ramctr_timing * ctrl) +{ + u8 val; + u32 val32; + + /* Maximum supported DDR3 frequency is 1066MHz (DDR3 2133) so make sure + * we cap it if we have faster DIMMs. + * Then, align it to the closest JEDEC standard frequency */ + if (ctrl->tCK <= TCK_1066MHZ) { + ctrl->tCK = TCK_1066MHZ; + ctrl->delay1 = 16; + ctrl->delay2 = 8; + ctrl->edge_offset[0] = 16; + ctrl->edge_offset[1] = 7; + ctrl->edge_offset[2] = 7; + ctrl->timC_offset[0] = 18; + ctrl->timC_offset[1] = 7; + ctrl->timC_offset[2] = 7; + ctrl->reg_c14_offset = 16; + ctrl->reg_5064b0 = 0x218; + } else if (ctrl->tCK <= TCK_933MHZ) { + ctrl->tCK = TCK_933MHZ; + ctrl->delay1 = 15; + ctrl->delay2 = 8; + ctrl->edge_offset[0] = 14; + ctrl->edge_offset[1] = 6; + ctrl->edge_offset[2] = 6; + ctrl->timC_offset[0] = 15; + ctrl->timC_offset[1] = 6; + ctrl->timC_offset[2] = 6; + ctrl->reg_c14_offset = 14; + ctrl->reg_5064b0 = 0x1d5; + } else if (ctrl->tCK <= TCK_800MHZ) { + ctrl->tCK = TCK_800MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 6; + ctrl->edge_offset[0] = 13; + ctrl->edge_offset[1] = 5; + ctrl->edge_offset[2] = 5; + ctrl->timC_offset[0] = 14; + ctrl->timC_offset[1] = 5; + ctrl->timC_offset[2] = 5; + ctrl->reg_c14_offset = 12; + ctrl->reg_5064b0 = 0x193; + } else if (ctrl->tCK <= TCK_666MHZ) { + ctrl->tCK = TCK_666MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 6; + ctrl->edge_offset[0] = 10; + ctrl->edge_offset[1] = 4; + ctrl->edge_offset[2] = 4; + ctrl->timC_offset[0] = 11; + ctrl->timC_offset[1] = 4; + ctrl->timC_offset[2] = 4; + ctrl->reg_c14_offset = 10; + ctrl->reg_5064b0 = 0x150; + } else { + ctrl->tCK = TCK_533MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 5; + ctrl->edge_offset[0] = 8; + ctrl->edge_offset[1] = 3; + ctrl->edge_offset[2] = 3; + ctrl->timC_offset[0] = 9; + ctrl->timC_offset[1] = 3; + ctrl->timC_offset[2] = 3; + ctrl->reg_c14_offset = 8; + ctrl->reg_5064b0 = 0x10d; + } + + val32 = (1000 << 8) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected DRAM frequency: %u MHz\n", val32); + + /* Find CAS and CWL latencies */ + val = (ctrl->tAA + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Minimum CAS latency : %uT\n", val); + /* Find lowest supported CAS latency that satisfies the minimum value */ + while (!((ctrl->cas_supported >> (val - 4)) & 1) + && (ctrl->cas_supported >> (val - 4))) { + val++; + } + /* Is CAS supported */ + if (!(ctrl->cas_supported & (1 << (val - 4)))) + printk(BIOS_DEBUG, "CAS not supported\n"); + printk(BIOS_DEBUG, "Selected CAS latency : %uT\n", val); + ctrl->CAS = val; + ctrl->CWL = get_CWL(ctrl->CAS); + printk(BIOS_DEBUG, "Selected CWL latency : %uT\n", ctrl->CWL); + + /* Find tRCD */ + ctrl->tRCD = (ctrl->tRCD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRCD : %uT\n", ctrl->tRCD); + + ctrl->tRP = (ctrl->tRP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRP : %uT\n", ctrl->tRP); + + /* Find tRAS */ + ctrl->tRAS = (ctrl->tRAS + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRAS : %uT\n", ctrl->tRAS); + + /* Find tWR */ + ctrl->tWR = (ctrl->tWR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWR : %uT\n", ctrl->tWR); + + /* Find tFAW */ + ctrl->tFAW = (ctrl->tFAW + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tFAW : %uT\n", ctrl->tFAW); + + /* Find tRRD */ + ctrl->tRRD = (ctrl->tRRD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRRD : %uT\n", ctrl->tRRD); + + /* Find tRTP */ + ctrl->tRTP = (ctrl->tRTP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRTP : %uT\n", ctrl->tRTP); + + /* Find tWTR */ + ctrl->tWTR = (ctrl->tWTR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWTR : %uT\n", ctrl->tWTR); + + /* Refresh-to-Active or Refresh-to-Refresh (tRFC) */ + ctrl->tRFC = (ctrl->tRFC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRFC : %uT\n", ctrl->tRFC); + + ctrl->tRC = (ctrl->tRC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Required tRC : %uT\n", ctrl->tRC); + + ctrl->tREFI = get_REFI(ctrl->tCK); + ctrl->tMOD = get_MOD(ctrl->tCK); + ctrl->tXSOffset = get_XSOffset(ctrl->tCK); + ctrl->tWLO = get_WLO(ctrl->tCK); + ctrl->tCKE = get_CKE(ctrl->tCK); + ctrl->tXPDLL = get_XPDLL(ctrl->tCK); + ctrl->tXP = get_XP(ctrl->tCK); + ctrl->tAONPD = get_AONPD(ctrl->tCK); +} + +static void dram_freq(ramctr_timing * ctrl) +{ + u8 val1, val2; + u32 reg1 = 0; + + /* Step 1 - Set target PCU frequency */ + + if (ctrl->tCK <= TCK_1066MHZ) { + val1 = 0x08; + ctrl->tCK = TCK_1066MHZ; + } else if (ctrl->tCK <= TCK_933MHZ) { + val1 = 0x07; + ctrl->tCK = TCK_933MHZ; + } else if (ctrl->tCK <= TCK_800MHZ) { + val1 = 0x06; + ctrl->tCK = TCK_800MHZ; + } else if (ctrl->tCK <= TCK_666MHZ) { + val1 = 0x05; + ctrl->tCK = TCK_666MHZ; + } else { + val1 = 0x04; + ctrl->tCK = TCK_533MHZ; + } + + /* Step 2 - Select frequency in the MCU */ + reg1 = val1; + reg1 |= 0x80000000; // set running bit + MCHBAR32(0x5e00) = reg1; + while (reg1 & 0x80000000) { + printk(BIOS_DEBUG, " PLL busy..."); + reg1 = MCHBAR32(0x5e00); + } + printk(BIOS_DEBUG, "done\n"); + + /* Step 3 - Verify lock frequency */ + reg1 = MCHBAR32(0x5e04); + val2 = (u8) reg1; + if (val2 > val1) { + printk(BIOS_DEBUG, "Lock frequency is lower, recalculating\n"); + switch (val2) { + case 8: + ctrl->tCK = TCK_1066MHZ; + break; + case 7: + ctrl->tCK = TCK_933MHZ; + break; + case 6: + ctrl->tCK = TCK_800MHZ; + break; + case 5: + ctrl->tCK = TCK_666MHZ; + break; + case 4: + ctrl->tCK = TCK_533MHZ; + break; + default: + printk(BIOS_DEBUG, "ERROR: PLL is off or unknown\n"); + break; + } + dram_timing(ctrl); // recalculate timings + } + printk(BIOS_DEBUG, "MCU frequency is set at : %d MHz\n", + (1000 << 8) / ctrl->tCK); +} + +static void dram_xover(ramctr_timing * ctrl) +{ + u32 reg; + int channel; + + FOR_ALL_CHANNELS { + // enable xover clk + printk(BIOS_DEBUG, "[%x] = %x\n", channel * 0x100 + 0xc14, + (ctrl->rankmap[channel] << 24)); + MCHBAR32(channel * 0x100 + 0xc14) = (ctrl->rankmap[channel] << 24); + + // enable xover ctl + reg = 0; + if (ctrl->rankmap[channel] & 0x5) { + reg |= 0x20000; + } + if (ctrl->rankmap[channel] & 0xa) { + reg |= 0x4000000; + } + // enable xover cmd + reg |= 0x4000; + printk(BIOS_DEBUG, "[%x] = %x\n", 0x100 * channel + 0x320c, + reg); + MCHBAR32(0x100 * channel + 0x320c) = reg; + } +} + +static void dram_timing_regs(ramctr_timing * ctrl) +{ + u32 reg, addr, val32, cpu, stretch; + struct cpuid_result cpures; + int channel; + + FOR_ALL_CHANNELS { + // DBP + reg = 0; + reg |= ctrl->tRCD; + reg |= (ctrl->tRP << 4); + reg |= (ctrl->CAS << 8); + reg |= (ctrl->CWL << 12); + reg |= (ctrl->tRAS << 16); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4000, + reg); + MCHBAR32(0x400 * channel + 0x4000) = reg; + + // RAP + reg = 0; + reg |= ctrl->tRRD; + reg |= (ctrl->tRTP << 4); + reg |= (ctrl->tCKE << 8); + reg |= (ctrl->tWTR << 12); + reg |= (ctrl->tFAW << 16); + reg |= (ctrl->tWR << 24); + reg |= (3 << 30); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4004, + reg); + MCHBAR32(0x400 * channel + 0x4004) = reg; + + // OTHP + addr = 0x400 * channel + 0x400c; + reg = 0; + reg |= ctrl->tXPDLL; + reg |= (ctrl->tXP << 5); + reg |= (ctrl->tAONPD << 8); + reg |= 0xa0000; + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + + MCHBAR32(0x400 * channel + 0x4014) = 0; + + MCHBAR32(addr) |= 0x00020000; + + // ODT stretch + reg = 0; + + cpures = cpuid(0); + cpu = cpures.eax; + if (IS_IVY_CPU(cpu) + || (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_D2(cpu))) { + stretch = 2; + addr = 0x400 * channel + 0x400c; + printk(BIOS_DEBUG, "[%x] = %x\n", + 0x400 * channel + 0x400c, reg); + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0xc0000) | (stretch << 18); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0x30000) | (stretch << 16); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + + } else if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) { + stretch = 3; + addr = 0x400 * channel + 0x401c; + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0x3000) | (stretch << 12); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0xc00) | (stretch << 10); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + } else { + stretch = 0; + } + + // REFI + reg = 0; + val32 = ctrl->tREFI; + reg = (reg & ~0xffff) | val32; + val32 = ctrl->tRFC; + reg = (reg & ~0x1ff0000) | (val32 << 16); + val32 = (u32) (ctrl->tREFI * 9) / 1024; + reg = (reg & ~0xfe000000) | (val32 << 25); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4298, + reg); + MCHBAR32(0x400 * channel + 0x4298) = reg; + + MCHBAR32(0x400 * channel + 0x4294) |= 0xff; + + // SRFTP + reg = 0; + val32 = tDLLK; + reg = (reg & ~0xfff) | val32; + val32 = ctrl->tXSOffset; + reg = (reg & ~0xf000) | (val32 << 12); + val32 = tDLLK - ctrl->tXSOffset; + reg = (reg & ~0x3ff0000) | (val32 << 16); + val32 = ctrl->tMOD - 8; + reg = (reg & ~0xf0000000) | (val32 << 28); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x42a4, + reg); + MCHBAR32(0x400 * channel + 0x42a4) = reg; + } +} + +static void dram_dimm_mapping(dimm_info * info, ramctr_timing * ctrl) +{ + int t; + u32 reg, val32; + int channel; + + FOR_ALL_CHANNELS { + dimm_attr *dimmA = 0; + dimm_attr *dimmB = 0; + reg = 0; + val32 = 0; + if (info->dimm[channel][0].size_mb >= + info->dimm[channel][1].size_mb) { + // dimm 0 is bigger, set it to dimmA + dimmA = &info->dimm[channel][0]; + dimmB = &info->dimm[channel][1]; + reg |= (0 << 16); + } else { + // dimm 1 is bigger, set it to dimmA + dimmA = &info->dimm[channel][1]; + dimmB = &info->dimm[channel][0]; + reg |= (1 << 16); + // swap dimm info + t = ctrl->rank_mirror[channel][1]; + ctrl->rank_mirror[channel][1] = + ctrl->rank_mirror[channel][3]; + ctrl->rank_mirror[channel][3] = t; + } + // dimmA + if (dimmA && (dimmA->ranks > 0)) { + val32 = dimmA->size_mb / 256; + reg = (reg & ~0xff) | val32; + val32 = dimmA->ranks - 1; + reg = (reg & ~0x20000) | (val32 << 17); + val32 = (dimmA->width / 8) - 1; + reg = (reg & ~0x80000) | (val32 << 19); + } + // dimmB + if (dimmB && (dimmB->ranks > 0)) { + val32 = dimmB->size_mb / 256; + reg = (reg & ~0xff00) | (val32 << 8); + val32 = dimmB->ranks - 1; + reg = (reg & ~0x40000) | (val32 << 18); + val32 = (dimmB->width / 8) - 1; + reg = (reg & ~0x100000) | (val32 << 20); + } + reg = (reg & ~0x200000) | (1 << 21); // rank interleave + reg = (reg & ~0x400000) | (1 << 22); // enhanced interleave + + // Set MAD-DIMM register + if ((dimmA && (dimmA->ranks > 0)) + || (dimmB && (dimmB->ranks > 0))) { + MCHBAR32(0x5004 + channel * 4) = reg; + } + } +} + +static void dram_zones(dimm_info * info, ramctr_timing * ctrl, int training) +{ + u32 reg, ch0size, ch1size; + u8 val; + reg = 0; + val = 0; + if (training) { + ch0size = + info->dimm[0][0].size_mb + info->dimm[0][1].size_mb ? 256 : 0; + ch1size = + info->dimm[1][0].size_mb + info->dimm[1][1].size_mb ? 256 : 0; + } else { + ch0size = info->dimm[0][0].size_mb + info->dimm[0][1].size_mb; + ch1size = info->dimm[1][0].size_mb + info->dimm[1][1].size_mb; + } + + if (ch0size >= ch1size) { + reg = MCHBAR32(0x5014); + val = ch1size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x24; + } else { + reg = MCHBAR32(0x5014); + val = ch0size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x21; + } +} + +/* FIXME: this function bugs. */ +static void dram_memorymap(dimm_info * info, int me_uma_size) +{ + u32 reg, val, reclaim; + u32 tom, gfxstolen, gttsize; + size_t tsegsize, mmiosize, toludbase, touudbase, gfxstolenbase, gttbase, + tsegbase, mestolenbase; + size_t tsegbasedelta, remapbase, remaplimit; + uint16_t ggc; + + mmiosize = 0x400; + + ggc = pci_read_config16(NORTHBRIDGE, GGC); + if (!(ggc & 2)) { + gfxstolen = ((ggc >> 3) & 0x1f) * 32; + gttsize = ((ggc >> 8) & 0x3); + } else { + gfxstolen = 0; + gttsize = 0; + } + + tsegsize = CONFIG_SMM_TSEG_SIZE >> 20; + + tom = + info->dimm[0][0].size_mb + info->dimm[0][1].size_mb + + info->dimm[0][1].size_mb + info->dimm[1][1].size_mb; + + mestolenbase = tom - me_uma_size; + + toludbase = MIN(4096 - mmiosize, tom - me_uma_size); + gfxstolenbase = toludbase - gfxstolen; + gttbase = gfxstolenbase - gttsize; + + tsegbase = gttbase - tsegsize; + + // Round tsegbase down to nearest address aligned to tsegsize + tsegbasedelta = tsegbase & (tsegsize - 1); + tsegbase &= ~(tsegsize - 1); + + gttbase -= tsegbasedelta; + gfxstolenbase -= tsegbasedelta; + toludbase -= tsegbasedelta; + + // Test if it is possible to reclaim a hole in the ram addressing + if (tom - me_uma_size > toludbase) { + // Reclaim is possible + reclaim = 1; + remapbase = MAX(4096, tom - me_uma_size); + remaplimit = + remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1; + touudbase = remaplimit + 1; + } else { + // Reclaim not possible + reclaim = 0; + touudbase = tom - me_uma_size; + } + + // Update memory map in pci-e configuration space + + // TOM (top of memory) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa0); + val = tom & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa0, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa4); + val = tom & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa4, reg); + + // TOLUD (top of low used dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xbc); + val = toludbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xbc, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xbc, reg); + + // TOUUD LSB (top of upper usable dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa8); + val = touudbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa8, reg); + + // TOUUD MSB + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xac); + val = touudbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xac, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xac, reg); + + if (reclaim) { + // REMAP BASE + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x94); + val = remapbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x94, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x94, reg); + + // REMAP LIMIT + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x98); + val = remaplimit & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x98, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x98, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x9c); + val = remaplimit & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x9c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x9c, reg); + } + // TSEG + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb8); + val = tsegbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb8, reg); + + // GFX stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb0); + val = gfxstolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb0, reg); + + // GTT stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb4); + val = gttbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb4, reg); + + if (me_uma_size) { + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x7c); + val = (0x80000 - me_uma_size) & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x7c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x7c, reg); + + // ME base + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x70); + val = mestolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x70, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x70, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x74); + val = mestolenbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x74, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x74, reg); + + // ME mask + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x78); + val = (0x80000 - me_uma_size) & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + reg = (reg & ~0x400) | (1 << 10); // set lockbit on ME mem + + reg = (reg & ~0x800) | (1 << 11); // set ME memory enable + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x78, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x78, reg); + } +} + +static void dram_ioregs(ramctr_timing * ctrl) +{ + u32 reg, comp2; + + int channel; + + // IO clock + FOR_ALL_CHANNELS { + MCHBAR32(0xc00 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO command + FOR_ALL_CHANNELS { + MCHBAR32(0x3200 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO control + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + + // Rcomp + printk(BIOS_DEBUG, "RCOMP..."); + reg = 0; + while (reg == 0) { + reg = MCHBAR32(0x5084) & 0x10000; + } + printk(BIOS_DEBUG, "done\n"); + + // Set comp2 + comp2 = get_COMP2(ctrl->tCK); + MCHBAR32(0x3714) = comp2; + printk(BIOS_DEBUG, "COMP2 done\n"); + + // Set comp1 + FOR_ALL_POPULATED_CHANNELS { + reg = MCHBAR32(0x1810 + channel * 0x100); //ch0 + reg = (reg & ~0xe00) | (1 << 9); //odt + reg = (reg & ~0xe00000) | (1 << 21); //clk drive up + reg = (reg & ~0x38000000) | (1 << 27); //ctl drive up + MCHBAR32(0x1810 + channel * 0x100) = reg; + } + printk(BIOS_DEBUG, "COMP1 done\n"); + + printk(BIOS_DEBUG, "FORCE RCOMP and wait 20us..."); + MCHBAR32(0x5f08) |= 0x100; + udelay(20); + printk(BIOS_DEBUG, "done\n"); +} + +static void wait_428c(int channel) +{ + while (1) { + if (read32(DEFAULT_MCHBAR | 0x428c | (channel << 10)) & 0x50) + return; + } +} + +static void write_reset(ramctr_timing * ctrl) +{ + int channel, slotrank; + + /* choose a populated channel. */ + channel = (ctrl->rankmap[0]) ? 0 : 1; + + wait_428c(channel); + + /* choose a populated rank. */ + slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x80c01); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x400001); + wait_428c(channel); +} + +static void dram_jedecreset(ramctr_timing * ctrl) +{ + u32 reg, addr; + int channel; + + while (!(MCHBAR32(0x5084) & 0x10000)) ; + do { + reg = MCHBAR32(0x428c); + } while ((reg & 0x14) == 0); + + // Set state of memory controller + reg = 0x112; + MCHBAR32(0x5030) = reg; + MCHBAR32(0x4ea0) = 0; + reg |= 2; //ddr reset + MCHBAR32(0x5030) = reg; + + // Assert dimm reset signal + reg = MCHBAR32(0x5030); + reg &= ~0x2; + MCHBAR32(0x5030) = reg; + + // Wait 200us + udelay(200); + + // Deassert dimm reset signal + MCHBAR32(0x5030) |= 2; + + // Wait 500us + udelay(500); + + // Enable DCLK + MCHBAR32(0x5030) |= 4; + + // XXX Wait 20ns + udelay(1); + + FOR_ALL_CHANNELS { + // Set valid rank CKE + reg = 0; + reg = (reg & ~0xf) | ctrl->rankmap[channel]; + addr = 0x400 * channel + 0x42a0; + MCHBAR32(addr) = reg; + + // Wait 10ns for ranks to settle + //udelay(0.01); + + reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4); + MCHBAR32(addr) = reg; + + // Write reset using a NOP + write_reset(ctrl); + } +} + +static odtmap get_ODT(ramctr_timing * ctrl, u8 rank) +{ + /* Get ODT based on rankmap: */ + int dimms_per_ch = 0; + int channel; + + FOR_ALL_CHANNELS { + dimms_per_ch = max ((ctrl->rankmap[channel] & 1) + + ((ctrl->rankmap[channel] >> 2) & 1), + dimms_per_ch); + } + + if (dimms_per_ch == 1) { + return (const odtmap){60, 60}; + } else if (dimms_per_ch == 2) { + return (const odtmap){120, 30}; + } else { + printk(BIOS_DEBUG, + "Huh, no dimms? m0 = %d m1 = %d dpc = %d\n", + ctrl->rankmap[0], + ctrl->rankmap[1], dimms_per_ch); + die(""); + } +} + +static void write_mrreg(ramctr_timing * ctrl, int channel, int slotrank, + int reg, u32 val) +{ + wait_428c(channel); + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + if (ctrl->rank_mirror[channel][slotrank]) { + reg = ((reg >> 1) & 1) | ((reg << 1) & 2); + val = (val & ~0x1f8) | ((val >> 1) & 0xa8) + | ((val & 0xa8) << 1); + } + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); +} + +static u32 make_mr0(ramctr_timing * ctrl, u8 rank) +{ + u16 mr0reg, mch_cas, mch_wr; + static const u8 mch_wr_t[12] = { 1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0 }; + mr0reg = 0x100; + + // Convert CAS to MCH register friendly + if (ctrl->CAS < 12) { + mch_cas = (u16) ((ctrl->CAS - 4) << 1); + } else { + mch_cas = (u16) (ctrl->CAS - 12); + mch_cas = ((mch_cas << 1) | 0x1); + } + + // Convert tWR to MCH register friendly + mch_wr = mch_wr_t[ctrl->tWR - 5]; + + mr0reg = (mr0reg & ~0x4) | (mch_cas & 0x1); + mr0reg = (mr0reg & ~0x70) | ((mch_cas & 0xe) << 3); + mr0reg = (mr0reg & ~0xe00) | (mch_wr << 9); + // Fast (desktop) 0x1 or slow (mobile) 0x0 + mr0reg = (mr0reg & ~0x1000) | (!ctrl->mobile << 12); + return mr0reg; +} + +static void dram_mr0(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS write_mrreg(ctrl, channel, rank, 0, + make_mr0(ctrl, rank)); +} + +static u32 encode_odt(u32 odt) +{ + switch (odt) { + case 30: + return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4 + case 60: + return (1 << 2); // RZQ/4 + case 120: + return (1 << 6); // RZQ/2 + default: + case 0: + return 0; + } +} + +static u32 make_mr1(ramctr_timing * ctrl, u8 rank) +{ + odtmap odt; + u32 mr1reg; + + odt = get_ODT(ctrl, rank); + mr1reg = 0x2; + + mr1reg |= encode_odt(odt.rttnom); + + return mr1reg; +} + +static void dram_mr1(ramctr_timing * ctrl, u8 rank) +{ + u16 mr1reg; + int channel; + + mr1reg = make_mr1(ctrl, rank); + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 1, mr1reg); + } +} + +static void dram_mr2(ramctr_timing * ctrl, u8 rank) +{ + u16 pasr, cwl, mr2reg; + odtmap odt; + int channel; + int srt; + + pasr = 0; + cwl = ctrl->CWL - 5; + odt = get_ODT(ctrl, rank); + + srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh; + + mr2reg = 0; + mr2reg = (mr2reg & ~0x7) | pasr; + mr2reg = (mr2reg & ~0x38) | (cwl << 3); + mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6); + mr2reg = (mr2reg & ~0x80) | (srt << 7); + mr2reg |= (odt.rttwr / 60) << 9; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 2, mr2reg); + } +} + +static void dram_mr3(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 3, 0); + } +} + +static void dram_mrscommands(ramctr_timing * ctrl) +{ + u8 rank; + u32 reg, addr; + int channel; + + for (rank = 0; rank < 4; rank++) { + // MR2 + printk(BIOS_SPEW, "MR2 rank %d...", rank); + dram_mr2(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR3 + printk(BIOS_SPEW, "MR3 rank %d...", rank); + dram_mr3(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR1 + printk(BIOS_SPEW, "MR1 rank %d...", rank); + dram_mr1(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR0 + printk(BIOS_SPEW, "MR0 rank %d...", rank); + dram_mr0(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + } + + write32(DEFAULT_MCHBAR + 0x4e20, 0x7); + write32(DEFAULT_MCHBAR + 0x4e30, 0xf1001); + write32(DEFAULT_MCHBAR + 0x4e00, 0x60002); + write32(DEFAULT_MCHBAR + 0x4e10, 0); + write32(DEFAULT_MCHBAR + 0x4e24, 0x1f003); + write32(DEFAULT_MCHBAR + 0x4e34, 0x1901001); + write32(DEFAULT_MCHBAR + 0x4e04, 0x60400); + write32(DEFAULT_MCHBAR + 0x4e14, 0x288); + write32(DEFAULT_MCHBAR + 0x4e84, 0x40004); + + // Drain + FOR_ALL_CHANNELS { + // Wait for ref drained + wait_428c(channel); + } + + // Refresh enable + MCHBAR32(0x5030) |= 8; + + FOR_ALL_POPULATED_CHANNELS { + addr = 0x400 * channel + 0x4020; + reg = MCHBAR32(addr); + reg &= ~0x200000; + MCHBAR32(addr) = reg; + + wait_428c(channel); + + rank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + // Drain + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (rank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x1); + + // Drain + wait_428c(channel); + } +} + +const u32 lane_registers[] = { + 0x0000, 0x0200, 0x0400, 0x0600, + 0x1000, 0x1200, 0x1400, 0x1600, + 0x0800 +}; + +static int clamp(int val, int low, int up) +{ + if (val < low) + return low; + if (val > up) + return up; + return val; +} + +static void program_timings(ramctr_timing * ctrl, int channel) +{ + u32 reg32, reg_4024, reg_c14, reg_c18, reg_4028; + int lane; + int slotrank, slot; + int full_shift = 0; + u16 slot320c[NUM_SLOTS]; + + FOR_ALL_POPULATED_RANKS if (full_shift < + -ctrl->timings[channel][slotrank].val_320c) + full_shift = -ctrl->timings[channel][slotrank].val_320c; + + for (slot = 0; slot < NUM_SLOTS; slot++) + switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) { + case 0: + default: + slot320c[slot] = 0x7f; + break; + case 1: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 0].val_320c + + full_shift; + break; + case 2: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 1].val_320c + + full_shift; + break; + case 3: + slot320c[slot] = + (ctrl->timings[channel][2 * slot].val_320c + + ctrl->timings[channel][2 * slot + + 1].val_320c) / 2 + + full_shift; + break; + } + + reg32 = (1 << 17) | (1 << 14); + reg32 |= ((slot320c[0] & 0x3f) << 6) | ((slot320c[0] & 0x40) << 9); + reg32 |= (slot320c[1] & 0x7f) << 18; + reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6); + + MCHBAR32(0x320c + 0x100 * channel) = reg32; + + reg_c14 = ctrl->rankmap[channel] << 24; + reg_c18 = 0; + + FOR_ALL_POPULATED_RANKS { + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + int offset_val_c14; + if (shift < 0) + shift = 0; + offset_val_c14 = ctrl->reg_c14_offset + shift; + reg_c14 |= (offset_val_c14 & 0x3f) << (6 * slotrank); + reg_c18 |= ((offset_val_c14 >> 6) & 1) << slotrank; + } + + MCHBAR32(0xc14 + channel * 0x100) = reg_c14; + MCHBAR32(0xc18 + channel * 0x100) = reg_c18; + + reg_4028 = MCHBAR32(0x4028 + 0x400 * channel); + reg_4028 &= 0xffff0000; + + reg_4024 = 0; + + FOR_ALL_POPULATED_RANKS { + int post_timA_min_high = 7, post_timA_max_high = 0; + int pre_timA_min_high = 7, pre_timA_max_high = 0; + int shift_402x = 0; + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + + if (shift < 0) + shift = 0; + + FOR_ALL_LANES { + if (post_timA_min_high > + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_min_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_min_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + if (post_timA_max_high < + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_max_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_max_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + } + + if (pre_timA_max_high - pre_timA_min_high < + post_timA_max_high - post_timA_min_high) + shift_402x = +1; + else if (pre_timA_max_high - pre_timA_min_high > + post_timA_max_high - post_timA_min_high) + shift_402x = -1; + + reg_4028 |= + (ctrl->timings[channel][slotrank].val_4028 + shift_402x - + post_timA_min_high) << (4 * slotrank); + reg_4024 |= + (ctrl->timings[channel][slotrank].val_4024 + + shift_402x) << (8 * slotrank); + + FOR_ALL_LANES { + MCHBAR32(lane_registers[lane] + 0x10 + 0x100 * channel + + 4 * slotrank) + = + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) & 0x3f) + | + ((ctrl->timings[channel][slotrank].lanes[lane]. + rising + shift) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift - + (post_timA_min_high << 6)) & 0x1c0) << 10) + | (ctrl->timings[channel][slotrank].lanes[lane]. + falling << 20)); + + MCHBAR32(lane_registers[lane] + 0x20 + 0x100 * channel + + 4 * slotrank) + = + ((clamp + (ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift, 0, 127) & 0x3f) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x3f) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x1c0) << 9) + | + ((clamp + (ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift, 0, 127) & 0x40) << 13)); + } + } + MCHBAR32(0x4024 + 0x400 * channel) = reg_4024; + MCHBAR32(0x4028 + 0x400 * channel) = reg_4028; +} + +static void test_timA(ramctr_timing * ctrl, int channel, int slotrank) +{ + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x4040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x100f | ((ctrl->CAS + 36) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); +} + +static int does_lane_work(ramctr_timing * ctrl, int channel, int slotrank, + int lane) +{ + u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA; + return ((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + channel * 0x100 + 4 + + ((timA / 32) & 1) * 4) + >> (timA % 32)) & 1); +} + +struct run { + int middle; + int end; + int start; + int all; +}; + +static struct run get_longest_zero_run(int *seq, int sz) +{ + int i, ls; + int bl = 0, bs = 0; + struct run ret; + + ls = 0; + for (i = 0; i < 2 * sz; i++) + if (seq[i % sz]) { + if (i - ls > bl) { + bl = i - ls; + bs = ls; + } + ls = i + 1; + } + if (bl == 0) { + ret.middle = sz / 2; + ret.start = 0; + ret.end = sz; + ret.all = 1; + return ret; + } + + ret.start = bs % sz; + ret.end = (bs + bl - 1) % sz; + ret.middle = (bs + (bl - 1) / 2) % sz; + ret.all = 0; + + return ret; +} + +static void discover_timA_coarse(ramctr_timing * ctrl, int channel, + int slotrank, int *upperA) +{ + int timA; + int statistics[NUM_LANES][128]; + int lane; + + for (timA = 0; timA < 128; timA++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = timA; + program_timings(ctrl, channel); + + test_timA(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timA] = + !does_lane_work(ctrl, channel, slotrank, lane); + printk(BIOS_SPEW, "Astat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timA, + statistics[lane][timA]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle; + upperA[lane] = rn.end; + if (upperA[lane] < rn.middle) + upperA[lane] += 128; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + printk(BIOS_SPEW, "Aend: %d, %d, %d, %x\n", channel, slotrank, + lane, upperA[lane]); + } +} + +static void discover_timA_fine(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int timA_delta; + int statistics[NUM_LANES][51]; + int lane, i; + + memset(statistics, 0, sizeof(statistics)); + + for (timA_delta = -25; timA_delta <= 25; timA_delta++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = upperA[lane] + timA_delta + 0x40; + program_timings(ctrl, channel); + + for (i = 0; i < 100; i++) { + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + statistics[lane][timA_delta + 25] += + does_lane_work(ctrl, channel, slotrank, + lane); + } + } + FOR_ALL_LANES { + printk(BIOS_SPEW, "A+stat: %d, %d, %d, %d (%x), %x\n", + channel, slotrank, lane, timA_delta, + upperA[lane] + timA_delta + 0x40, + statistics[lane][timA_delta + 25]); + } + } + FOR_ALL_LANES { + int last_zero, first_all; + + for (last_zero = -25; last_zero <= 25; last_zero++) + if (statistics[lane][last_zero + 25]) + break; + last_zero--; + for (first_all = -25; first_all <= 25; first_all++) + if (statistics[lane][first_all + 25] == 100) + break; + + printk(BIOS_SPEW, "lane %d: %d, %d\n", lane, last_zero, + first_all); + + ctrl->timings[channel][slotrank].lanes[lane].timA = + (last_zero + first_all) / 2 + upperA[lane]; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + } +} + +static void discover_402x(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int works[NUM_LANES]; + int lane; + while (1) { + int all_works = 1, some_works = 0; + program_timings(ctrl, channel); + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + works[lane] = + !does_lane_work(ctrl, channel, slotrank, lane); + if (works[lane]) + some_works = 1; + else + all_works = 0; + } + if (all_works) + return; + if (!some_works) { + if (ctrl->timings[channel][slotrank].val_4024 < 2) + die("402x discovery failed"); + ctrl->timings[channel][slotrank].val_4024 -= 2; + printk(BIOS_SPEW, "4024 -= 2;\n"); + continue; + } + ctrl->timings[channel][slotrank].val_4028 += 2; + printk(BIOS_SPEW, "4028 += 2;\n"); + if (ctrl->timings[channel][slotrank].val_4028 >= 0x10) + die("402x discovery failed"); + FOR_ALL_LANES if (works[lane]) { + ctrl->timings[channel][slotrank].lanes[lane].timA += + 128; + upperA[lane] += 128; + printk(BIOS_SPEW, "increment %d, %d, %d\n", channel, + slotrank, lane); + } + } +} + +struct timA_minmax { + int timA_min_high, timA_max_high; +}; + +static void pre_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + int lane; + mnmx->timA_min_high = 7; + mnmx->timA_max_high = 0; + + FOR_ALL_LANES { + if (mnmx->timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_min_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + if (mnmx->timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_max_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + } +} + +static void post_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + struct timA_minmax post; + int shift_402x = 0; + + /* Get changed maxima. */ + pre_timA_change(ctrl, channel, slotrank, &post); + + if (mnmx->timA_max_high - mnmx->timA_min_high < + post.timA_max_high - post.timA_min_high) + shift_402x = +1; + else if (mnmx->timA_max_high - mnmx->timA_min_high > + post.timA_max_high - post.timA_min_high) + shift_402x = -1; + else + shift_402x = 0; + + ctrl->timings[channel][slotrank].val_4028 += shift_402x; + ctrl->timings[channel][slotrank].val_4024 += shift_402x; + printk(BIOS_SPEW, "4024 += %d;\n", shift_402x); + printk(BIOS_SPEW, "4028 += %d;\n", shift_402x); +} + +static void read_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + u32 r32; + int all_high, some_high; + int upperA[NUM_LANES]; + struct timA_minmax mnmx; + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + write32(DEFAULT_MCHBAR + 0x3400, (slotrank << 2) | 0x8001); + + ctrl->timings[channel][slotrank].val_4028 = 4; + ctrl->timings[channel][slotrank].val_4024 = 55; + program_timings(ctrl, channel); + + discover_timA_coarse(ctrl, channel, slotrank, upperA); + + all_high = 1; + some_high = 0; + FOR_ALL_LANES if (ctrl->timings[channel][slotrank].lanes[lane]. + timA >= 0x40) + some_high = 1; + else + all_high = 0; + if (all_high) { + ctrl->timings[channel][slotrank].val_4028--; + printk(BIOS_SPEW, "4028--;\n"); + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + timA -= 0x40; + upperA[lane] -= 0x40; + + } + } else if (some_high) { + ctrl->timings[channel][slotrank].val_4024++; + ctrl->timings[channel][slotrank].val_4028++; + printk(BIOS_SPEW, "4024++;\n"); + printk(BIOS_SPEW, "4028++;\n"); + } + + program_timings(ctrl, channel); + + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_402x(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_timA_fine(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA -= mnmx.timA_min_high * 0x40; + ctrl->timings[channel][slotrank].val_4028 -= mnmx.timA_min_high; + printk(BIOS_SPEW, "4028 -= %d;\n", mnmx.timA_min_high); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + + printk(BIOS_SPEW, "4/8: %d, %d, %x, %x\n", channel, slotrank, + ctrl->timings[channel][slotrank].val_4024, + ctrl->timings[channel][slotrank].val_4028); + + FOR_ALL_LANES + printk(BIOS_SPEW, "%d, %d, %d, %x\n", channel, slotrank, + lane, + ctrl->timings[channel][slotrank].lanes[lane].timA); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + } + + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + 4 * lane, 0); +} + +static void test_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int lane; + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x4140 + 0x400 * channel + 4 * lane); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) + | 4 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (6 << 16)); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8041001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x80411f4); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) + | 8 | (ctrl->CAS << 16)); + + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x40011f4 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timC; + int statistics[NUM_LANES][MAX_TIMC + 1]; + int lane; + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + for (timC = 0; timC <= MAX_TIMC; timC++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timC = timC; + program_timings(ctrl, channel); + + test_timC(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timC] = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + printk(BIOS_SPEW, "Cstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timC, + statistics[lane][timC]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_TIMC + 1); + ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle; + if (rn.all) + die("timC discovery failed"); + printk(BIOS_SPEW, "Cval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timC); + } +} + +static int get_precedening_channels(ramctr_timing * ctrl, int target_channel) +{ + int channel, ret = 0; + FOR_ALL_POPULATED_CHANNELS if (channel < target_channel) + ret++; + return ret; +} + +static void fill_pattern0(ramctr_timing * ctrl, int channel, u32 a, u32 b) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + printk(BIOS_SPEW, "channel_offset=%x\n", channel_offset); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + 4 * j, j & 2 ? b : a); + sfence(); +} + +static int num_of_channels(const ramctr_timing * ctrl) +{ + int ret = 0; + int channel; + FOR_ALL_POPULATED_CHANNELS ret++; + return ret; +} + +static void fill_pattern1(ramctr_timing * ctrl, int channel) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + j * 4, 0xffffffff); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + channel_step + j * 4, 0); + sfence(); +} + +static void precharge(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + } +} + +static void test_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + write_mrreg(ctrl, channel, slotrank, 1, + 0x80 | make_mr1(ctrl, slotrank)); + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->delay2) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + 8 | (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f107); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4000c01 | ((ctrl->CAS + 38) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 4); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x400 * channel + 0x4284, 0x40001); + wait_428c(channel); + + write_mrreg(ctrl, channel, slotrank, 1, + 0x1080 | make_mr1(ctrl, slotrank)); +} + +static void discover_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timB; + int statistics[NUM_LANES][128]; + int lane; + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052 | (slotrank << 2)); + + for (timB = 0; timB < 128; timB++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timB = timB; + program_timings(ctrl, channel); + + test_timB(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timB] = + !((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + + channel * 0x100 + 4 + ((timB / 32) & 1) * 4) + >> (timB % 32)) & 1); + printk(BIOS_SPEW, "Bstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timB, + statistics[lane][timB]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timB = rn.end; + if (rn.all) + die("timB discovery failed"); + printk(BIOS_SPEW, "Bval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timB); + } +} + +static int get_timB_high_adjust(u64 val) +{ + int i; + if (val >= 0xfffffffffff00000LL) + return -1; + if (val >= 0xfffffff000000000LL) + return -2; + if (val >= 0xfff0000000000000LL) + return -3; + + for (i = 0; i < 8; i++) + if (val >> (8 * (7 - i) + 4)) + return i; + return 8; +} + +static void adjust_high_timB(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + write32(DEFAULT_MCHBAR + 0x3400, 0x200); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern1(ctrl, channel); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 1); + } + FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS { + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x10001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRCD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x8041003); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x3e2); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | ((ctrl->tRP) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0xc01 | ((ctrl->tRCD) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x3f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4000c01 | + ((ctrl->tRP + + ctrl->timings[channel][slotrank].val_4024 + + ctrl->timings[channel][slotrank].val_4028) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60008); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); + wait_428c(channel); + FOR_ALL_LANES { + u64 res = + read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 4); + res |= + ((u64) read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 8)) << 32; + ctrl->timings[channel][slotrank].lanes[lane].timB += + get_timB_high_adjust(res) * 64; + printk(BIOS_SPEW, "Bval+: %d, %d, %d, %x\n", channel, + slotrank, lane, + ctrl->timings[channel][slotrank].lanes[lane]. + timB); + } + } + write32(DEFAULT_MCHBAR + 0x3400, 0); +} + +static void write_op(ramctr_timing * ctrl, int channel) +{ + int slotrank; + + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); +} + +static void write_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS + write32(DEFAULT_MCHBAR + 0x4008 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4008 + + 0x400 * channel) | 0x8000000); + + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + } + + FOR_ALL_CHANNELS + FOR_ALL_POPULATED_RANKS write_mrreg(ctrl, channel, slotrank, 1, + make_mr1(ctrl, + slotrank) | 0x1080); + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS discover_timB(ctrl, channel, + slotrank); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS write_mrreg(ctrl, channel, + slotrank, 1, + make_mr1(ctrl, + slotrank)); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + FOR_ALL_POPULATED_CHANNELS wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) | 8); + + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + ~0x00200000 & read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel)); + read32(DEFAULT_MCHBAR + 0x428c + 0x400 * channel); + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + printk(BIOS_SPEW, "CPE\n"); + precharge(ctrl); + printk(BIOS_SPEW, "CPF\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + discover_timC(ctrl, channel, slotrank); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + adjust_high_timB(ctrl); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static int test_320c(ramctr_timing * ctrl, int channel, int slotrank) +{ + struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank]; + int timC_delta; + int lanes_ok = 0; + int ctr = 0; + int lane; + + for (timC_delta = -5; timC_delta <= 5; timC_delta++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].timC = + saved_rt.lanes[lane].timC + timC_delta; + } + program_timings(ctrl, channel); + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 4 * lane + 0x4f40, 0); + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + ((max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10) + | 8 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | ctr | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4244 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4001020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4248 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, 0xf1001); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + u32 r32 = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + + if (r32 == 0) + lanes_ok |= 1 << lane; + } + ctr++; + if (lanes_ok == ((1 << NUM_LANES) - 1)) + break; + } + + ctrl->timings[channel][slotrank] = saved_rt; + return lanes_ok != ((1 << NUM_LANES) - 1); +} + +const u32 pattern[][16] = { + {0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff}, + {0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000}, + {0xe62d6424, 0x9277e09e, 0x8f43dc3f, 0x76eae589, + 0x0010fdc6, 0xdc55e01c, 0x5effb0ab, 0x6cba5d29, + 0xa43d1e64, 0xab5c2e0f, 0x7796ed16, 0x96023bf4, + 0xa74c831d, 0x90f138c0, 0x17830a8a, 0x5ac17c47}, + {0x359ebbeb, 0x2b9b4512, 0xef584d98, 0x106bf7cb, + 0x363525ad, 0xb3a4dfdc, 0xa6b9fcd8, 0xd21689ec, + 0x84a3695b, 0xbd9c2e27, 0xdb3d0f44, 0x988158f1, + 0xcca91d3f, 0xb62a6d12, 0xe905e4cf, 0x7f1fa626}, + {0xe58efeae, 0xcd006081, 0xa9119403, 0xbcfbd35f, + 0x213b3bf7, 0x7bfcb773, 0xc85143f9, 0x0bdbff50, + 0xa3053c90, 0x51d66cb7, 0x296f4387, 0xb715f99e, + 0xfaddc989, 0xbb1de8a7, 0x39206b4d, 0x80174a57}, + {0xa1622ac1, 0xb4f4a5f0, 0x16dc2bc3, 0x50fb0954, + 0x2e261721, 0x52b82c3c, 0x821902b8, 0x0d4b6c38, + 0x1f618631, 0x047956f3, 0xd4337f5a, 0x591f8002, + 0x27f28db2, 0xfae37369, 0xb3f27580, 0x3cdb6397}, + {0x3dee23be, 0x19f36408, 0x227f4a6a, 0x024603c5, + 0xd5e062db, 0x6d8d4c5c, 0x7ff693b0, 0x76641be9, + 0x9e74f41c, 0xe7bc7f33, 0x2636f2e9, 0x70279750, + 0xce2355aa, 0x32d230ef, 0x22f9b468, 0xadd4e7a2}, + {0x936c0fed, 0xba0612d5, 0xa97c1ea7, 0x10e29d67, + 0x1c4c5dc8, 0x83645621, 0xcd8b521c, 0xb8301817, + 0xac7d6571, 0xcc41d200, 0x4ebdefdd, 0xd2917bde, + 0x60f75acc, 0x7791534b, 0x26ea2a83, 0x6b74513a}, + {0xd1957b85, 0xc6f8f9ca, 0xf04fb4be, 0xfeb786fb, + 0xa1dea3aa, 0x67fe7db6, 0x25d49c87, 0xe3d54870, + 0x93dc1f86, 0x7d0c1a18, 0x9272e128, 0x68e1b876, + 0xce284c9e, 0x8fa18792, 0x5785a340, 0xb6fcf198}, + {0xff7d8e4a, 0x0c21ee43, 0xe820b388, 0xb4443c0e, + 0xa1e6e498, 0x5c426110, 0x1b434ef3, 0xbef05b91, + 0xa6907968, 0x53662ac3, 0x6defac32, 0x2c11c29c, + 0x6175cced, 0xb17dd3ad, 0x6e6a1076, 0x1372b1fa}, + {0x4408ed06, 0x49460ffd, 0xb49d26cb, 0x6a3662a5, + 0x5e857047, 0xa387cd4a, 0x04edc81e, 0xfd94d8d4, + 0x2fe48d91, 0x9d2356bc, 0x96131878, 0xaca3fce4, + 0xbb312c6c, 0x5023b090, 0x3614be70, 0xa14dfabb}, + {0xd4cc1e83, 0x757a1930, 0xc3d16a61, 0x9e0d6681, + 0x8a081fa9, 0xbd11c888, 0x1672f010, 0xa083f71c, + 0x1ec02eef, 0xc4586ca8, 0x6d322b35, 0x56054679, + 0x1552a0ff, 0x5cb7707e, 0xdfb55d4a, 0xcc76cc07}, + {0x507cf71f, 0x2166421a, 0x54be4af0, 0xfd42158c, + 0x417b1f7f, 0x9466860b, 0x3a0075bf, 0x2055575c, + 0xcedfe7ab, 0xbe85aa5f, 0x39d0c2e3, 0x851c19df, + 0x39a35a3f, 0x3fb10d7d, 0x20b14899, 0x703b7f08}, + {0x8a7d9dd1, 0x33235565, 0xbd3d2e57, 0xa48c2726, + 0x0d5e2e13, 0xae421ff9, 0x8784a224, 0xf66c1510, + 0x057627aa, 0x8fb0cb41, 0x4289975a, 0xb181adfa, + 0x59f2059a, 0xe86feb05, 0x84222fc1, 0x319b3ce9}, + {0xe1e243b8, 0x3b0bcc1a, 0x70396f00, 0x5caff44d, + 0xe96961b3, 0xad73f692, 0x8b841a2d, 0xf5838839, + 0xec9c9d04, 0xcc2b5562, 0xf8ca2549, 0xa9c52ff8, + 0x3b2fde68, 0x3d4dc7f0, 0xa57387d0, 0x051199ad}, + {0x5f0ce4fc, 0xd830fbb7, 0x90abeb8f, 0x96d9cdbb, + 0x58f80a80, 0x0baaca36, 0x81a23623, 0x77127614, + 0xaa8382cd, 0x0922fbca, 0xd84d37e1, 0x721297df, + 0x160f3b3a, 0x10a1ecdc, 0x151c92f4, 0xc1fdcdab}, + {0x261c45cc, 0xfeddd2da, 0xfc3cb1c1, 0x6639641f, + 0x2c011892, 0x7108bee2, 0x8545e0b9, 0x7dd36dab, + 0x07d91950, 0x1520adcb, 0xf84aa939, 0x07d9bb2d, + 0xdf1ed826, 0xaee3c814, 0x1dca1e81, 0xc8e9f486}, + {0x933d306a, 0xaab7103d, 0xa8be37be, 0x49612f3a, + 0xb0cf28e5, 0xf9648902, 0x106d7c11, 0xf32e1813, + 0x21af36ef, 0xe695e4c4, 0x7ee1831d, 0x2aeda467, + 0x99d0c655, 0x3f0691ab, 0xcd68f7c1, 0xb469a20e}, + {0x8557aef0, 0x3eb0e373, 0x0853ac31, 0xe5bded62, + 0x3eddb0dd, 0x6bbf1caf, 0x2119c3d9, 0xe1732350, + 0x55456c75, 0xf6119375, 0x498dd1ad, 0x13f80916, + 0xb97f9f5e, 0x921d9f4c, 0xabdee367, 0x1d6bb8bf}, + {0xd165a3be, 0xd8b41598, 0xa20e1809, 0xefd5c8ce, + 0x18935c80, 0xdf1911f9, 0xc9e449eb, 0xb887a4d7, + 0x4a324f6f, 0x533e8031, 0x1c21c074, 0xa95f1ea5, + 0x765b320a, 0x839d7dfb, 0xc7d3aa93, 0xe534ae3d}, + {0xbe8592c8, 0x068457e6, 0x89b94fa3, 0xd522ad02, + 0x7e7db0b7, 0x2c5b896f, 0x9f8ecb37, 0x05b983ff, + 0x3fe9b25f, 0x34a6215b, 0x0592ba34, 0xd564f85a, + 0x156c426d, 0x25ad5460, 0xe7b5e8b7, 0xa73285c6}, + {0x5ad8d838, 0x27b42d36, 0xcc806ad1, 0x157a058a, + 0x7297735a, 0xffd6df8d, 0xff96f7a2, 0x155b27ea, + 0x84708101, 0x979fd78b, 0x49797d0c, 0x0dc93e3c, + 0x20287332, 0xed759f88, 0xe5068529, 0xb83aa781}, + {0xc38b302c, 0x57b54075, 0xac810692, 0xb0d493e7, + 0x4adda486, 0x0665ce2e, 0xb2a9c003, 0xafacc4ce, + 0x4d5e906d, 0xb3d52fab, 0xe6962c6b, 0x850f4dd1, + 0x5021656c, 0x5df6c06b, 0x9255125b, 0x2363c478}, + {0x188b715c, 0xe8b884b0, 0x5e6d0b9a, 0x1f0051e1, + 0xd2d35d4c, 0xbfeaecbe, 0xc84bb0ad, 0x67a232d6, + 0x99001587, 0xbf4313e1, 0x74f64061, 0x2c1fc562, + 0xb6fe8ca6, 0x5226a239, 0xf5198574, 0x61b51dca}, + {0x51dcecd3, 0xbadbe596, 0xebe3e84a, 0x772bfdfc, + 0x03656ac5, 0xa7c36e91, 0x6cd32cf0, 0xc3f699dd, + 0x7d5aba01, 0x51e38e82, 0x23103a98, 0x20298b9d, + 0x19436510, 0x63ad7e6c, 0x8bc2b33f, 0x27079917}, + {0x8bd5be78, 0xf2403bfa, 0x780ebdb6, 0x94c53b64, + 0x6241c2e2, 0x5bfb081e, 0x6799e88f, 0xc997b7d1, + 0x466ac8b1, 0xbf5909da, 0x497ea39f, 0x402ffb48, + 0xd7470c2d, 0x8510aba9, 0x6c52a1c9, 0x812ca967}, + {0x031f7ab4, 0xd32fe890, 0x36ae6de5, 0x083dcde4, + 0x99a7f12f, 0xe44864a7, 0x02b75fff, 0xf25dda35, + 0x7679ff4f, 0xed421e01, 0xd9c2cfa1, 0xd36b4e82, + 0x5315d908, 0xc7ebcb2a, 0xb6f3e4c1, 0xf5bfbae9}, + {0x3f4a2a96, 0x64d8bd5a, 0x19acd70d, 0xf62fcdd9, + 0x5de99cdf, 0x32f3b7cb, 0x2c020578, 0x4e9bafb8, + 0x74919a08, 0xaba33e91, 0xa6bd2254, 0x2435a9b9, + 0x47e2a1b4, 0xe837a28e, 0xe113f1b0, 0x7654bd79}, + {0x05537a6c, 0x77be1a5c, 0x4c7492c9, 0x9086bfb0, + 0x257adc18, 0xf4787fc1, 0xe3fb6d53, 0x9525e589, + 0x445a65bc, 0x833f7d08, 0x69cf1f7e, 0x9a6372e1, + 0xceedb52e, 0x31032997, 0xd1c36828, 0x132772d6}, + {0x0a166972, 0x89beaf3b, 0x8d780fbc, 0x8aea5392, + 0x58347a41, 0x1e381ec2, 0xcc6280c8, 0xee0863e1, + 0x976e2dd2, 0x8c6ee6e2, 0xa0ca57cd, 0x95114a7d, + 0x3c096704, 0xa941769d, 0x2de20c05, 0x0bf8f812}, + {0x22779d6c, 0x94e12e8f, 0x5ce40299, 0xea1b55b0, + 0x9ebec05d, 0xe076cd2b, 0x8fef5648, 0x6a284c65, + 0xa790b705, 0xf0b19997, 0x0d8ca8af, 0x17440419, + 0xef4f702f, 0x33cbcbb1, 0x83d60f26, 0x48988397}, + {0x0fed7f53, 0xb5acbb67, 0xc031c73f, 0x5364d9ef, + 0xa6dbd12d, 0x82174a6c, 0xccf8e7ab, 0xc473c036, + 0xcff493d8, 0xad9afc3b, 0x316a24e8, 0x1842bea4, + 0x4cc0c82e, 0x28ccd91e, 0xd7311b5d, 0x50a89860}, +}; + +static void fill_pattern5(ramctr_timing * ctrl, int channel) +{ + unsigned i, j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (i = 0; i < sizeof(pattern) / sizeof(pattern[0]); i++) { + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + i * channel_step + + j * 4, pattern[i][j]); + } + sfence(); +} + +static void reprogram_320c(ramctr_timing * ctrl) +{ + int channel, slotrank; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + /* jedec reset */ + dram_jedecreset(ctrl); + /* mrs commands. */ + dram_mrscommands(ctrl); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); +} + +static void command_training(ramctr_timing * ctrl) +{ + int channel; + int slotrank; + u32 reg_4004_b30; + int delta = 0; + int c320c; + int stat[NUM_SLOTRANKS][256]; + + /* FIXME: vendor BIOS discovers this by trying 0 and 2. Apparently 2 should work for + all systems but 0 is slightly more efficient for the systems that can tolerate it. + */ + reg_4004_b30 = 2; + + FOR_ALL_POPULATED_CHANNELS + MCHBAR32(0x4004 + 0x400 * channel) = + ctrl->tRRD + | (ctrl->tRTP << 4) + | (ctrl->tCKE << 8) + | (ctrl->tWTR << 12) + | (ctrl->tFAW << 16) + | (ctrl->tWR << 24) + | (reg_4004_b30 << 30); + + if (reg_4004_b30 == 2) + delta = 2; + else if (reg_4004_b30 == 0) + delta = 4; + + FOR_ALL_CHANNELS { + FOR_ALL_POPULATED_RANKS ctrl->timings[channel][slotrank]. + val_4024 -= delta; + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + FOR_ALL_POPULATED_CHANNELS { + for (c320c = -127; c320c <= 127; c320c++) { + FOR_ALL_POPULATED_RANKS ctrl-> + timings[channel][slotrank].val_320c = c320c; + program_timings(ctrl, channel); + reprogram_320c(ctrl); + FOR_ALL_POPULATED_RANKS { + stat[slotrank][c320c + 127] = + test_320c(ctrl, channel, slotrank); + printk(BIOS_SPEW, "3stat: %d, %d, %d: %d\n", + channel, slotrank, c320c, + stat[slotrank][c320c + 127]); + } + } + FOR_ALL_POPULATED_RANKS { + struct run rn = + get_longest_zero_run(stat[slotrank], 255); + ctrl->timings[channel][slotrank].val_320c = + rn.middle - 127; + printk(BIOS_SPEW, "3val: %d, %d: %d\n", channel, + slotrank, + ctrl->timings[channel][slotrank].val_320c); + if (rn.all) + die("c320c discovery failed"); + } + } + + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + + reprogram_320c(ctrl); +} + +static void discover_edges_real(ramctr_timing * ctrl, int channel, int slotrank, + int *edges) +{ + int edge; + int statistics[NUM_LANES][MAX_EDGE_TIMING + 1]; + int lane; + + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].rising = + edge; + ctrl->timings[channel][slotrank].lanes[lane].falling = + edge; + } + printk(BIOS_SPEW, "edge %02x\n", edge); + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + 4 * lane + + 0x4140); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x40411f4); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + FOR_ALL_LANES { + statistics[lane][edge] = + read32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + lane * 4); + printk(BIOS_SPEW, "estat %d, %d, %d, %d %02x\n", + channel, slotrank, lane, edge, + statistics[lane][edge]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_EDGE_TIMING + 1); + edges[lane] = rn.middle; + if (rn.all) + die("edge discovery failed"); + printk(BIOS_SPEW, "eval %d, %d, %d, %02x\n", channel, slotrank, + lane, edges[lane]); + } +} + +static void discover_edges(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + u32 r32; + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 4 * lane + + 0x400 * channel + 0x4080, 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0, 0); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + FOR_ALL_LANES read32(DEFAULT_MCHBAR + 0x400 * channel + + lane * 4 + 0x4140); + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } + + program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + lane * 4, + ~read32(DEFAULT_MCHBAR + 0x4040 + + 0x400 * channel + lane * 4) & 0xff); + } + + fill_pattern0(ctrl, channel, 0, 0xffffffff); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void discover_edges_write_real(ramctr_timing * ctrl, int channel, + int slotrank, int *edges) +{ + int edge; + u32 raw_statistics[MAX_EDGE_TIMING + 1]; + int statistics[MAX_EDGE_TIMING + 1]; + const int reg3000b24[] = { 0, 0xc, 0x2c }; + int lane, i; + int lower[NUM_LANES]; + int upper[NUM_LANES]; + + FOR_ALL_LANES { + lower[lane] = 0; + upper[lane] = MAX_EDGE_TIMING; + } + + for (i = 0; i < 3; i++) { + /* FIXME: trace shows that vendor BIOS also tests with other patterns. + I'm not sure whether it's really needed. + */ + write32(DEFAULT_MCHBAR + 0x3000 + 0x100 * channel, + reg3000b24[i] << 24); + printk(BIOS_SPEW, "patterned\n"); + printk(BIOS_SPEW, "[%x] = 0x%08x\n(%d, %d)\n", + 0x3000 + 0x100 * channel, reg3000b24[i] << 24, channel, + slotrank); + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + rising = edge; + ctrl->timings[channel][slotrank].lanes[lane]. + falling = edge; + } + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + + 4 * lane + 0x4140); + } + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x4 | (ctrl->tRCD << 16) + | (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << + 10)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, + 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8005020 | ((ctrl->tWTR + ctrl->CWL + 8) << + 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4005020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + lane * 4); + } + + raw_statistics[edge] = + MCHBAR32(0x436c + 0x400 * channel); + } + FOR_ALL_LANES { + struct run rn; + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) + statistics[edge] = + ! !(raw_statistics[edge] & (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_EDGE_TIMING + 1); + printk(BIOS_SPEW, + "edges: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, rn.middle, + rn.end, rn.start + ctrl->edge_offset[i], + rn.end - ctrl->edge_offset[i]); + lower[lane] = + max(rn.start + ctrl->edge_offset[i], lower[lane]); + upper[lane] = + min(rn.end - ctrl->edge_offset[i], upper[lane]); + edges[lane] = (lower[lane] + upper[lane]) / 2; + + } + } + + write32(DEFAULT_MCHBAR + 0x3000, 0); + printk(BIOS_SPEW, "CPA\n"); +} + +static void discover_edges_write(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank) +{ + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) + << 10) | (ctrl->tRCD << 16) | 4); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x80011e0 | + ((ctrl->tWTR + ctrl->CWL + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + + 0x400 * channel, + 0x40011e0 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + + 0x400 * channel, + 0x1001 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC_write(ramctr_timing * ctrl) +{ + const u8 rege3c_b24[3] = { 0, 0xf, 0x2f }; + int i; + + int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + lower[channel][slotrank][lane] = 0; + upper[channel][slotrank][lane] = MAX_TIMC; + } + + write32(DEFAULT_MCHBAR + 0x4ea8, 1); + + for (i = 0; i < 3; i++) + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100), + (rege3c_b24[i] << 24) + | (read32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100)) + & ~0x3f000000)); + udelay(2); + FOR_ALL_POPULATED_RANKS { + int timC; + u32 raw_statistics[MAX_TIMC + 1]; + int statistics[MAX_TIMC + 1]; + + /* FIXME: trace shows that vendor BIOS also tests with other patterns. + I'm not sure whether it's really needed. + */ + fill_pattern5(ctrl, channel); + for (timC = 0; timC < MAX_TIMC + 1; timC++) { + FOR_ALL_LANES + ctrl->timings[channel][slotrank].lanes[lane].timC = timC; + program_timings(ctrl, channel); + + test_timC_write (ctrl, channel, slotrank); + + raw_statistics[timC] = + MCHBAR32(0x436c + 0x400 * channel); + printk(BIOS_SPEW, "Cstat %02x %02x\n", timC, + raw_statistics[timC]); + } + FOR_ALL_LANES { + struct run rn; + for (timC = 0; timC <= MAX_TIMC; timC++) + statistics[timC] = + !!(raw_statistics[timC] & + (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_TIMC + 1); + if (rn.all) + die("timC write discovery failed"); + printk(BIOS_SPEW, + "timC: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, + rn.middle, rn.end, + rn.start + ctrl->timC_offset[i], + rn.end - ctrl->timC_offset[i]); + lower[channel][slotrank][lane] = + max(rn.start + ctrl->timC_offset[i], + lower[channel][slotrank][lane]); + upper[channel][slotrank][lane] = + min(rn.end - ctrl->timC_offset[i], + upper[channel][slotrank][lane]); + + } + } + } + + write32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c, + 0 | (read32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c) & + ~0x3f000000)); + udelay(2); + + write32(DEFAULT_MCHBAR + 0x4ea8, 0); + + printk(BIOS_SPEW, "CPB\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + printk(BIOS_SPEW, "timC [%d, %d, %d] = 0x%x\n", channel, + slotrank, lane, + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2); + ctrl->timings[channel][slotrank].lanes[lane].timC = + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2; + } + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); +} + +static void normalize_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + int mat = 0; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + int delta; + FOR_ALL_LANES mat = + max(ctrl->timings[channel][slotrank].lanes[lane].timA, mat); + delta = (mat >> 6) - ctrl->timings[channel][slotrank].val_4028; + ctrl->timings[channel][slotrank].val_4024 += delta; + ctrl->timings[channel][slotrank].val_4028 += delta; + } FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); +} + +static void write_controller_mr(ramctr_timing * ctrl) +{ + int channel, slotrank; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + write32(DEFAULT_MCHBAR | 0x0004 | (channel << 8) | + lane_registers[slotrank], make_mr0(ctrl, slotrank)); + write32(DEFAULT_MCHBAR | 0x0008 | (channel << 8) | + lane_registers[slotrank], make_mr1(ctrl, slotrank)); + } +} + +static void channel_test(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS + if (read32(DEFAULT_MCHBAR | 0x42a0 | (channel << 10)) & 0xa000) + die("Mini channel test failed (1)\n"); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0x12345678, 0x98765432); + + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + for (slotrank = 0; slotrank < 4; slotrank++) + FOR_ALL_CHANNELS + if (ctrl->rankmap[channel] & (1 << slotrank)) { + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR | (0x4f40 + 4 * lane), 0); + write32(DEFAULT_MCHBAR | (0x4d40 + 4 * lane), 0); + } + wait_428c(channel); + write32(DEFAULT_MCHBAR | 0x4220 | (channel << 10), 0x0001f006); + write32(DEFAULT_MCHBAR | 0x4230 | (channel << 10), 0x0028a004); + write32(DEFAULT_MCHBAR | 0x4200 | (channel << 10), + 0x00060000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4210 | (channel << 10), 0x00000244); + write32(DEFAULT_MCHBAR | 0x4224 | (channel << 10), 0x0001f201); + write32(DEFAULT_MCHBAR | 0x4234 | (channel << 10), 0x08281064); + write32(DEFAULT_MCHBAR | 0x4204 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4214 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x4228 | (channel << 10), 0x0001f105); + write32(DEFAULT_MCHBAR | 0x4238 | (channel << 10), 0x04281064); + write32(DEFAULT_MCHBAR | 0x4208 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4218 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x422c | (channel << 10), 0x0001f002); + write32(DEFAULT_MCHBAR | 0x423c | (channel << 10), 0x00280c01); + write32(DEFAULT_MCHBAR | 0x420c | (channel << 10), + 0x00060400 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x421c | (channel << 10), 0x00000240); + write32(DEFAULT_MCHBAR | 0x4284 | (channel << 10), 0x000c0001); + wait_428c(channel); + FOR_ALL_LANES + if (read32(DEFAULT_MCHBAR | 0x4340 | (channel << 10))) + die("Mini channel test failed (2)\n"); + } +} + +static void set_scrambling_seed(ramctr_timing * ctrl) +{ + int channel; + + /* FIXME: we hardcode seeds. Do we need to use some PRNG for them? + I don't think so. */ + static u32 seeds[NUM_CHANNELS][3] = { + {0x00009a36, 0xbafcfdcf, 0x46d1ab68}, + {0x00028bfa, 0x53fe4b49, 0x19ed5483} + }; + FOR_ALL_POPULATED_CHANNELS { + MCHBAR32(0x4020 + 0x400 * channel) &= ~0x10000000; + write32(DEFAULT_MCHBAR | 0x4034, seeds[channel][0]); + write32(DEFAULT_MCHBAR | 0x403c, seeds[channel][1]); + write32(DEFAULT_MCHBAR | 0x4038, seeds[channel][2]); + } +} + +static void set_4f8c(void) +{ + struct cpuid_result cpures; + u32 cpu; + + cpures = cpuid(0); + cpu = (cpures.eax); + if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) { + MCHBAR32(0x4f8c) = 0x141D1519; + } else { + MCHBAR32(0x4f8c) = 0x551D1519; + } +} + +static void prepare_training(ramctr_timing * ctrl) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS { + // Always drive command bus + MCHBAR32(0x4004 + 0x400 * channel) |= 0x20000000; + } + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS wait_428c(channel); +} + +static void hardcode1(ramctr_timing * ctrl) +{ + int channel; + u32 reg; + FOR_ALL_POPULATED_CHANNELS { + reg = read32(DEFAULT_MCHBAR | 0x400c | (channel << 10)); + write32(DEFAULT_MCHBAR | 0x400c | (channel << 10), + (reg & 0xFFF0FFFF) + | (ctrl->ref_card_offset[channel] << 16) + | (ctrl->ref_card_offset[channel] << 18)); + write32(DEFAULT_MCHBAR | 0x4008 | (channel << 10), 0x0a042220); // FIXME: hardcoded + } +} + +static void set_42a0(ramctr_timing * ctrl) +{ + int channel; + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR | (0x42a0 + 0x400 * channel), + 0x00001000 | ctrl->rankmap[channel]); + MCHBAR32(0x4004 + 0x400 * channel) &= ~0x20000000; // OK + } +} + +void init_dram_ddr3(spd_raw_data * spds, int mobile, int min_tck) +{ + int me_uma_size; + + MCHBAR32(0x5f00) |= 1; + + report_platform_info(); + + /* Wait for ME to be ready */ + intel_early_me_init(); + me_uma_size = intel_early_me_uma_size(); + + printk(BIOS_DEBUG, "Starting native Platform init\n"); + + u32 reg_5d10; + + wait_txt_clear(); + + wrmsr(0x000002e6, (msr_t) { + .lo = 0,.hi = 0}); + + reg_5d10 = read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + if ((pcie_read_config16(SOUTHBRIDGE, 0xa2) & 0xa0) == 0x20 /* 0x0004 */ + && reg_5d10) { + /* Need reset. */ + outb(0x6, 0xcf9); + + while (1) ; + } + + ramctr_timing ctrl; + + dimm_info info; + + early_pch_init_native(); + early_thermal_init(); + + ctrl.mobile = mobile; + ctrl.tCK = min_tck; + + /* Get DDR3 SPD data */ + dram_find_spds_ddr3(spds, &info, &ctrl); + + /* Find fastest common supported parameters */ + dram_find_common_params(&info, &ctrl); + + /* Calculate timings */ + dram_timing(&ctrl); + + /* Set MCU frequency */ + dram_freq(&ctrl); + + /* Set version register */ + MCHBAR32(0x5034) = 0xC04EB002; + + /* Enable crossover */ + dram_xover(&ctrl); + + /* Set timing and refresh registers */ + dram_timing_regs(&ctrl); + + /* Power mode preset */ + MCHBAR32(0x4e80) = 0x5500; + + /* Set scheduler parameters */ + MCHBAR32(0x4c20) = 0x10100005; + + /* Set cpu specific register */ + set_4f8c(); + + /* Clear IO reset bit */ + MCHBAR32(0x5030) &= ~0x20; + + /* Set MAD-DIMM registers */ + dram_dimm_mapping(&info, &ctrl); + printk(BIOS_DEBUG, "Done dimm mapping\n"); + + /* Zone config */ + dram_zones(&info, &ctrl, 1); + + /* Set memory map */ + dram_memorymap(&info, me_uma_size); + printk(BIOS_DEBUG, "Done memory map\n"); + + /* Set IO registers */ + dram_ioregs(&ctrl); + printk(BIOS_DEBUG, "Done io registers\n"); + + udelay(1); + + /* Do jedec ddr3 reset sequence */ + dram_jedecreset(&ctrl); + printk(BIOS_DEBUG, "Done jedec reset\n"); + + /* MRS commands */ + dram_mrscommands(&ctrl); + printk(BIOS_DEBUG, "Done MRS commands\n"); + dram_mrscommands(&ctrl); + + /* Prepare for memory training */ + prepare_training(&ctrl); + + read_training(&ctrl); + write_training(&ctrl); + + printk(BIOS_SPEW, "CP5a\n"); + + discover_edges(&ctrl); + + printk(BIOS_SPEW, "CP5b\n"); + + command_training(&ctrl); + + printk(BIOS_SPEW, "CP5c\n"); + + discover_edges_write(&ctrl); + + discover_timC_write(&ctrl); + + normalize_training(&ctrl); + + hardcode1(&ctrl); + + write_controller_mr(&ctrl); + + channel_test(&ctrl); + + write32(DEFAULT_MCHBAR | 0x5024, 0x00a030ce); // FIXME: hardcoded + + set_scrambling_seed(&ctrl); + + set_42a0(&ctrl); + + write32(DEFAULT_MCHBAR | 0x4cd4, 0x00000046); // FIXME: hardcoded + + write32(DEFAULT_MCHBAR | 0x400c, (read32(DEFAULT_MCHBAR | 0x400c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x440c, (read32(DEFAULT_MCHBAR | 0x440c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x4cb0, 0x00000740); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x4380, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4780, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4f88, 0x5f7003ff); // OK + write32(DEFAULT_MCHBAR | 0x5064, 0x00073000 | ctrl.reg_5064b0); // OK + write32(DEFAULT_MCHBAR | 0x4384, 0x009b6ea1); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x4784, 0x009b6ea1); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x5880, 0xca9171e5); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x5888); // !!! = 0x00e4d5d0 + write32(DEFAULT_MCHBAR | 0x5888, 0x00e4d5d0); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x58a8); // !!! = 0x00000000 + write32(DEFAULT_MCHBAR | 0x58a8, 0x00000000); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x4294); // !!! = 0x000098ff + write32(DEFAULT_MCHBAR | 0x4294, 0x000198ff); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x4694); // !!! = 0x000098ff + write32(DEFAULT_MCHBAR | 0x4694, 0x000198ff); // FIXME: hardcoded + + MCHBAR32(0x5030) |= 1; // OK + MCHBAR32(0x5030) |= 0x80; // OK + MCHBAR32(0x5f18) = 0xfa; // OK + read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + write32(DEFAULT_MCHBAR | 0x5d10, 0x2010040c); // FIXME: hardcoded + + /* Zone config */ + dram_zones(&info, &ctrl, 0); + + intel_early_me_status(); + intel_early_me_init_done(ME_INIT_STATUS_SUCCESS); + intel_early_me_status(); + + post_system_agent_init(); + report_memory_config(); +} diff --git a/src/northbridge/intel/sandybridge/raminit_native.h b/src/northbridge/intel/sandybridge/raminit_native.h new file mode 100644 index 0000000..ff7d7c1 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RAMINIT_H +#define RAMINIT_H + +#include <device/dram/ddr3.h> + +/* The order is ch0dimmA, ch0dimmB, ch1dimmA, ch1dimmB. */ +void init_dram_ddr3(spd_raw_data *spds, int mobile, int min_tck); +void read_spd(spd_raw_data *spd, u8 addr); + +#endif /* RAMINIT_H */ diff --git a/src/southbridge/intel/bd82x6x/Makefile.inc b/src/southbridge/intel/bd82x6x/Makefile.inc index 64038e5..fa21277 100644 --- a/src/southbridge/intel/bd82x6x/Makefile.inc +++ b/src/southbridge/intel/bd82x6x/Makefile.inc @@ -45,10 +45,14 @@ smm-$(CONFIG_SPI_FLASH_SMM) += ../common/spi.c ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c me.c me_8.x.c finalize.c pch.c -romstage-y += early_usb.c early_smbus.c early_me.c me_status.c gpio.c +romstage-y += early_usb.c early_smbus.c me_status.c gpio.c romstage-y += reset.c romstage-y += early_spi.c early_pch.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += early_thermal.c early_pch_native.c early_me_native.c + ifeq ($(CONFIG_BUILD_WITH_FAKE_IFD),y) IFD_BIN_PATH := $(objgenerated)/ifdfake.bin IFD_SECTIONS := $(addprefix -b ,$(CONFIG_IFD_BIOS_SECTION:"%"=%)) \ diff --git a/src/southbridge/intel/bd82x6x/early_me_native.c b/src/southbridge/intel/bd82x6x/early_me_native.c new file mode 100644 index 0000000..ab54ffd --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_me_native.c @@ -0,0 +1,272 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <arch/hlt.h> +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> +#include <device/pci_ids.h> +#include <string.h> +#include "me.h" +#include "pch.h" + +static const char *me_ack_values[] = { + [ME_HFS_ACK_NO_DID] = "No DID Ack received", + [ME_HFS_ACK_RESET] = "Non-power cycle reset", + [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset", + [ME_HFS_ACK_S3] = "Go to S3", + [ME_HFS_ACK_S4] = "Go to S4", + [ME_HFS_ACK_S5] = "Go to S5", + [ME_HFS_ACK_GBL_RESET] = "Global Reset", + [ME_HFS_ACK_CONTINUE] = "Continue to boot" +}; + +static inline void pci_read_dword_ptr(void *ptr, int offset) +{ + u32 dword = pci_read_config32(PCH_ME_DEV, offset); + memcpy(ptr, &dword, sizeof(dword)); +} + +static inline void pci_write_dword_ptr(void *ptr, int offset) +{ + u32 dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + pci_write_config32(PCH_ME_DEV, offset, dword); +} + +void intel_early_me_status(void) +{ + struct me_hfs hfs; + struct me_gmes gmes; + + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + pci_read_dword_ptr(&gmes, PCI_ME_GMES); + + intel_me_status(&hfs, &gmes); +} + +int intel_early_me_init(void) +{ + int count; + struct me_uma uma; + struct me_hfs hfs; + + printk(BIOS_INFO, "Intel ME early init\n"); + + /* Wait for ME UMA SIZE VALID bit to be set */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) + break; + udelay(ME_DELAY); + } + if (!count) { + printk(BIOS_ERR, "ERROR: ME is not ready!\n"); + return -1; + } + + /* Check for valid firmware */ + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + if (hfs.fpt_bad) { + printk(BIOS_WARNING, "WARNING: ME has bad firmware\n"); + return -1; + } + + printk(BIOS_INFO, "Intel ME firmware is ready\n"); + return 0; +} + +int intel_early_me_uma_size(void) +{ + struct me_uma uma; + + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) { + printk(BIOS_DEBUG, "ME: Requested %uMB UMA\n", uma.size); + return uma.size; + } + + printk(BIOS_DEBUG, "ME: Invalid UMA size\n"); + return 0; +} + +static inline void set_global_reset(int enable) +{ + u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3); + + /* Clear CF9 Without Resume Well Reset Enable */ + etr3 &= ~ETR3_CWORWRE; + + /* CF9GR indicates a Global Reset */ + if (enable) + etr3 |= ETR3_CF9GR; + else + etr3 &= ~ETR3_CF9GR; + + pci_write_config32(PCH_LPC_DEV, ETR3, etr3); +} + +int intel_early_me_init_done(u8 status) +{ + u8 reset, errorcode, opmode; + u16 reg16; + u32 mebase_l, mebase_h; + u32 millisec; + u32 hfs, me_fws2; + struct me_did did = { + .init_done = ME_INIT_DONE, + .status = status + }; + u32 meDID; + + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xff000) >> 12; + + opmode = (hfs & 0xf0) >> 4; + errorcode = hfs & 0xf; + + if (opmode != ME_HFS_MODE_NORMAL) { + printk(BIOS_NOTICE, "ME: Wrong mode : %d\n", opmode); + //return 0; + } + if (errorcode) { + printk(BIOS_NOTICE, "ME: HFS error : %d\n", errorcode); + //return 0; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + // Poll cpu replaced for 50ms + millisec = 0; + while ((((me_fws2 & 0x100) >> 8) == 0) && millisec < 50) { + udelay(1000); + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + millisec++; + } + if (millisec >= 50 || ((me_fws2 & 0x100) >> 8) == 0x0) { + printk(BIOS_NOTICE, "Waited long enough, or CPU was not replaced, continue...\n"); + } else if ((me_fws2 & 0x100) == 0x100) { + if ((me_fws2 & 0x80) == 0x80) { + printk(BIOS_NOTICE, "CPU was replaced & warm reset required...\n"); + reg16 = pcie_read_config16(PCI_DEV(0, 31, 0), 0xa2) & ~0x80; + pcie_write_config16(PCI_DEV(0, 31, 0), 0xa2, reg16); + set_global_reset(0); + outb(0x6, 0xcf9); + hlt(); + } + + if (((me_fws2 & 0x10) == 0x10) && (me_fws2 & 0x80) == 0x00) { + printk(BIOS_NOTICE, "Full training required\n"); + } + } + + printk(BIOS_NOTICE, "PASSED! Tell ME that DRAM is ready\n"); + + /* MEBASE from MESEG_BASE[35:20] */ + mebase_l = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_L); + mebase_h = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_H) & 0xf; + did.uma_base = (mebase_l >> 20) | (mebase_h << 12); + + meDID = did.uma_base | (1 << 28);// | (1 << 23); + pci_write_config32(PCI_DEV(0, 0x16, 0), PCI_ME_H_GS, meDID); + + udelay(1100); + + /* Must wait for ME acknowledgement */ + millisec = 0; + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + while ((((hfs & 0xf0) >> 4) != ME_HFS_BIOS_DRAM_ACK) && (millisec < 5000)) { + udelay(1000); + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + millisec++; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + + /* Return the requested BIOS action */ + printk(BIOS_NOTICE, "ME: Requested BIOS Action: %s\n", + me_ack_values[(hfs & 0xe) >> 1]); + + reset = inb(0xcf9); + reset &= 0xf1; + switch ((hfs & 0xe) >> 1) { + case ME_HFS_ACK_NO_DID: + case ME_HFS_ACK_CONTINUE: + /* Continue to boot */ + return 0; + case ME_HFS_ACK_RESET: + /* Non-power cycle reset */ + set_global_reset(0); + reset |= 0x06; + break; + case ME_HFS_ACK_PWR_CYCLE: + /* Power cycle reset */ + set_global_reset(0); + reset |= 0x0e; + break; + case ME_HFS_ACK_GBL_RESET: + /* Global reset */ + set_global_reset(1); + reset |= 0x0e; + break; + case ME_HFS_ACK_S3: + case ME_HFS_ACK_S4: + case ME_HFS_ACK_S5: + break; + } + + /* Perform the requested reset */ + if (reset) { + outb(reset, 0xcf9); + hlt(); + } + return -1; +} diff --git a/src/southbridge/intel/bd82x6x/early_pch_native.c b/src/southbridge/intel/bd82x6x/early_pch_native.c new file mode 100644 index 0000000..d7f3a2e --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_pch_native.c @@ -0,0 +1,375 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko <phcoder(a)gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include <delay.h> + +#include "pch.h" +/* For DMI bar. */ +#include "northbridge/intel/sandybridge/sandybridge.h" + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) + +static void +wait_2338 (void) +{ + while (read8 (DEFAULT_RCBA | 0x2338) & 1); +} + +static u32 +read_2338 (u32 edx) +{ + u32 ret; + + write32 (DEFAULT_RCBA | 0x2330, edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + ret = read32 (DEFAULT_RCBA | 0x2334); + wait_2338 (); + read8 (DEFAULT_RCBA | 0x2338); + return ret; +} + +static void +write_2338 (u32 edx, u32 val) +{ + read_2338 (edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + + write32 (DEFAULT_RCBA | 0x2334, val); + wait_2338 (); + write16 (DEFAULT_RCBA | 0x2338, + (read16 (DEFAULT_RCBA | 0x2338) & 0x1ff) | 0x600); + read8 (DEFAULT_RCBA | 0x2338); +} + + +static void +init_dmi (void) +{ + int i; + + write32 (DEFAULT_DMIBAR | 0x0914, + read32 (DEFAULT_DMIBAR | 0x0914) | 0x80000000); + write32 (DEFAULT_DMIBAR | 0x0934, + read32 (DEFAULT_DMIBAR | 0x0934) | 0x80000000); + for (i = 0; i < 4; i++) + { + write32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4)) & 0xf3ffffff); + write32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4)) | 0x800); + } + write32 (DEFAULT_DMIBAR | 0x0c30, (read32 (DEFAULT_DMIBAR | 0x0c30) + & 0xfffffff) | 0x40000000); + for (i = 0; i < 2; i++) + { + write32 (DEFAULT_DMIBAR | 0x0904 | (i << 5), + read32 (DEFAULT_DMIBAR | 0x0904 | (i << 5)) & 0xfe3fffff); + write32 (DEFAULT_DMIBAR | 0x090c | (i << 5), + read32 (DEFAULT_DMIBAR | 0x090c | (i << 5)) & 0xfff1ffff); + } + write32 (DEFAULT_DMIBAR | 0x090c, + read32 (DEFAULT_DMIBAR | 0x090c) & 0xfe1fffff); + write32 (DEFAULT_DMIBAR | 0x092c, + read32 (DEFAULT_DMIBAR | 0x092c) & 0xfe1fffff); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x090c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x090c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x092c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x092c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0c04); // !!! = 0x2e680008 + write32 (DEFAULT_DMIBAR | 0x0c04, 0x2e680008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0910); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0910, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0930); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0930, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0a04); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a04, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a14); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a14, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a24); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a24, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a34); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a34, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0900); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0900, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0920); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0920, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0c0c); // !!! = 0x16063400 + write32 (DEFAULT_DMIBAR | 0x0c0c, 0x00063400); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0258); // !!! = 0x40000600 + write32 (DEFAULT_DMIBAR | 0x0258, 0x60000600); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x022c); // !!! = 0x00c26460 + write32 (DEFAULT_DMIBAR | 0x022c, 0x00c2403c); + read8 (DEFAULT_RCBA | 0x21a4); // !!! = 0x42 + + read32 (DEFAULT_RCBA | 0x21a4); // !!! = 0x00012c42 + read32 (DEFAULT_RCBA | 0x2340); // !!! = 0x0013001b + write32 (DEFAULT_RCBA | 0x2340, 0x003a001b); + read8 (DEFAULT_RCBA | 0x21b0); // !!! = 0x01 + write8 (DEFAULT_RCBA | 0x21b0, 0x02); + read32 (DEFAULT_DMIBAR | 0x0084); // !!! = 0x0041ac41 + write32 (DEFAULT_DMIBAR | 0x0084, 0x0041ac42); + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0041 + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + + read32 (DEFAULT_DMIBAR | 0x0014); // !!! = 0x8000007f + write32 (DEFAULT_DMIBAR | 0x0014, 0x80000019); + read32 (DEFAULT_DMIBAR | 0x0020); // !!! = 0x01000000 + write32 (DEFAULT_DMIBAR | 0x0020, 0x81000022); + read32 (DEFAULT_DMIBAR | 0x002c); // !!! = 0x02000000 + write32 (DEFAULT_DMIBAR | 0x002c, 0x82000044); + read32 (DEFAULT_DMIBAR | 0x0038); // !!! = 0x07000080 + write32 (DEFAULT_DMIBAR | 0x0038, 0x87000080); + read8 (DEFAULT_DMIBAR | 0x0004); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0004, 0x01); + + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x01200654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x012a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + read8 (DEFAULT_RCBA | 0x1114); // !!! = 0x00 + write8 (DEFAULT_RCBA | 0x1114, 0x05); + read32 (DEFAULT_RCBA | 0x2014); // !!! = 0x80000011 + write32 (DEFAULT_RCBA | 0x2014, 0x80000019); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2020, 0x81000022); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x81000022 + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2030, 0x82000044); + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x82000044 + read32 (DEFAULT_RCBA | 0x2040); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2040, 0x87000080); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + write32 (DEFAULT_RCBA | 0x0050, 0x812a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x812a0654 + read16 (DEFAULT_RCBA | 0x201a); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2026); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2036); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2046); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x001a); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0026); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0032); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x003e); // !!! = 0x0000 +} + +void +early_pch_init (void) +{ + pcie_write_config8 (SOUTHBRIDGE, 0xa6, + pcie_read_config8 (SOUTHBRIDGE, 0xa6) | 2); + + write32 (DEFAULT_RCBA | 0x2088, 0x00109000); + read32 (DEFAULT_RCBA | 0x20ac); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x20ac, 0x40000000); + write32 (DEFAULT_RCBA | 0x100c, 0x01110000); + write8 (DEFAULT_RCBA | 0x2340, 0x1b); + read32 (DEFAULT_RCBA | 0x2314); // !!! = 0x0a080000 + write32 (DEFAULT_RCBA | 0x2314, 0x0a280000); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xc809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + write32 (DEFAULT_RCBA | 0x2324, 0x00854c74); + read8 (DEFAULT_RCBA | 0x0400); // !!! = 0x00 + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + + write_2338 (0xea007f62, 0x00590133); + write_2338 (0xec007f62, 0x00590133); + write_2338 (0xec007f64, 0x59555588); + write_2338 (0xea0040b9, 0x0001051c); + write_2338 (0xeb0040a1, 0x800084ff); + write_2338 (0xec0040a1, 0x800084ff); + write_2338 (0xea004001, 0x00008400); + write_2338 (0xeb004002, 0x40201758); + write_2338 (0xec004002, 0x40201758); + write_2338 (0xea004002, 0x00601758); + write_2338 (0xea0040a1, 0x810084ff); + write_2338 (0xeb0040b1, 0x0001c598); + write_2338 (0xec0040b1, 0x0001c598); + write_2338 (0xeb0040b6, 0x0001c598); + write_2338 (0xea0000a9, 0x80ff969f); + write_2338 (0xea0001a9, 0x80ff969f); + write_2338 (0xeb0040b2, 0x0001c396); + write_2338 (0xeb0040b3, 0x0001c396); + write_2338 (0xec0040b2, 0x0001c396); + write_2338 (0xea0001a9, 0x80ff94ff); + write_2338 (0xea000151, 0x0088037f); + write_2338 (0xea0000a9, 0x80ff94ff); + write_2338 (0xea000051, 0x0088037f); + + write_2338 (0xea007f05, 0x00010642); + write_2338 (0xea0040b7, 0x0001c91c); + write_2338 (0xea0040b8, 0x0001c91c); + write_2338 (0xeb0040a1, 0x820084ff); + write_2338 (0xec0040a1, 0x820084ff); + write_2338 (0xea007f0a, 0xc2480000); + + write_2338 (0xec00404d, 0x1ff177f); + write_2338 (0xec000084, 0x5a600000); + write_2338 (0xec000184, 0x5a600000); + write_2338 (0xec000284, 0x5a600000); + write_2338 (0xec000384, 0x5a600000); + write_2338 (0xec000094, 0x000f0501); + write_2338 (0xec000194, 0x000f0501); + write_2338 (0xec000294, 0x000f0501); + write_2338 (0xec000394, 0x000f0501); + write_2338 (0xec000096, 0x00000001); + write_2338 (0xec000196, 0x00000001); + write_2338 (0xec000296, 0x00000001); + write_2338 (0xec000396, 0x00000001); + write_2338 (0xec000001, 0x00008c08); + write_2338 (0xec000101, 0x00008c08); + write_2338 (0xec000201, 0x00008c08); + write_2338 (0xec000301, 0x00008c08); + write_2338 (0xec0040b5, 0x0001c518); + write_2338 (0xec000087, 0x06077597); + write_2338 (0xec000187, 0x06077597); + write_2338 (0xec000287, 0x06077597); + write_2338 (0xec000387, 0x06077597); + write_2338 (0xea000050, 0x00bb0157); + write_2338 (0xea000150, 0x00bb0157); + write_2338 (0xec007f60, 0x77777d77); + write_2338 (0xea00008d, 0x01320000); + write_2338 (0xea00018d, 0x01320000); + write_2338 (0xec0007b2, 0x04514b5e); + write_2338 (0xec00078c, 0x40000200); + write_2338 (0xec000780, 0x02000020); + + init_dmi(); +} diff --git a/src/southbridge/intel/bd82x6x/early_thermal.c b/src/southbridge/intel/bd82x6x/early_thermal.c new file mode 100644 index 0000000..02ec9a7 --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_thermal.c @@ -0,0 +1,70 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include "pch.h" +#include "cpu/intel/model_206ax/model_206ax.h" +#include <cpu/x86/msr.h> + +/* Early thermal init, must be done prior to giving ME its memory + which is done at the end of raminit. */ +void early_thermal_init(void) +{ + device_t dev; + msr_t msr; + + dev = PCI_DEV(0x0, 0x1f, 0x6); + + /* Program address for temporary BAR. */ + pci_write_config32(dev, 0x40, 0x40000000); + pci_write_config32(dev, 0x44, 0x0); + + /* Activate temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) | 5); + + + write16 (0x40000004, 0x3a2b); + write8 (0x4000000c, 0xff); + write8 (0x4000000d, 0x00); + write8 (0x4000000e, 0x40); + write8 (0x40000082, 0x00); + write8 (0x40000001, 0xba); + + /* Perform init. */ + /* Configure TJmax. */ + msr = rdmsr(MSR_TEMPERATURE_TARGET); + write16(0x40000012, ((msr.lo >> 16) & 0xff) << 6); + /* Northbridge temperature slope and offset. */ + write16(0x40000016, 0x808c); + + write16 (0x40000014, 0xde87); + + /* Enable thermal data reporting, processor, PCH and northbridge. */ + write16(0x4000001a, (read16(0x4000001a) & ~0xf) | 0x10f0); + + /* Disable temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) & ~1); + pci_write_config32(dev, 0x40, 0); + + write32 (DEFAULT_RCBA | 0x38b0, + (read32 (DEFAULT_RCBA | 0x38b0) & 0xffff8003) | 0x403c); +} diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h index 90de855..83128e2 100644 --- a/src/southbridge/intel/bd82x6x/pch.h +++ b/src/southbridge/intel/bd82x6x/pch.h @@ -74,6 +74,8 @@ void enable_smbus(void); void enable_usb_bar(void); int smbus_read_byte(unsigned device, unsigned address); int early_spi_read(u32 offset, u32 size, u8 *buffer); +void early_thermal_init(void); +void early_pch_init_native(void); #endif #endif diff --git a/src/southbridge/intel/bd82x6x/smi.c b/src/southbridge/intel/bd82x6x/smi.c index 0166edf..a20232e 100644 --- a/src/southbridge/intel/bd82x6x/smi.c +++ b/src/southbridge/intel/bd82x6x/smi.c @@ -29,10 +29,7 @@ #include <cpu/x86/smm.h> #include <string.h> #include "pch.h" - -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE #include "northbridge/intel/sandybridge/sandybridge.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/bd82x6x/usb_ehci.c b/src/southbridge/intel/bd82x6x/usb_ehci.c index 78f92d9..97f20bd 100644 --- a/src/southbridge/intel/bd82x6x/usb_ehci.c +++ b/src/southbridge/intel/bd82x6x/usb_ehci.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <kconfig.h> #include <console/console.h> #include <device/device.h> #include <device/pci.h> @@ -36,11 +37,43 @@ static void usb_ehci_init(struct device *dev) RCBA32(0x35b0) = reg32; printk(BIOS_DEBUG, "EHCI: Setting up controller.. "); + + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + pci_write_config32(dev, 0x84, 0x930c8811); + pci_write_config32(dev, 0x88, 0x24000d30); + pci_write_config32(dev, 0xf4, 0x80408588); + pci_write_config32(dev, 0xf4, 0x80808588); + pci_write_config32(dev, 0xf4, 0x00808588); + pci_write_config32(dev, 0xfc, 0x205b1708); +#endif + reg32 = pci_read_config32(dev, PCI_COMMAND); reg32 |= PCI_COMMAND_MASTER; //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + struct resource *res; + u8 access_cntl; + + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); +#endif + printk(BIOS_DEBUG, "done.\n"); } diff --git a/src/southbridge/intel/ibexpeak/me.c b/src/southbridge/intel/ibexpeak/me.c index bc56012..0e1b5b8 100644 --- a/src/southbridge/intel/ibexpeak/me.c +++ b/src/southbridge/intel/ibexpeak/me.c @@ -377,125 +377,7 @@ static int mkhi_end_of_post(void) printk(BIOS_INFO, "ME: END OF POST message successful\n"); return 0; } -#endif - -#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && !defined(__SMM__) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) -/* Get ME firmware version */ -static int mkhi_get_fw_version(void) -{ - struct me_fw_version version; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_GEN, - .command = MKHI_GET_FW_VERSION, - }; - struct mei_header mei = { - .is_complete = 1, - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - .length = sizeof(mkhi), - }; - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) { - printk(BIOS_ERR, "ME: GET FW VERSION message failed\n"); - return -1; - } - - printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) " - "%u.%u.%u.%u (recovery)\n", - version.code_major, version.code_minor, - version.code_build_number, version.code_hot_fix, - version.recovery_major, version.recovery_minor, - version.recovery_build_number, version.recovery_hot_fix); - - return 0; -} - -static inline void print_cap(const char *name, int state) -{ - printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n", - name, state ? "en" : "dis"); -} - -/* Get ME Firmware Capabilities */ -static int mkhi_get_fwcaps(void) -{ - u32 rule_id = 0; - struct me_fwcaps cap; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_FWCAPS, - .command = MKHI_FWCAPS_GET_RULE, - }; - struct mei_header mei = { - .is_complete = 1, - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - .length = sizeof(mkhi) + sizeof(rule_id), - }; - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) { - printk(BIOS_ERR, "ME: GET FWCAPS message failed\n"); - return -1; - } - - print_cap("Full Network manageability", cap.caps_sku.full_net); - print_cap("Regular Network manageability", cap.caps_sku.std_net); - print_cap("Manageability", cap.caps_sku.manageability); - print_cap("Small business technology", cap.caps_sku.small_business); - print_cap("Level III manageability", cap.caps_sku.l3manageability); - print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at); - print_cap("IntelR Capability Licensing Service (CLS)", - cap.caps_sku.intel_cls); - print_cap("IntelR Power Sharing Technology (MPC)", - cap.caps_sku.intel_mpc); - print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking); - print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp); - print_cap("IPV6", cap.caps_sku.ipv6); - print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm); - print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och); - print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan); - print_cap("TLS", cap.caps_sku.tls); - print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan); - - return 0; -} -#endif -#if CONFIG_CHROMEOS && 0 /* DISABLED */ -/* Tell ME to issue a global reset */ -int mkhi_global_reset(void) -{ - struct me_global_reset reset = { - .request_origin = GLOBAL_RESET_BIOS_POST, - .reset_type = CBM_RR_GLOBAL_RESET, - }; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_CBM, - .command = MKHI_GLOBAL_RESET, - }; - struct mei_header mei = { - .is_complete = 1, - .length = sizeof(mkhi) + sizeof(reset), - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - }; - - printk(BIOS_NOTICE, "ME: Requesting global reset\n"); - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, &reset, NULL, 0) < 0) { - /* No response means reset will happen shortly... */ - hlt(); - } - - /* If the ME responded it rejected the reset request */ - printk(BIOS_ERR, "ME: Global Reset failed\n"); - return -1; -} -#endif - -#ifdef __SMM__ static void intel_me7_finalize_smm(void) { struct me_hfs hfs; @@ -723,13 +605,6 @@ static void intel_me_init(device_t dev) if (intel_mei_setup(dev) < 0) break; -#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) - /* Print ME firmware version */ - mkhi_get_fw_version(); - /* Print ME firmware capabilities */ - mkhi_get_fwcaps(); -#endif - /* * Leave the ME unlocked in this path. * It will be locked via SMI command later. diff --git a/src/southbridge/intel/ibexpeak/smi.c b/src/southbridge/intel/ibexpeak/smi.c index 981be3b..2ce9072 100644 --- a/src/southbridge/intel/ibexpeak/smi.c +++ b/src/southbridge/intel/ibexpeak/smi.c @@ -31,13 +31,7 @@ #include <string.h> #include "pch.h" -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE -#include "northbridge/intel/sandybridge/sandybridge.h" -#endif - -#if CONFIG_NORTHBRIDGE_INTEL_NEHALEM #include "northbridge/intel/nehalem/nehalem.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/ibexpeak/usb_ehci.c b/src/southbridge/intel/ibexpeak/usb_ehci.c index 21a257f..ea767c4 100644 --- a/src/southbridge/intel/ibexpeak/usb_ehci.c +++ b/src/southbridge/intel/ibexpeak/usb_ehci.c @@ -30,6 +30,8 @@ static void usb_ehci_init(struct device *dev) { u32 reg32; + struct resource *res; + u8 access_cntl; /* Disable Wake on Disconnect in RMH */ reg32 = RCBA32(0x35b0); @@ -50,6 +52,21 @@ static void usb_ehci_init(struct device *dev) //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); + printk(BIOS_DEBUG, "done.\n"); }
1
0
0
0
Patch set updated for coreboot: 3f3652a x230: Deploy VBT
by Vladimir Serbinenko
31 May '14
31 May '14
Vladimir Serbinenko (phcoder(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5396
-gerrit commit 3f3652a8082cbc0b9253a3ac7b05795055db4992 Author: Vladimir Serbinenko <phcoder(a)gmail.com> Date: Sun Feb 23 00:13:56 2014 +0100 x230: Deploy VBT Change-Id: Ide31a56bfdbc31cd3b87993dfb4ed8ef0107cdba Signed-off-by: Vladimir Serbinenko <phcoder(a)gmail.com> --- src/mainboard/lenovo/x230/Makefile.inc | 1 + src/mainboard/lenovo/x230/gma.c | 297 ++++++++++++++++++++++++++++++++ src/northbridge/intel/sandybridge/gma.c | 30 ++++ src/northbridge/intel/sandybridge/gma.h | 2 + 4 files changed, 330 insertions(+) diff --git a/src/mainboard/lenovo/x230/Makefile.inc b/src/mainboard/lenovo/x230/Makefile.inc index d514d4b..4ce3d61 100644 --- a/src/mainboard/lenovo/x230/Makefile.inc +++ b/src/mainboard/lenovo/x230/Makefile.inc @@ -18,3 +18,4 @@ ## smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c +ramstage-y += gma.c diff --git a/src/mainboard/lenovo/x230/gma.c b/src/mainboard/lenovo/x230/gma.c new file mode 100644 index 0000000..20a461b --- /dev/null +++ b/src/mainboard/lenovo/x230/gma.c @@ -0,0 +1,297 @@ +#include <arch/io.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> + +#include "northbridge/intel/sandybridge/sandybridge.h" + +/* This array contains the information on flat panel. When using native + graphics init coreboot copies it to where VGA Option ROM would be so + that OS can find it and able to use internal display. This contains no + executable code and is just information on the panel. + */ + +unsigned char fake_vbt[8192] = +{ +0x24, 0x56, 0x42, 0x54, 0x20, 0x53, 0x4e, 0x42, 0x2f, 0x49, 0x56, 0x42, 0x2d, 0x4d, 0x4f, 0x42, +0x49, 0x4c, 0x45, 0x20, 0x64, 0x00, 0x30, 0x00, 0x6b, 0x11, 0xc6, 0x00, 0x30, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x42, 0x49, 0x4f, 0x53, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x20, +0xa8, 0x00, 0x16, 0x00, 0x3b, 0x11, 0xfe, 0xea, 0x00, 0x00, 0x64, 0x01, 0x01, 0x14, 0x0d, 0x32, +0x31, 0x36, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x28, 0x52, 0x29, 0x20, 0x53, 0x61, 0x6e, 0x64, +0x79, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2f, 0x49, 0x76, 0x79, 0x62, 0x72, 0x69, 0x64, 0x67, +0x65, 0x20, 0x50, 0x43, 0x49, 0x20, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x65, +0x64, 0x20, 0x53, 0x56, 0x47, 0x41, 0x20, 0x42, 0x49, 0x4f, 0x53, 0x0d, 0x0a, 0x42, 0x75, 0x69, +0x6c, 0x64, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x32, 0x31, 0x36, 0x31, 0x5f, +0x52, 0x59, 0x61, 0x6e, 0x20, 0x50, 0x43, 0x20, 0x31, 0x34, 0x2e, 0x33, 0x34, 0x20, 0x20, 0x31, +0x32, 0x2f, 0x30, 0x37, 0x2f, 0x32, 0x30, 0x31, 0x32, 0x20, 0x20, 0x30, 0x38, 0x3a, 0x31, 0x38, +0x3a, 0x33, 0x31, 0x0d, 0x0a, 0x44, 0x45, 0x43, 0x4f, 0x4d, 0x50, 0x49, 0x4c, 0x41, 0x54, 0x49, +0x4f, 0x4e, 0x20, 0x4f, 0x52, 0x20, 0x44, 0x49, 0x53, 0x41, 0x53, 0x53, 0x45, 0x4d, 0x42, 0x4c, +0x59, 0x20, 0x50, 0x52, 0x4f, 0x48, 0x49, 0x42, 0x49, 0x54, 0x45, 0x44, 0x0d, 0x0a, 0x43, 0x6f, +0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, 0x32, 0x30, 0x30, 0x30, +0x2d, 0x32, 0x30, 0x31, 0x31, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, +0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65, 0x73, +0x65, 0x72, 0x76, 0x65, 0x64, 0x2e, 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x00, 0xc0, 0x03, 0x08, 0x04, +0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x07, 0x03, 0x40, 0x01, 0x09, 0xfd, 0x32, 0x00, 0x44, 0x04, +0x40, 0x06, 0x04, 0x02, 0x09, 0x01, 0x00, 0x0a, 0x02, 0x08, 0x0c, 0x04, 0x08, 0x03, 0x01, 0x02, +0x05, 0x01, 0x04, 0x0d, 0x01, 0x04, 0x0b, 0x01, 0x02, 0x07, 0x01, 0x04, 0x15, 0x01, 0x04, 0x45, +0x01, 0x04, 0x0e, 0x04, 0x08, 0x46, 0x04, 0x40, 0x28, 0x20, 0x08, 0x48, 0x40, 0x08, 0x10, 0x00, +0x02, 0x0d, 0x01, 0x02, 0x04, 0x00, 0x00, 0x21, 0x08, 0x00, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xcb, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd6, 0x60, 0x00, 0x10, 0x10, +0x01, 0xb6, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, +0x00, 0x07, 0x10, 0x01, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xd6, 0x60, 0x00, 0x10, +0x10, 0x01, 0xc8, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, +0x00, 0x00, 0x07, 0x20, 0x01, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0xd6, 0x60, 0x00, +0x10, 0x10, 0x01, 0xda, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x09, 0x00, 0x00, 0x06, 0x00, +0x00, 0x00, 0x00, 0x07, 0x30, 0x01, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x01, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x40, 0x42, +0x44, 0x46, 0x48, 0x4a, 0x4c, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x80, 0x81, 0x82, 0x83, +0x84, 0x1a, 0x00, 0xfc, 0xc2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x30, 0x00, 0x51, 0x07, 0x1e, 0x00, +0x48, 0x0a, 0x0f, 0x00, 0x86, 0x03, 0x28, 0x00, 0xb3, 0x03, 0x02, 0x00, 0xbd, 0x03, 0x38, 0x00, +0x61, 0x04, 0xc8, 0x00, 0xb1, 0x06, 0x30, 0x00, 0xe4, 0x06, 0x18, 0x00, 0xff, 0x06, 0x18, 0x00, +0x1a, 0x07, 0x18, 0x00, 0x12, 0x0a, 0x10, 0x00, 0x25, 0x0a, 0x08, 0x00, 0x30, 0x0a, 0x08, 0x00, +0x3b, 0x0a, 0x08, 0x00, 0x1d, 0x08, 0x08, 0x00, 0x61, 0x08, 0x12, 0x00, 0x73, 0x08, 0x12, 0x00, +0x85, 0x08, 0x12, 0x00, 0x97, 0x08, 0x12, 0x00, 0xac, 0x08, 0x0a, 0x00, 0xb6, 0x08, 0x0a, 0x00, +0xc0, 0x08, 0x0a, 0x00, 0xca, 0x08, 0x0a, 0x00, 0xd7, 0x08, 0x0a, 0x00, 0xe1, 0x08, 0x0a, 0x00, +0xeb, 0x08, 0x0a, 0x00, 0xf5, 0x08, 0x0a, 0x00, 0x07, 0x09, 0x0a, 0x00, 0x11, 0x09, 0x0a, 0x00, +0x1b, 0x09, 0x0a, 0x00, 0x25, 0x09, 0x0a, 0x00, 0x2f, 0x09, 0x0a, 0x00, 0x39, 0x09, 0x0a, 0x00, +0x43, 0x09, 0x0a, 0x00, 0x4d, 0x09, 0x0a, 0x00, 0x57, 0x09, 0x0a, 0x00, 0x61, 0x09, 0x0a, 0x00, +0x6b, 0x09, 0x0a, 0x00, 0x75, 0x09, 0x0a, 0x00, 0x7f, 0x09, 0x0a, 0x00, 0x89, 0x09, 0x0a, 0x00, +0x93, 0x09, 0x0a, 0x00, 0x9d, 0x09, 0x0a, 0x00, 0x06, 0x75, 0x00, 0xfc, 0xff, 0x02, 0x80, 0x00, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x8e, 0x29, 0x00, 0x80, 0x9c, 0x01, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x11, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xf0, +0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xf0, +0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, +0x07, 0x07, 0x00, 0xfe, 0xff, 0xce, 0x18, 0x00, 0xff, 0xff, 0x08, 0x3d, 0x00, 0xfc, 0xff, 0x02, +0x40, 0xf0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x01, 0x44, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, +0x48, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x03, 0x03, +0x50, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xf0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, +0x58, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x09, 0x60, 0x00, 0x00, 0x00, 0x32, +0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, +0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, +0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, +0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, +0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, +0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x0a, 0xcb, 0x00, +0x0a, 0x80, 0x04, 0x60, 0x03, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x05, 0x58, 0x02, 0xff, +0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x05, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, +0x07, 0x40, 0x05, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x40, 0x07, 0x70, 0x05, 0xff, 0xff, 0xff, +0xbf, 0xff, 0xff, 0x80, 0x07, 0xa0, 0x05, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x0b, 0xc7, 0x00, 0x21, 0x80, +0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, +0x05, 0x70, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x02, 0x05, 0x72, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x13, 0x00, 0x4a, 0x00, 0x04, 0x00, 0x03, 0x08, 0x3c, 0x84, +0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0xef, 0x87, 0x0d, 0x03, 0x00, 0xf7, 0x03, +0xc8, 0x0e, 0x09, 0x00, 0x01, 0x22, 0x06, 0x5a, 0x00, 0x7e, 0x06, 0x2d, 0x00, 0x0f, 0x8b, 0x00, +0x09, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x84, 0x00, 0x10, 0x00, +0x03, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, +0x40, 0x00, 0x00, 0x40, 0x08, 0x00, 0x20, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x08, 0x00, 0x03, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, +0x04, 0x08, 0x00, 0x40, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, +0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, +0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, +0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, +0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0c, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x20, 0x00, 0x0f, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, +0x9e, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x16, 0x4b, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x84, 0x0e, 0x00, 0x00, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, +0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, +0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, +0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x17, 0x48, +0x00, 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18, 0x88, 0x36, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00, 0x30, 0x40, 0x30, 0x70, 0x13, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1e, 0xa8, 0x2f, 0x78, 0xe0, 0x51, 0x1a, 0x26, 0x40, 0x58, 0x98, 0x13, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x48, 0x3f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, +0xc0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x18, 0x28, 0x00, 0x36, 0x7f, 0x03, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x36, 0x7f, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, +0x36, 0x7f, 0x01, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x36, 0x7f, 0x06, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x19, 0x28, 0x00, 0x19, 0x00, 0xfa, 0x00, 0xfa, 0x00, 0x19, 0x00, 0x90, +0x01, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xc8, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, +0x00, 0x40, 0x00, 0x2c, 0x01, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x2c, 0x01, 0x1a, +0x02, 0x00, 0x00, 0x40, 0x1b, 0xcc, 0x00, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, +0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, +0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, +0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, +0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, +0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, +0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, +0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, +0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, +0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, +0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0xff, 0x1c, 0x36, 0x00, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, 0x08, 0x60, +0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, +0x08, 0x60, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, +0x1d, 0x10, 0x08, 0x60, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1d, 0x34, 0x00, 0x10, +0x00, 0x01, 0x08, 0x01, 0x09, 0x04, 0x0c, 0x40, 0x48, 0x20, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x08, 0x00, 0x01, 0x08, 0x01, 0x09, 0x04, 0x0c, 0x40, 0x48, 0x00, 0x08, 0x00, 0x01, +0x01, 0x09, 0x08, 0x02, 0x05, 0x04, 0x0c, 0x00, 0x08, 0x00, 0x01, 0x01, 0x09, 0x08, 0x02, 0x05, +0x04, 0x0c, 0x00, 0x1e, 0x11, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x18, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, +0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, +0x00, 0x00, 0x29, 0x94, 0x00, 0x03, 0x0c, 0x0b, 0x20, 0x32, 0x0b, 0x12, 0x44, 0x0b, 0x0a, 0x4e, +0x0b, 0x20, 0x74, 0x0b, 0x12, 0x86, 0x0b, 0x0a, 0x90, 0x0b, 0x20, 0xb6, 0x0b, 0x12, 0xc8, 0x0b, +0x0a, 0xd2, 0x0b, 0x20, 0xf8, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x14, 0x0c, 0x20, 0x3a, 0x0c, 0x12, +0x4c, 0x0c, 0x0a, 0x56, 0x0c, 0x20, 0x7c, 0x0c, 0x12, 0x8e, 0x0c, 0x0a, 0x98, 0x0c, 0x20, 0xbe, +0x0c, 0x12, 0xd0, 0x0c, 0x0a, 0xda, 0x0c, 0x20, 0x00, 0x0d, 0x12, 0x12, 0x0d, 0x0a, 0x1c, 0x0d, +0x20, 0x42, 0x0d, 0x12, 0x54, 0x0d, 0x0a, 0x5e, 0x0d, 0x20, 0x84, 0x0d, 0x12, 0x96, 0x0d, 0x0a, +0xa0, 0x0d, 0x20, 0xc6, 0x0d, 0x12, 0xd8, 0x0d, 0x0a, 0xe2, 0x0d, 0x20, 0x08, 0x0e, 0x12, 0x1a, +0x0e, 0x0a, 0x24, 0x0e, 0x20, 0x4a, 0x0e, 0x12, 0x5c, 0x0e, 0x0a, 0x66, 0x0e, 0x20, 0x8c, 0x0e, +0x12, 0x9e, 0x0e, 0x0a, 0xa8, 0x0e, 0x20, 0xce, 0x0e, 0x12, 0xe0, 0x0e, 0x0a, 0xea, 0x0e, 0x20, +0x10, 0x0f, 0x12, 0x22, 0x0f, 0x0a, 0x2c, 0x0f, 0x0d, 0x2a, 0xf0, 0x04, 0x80, 0x02, 0xe0, 0x01, +0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, +0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, +0xff, 0xff, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, 0x08, 0x60, 0x22, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x03, +0x58, 0x02, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, +0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, +0x27, 0x00, 0xff, 0xff, 0xa0, 0x0f, 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, 0x14, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, +0x56, 0x05, 0x00, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x20, 0x00, 0x08, 0x72, 0x0c, 0x00, +0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, +0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x60, 0x1d, 0x56, 0xd8, 0x50, 0x00, 0x18, 0x30, 0x30, 0x40, +0x47, 0x00, 0x15, 0x9c, 0x10, 0x00, 0x00, 0x1b, 0x36, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, +0x00, 0x0c, 0x00, 0x05, 0x00, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, +0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, +0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00, 0x30, 0x40, +0x30, 0x70, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x05, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x78, 0x05, 0x1a, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, +0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, +0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x78, 0x20, 0x51, 0x1a, +0x10, 0x40, 0x10, 0x70, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x01, 0x90, +0x05, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x78, 0x05, 0x1a, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, +0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, +0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0xa8, 0x2f, 0x78, 0xe0, +0x51, 0x1a, 0x26, 0x40, 0x58, 0x98, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, +0x01, 0x90, 0x06, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x06, 0xb0, 0x04, 0x80, 0x11, 0x0e, 0x00, +0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, +0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x48, 0x3f, +0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, +0x36, 0x7f, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x56, 0x05, 0x00, 0x03, 0x80, 0x11, +0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, +0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, +0x66, 0x21, 0x56, 0xaa, 0x51, 0x00, 0x1e, 0x30, 0x46, 0x90, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x36, 0x7f, 0x03, 0x90, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x90, 0x06, 0x1a, 0x04, +0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, +0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, +0xff, 0xff, 0x7c, 0x2e, 0x90, 0xa0, 0x60, 0x1a, 0x1e, 0x40, 0x30, 0x20, 0x36, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x04, 0x90, 0x09, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, +0xb0, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, +0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, +0x27, 0x00, 0xff, 0xff, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, 0x23, 0x40, 0x30, 0x20, 0x2a, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x05, 0x90, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x0c, +0xa0, 0x05, 0x84, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, +0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, +0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x9a, 0x29, 0xa0, 0xd0, 0x51, 0x84, 0x22, 0x30, 0x50, 0x99, +0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, +0x00, 0x0c, 0x40, 0x06, 0x84, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, +0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, +0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x40, 0xc8, 0x60, 0x84, 0x64, 0x30, +0x18, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x0c, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, +0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, +0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, +0x26, 0x30, 0x18, 0x88, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, +0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x20, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, +0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, +0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0xea, 0x1a, 0x00, 0xa0, +0x50, 0x20, 0x17, 0x30, 0x0c, 0x30, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, +0x03, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, 0x38, 0x04, 0x80, 0x11, 0x0e, 0x00, +0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, +0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x02, 0x3a, +0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2d, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, +0x36, 0x7f, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x06, 0x80, 0x11, +0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, +0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, +0x29, 0x40, 0x00, 0x60, 0x80, 0x00, 0x13, 0x60, 0x10, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x4c, 0x46, 0x50, 0x5f, +0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, +0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, +0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, +0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, +0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, +0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, +0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, +0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, +0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, +0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, +0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, +0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, +0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x2b, 0x71, 0x00, 0x06, +0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4a, 0xdc, 0x00, 0x03, +0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, +0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, +0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, +0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, +0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0x2c, 0x15, 0x00, 0x89, 0x46, 0x00, 0x00, 0x00, 0x49, 0x00, 0x0a, 0x00, 0x55, 0x00, 0x50, 0x00, +0x64, 0x00, 0x2c, 0x01, 0x96, 0x00, 0xe8, 0x03, 0x2e, 0xb0, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00 +}; diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index a82a284..b7b4da5 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -21,6 +21,7 @@ #include <console/console.h> #include <bootmode.h> #include <delay.h> +#include <string.h> #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> @@ -652,6 +653,35 @@ static void gma_func0_init(struct device *dev) i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif + + /* Linux relies on VBT for panel info. */ + if (read16(0xc0000) != 0xaa55) { + optionrom_header_t *oh = (void *)0xc0000; + optionrom_pcir_t *pcir; + int sz; + + memset(oh->reserved, 0, 8192); + + sz = (0x80 + sizeof(fake_vbt) + 511) / 512; + oh->signature = 0xaa55; + oh->size = sz; + oh->pcir_offset = 0x40; + oh->vbt_offset = 0x80; + + pcir = (void *)0xc0040; + pcir->signature = 0x52494350; // PCIR + pcir->vendor = dev->vendor; + pcir->device = dev->device; + pcir->length = sizeof(*pcir); + pcir->revision = dev->class; + pcir->classcode[0] = dev->class >> 8; + pcir->classcode[1] = dev->class >> 16; + pcir->classcode[2] = dev->class >> 24; + pcir->imagelength = sz; + pcir->indicator = 0x80; + + memcpy((void *)0xc0080, fake_vbt, sizeof(fake_vbt)); + } } static void gma_set_subsystem(device_t dev, unsigned vendor, unsigned device) diff --git a/src/northbridge/intel/sandybridge/gma.h b/src/northbridge/intel/sandybridge/gma.h index f128412..0248473 100644 --- a/src/northbridge/intel/sandybridge/gma.h +++ b/src/northbridge/intel/sandybridge/gma.h @@ -166,6 +166,8 @@ typedef struct { #define VBT_SIGNATURE 0x54425624 +extern u8 fake_vbt[8192]; + struct northbridge_intel_sandybridge_config; void i915lightup(const struct northbridge_intel_sandybridge_config *info,
1
0
0
0
Patch set updated for coreboot: 535eb55 sandy/ivybridge: Native raminit.
by Vladimir Serbinenko
31 May '14
31 May '14
Vladimir Serbinenko (phcoder(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5786
-gerrit commit 535eb55557ad8f3d432ffe8ec6d6154f82e08796 Author: Vladimir Serbinenko <phcoder(a)gmail.com> Date: Sun May 18 11:05:56 2014 +0200 sandy/ivybridge: Native raminit. Based on damo22 work and my X230 tracing. Works for my X230 in a variety of RAM configs. Also-By: Damien Zammit <damien(a)zamaudio.com> Change-Id: I1aa024c55a8416fc53b25e7123037df0e55a2769 Signed-off-by: Vladimir Serbinenko <phcoder(a)gmail.com> --- 3rdparty | 2 +- src/cpu/intel/Makefile.inc | 1 + src/cpu/x86/smm/smmhandler_tseg.S | 2 +- src/cpu/x86/smm/smmrelocate.S | 4 +- src/device/dram/ddr3.c | 8 +- src/include/device/dram/ddr3.h | 3 + src/mainboard/lenovo/x230/Kconfig | 6 +- src/mainboard/lenovo/x230/romstage.c | 100 +- src/northbridge/intel/Makefile.inc | 1 + src/northbridge/intel/sandybridge/Kconfig | 12 +- src/northbridge/intel/sandybridge/Makefile.inc | 5 +- src/northbridge/intel/sandybridge/gma.c | 4 +- src/northbridge/intel/sandybridge/raminit_native.c | 3766 ++++++++++++++++++++ src/northbridge/intel/sandybridge/raminit_native.h | 29 + src/southbridge/intel/bd82x6x/Makefile.inc | 6 +- src/southbridge/intel/bd82x6x/early_me_native.c | 272 ++ src/southbridge/intel/bd82x6x/early_pch_native.c | 375 ++ src/southbridge/intel/bd82x6x/early_thermal.c | 70 + src/southbridge/intel/bd82x6x/pch.h | 2 + src/southbridge/intel/bd82x6x/smi.c | 3 - src/southbridge/intel/bd82x6x/usb_ehci.c | 33 + src/southbridge/intel/ibexpeak/me.c | 120 +- src/southbridge/intel/ibexpeak/smi.c | 6 - src/southbridge/intel/ibexpeak/usb_ehci.c | 17 + 24 files changed, 4629 insertions(+), 218 deletions(-) diff --git a/3rdparty b/3rdparty index 324ec3c..45f0c04 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 324ec3cb642a278d6d97ae809bc6098045bc6e65 +Subproject commit 45f0c04fd788fb29d9e303b2b2d1657ddb03448a diff --git a/src/cpu/intel/Makefile.inc b/src/cpu/intel/Makefile.inc index 0392f69..68ea05d 100644 --- a/src/cpu/intel/Makefile.inc +++ b/src/cpu/intel/Makefile.inc @@ -19,6 +19,7 @@ subdirs-$(CONFIG_CPU_INTEL_SOCKET_RPGA989) += socket_rPGA989 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += model_2065x subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += model_206ax +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_model_206ax subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_model_206ax diff --git a/src/cpu/x86/smm/smmhandler_tseg.S b/src/cpu/x86/smm/smmhandler_tseg.S index b33fcdf..380935a 100644 --- a/src/cpu/x86/smm/smmhandler_tseg.S +++ b/src/cpu/x86/smm/smmhandler_tseg.S @@ -57,7 +57,7 @@ #define SMI_UNLOCKED 1 #define __PRE_RAM__ -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include <northbridge/intel/sandybridge/sandybridge.h> #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM diff --git a/src/cpu/x86/smm/smmrelocate.S b/src/cpu/x86/smm/smmrelocate.S index bdc9771..bc90fab 100644 --- a/src/cpu/x86/smm/smmrelocate.S +++ b/src/cpu/x86/smm/smmrelocate.S @@ -48,7 +48,7 @@ #if CONFIG_SMM_TSEG -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE #include <northbridge/intel/sandybridge/sandybridge.h> #define TSEG_BAR (DEFAULT_PCIEXBAR | TSEG) #elif CONFIG_NORTHBRIDGE_INTEL_NEHALEM @@ -195,7 +195,7 @@ smm_relocate: xorl %edx, %edx wrmsr -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE /* * IED base is top 4M of TSEG */ diff --git a/src/device/dram/ddr3.c b/src/device/dram/ddr3.c index 9b4f490..69782ab 100644 --- a/src/device/dram/ddr3.c +++ b/src/device/dram/ddr3.c @@ -110,7 +110,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) { int ret; u16 crc, spd_crc; - u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width, sdram_width; + u8 ftb_divisor, ftb_dividend, capacity_shift, bus_width; u8 reg8; u32 mtb; /* medium time base */ unsigned int val, param; @@ -209,8 +209,8 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) printram(" Invalid SDRAM width\n"); ret = SPD_STATUS_INVALID_FIELD; } - sdram_width = (4 << val); - printram(" SDRAM width : %u\n", sdram_width); + dimm->width = (4 << val); + printram(" SDRAM width : %u\n", dimm->width); /* Memory bus width */ reg8 = spd[8]; @@ -236,7 +236,7 @@ int spd_decode_ddr3(dimm_attr * dimm, spd_raw_data spd) * capacity_shift * The rest is the JEDEC formula */ dimm->size_mb = ((1 << (capacity_shift + (25 - 20))) * bus_width - * dimm->ranks) / sdram_width; + * dimm->ranks) / dimm->width; /* Fine Timebase (FTB) Dividend/Divisor */ /* Dividend */ diff --git a/src/include/device/dram/ddr3.h b/src/include/device/dram/ddr3.h index b19c51c..4bf5058 100644 --- a/src/include/device/dram/ddr3.h +++ b/src/include/device/dram/ddr3.h @@ -37,6 +37,7 @@ * @{ */ #define TCK_1066MHZ 240 +#define TCK_933MHZ 275 #define TCK_800MHZ 320 #define TCK_666MHZ 384 #define TCK_533MHZ 480 @@ -137,6 +138,8 @@ typedef struct dimm_attr_st { u16 cas_supported; /* Flags extracted from SPD */ dimm_flags_t flags; + /* SDRAM width */ + u8 width; /* Number of ranks */ u8 ranks; /* Number or row address bits */ diff --git a/src/mainboard/lenovo/x230/Kconfig b/src/mainboard/lenovo/x230/Kconfig index d3aa6e9..6fd309e 100644 --- a/src/mainboard/lenovo/x230/Kconfig +++ b/src/mainboard/lenovo/x230/Kconfig @@ -3,7 +3,7 @@ if BOARD_LENOVO_X230 config BOARD_SPECIFIC_OPTIONS # dummy def_bool y select CPU_INTEL_SOCKET_RPGA989 - select NORTHBRIDGE_INTEL_IVYBRIDGE + select NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE select SOUTHBRIDGE_INTEL_C216 select EC_LENOVO_PMH7 select EC_LENOVO_H8 @@ -47,10 +47,6 @@ config MMCONF_BASE_ADDRESS hex default 0xf0000000 -config CACHE_ROM_SIZE_OVERRIDE - hex - default 0x800000 - config IRQ_SLOT_COUNT int default 18 diff --git a/src/mainboard/lenovo/x230/romstage.c b/src/mainboard/lenovo/x230/romstage.c index 6e4e685..ff664f4 100644 --- a/src/mainboard/lenovo/x230/romstage.c +++ b/src/mainboard/lenovo/x230/romstage.c @@ -32,7 +32,7 @@ #include <cbmem.h> #include <console/console.h> #include "northbridge/intel/sandybridge/sandybridge.h" -#include "northbridge/intel/sandybridge/raminit.h" +#include "northbridge/intel/sandybridge/raminit_native.h" #include "southbridge/intel/bd82x6x/pch.h" #include "southbridge/intel/bd82x6x/gpio.h" #include <arch/cpu.h> @@ -108,66 +108,39 @@ static void rcba_config(void) RCBA32(BUC) = 0; } +static void +init_usb (void) +{ + write32 (DEFAULT_RCBABASE | 0x3598, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35a0, 0x04000201); + write32 (DEFAULT_RCBABASE | 0x35a4, 0x00000200); + write32 (DEFAULT_RCBABASE | 0x35a8, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35ac, 0x00000000); + write32 (DEFAULT_RCBABASE | 0x35b0, 0x00000000); + + write32 (DEFAULT_RCBABASE | 0x3560, 0x024c8001); + + outw (inw (DEFAULT_PMBASE | 0x003c) | 2, DEFAULT_PMBASE | 0x003c); + + write32 (DEFAULT_RCBABASE | 0x359c, 0x00000040); + pcie_write_config32 (PCI_DEV (0, 0x14, 0), 0xe4, 0x00000000); + outw (0x0000, DEFAULT_PMBASE | 0x003c); +} + + void main(unsigned long bist) { int boot_mode = 0; int cbmem_was_initted; u32 pm1_cnt; u16 pm1_sts; + spd_raw_data spd[4]; if (MCHBAR16(SSKPD) == 0xCAFE) { outb(0x6, 0xcf9); hlt (); } - struct pei_data pei_data = { - .pei_version = PEI_VERSION, - .mchbar = DEFAULT_MCHBAR, - .dmibar = DEFAULT_DMIBAR, - .epbar = DEFAULT_EPBAR, - .pciexbar = CONFIG_MMCONF_BASE_ADDRESS, - .smbusbar = SMBUS_IO_BASE, - .wdbbar = 0x4000000, - .wdbsize = 0x1000, - .hpet_address = CONFIG_HPET_ADDRESS, - .rcba = DEFAULT_RCBABASE, - .pmbase = DEFAULT_PMBASE, - .gpiobase = DEFAULT_GPIOBASE, - .thermalbase = 0xfed08000, - .system_type = 0, // 0 Mobile, 1 Desktop/Server - .tseg_size = CONFIG_SMM_TSEG_SIZE, - .spd_addresses = { 0xA0, 0x00,0xA2,0x00 }, - .ts_addresses = { 0x00, 0x00, 0x00, 0x00 }, - .ec_present = 1, - .gbe_enable = 1, - .ddr3lv_support = 0, - // 0 = leave channel enabled - // 1 = disable dimm 0 on channel - // 2 = disable dimm 1 on channel - // 3 = disable dimm 0+1 on channel - .dimm_channel0_disabled = 2, - .dimm_channel1_disabled = 2, - .max_ddr3_freq = 1600, - .usb_port_config = { - /* enabled usb oc pin length */ - { 1, 0, 0x0080 }, /* P0 (left, fan side), OC 0 */ - { 1, 1, 0x0080 }, /* P1 (left touchpad side), OC 1 */ - { 1, 3, 0x0080 }, /* P2: dock, OC 3 */ - { 1, 0, 0x0040 }, /* P3: wwan, no OC */ - { 1, 0, 0x0080 }, /* P4: Wacom tablet on X230t, otherwise empty */ - { 1, 0, 0x0080 }, /* P5: Expresscard, no OC */ - { 0, 0, 0x0000 }, /* P6: Empty */ - { 1, 0, 0x0080 }, /* P7: dock, no OC */ - { 0, 0, 0x0000 }, /* P8: Empty */ - { 1, 5, 0x0080 }, /* P9: Right (EHCI debug), OC 5 */ - { 1, 0, 0x0040 }, /* P10: fingerprint reader, no OC */ - { 1, 0, 0x0040 }, /* P11: bluetooth, no OC. */ - { 1, 0, 0x0040 }, /* P12: wlan, no OC */ - { 1, 0, 0x0080 }, /* P13: webcam, no OC */ - }, - .ddr_refresh_rate_config = 2, /* Force double refresh rate */ - }; - timestamp_init(get_initial_timestamp()); timestamp_add_now(TS_START_ROMSTAGE); @@ -182,6 +155,8 @@ void main(unsigned long bist) setup_pch_gpios(&x230_gpio_map); + init_usb(); + /* Initialize console device(s) */ console_init(); @@ -217,31 +192,16 @@ void main(unsigned long bist) /* Enable SPD ROMs and DDR-III DRAM */ enable_smbus(); - /* Prepare USB controller early in S3 resume */ - if (boot_mode == 2) - enable_usb_bar(); - post_code(0x39); post_code(0x3a); - pei_data.boot_mode = boot_mode; timestamp_add_now(TS_BEFORE_INITRAM); - /* MRC.bin has a bug and sometimes halts (instead of reboot?). - */ - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) & ~(1 << 5); /* reset */ - outw((0 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* let timer go */ - } - - sdram_initialize(&pei_data); + memset (spd, 0, sizeof (spd)); + read_spd (&spd[0], 0x50); + read_spd (&spd[2], 0x51); - if (boot_mode != 2) - { - RCBA32(GCS) = RCBA32(GCS) | (1 << 5); /* No reset */ - outw((1 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */ - } + init_dram_ddr3 (spd, 1, TCK_800MHZ); timestamp_add_now(TS_AFTER_INITRAM); post_code(0x3c); @@ -254,8 +214,8 @@ void main(unsigned long bist) MCHBAR16(SSKPD) = 0xCAFE; cbmem_was_initted = !cbmem_recovery(boot_mode==2); - if (boot_mode!=2) - save_mrc_data(&pei_data); +// if (boot_mode!=2) +// save_mrc_data(&pei_data); #if CONFIG_HAVE_ACPI_RESUME /* If there is no high memory area, we didn't boot before, so diff --git a/src/northbridge/intel/Makefile.inc b/src/northbridge/intel/Makefile.inc index 808a1b2..f1c2540 100644 --- a/src/northbridge/intel/Makefile.inc +++ b/src/northbridge/intel/Makefile.inc @@ -15,6 +15,7 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I5000) += i5000 subdirs-$(CONFIG_NORTHBRIDGE_INTEL_NEHALEM) += nehalem subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += sandybridge +subdirs-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_HASWELL) += haswell subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_SANDYBRIDGE) += fsp_sandybridge subdirs-$(CONFIG_NORTHBRIDGE_INTEL_FSP_IVYBRIDGE) += fsp_sandybridge diff --git a/src/northbridge/intel/sandybridge/Kconfig b/src/northbridge/intel/sandybridge/Kconfig index fb92e40..9bac706 100644 --- a/src/northbridge/intel/sandybridge/Kconfig +++ b/src/northbridge/intel/sandybridge/Kconfig @@ -31,7 +31,14 @@ config NORTHBRIDGE_INTEL_IVYBRIDGE select MMCONF_SUPPORT_DEFAULT select CPU_INTEL_MODEL_306AX -if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE +config NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + bool + select CACHE_MRC_BIN + select MMCONF_SUPPORT + select MMCONF_SUPPORT_DEFAULT + select CPU_INTEL_MODEL_306AX + +if NORTHBRIDGE_INTEL_SANDYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE || NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config VGA_BIOS_ID string @@ -52,7 +59,8 @@ config MRC_CACHE_SIZE config DCACHE_RAM_BASE hex - default 0xff7e0000 + default 0xff7e0000 if !NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE + default 0xfefe0000 if NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE config DCACHE_RAM_SIZE hex diff --git a/src/northbridge/intel/sandybridge/Makefile.inc b/src/northbridge/intel/sandybridge/Makefile.inc index 6655e2a..ae20de5 100644 --- a/src/northbridge/intel/sandybridge/Makefile.inc +++ b/src/northbridge/intel/sandybridge/Makefile.inc @@ -26,7 +26,10 @@ ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c ramstage-y += mrccache.c romstage-y += ram_calc.c -romstage-y += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += raminit.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += raminit_native.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += ../../../device/dram/ddr3.c romstage-y += mrccache.c romstage-y += early_init.c romstage-y += report_platform.c diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index d271a1a..a82a284 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -650,9 +650,7 @@ static void gma_func0_init(struct device *dev) physbase = pci_read_config32(dev, 0x5c) & ~0xf; graphics_base = dev->resource_list[1].base; - int lightup_ok = i915lightup(conf, physbase, iobase, mmiobase, graphics_base); - if (lightup_ok) - gfx_set_init_done(1); + i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif } diff --git a/src/northbridge/intel/sandybridge/raminit_native.c b/src/northbridge/intel/sandybridge/raminit_native.c new file mode 100644 index 0000000..1422e5c --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.c @@ -0,0 +1,3766 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Damien Zammit <damien(a)zamaudio.com> + * Copyright (C) 2014 Vladimir Serbinenko <phcoder(a)gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <console/usb.h> +#include <bootmode.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include "raminit_native.h" +#include "sandybridge.h" +#include <delay.h> + +/* Management Engine is in the southbridge */ +#include "southbridge/intel/bd82x6x/me.h" +/* For SPD. */ +#include "southbridge/intel/bd82x6x/smbus.h" +#include "arch/cpu.h" +#include "cpu/x86/msr.h" + +/* FIXME: no ECC support. */ +/* FIXME: no support for 3-channel chipsets. */ +/* FIXME: no S3. */ +/* FIXME: no timing caching. */ + +#define BASEFREQ 133 +#define tDLLK 512 + +#define IS_SANDY_CPU(x) ((x & 0xffff0) == 0x206a0) +#define IS_SANDY_CPU_C(x) ((x & 0xf) == 4) +#define IS_SANDY_CPU_D0(x) ((x & 0xf) == 5) +#define IS_SANDY_CPU_D1(x) ((x & 0xf) == 6) +#define IS_SANDY_CPU_D2(x) ((x & 0xf) == 7) + +#define IS_IVY_CPU(x) ((x & 0xffff0) == 0x306a0) +#define IS_IVY_CPU_C(x) ((x & 0xf) == 4) +#define IS_IVY_CPU_K(x) ((x & 0xf) == 5) +#define IS_IVY_CPU_D(x) ((x & 0xf) == 6) +#define IS_IVY_CPU_E(x) ((x & 0xf) >= 8) + +#define NUM_CHANNELS 2 +#define NUM_SLOTRANKS 4 +#define NUM_SLOTS 2 +#define NUM_LANES 8 + +typedef struct odtmap_st { + u16 rttwr; + u16 rttnom; +} odtmap; + +typedef struct dimm_info_st { + dimm_attr dimm[NUM_CHANNELS][NUM_SLOTS]; +} dimm_info; + +struct ram_rank_timings { + /* Register 4024. One byte per slotrank. */ + u8 val_4024; + /* Register 4028. One nibble per slotrank. */ + u8 val_4028; + + int val_320c; + + struct ram_lane_timings { + /* lane register offset 0x10. */ + u16 timA; /* bits 0 - 5, bits 16 - 18 */ + u8 rising; /* bits 8 - 14 */ + u8 falling; /* bits 20 - 26. */ + + /* lane register offset 0x20. */ + int timC; /* bit 0 - 5, 19. */ + u16 timB; /* bits 8 - 13, 15 - 17. */ + } lanes[NUM_LANES]; +}; + +typedef struct ramctr_timing_st { + int mobile; + + enum spd_memory_type dram_type; + u16 cas_supported; + /* tLatencies are in units of ns, scaled by x256 */ + u32 tCK; + u32 tAA; + u32 tWR; + u32 tRCD; + u32 tRRD; + u32 tRP; + u32 tRAS; + u32 tRC; + u32 tRFC; + u32 tWTR; + u32 tRTP; + u32 tFAW; + /* Latencies in terms of clock cycles + * They are saved separately as they are needed for DRAM MRS commands*/ + u8 CAS; /* CAS read latency */ + u8 CWL; /* CAS write latency */ + /* Number of dimms currently connected */ + u8 n_dimms; + + u32 tREFI; + u32 tMOD; + u32 tXSOffset; + u32 tWLO; + u32 tCKE; + u32 tXPDLL; + u32 tXP; + u32 tAONPD; + + u32 delay1; + u32 delay2; + u16 reg_5064b0; /* bits 0-11. */ + + u8 eccsupport; + u8 dualchannel; + + u8 rankmap[NUM_CHANNELS]; + int ref_card_offset[NUM_CHANNELS]; + + int reg_c14_offset; + + int edge_offset[3]; + int timC_offset[3]; + + int extended_temperature_range; + int auto_self_refresh; + + int rank_mirror[NUM_CHANNELS][NUM_SLOTRANKS]; + + struct ram_rank_timings timings[NUM_CHANNELS][NUM_SLOTRANKS]; +} ramctr_timing; + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) +#define NORTHBRIDGE PCI_DEV(0, 0x0, 0) +#define FOR_ALL_LANES for (lane = 0; lane < NUM_LANES; lane++) +#define FOR_ALL_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) +#define FOR_ALL_POPULATED_RANKS for (slotrank = 0; slotrank < NUM_SLOTRANKS; slotrank++) if (ctrl->rankmap[channel] & (1 << slotrank)) +#define FOR_ALL_POPULATED_CHANNELS for (channel = 0; channel < NUM_CHANNELS; channel++) if (ctrl->rankmap[channel]) +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define MAX_EDGE_TIMING 71 +#define MAX_TIMC 127 +#define MAX_TIMB 511 +#define MAX_TIMA 127 + +static void program_timings(ramctr_timing * ctrl, int channel); + +static const char *ecc_decoder[] = { + "inactive", + "active on IO", + "disabled on IO", + "active" +}; + +static void wait_txt_clear(void) +{ + struct cpuid_result cp; + + cp = cpuid_ext(0x1, 0x0); + /* Check if TXT is supported? */ + if (!(cp.ecx & 0x40)) + return; + /* Some TXT public bit. */ + if (!(read32(0xfed30010) & 1)) + return; + /* Wait for TXT clear. */ + while (!(read8(0xfed40000) & (1 << 7))) ; +} + +static void sfence(void) +{ + asm volatile ("sfence"); +} + +/* + * Dump in the log memory controller configuration as read from the memory + * controller registers. + */ +static void report_memory_config(void) +{ + u32 addr_decoder_common, addr_decode_ch[NUM_CHANNELS]; + int i; + + addr_decoder_common = MCHBAR32(0x5000); + addr_decode_ch[0] = MCHBAR32(0x5004); + addr_decode_ch[1] = MCHBAR32(0x5008); + + printk(BIOS_DEBUG, "memcfg DDR3 clock %d MHz\n", + (MCHBAR32(0x5e04) * 13333 * 2 + 50) / 100); + printk(BIOS_DEBUG, "memcfg channel assignment: A: %d, B % d, C % d\n", + addr_decoder_common & 3, (addr_decoder_common >> 2) & 3, + (addr_decoder_common >> 4) & 3); + + for (i = 0; i < ARRAY_SIZE(addr_decode_ch); i++) { + u32 ch_conf = addr_decode_ch[i]; + printk(BIOS_DEBUG, "memcfg channel[%d] config (%8.8x):\n", i, + ch_conf); + printk(BIOS_DEBUG, " ECC %s\n", + ecc_decoder[(ch_conf >> 24) & 3]); + printk(BIOS_DEBUG, " enhanced interleave mode %s\n", + ((ch_conf >> 22) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " rank interleave %s\n", + ((ch_conf >> 21) & 1) ? "on" : "off"); + printk(BIOS_DEBUG, " DIMMA %d MB width x%d %s rank%s\n", + ((ch_conf >> 0) & 0xff) * 256, + ((ch_conf >> 19) & 1) ? 16 : 8, + ((ch_conf >> 17) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? "" : ", selected"); + printk(BIOS_DEBUG, " DIMMB %d MB width x%d %s rank%s\n", + ((ch_conf >> 8) & 0xff) * 256, + ((ch_conf >> 20) & 1) ? 16 : 8, + ((ch_conf >> 18) & 1) ? "dual" : "single", + ((ch_conf >> 16) & 1) ? ", selected" : ""); + } +} + +static void post_system_agent_init(void) +{ + /* If PCIe init is skipped, set the PEG clock gating */ + MCHBAR32(0x7010) = MCHBAR32(0x7010) | 0x01; +} + +void read_spd(spd_raw_data * spd, u8 addr) +{ + int j; + for (j = 0; j < 256; j++) + (*spd)[j] = do_smbus_read_byte(SMBUS_IO_BASE, addr, j); +} + +static void dram_find_spds_ddr3(spd_raw_data * spd, dimm_info * dimm, + ramctr_timing * ctrl) +{ + int dimms = 0; + int channel, slot, spd_slot; + + memset (ctrl->rankmap, 0, sizeof (ctrl->rankmap)); + + ctrl->extended_temperature_range = 1; + ctrl->auto_self_refresh = 1; + + FOR_ALL_CHANNELS { + int ref_card[NUM_SLOTS]; + for (slot = 0; slot < NUM_SLOTS; slot++) { + spd_slot = 2 * channel + slot; + spd_decode_ddr3(&dimm->dimm[channel][slot], spd[spd_slot]); + if (dimm->dimm[channel][slot].dram_type != SPD_MEMORY_TYPE_SDRAM_DDR3) { + // set dimm invalid + dimm->dimm[channel][slot].ranks = 0; + dimm->dimm[channel][slot].size_mb = 0; + continue; + } + + dram_print_spd_ddr3(&dimm->dimm[channel][slot]); + dimms++; + ctrl->rank_mirror[channel][slot * 2] = 0; + ctrl->rank_mirror[channel][slot * 2 + 1] = spd[spd_slot][0x3f] & 1; + + ctrl->auto_self_refresh &= (spd[spd_slot][31] >> 2) & 1; + ctrl->extended_temperature_range &= spd[spd_slot][31] & 1; + + ctrl->rankmap[channel] = ((1 << dimm->dimm[channel][slot].ranks) - 1) << (2 * slot); + ref_card[channel] = spd[spd_slot][62] & 0x1f;; + printk(BIOS_DEBUG, "rankmap[%d] = 0x%x\n", channel, ctrl->rankmap[channel]); + } + if ((ctrl->rankmap[channel] & 3) && (ctrl->rankmap[channel] & 0xc) + && ref_card[0] <= 5 && ref_card[1] <= 5) { + const int ref_card_offset_table[6][6] = { + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 2, 2, }, + { 0, 0, 0, 0, 1, 1, }, + { 2, 2, 2, 1, 0, 0, }, + { 2, 2, 2, 1, 0, 0, }, + }; + ctrl->ref_card_offset[channel] = ref_card_offset_table[ref_card[0]][ref_card[1]]; + } else + ctrl->ref_card_offset[channel] = 0; + } + + if (!dimms) + die("No DIMMs were found"); +} + +static void dram_find_common_params(const dimm_info * dimms, + ramctr_timing * ctrl) +{ + size_t valid_dimms; + int channel, slot; + ctrl->cas_supported = 0xff; + valid_dimms = 0; + FOR_ALL_CHANNELS + for (slot = 0; slot < 2; slot++) { + const dimm_attr *dimm = &dimms->dimm[channel][slot]; + if (dimm->dram_type == SPD_MEMORY_TYPE_UNDEFINED) + continue; + valid_dimms++; + + if (valid_dimms == 1) { + /* First DIMM defines the type of DIMM */ + ctrl->dram_type = dimm->dram_type; + } else { + /* Check if we have mismatched DIMMs */ + if (ctrl->dram_type != dimm->dram_type) + die("Mismatched DIMM Types"); + } + /* Find all possible CAS combinations */ + ctrl->cas_supported &= dimm->cas_supported; + + /* Find the smallest common latencies supported by all DIMMs */ + ctrl->tCK = MAX(ctrl->tCK, dimm->tCK); + ctrl->tAA = MAX(ctrl->tAA, dimm->tAA); + ctrl->tWR = MAX(ctrl->tWR, dimm->tWR); + ctrl->tRCD = MAX(ctrl->tRCD, dimm->tRCD); + ctrl->tRRD = MAX(ctrl->tRRD, dimm->tRRD); + ctrl->tRP = MAX(ctrl->tRP, dimm->tRP); + ctrl->tRAS = MAX(ctrl->tRAS, dimm->tRAS); + ctrl->tRC = MAX(ctrl->tRC, dimm->tRC); + ctrl->tRFC = MAX(ctrl->tRFC, dimm->tRFC); + ctrl->tWTR = MAX(ctrl->tWTR, dimm->tWTR); + ctrl->tRTP = MAX(ctrl->tRTP, dimm->tRTP); + ctrl->tFAW = MAX(ctrl->tFAW, dimm->tFAW); + } + + ctrl->n_dimms = valid_dimms; + if (!ctrl->cas_supported) + die("Unsupported DIMM combination. " + "DIMMS do not support common CAS latency"); + if (!valid_dimms) + die("No valid DIMMs found"); + + ctrl->dualchannel = + (pcie_read_config32(PCI_DEV(0, 0, 0), 0xE4) & 0x4000) >> 14; + if (ctrl->dualchannel) { + printk(BIOS_DEBUG, "Dual channel supported\n"); + } else { + printk(BIOS_DEBUG, "Dual channel not supported\n"); + } +} + +static u8 get_CWL(u8 CAS) +{ + /* Get CWL based on CAS using the following rule: + * _________________________________________ + * CAS: | 4T | 5T | 6T | 7T | 8T | 9T | 10T | 11T | + * CWL: | 5T | 5T | 5T | 6T | 6T | 7T | 7T | 8T | + */ + static const u8 cas_cwl_map[] = { 5, 5, 5, 6, 6, 7, 7, 8 }; + if (CAS > 11) + return 8; + return cas_cwl_map[CAS - 4]; +} + +static u32 get_REFI(u32 tCK) +{ + /* Get REFI based on MCU frequency using the following rule: + * _________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * REFI: | 3120 | 4160 | 5200 | 6240 | 7280 | 8320 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u32 frq_refi_map[] = + { 3120, 4160, 5200, 6240, 7280, 8320 }; + if (FRQ > 8) + return 8320; + return frq_refi_map[FRQ - 3]; +} + +static u8 get_XSOffset(u32 tCK) +{ + /* Get XSOffset based on MCU frequency using the following rule: + * _________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XSOffset : | 4 | 6 | 7 | 8 | 10 | 11 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xs_map[] = { 4, 6, 7, 8, 10, 11 }; + if (FRQ > 8) + return 11; + return frq_xs_map[FRQ - 3]; +} + +static u8 get_MOD(u32 tCK) +{ + /* Get MOD based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * MOD : | 12 | 12 | 12 | 12 | 15 | 16 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_mod_map[] = { 12, 12, 12, 12, 15, 16 }; + if (FRQ > 8) + return 16; + return frq_mod_map[FRQ - 3]; +} + +static u8 get_WLO(u32 tCK) +{ + /* Get WLO based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * WLO : | 4 | 5 | 6 | 6 | 8 | 8 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_wlo_map[] = { 4, 5, 6, 6, 8, 8 }; + if (FRQ > 8) + return 8; + return frq_wlo_map[FRQ - 3]; +} + +static u8 get_CKE(u32 tCK) +{ + /* Get CKE based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * CKE : | 3 | 3 | 4 | 4 | 5 | 6 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_cke_map[] = { 3, 3, 4, 4, 5, 6 }; + if (FRQ > 8) + return 6; + return frq_cke_map[FRQ - 3]; +} + +static u8 get_XPDLL(u32 tCK) +{ + /* Get XPDLL based on MCU frequency using the following rule: + * _____________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XPDLL : | 10 | 13 | 16 | 20 | 23 | 26 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xpdll_map[] = { 10, 13, 16, 20, 23, 26 }; + if (FRQ > 8) + return 26; + return frq_xpdll_map[FRQ - 3]; +} + +static u8 get_XP(u32 tCK) +{ + /* Get XP based on MCU frequency using the following rule: + * _______________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * XP : | 3 | 4 | 4 | 5 | 6 | 7 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_xp_map[] = { 3, 4, 4, 5, 6, 7 }; + if (FRQ > 8) + return 7; + return frq_xp_map[FRQ - 3]; +} + +static u8 get_AONPD(u32 tCK) +{ + /* Get AONPD based on MCU frequency using the following rule: + * ________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * AONPD : | 4 | 5 | 6 | 8 | 8 | 10 | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u8 frq_aonpd_map[] = { 4, 5, 6, 8, 8, 10 }; + if (FRQ > 8) + return 10; + return frq_aonpd_map[FRQ - 3]; +} + +static u32 get_COMP2(u32 tCK) +{ + /* Get COMP2 based on MCU frequency using the following rule: + * ___________________________________________________________ + * FRQ : | 3 | 4 | 5 | 6 | 7 | 8 | + * COMP : | D6BEDCC | CE7C34C | CA57A4C | C6369CC | C42514C | C21410C | + */ + u32 FRQ = (u32) 256000 / (tCK * BASEFREQ); + static const u32 frq_comp2_map[] = { 0xD6BEDCC, 0xCE7C34C, 0xCA57A4C, + 0xC6369CC, 0xC42514C, 0xC21410C + }; + if (FRQ > 8) + return 0xD6BEDCC; + return frq_comp2_map[FRQ - 3]; +} + +static void dram_timing(ramctr_timing * ctrl) +{ + u8 val; + u32 val32; + + /* Maximum supported DDR3 frequency is 1066MHz (DDR3 2133) so make sure + * we cap it if we have faster DIMMs. + * Then, align it to the closest JEDEC standard frequency */ + if (ctrl->tCK <= TCK_1066MHZ) { + ctrl->tCK = TCK_1066MHZ; + ctrl->delay1 = 16; + ctrl->delay2 = 8; + ctrl->edge_offset[0] = 16; + ctrl->edge_offset[1] = 7; + ctrl->edge_offset[2] = 7; + ctrl->timC_offset[0] = 18; + ctrl->timC_offset[1] = 7; + ctrl->timC_offset[2] = 7; + ctrl->reg_c14_offset = 16; + ctrl->reg_5064b0 = 0x218; + } else if (ctrl->tCK <= TCK_933MHZ) { + ctrl->tCK = TCK_933MHZ; + ctrl->delay1 = 15; + ctrl->delay2 = 8; + ctrl->edge_offset[0] = 14; + ctrl->edge_offset[1] = 6; + ctrl->edge_offset[2] = 6; + ctrl->timC_offset[0] = 15; + ctrl->timC_offset[1] = 6; + ctrl->timC_offset[2] = 6; + ctrl->reg_c14_offset = 14; + ctrl->reg_5064b0 = 0x1d5; + } else if (ctrl->tCK <= TCK_800MHZ) { + ctrl->tCK = TCK_800MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 6; + ctrl->edge_offset[0] = 13; + ctrl->edge_offset[1] = 5; + ctrl->edge_offset[2] = 5; + ctrl->timC_offset[0] = 14; + ctrl->timC_offset[1] = 5; + ctrl->timC_offset[2] = 5; + ctrl->reg_c14_offset = 12; + ctrl->reg_5064b0 = 0x193; + } else if (ctrl->tCK <= TCK_666MHZ) { + ctrl->tCK = TCK_666MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 6; + ctrl->edge_offset[0] = 10; + ctrl->edge_offset[1] = 4; + ctrl->edge_offset[2] = 4; + ctrl->timC_offset[0] = 11; + ctrl->timC_offset[1] = 4; + ctrl->timC_offset[2] = 4; + ctrl->reg_c14_offset = 10; + ctrl->reg_5064b0 = 0x150; + } else { + ctrl->tCK = TCK_533MHZ; + ctrl->delay1 = 12; + ctrl->delay2 = 5; + ctrl->edge_offset[0] = 8; + ctrl->edge_offset[1] = 3; + ctrl->edge_offset[2] = 3; + ctrl->timC_offset[0] = 9; + ctrl->timC_offset[1] = 3; + ctrl->timC_offset[2] = 3; + ctrl->reg_c14_offset = 8; + ctrl->reg_5064b0 = 0x10d; + } + + val32 = (1000 << 8) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected DRAM frequency: %u MHz\n", val32); + + /* Find CAS and CWL latencies */ + val = (ctrl->tAA + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Minimum CAS latency : %uT\n", val); + /* Find lowest supported CAS latency that satisfies the minimum value */ + while (!((ctrl->cas_supported >> (val - 4)) & 1) + && (ctrl->cas_supported >> (val - 4))) { + val++; + } + /* Is CAS supported */ + if (!(ctrl->cas_supported & (1 << (val - 4)))) + printk(BIOS_DEBUG, "CAS not supported\n"); + printk(BIOS_DEBUG, "Selected CAS latency : %uT\n", val); + ctrl->CAS = val; + ctrl->CWL = get_CWL(ctrl->CAS); + printk(BIOS_DEBUG, "Selected CWL latency : %uT\n", ctrl->CWL); + + /* Find tRCD */ + ctrl->tRCD = (ctrl->tRCD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRCD : %uT\n", ctrl->tRCD); + + ctrl->tRP = (ctrl->tRP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRP : %uT\n", ctrl->tRP); + + /* Find tRAS */ + ctrl->tRAS = (ctrl->tRAS + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRAS : %uT\n", ctrl->tRAS); + + /* Find tWR */ + ctrl->tWR = (ctrl->tWR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWR : %uT\n", ctrl->tWR); + + /* Find tFAW */ + ctrl->tFAW = (ctrl->tFAW + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tFAW : %uT\n", ctrl->tFAW); + + /* Find tRRD */ + ctrl->tRRD = (ctrl->tRRD + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRRD : %uT\n", ctrl->tRRD); + + /* Find tRTP */ + ctrl->tRTP = (ctrl->tRTP + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRTP : %uT\n", ctrl->tRTP); + + /* Find tWTR */ + ctrl->tWTR = (ctrl->tWTR + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tWTR : %uT\n", ctrl->tWTR); + + /* Refresh-to-Active or Refresh-to-Refresh (tRFC) */ + ctrl->tRFC = (ctrl->tRFC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Selected tRFC : %uT\n", ctrl->tRFC); + + ctrl->tRC = (ctrl->tRC + ctrl->tCK - 1) / ctrl->tCK; + printk(BIOS_DEBUG, "Required tRC : %uT\n", ctrl->tRC); + + ctrl->tREFI = get_REFI(ctrl->tCK); + ctrl->tMOD = get_MOD(ctrl->tCK); + ctrl->tXSOffset = get_XSOffset(ctrl->tCK); + ctrl->tWLO = get_WLO(ctrl->tCK); + ctrl->tCKE = get_CKE(ctrl->tCK); + ctrl->tXPDLL = get_XPDLL(ctrl->tCK); + ctrl->tXP = get_XP(ctrl->tCK); + ctrl->tAONPD = get_AONPD(ctrl->tCK); +} + +static void dram_freq(ramctr_timing * ctrl) +{ + u8 val1, val2; + u32 reg1 = 0; + + /* Step 1 - Set target PCU frequency */ + + if (ctrl->tCK <= TCK_1066MHZ) { + val1 = 0x08; + ctrl->tCK = TCK_1066MHZ; + } else if (ctrl->tCK <= TCK_933MHZ) { + val1 = 0x07; + ctrl->tCK = TCK_933MHZ; + } else if (ctrl->tCK <= TCK_800MHZ) { + val1 = 0x06; + ctrl->tCK = TCK_800MHZ; + } else if (ctrl->tCK <= TCK_666MHZ) { + val1 = 0x05; + ctrl->tCK = TCK_666MHZ; + } else { + val1 = 0x04; + ctrl->tCK = TCK_533MHZ; + } + + /* Step 2 - Select frequency in the MCU */ + reg1 = val1; + reg1 |= 0x80000000; // set running bit + MCHBAR32(0x5e00) = reg1; + while (reg1 & 0x80000000) { + printk(BIOS_DEBUG, " PLL busy..."); + reg1 = MCHBAR32(0x5e00); + } + printk(BIOS_DEBUG, "done\n"); + + /* Step 3 - Verify lock frequency */ + reg1 = MCHBAR32(0x5e04); + val2 = (u8) reg1; + if (val2 > val1) { + printk(BIOS_DEBUG, "Lock frequency is lower, recalculating\n"); + switch (val2) { + case 8: + ctrl->tCK = TCK_1066MHZ; + break; + case 7: + ctrl->tCK = TCK_933MHZ; + break; + case 6: + ctrl->tCK = TCK_800MHZ; + break; + case 5: + ctrl->tCK = TCK_666MHZ; + break; + case 4: + ctrl->tCK = TCK_533MHZ; + break; + default: + printk(BIOS_DEBUG, "ERROR: PLL is off or unknown\n"); + break; + } + dram_timing(ctrl); // recalculate timings + } + printk(BIOS_DEBUG, "MCU frequency is set at : %d MHz\n", + (1000 << 8) / ctrl->tCK); +} + +static void dram_xover(ramctr_timing * ctrl) +{ + u32 reg; + int channel; + + FOR_ALL_CHANNELS { + // enable xover clk + printk(BIOS_DEBUG, "[%x] = %x\n", channel * 0x100 + 0xc14, + (ctrl->rankmap[channel] << 24)); + MCHBAR32(channel * 0x100 + 0xc14) = (ctrl->rankmap[channel] << 24); + + // enable xover ctl + reg = 0; + if (ctrl->rankmap[channel] & 0x5) { + reg |= 0x20000; + } + if (ctrl->rankmap[channel] & 0xa) { + reg |= 0x4000000; + } + // enable xover cmd + reg |= 0x4000; + printk(BIOS_DEBUG, "[%x] = %x\n", 0x100 * channel + 0x320c, + reg); + MCHBAR32(0x100 * channel + 0x320c) = reg; + } +} + +static void dram_timing_regs(ramctr_timing * ctrl) +{ + u32 reg, addr, val32, cpu, stretch; + struct cpuid_result cpures; + int channel; + + FOR_ALL_CHANNELS { + // DBP + reg = 0; + reg |= ctrl->tRCD; + reg |= (ctrl->tRP << 4); + reg |= (ctrl->CAS << 8); + reg |= (ctrl->CWL << 12); + reg |= (ctrl->tRAS << 16); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4000, + reg); + MCHBAR32(0x400 * channel + 0x4000) = reg; + + // RAP + reg = 0; + reg |= ctrl->tRRD; + reg |= (ctrl->tRTP << 4); + reg |= (ctrl->tCKE << 8); + reg |= (ctrl->tWTR << 12); + reg |= (ctrl->tFAW << 16); + reg |= (ctrl->tWR << 24); + reg |= (3 << 30); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4004, + reg); + MCHBAR32(0x400 * channel + 0x4004) = reg; + + // OTHP + addr = 0x400 * channel + 0x400c; + reg = 0; + reg |= ctrl->tXPDLL; + reg |= (ctrl->tXP << 5); + reg |= (ctrl->tAONPD << 8); + reg |= 0xa0000; + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + + MCHBAR32(0x400 * channel + 0x4014) = 0; + + MCHBAR32(addr) |= 0x00020000; + + // ODT stretch + reg = 0; + + cpures = cpuid(0); + cpu = cpures.eax; + if (IS_IVY_CPU(cpu) + || (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_D2(cpu))) { + stretch = 2; + addr = 0x400 * channel + 0x400c; + printk(BIOS_DEBUG, "[%x] = %x\n", + 0x400 * channel + 0x400c, reg); + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0xc0000) | (stretch << 18); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0x30000) | (stretch << 16); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + + } else if (IS_SANDY_CPU(cpu) && IS_SANDY_CPU_C(cpu)) { + stretch = 3; + addr = 0x400 * channel + 0x401c; + reg = MCHBAR32(addr); + + if (((ctrl->rankmap[channel] & 3) == 0) + || (ctrl->rankmap[channel] & 0xc) == 0) { + + // Rank 0 - operate on rank 2 + reg = (reg & ~0x3000) | (stretch << 12); + + // Rank 2 - operate on rank 0 + reg = (reg & ~0xc00) | (stretch << 10); + + printk(BIOS_DEBUG, "[%x] = %x\n", addr, reg); + MCHBAR32(addr) = reg; + } + } else { + stretch = 0; + } + + // REFI + reg = 0; + val32 = ctrl->tREFI; + reg = (reg & ~0xffff) | val32; + val32 = ctrl->tRFC; + reg = (reg & ~0x1ff0000) | (val32 << 16); + val32 = (u32) (ctrl->tREFI * 9) / 1024; + reg = (reg & ~0xfe000000) | (val32 << 25); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x4298, + reg); + MCHBAR32(0x400 * channel + 0x4298) = reg; + + MCHBAR32(0x400 * channel + 0x4294) |= 0xff; + + // SRFTP + reg = 0; + val32 = tDLLK; + reg = (reg & ~0xfff) | val32; + val32 = ctrl->tXSOffset; + reg = (reg & ~0xf000) | (val32 << 12); + val32 = tDLLK - ctrl->tXSOffset; + reg = (reg & ~0x3ff0000) | (val32 << 16); + val32 = ctrl->tMOD - 8; + reg = (reg & ~0xf0000000) | (val32 << 28); + printk(BIOS_DEBUG, "[%x] = %x\n", 0x400 * channel + 0x42a4, + reg); + MCHBAR32(0x400 * channel + 0x42a4) = reg; + } +} + +static void dram_dimm_mapping(dimm_info * info, ramctr_timing * ctrl) +{ + int t; + u32 reg, val32; + int channel; + + FOR_ALL_CHANNELS { + dimm_attr *dimmA = 0; + dimm_attr *dimmB = 0; + reg = 0; + val32 = 0; + if (info->dimm[channel][0].size_mb >= + info->dimm[channel][1].size_mb) { + // dimm 0 is bigger, set it to dimmA + dimmA = &info->dimm[channel][0]; + dimmB = &info->dimm[channel][1]; + reg |= (0 << 16); + } else { + // dimm 1 is bigger, set it to dimmA + dimmA = &info->dimm[channel][1]; + dimmB = &info->dimm[channel][0]; + reg |= (1 << 16); + // swap dimm info + t = ctrl->rank_mirror[channel][1]; + ctrl->rank_mirror[channel][1] = + ctrl->rank_mirror[channel][3]; + ctrl->rank_mirror[channel][3] = t; + } + // dimmA + if (dimmA && (dimmA->ranks > 0)) { + val32 = dimmA->size_mb / 256; + reg = (reg & ~0xff) | val32; + val32 = dimmA->ranks - 1; + reg = (reg & ~0x20000) | (val32 << 17); + val32 = (dimmA->width / 8) - 1; + reg = (reg & ~0x80000) | (val32 << 19); + } + // dimmB + if (dimmB && (dimmB->ranks > 0)) { + val32 = dimmB->size_mb / 256; + reg = (reg & ~0xff00) | (val32 << 8); + val32 = dimmB->ranks - 1; + reg = (reg & ~0x40000) | (val32 << 18); + val32 = (dimmB->width / 8) - 1; + reg = (reg & ~0x100000) | (val32 << 20); + } + reg = (reg & ~0x200000) | (1 << 21); // rank interleave + reg = (reg & ~0x400000) | (1 << 22); // enhanced interleave + + // Set MAD-DIMM register + if ((dimmA && (dimmA->ranks > 0)) + || (dimmB && (dimmB->ranks > 0))) { + MCHBAR32(0x5004 + channel * 4) = reg; + } + } +} + +static void dram_zones(dimm_info * info, ramctr_timing * ctrl, int training) +{ + u32 reg, ch0size, ch1size; + u8 val; + reg = 0; + val = 0; + if (training) { + ch0size = + info->dimm[0][0].size_mb + info->dimm[0][1].size_mb ? 256 : 0; + ch1size = + info->dimm[1][0].size_mb + info->dimm[1][1].size_mb ? 256 : 0; + } else { + ch0size = info->dimm[0][0].size_mb + info->dimm[0][1].size_mb; + ch1size = info->dimm[1][0].size_mb + info->dimm[1][1].size_mb; + } + + if (ch0size >= ch1size) { + reg = MCHBAR32(0x5014); + val = ch1size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x24; + } else { + reg = MCHBAR32(0x5014); + val = ch0size / 256; + reg = (reg & ~0xff000000) | val << 24; + reg = (reg & ~0xff0000) | (2 * val) << 16; + MCHBAR32(0x5014) = reg; + MCHBAR32(0x5000) = 0x21; + } +} + +/* FIXME: this function bugs. */ +static void dram_memorymap(dimm_info * info, int me_uma_size) +{ + u32 reg, val, reclaim; + u32 tom, gfxstolen, gttsize; + size_t tsegsize, mmiosize, toludbase, touudbase, gfxstolenbase, gttbase, + tsegbase, mestolenbase; + size_t tsegbasedelta, remapbase, remaplimit; + uint16_t ggc; + + mmiosize = 0x400; + + ggc = pci_read_config16(NORTHBRIDGE, GGC); + if (!(ggc & 2)) { + gfxstolen = ((ggc >> 3) & 0x1f) * 32; + gttsize = ((ggc >> 8) & 0x3); + } else { + gfxstolen = 0; + gttsize = 0; + } + + tsegsize = CONFIG_SMM_TSEG_SIZE >> 20; + + tom = + info->dimm[0][0].size_mb + info->dimm[0][1].size_mb + + info->dimm[0][1].size_mb + info->dimm[1][1].size_mb; + + mestolenbase = tom - me_uma_size; + + toludbase = MIN(4096 - mmiosize, tom - me_uma_size); + gfxstolenbase = toludbase - gfxstolen; + gttbase = gfxstolenbase - gttsize; + + tsegbase = gttbase - tsegsize; + + // Round tsegbase down to nearest address aligned to tsegsize + tsegbasedelta = tsegbase & (tsegsize - 1); + tsegbase &= ~(tsegsize - 1); + + gttbase -= tsegbasedelta; + gfxstolenbase -= tsegbasedelta; + toludbase -= tsegbasedelta; + + // Test if it is possible to reclaim a hole in the ram addressing + if (tom - me_uma_size > toludbase) { + // Reclaim is possible + reclaim = 1; + remapbase = MAX(4096, tom - me_uma_size); + remaplimit = + remapbase + MIN(4096, tom - me_uma_size) - toludbase - 1; + touudbase = remaplimit + 1; + } else { + // Reclaim not possible + reclaim = 0; + touudbase = tom - me_uma_size; + } + + // Update memory map in pci-e configuration space + + // TOM (top of memory) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa0); + val = tom & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa0, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa4); + val = tom & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa4, reg); + + // TOLUD (top of low used dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xbc); + val = toludbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xbc, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xbc, reg); + + // TOUUD LSB (top of upper usable dram) + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xa8); + val = touudbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xa8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xa8, reg); + + // TOUUD MSB + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xac); + val = touudbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xac, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xac, reg); + + if (reclaim) { + // REMAP BASE + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x94); + val = remapbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x94, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x94, reg); + + // REMAP LIMIT + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x98); + val = remaplimit & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x98, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x98, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x9c); + val = remaplimit & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x9c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x9c, reg); + } + // TSEG + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb8); + val = tsegbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb8, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb8, reg); + + // GFX stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb0); + val = gfxstolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb0, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb0, reg); + + // GTT stolen memory + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0xb4); + val = gttbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0xb4, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0xb4, reg); + + if (me_uma_size) { + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x7c); + val = (0x80000 - me_uma_size) & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x7c, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x7c, reg); + + // ME base + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x70); + val = mestolenbase & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x70, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x70, reg); + + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x74); + val = mestolenbase & 0xfffff000; + reg = (reg & ~0x000fffff) | (val >> 12); + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x74, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x74, reg); + + // ME mask + reg = pcie_read_config32(PCI_DEV(0, 0, 0), 0x78); + val = (0x80000 - me_uma_size) & 0xfff; + reg = (reg & ~0xfff00000) | (val << 20); + reg = (reg & ~0x400) | (1 << 10); // set lockbit on ME mem + + reg = (reg & ~0x800) | (1 << 11); // set ME memory enable + printk(BIOS_DEBUG, "PCI:[%x] = %x\n", 0x78, reg); + pcie_write_config32(PCI_DEV(0, 0, 0), 0x78, reg); + } +} + +static void dram_ioregs(ramctr_timing * ctrl) +{ + u32 reg, comp2; + + int channel; + + // IO clock + FOR_ALL_CHANNELS { + MCHBAR32(0xc00 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO command + FOR_ALL_CHANNELS { + MCHBAR32(0x3200 + 0x100 * channel) = ctrl->rankmap[channel]; + } + + // IO control + FOR_ALL_POPULATED_CHANNELS { + program_timings(ctrl, channel); + } + + // Rcomp + printk(BIOS_DEBUG, "RCOMP..."); + reg = 0; + while (reg == 0) { + reg = MCHBAR32(0x5084) & 0x10000; + } + printk(BIOS_DEBUG, "done\n"); + + // Set comp2 + comp2 = get_COMP2(ctrl->tCK); + MCHBAR32(0x3714) = comp2; + printk(BIOS_DEBUG, "COMP2 done\n"); + + // Set comp1 + FOR_ALL_POPULATED_CHANNELS { + reg = MCHBAR32(0x1810 + channel * 0x100); //ch0 + reg = (reg & ~0xe00) | (1 << 9); //odt + reg = (reg & ~0xe00000) | (1 << 21); //clk drive up + reg = (reg & ~0x38000000) | (1 << 27); //ctl drive up + MCHBAR32(0x1810 + channel * 0x100) = reg; + } + printk(BIOS_DEBUG, "COMP1 done\n"); + + printk(BIOS_DEBUG, "FORCE RCOMP and wait 20us..."); + MCHBAR32(0x5f08) |= 0x100; + udelay(20); + printk(BIOS_DEBUG, "done\n"); +} + +static void wait_428c(int channel) +{ + while (1) { + if (read32(DEFAULT_MCHBAR | 0x428c | (channel << 10)) & 0x50) + return; + } +} + +static void write_reset(ramctr_timing * ctrl) +{ + int channel, slotrank; + + /* choose a populated channel. */ + channel = (ctrl->rankmap[0]) ? 0 : 1; + + wait_428c(channel); + + /* choose a populated rank. */ + slotrank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x80c01); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x400001); + wait_428c(channel); +} + +static void dram_jedecreset(ramctr_timing * ctrl) +{ + u32 reg, addr; + int channel; + + while (!(MCHBAR32(0x5084) & 0x10000)) ; + do { + reg = MCHBAR32(0x428c); + } while ((reg & 0x14) == 0); + + // Set state of memory controller + reg = 0x112; + MCHBAR32(0x5030) = reg; + MCHBAR32(0x4ea0) = 0; + reg |= 2; //ddr reset + MCHBAR32(0x5030) = reg; + + // Assert dimm reset signal + reg = MCHBAR32(0x5030); + reg &= ~0x2; + MCHBAR32(0x5030) = reg; + + // Wait 200us + udelay(200); + + // Deassert dimm reset signal + MCHBAR32(0x5030) |= 2; + + // Wait 500us + udelay(500); + + // Enable DCLK + MCHBAR32(0x5030) |= 4; + + // XXX Wait 20ns + udelay(1); + + FOR_ALL_CHANNELS { + // Set valid rank CKE + reg = 0; + reg = (reg & ~0xf) | ctrl->rankmap[channel]; + addr = 0x400 * channel + 0x42a0; + MCHBAR32(addr) = reg; + + // Wait 10ns for ranks to settle + //udelay(0.01); + + reg = (reg & ~0xf0) | (ctrl->rankmap[channel] << 4); + MCHBAR32(addr) = reg; + + // Write reset using a NOP + write_reset(ctrl); + } +} + +static odtmap get_ODT(ramctr_timing * ctrl, u8 rank) +{ + /* Get ODT based on rankmap: */ + int dimms_per_ch = 0; + int channel; + + FOR_ALL_CHANNELS { + dimms_per_ch = max ((ctrl->rankmap[channel] & 1) + + ((ctrl->rankmap[channel] >> 2) & 1), + dimms_per_ch); + } + + if (dimms_per_ch == 1) { + return (const odtmap){60, 60}; + } else if (dimms_per_ch == 2) { + return (const odtmap){120, 30}; + } else { + printk(BIOS_DEBUG, + "Huh, no dimms? m0 = %d m1 = %d dpc = %d\n", + ctrl->rankmap[0], + ctrl->rankmap[1], dimms_per_ch); + die(""); + } +} + +static void write_mrreg(ramctr_timing * ctrl, int channel, int slotrank, + int reg, u32 val) +{ + wait_428c(channel); + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + if (ctrl->rank_mirror[channel][slotrank]) { + reg = ((reg >> 1) & 1) | ((reg << 1) & 2); + val = (val & ~0x1f8) | ((val >> 1) & 0xa8) + | ((val & 0xa8) << 1); + } + + printk(BIOS_SPEW, "MRd: %x <= %x\n", reg, val); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x41001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x0f000); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | (reg << 20) | val | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); +} + +static u32 make_mr0(ramctr_timing * ctrl, u8 rank) +{ + u16 mr0reg, mch_cas, mch_wr; + static const u8 mch_wr_t[12] = { 1, 2, 3, 4, 0, 5, 0, 6, 0, 7, 0, 0 }; + mr0reg = 0x100; + + // Convert CAS to MCH register friendly + if (ctrl->CAS < 12) { + mch_cas = (u16) ((ctrl->CAS - 4) << 1); + } else { + mch_cas = (u16) (ctrl->CAS - 12); + mch_cas = ((mch_cas << 1) | 0x1); + } + + // Convert tWR to MCH register friendly + mch_wr = mch_wr_t[ctrl->tWR - 5]; + + mr0reg = (mr0reg & ~0x4) | (mch_cas & 0x1); + mr0reg = (mr0reg & ~0x70) | ((mch_cas & 0xe) << 3); + mr0reg = (mr0reg & ~0xe00) | (mch_wr << 9); + // Fast (desktop) 0x1 or slow (mobile) 0x0 + mr0reg = (mr0reg & ~0x1000) | (!ctrl->mobile << 12); + return mr0reg; +} + +static void dram_mr0(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS write_mrreg(ctrl, channel, rank, 0, + make_mr0(ctrl, rank)); +} + +static u32 encode_odt(u32 odt) +{ + switch (odt) { + case 30: + return (1 << 9) | (1 << 2); // RZQ/8, RZQ/4 + case 60: + return (1 << 2); // RZQ/4 + case 120: + return (1 << 6); // RZQ/2 + default: + case 0: + return 0; + } +} + +static u32 make_mr1(ramctr_timing * ctrl, u8 rank) +{ + odtmap odt; + u32 mr1reg; + + odt = get_ODT(ctrl, rank); + mr1reg = 0x2; + + mr1reg |= encode_odt(odt.rttnom); + + return mr1reg; +} + +static void dram_mr1(ramctr_timing * ctrl, u8 rank) +{ + u16 mr1reg; + int channel; + + mr1reg = make_mr1(ctrl, rank); + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 1, mr1reg); + } +} + +static void dram_mr2(ramctr_timing * ctrl, u8 rank) +{ + u16 pasr, cwl, mr2reg; + odtmap odt; + int channel; + int srt; + + pasr = 0; + cwl = ctrl->CWL - 5; + odt = get_ODT(ctrl, rank); + + srt = ctrl->extended_temperature_range && !ctrl->auto_self_refresh; + + mr2reg = 0; + mr2reg = (mr2reg & ~0x7) | pasr; + mr2reg = (mr2reg & ~0x38) | (cwl << 3); + mr2reg = (mr2reg & ~0x40) | (ctrl->auto_self_refresh << 6); + mr2reg = (mr2reg & ~0x80) | (srt << 7); + mr2reg |= (odt.rttwr / 60) << 9; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 2, mr2reg); + } +} + +static void dram_mr3(ramctr_timing * ctrl, u8 rank) +{ + int channel; + + FOR_ALL_CHANNELS { + write_mrreg(ctrl, channel, rank, 3, 0); + } +} + +static void dram_mrscommands(ramctr_timing * ctrl) +{ + u8 rank; + u32 reg, addr; + int channel; + + for (rank = 0; rank < 4; rank++) { + // MR2 + printk(BIOS_SPEW, "MR2 rank %d...", rank); + dram_mr2(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR3 + printk(BIOS_SPEW, "MR3 rank %d...", rank); + dram_mr3(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR1 + printk(BIOS_SPEW, "MR1 rank %d...", rank); + dram_mr1(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + + // MR0 + printk(BIOS_SPEW, "MR0 rank %d...", rank); + dram_mr0(ctrl, rank); + printk(BIOS_SPEW, "done\n"); + } + + write32(DEFAULT_MCHBAR + 0x4e20, 0x7); + write32(DEFAULT_MCHBAR + 0x4e30, 0xf1001); + write32(DEFAULT_MCHBAR + 0x4e00, 0x60002); + write32(DEFAULT_MCHBAR + 0x4e10, 0); + write32(DEFAULT_MCHBAR + 0x4e24, 0x1f003); + write32(DEFAULT_MCHBAR + 0x4e34, 0x1901001); + write32(DEFAULT_MCHBAR + 0x4e04, 0x60400); + write32(DEFAULT_MCHBAR + 0x4e14, 0x288); + write32(DEFAULT_MCHBAR + 0x4e84, 0x40004); + + // Drain + FOR_ALL_CHANNELS { + // Wait for ref drained + wait_428c(channel); + } + + // Refresh enable + MCHBAR32(0x5030) |= 8; + + FOR_ALL_POPULATED_CHANNELS { + addr = 0x400 * channel + 0x4020; + reg = MCHBAR32(addr); + reg &= ~0x200000; + MCHBAR32(addr) = reg; + + wait_428c(channel); + + rank = (ctrl->rankmap[channel] & 1) ? 0 : 2; + + // Drain + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (rank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x1); + + // Drain + wait_428c(channel); + } +} + +const u32 lane_registers[] = { + 0x0000, 0x0200, 0x0400, 0x0600, + 0x1000, 0x1200, 0x1400, 0x1600, + 0x0800 +}; + +static int clamp(int val, int low, int up) +{ + if (val < low) + return low; + if (val > up) + return up; + return val; +} + +static void program_timings(ramctr_timing * ctrl, int channel) +{ + u32 reg32, reg_4024, reg_c14, reg_c18, reg_4028; + int lane; + int slotrank, slot; + int full_shift = 0; + u16 slot320c[NUM_SLOTS]; + + FOR_ALL_POPULATED_RANKS if (full_shift < + -ctrl->timings[channel][slotrank].val_320c) + full_shift = -ctrl->timings[channel][slotrank].val_320c; + + for (slot = 0; slot < NUM_SLOTS; slot++) + switch ((ctrl->rankmap[channel] >> (2 * slot)) & 3) { + case 0: + default: + slot320c[slot] = 0x7f; + break; + case 1: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 0].val_320c + + full_shift; + break; + case 2: + slot320c[slot] = + ctrl->timings[channel][2 * slot + 1].val_320c + + full_shift; + break; + case 3: + slot320c[slot] = + (ctrl->timings[channel][2 * slot].val_320c + + ctrl->timings[channel][2 * slot + + 1].val_320c) / 2 + + full_shift; + break; + } + + reg32 = (1 << 17) | (1 << 14); + reg32 |= ((slot320c[0] & 0x3f) << 6) | ((slot320c[0] & 0x40) << 9); + reg32 |= (slot320c[1] & 0x7f) << 18; + reg32 |= (full_shift & 0x3f) | ((full_shift & 0x40) << 6); + + MCHBAR32(0x320c + 0x100 * channel) = reg32; + + reg_c14 = ctrl->rankmap[channel] << 24; + reg_c18 = 0; + + FOR_ALL_POPULATED_RANKS { + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + int offset_val_c14; + if (shift < 0) + shift = 0; + offset_val_c14 = ctrl->reg_c14_offset + shift; + reg_c14 |= (offset_val_c14 & 0x3f) << (6 * slotrank); + reg_c18 |= ((offset_val_c14 >> 6) & 1) << slotrank; + } + + MCHBAR32(0xc14 + channel * 0x100) = reg_c14; + MCHBAR32(0xc18 + channel * 0x100) = reg_c18; + + reg_4028 = MCHBAR32(0x4028 + 0x400 * channel); + reg_4028 &= 0xffff0000; + + reg_4024 = 0; + + FOR_ALL_POPULATED_RANKS { + int post_timA_min_high = 7, post_timA_max_high = 0; + int pre_timA_min_high = 7, pre_timA_max_high = 0; + int shift_402x = 0; + int shift = + ctrl->timings[channel][slotrank].val_320c + full_shift; + + if (shift < 0) + shift = 0; + + FOR_ALL_LANES { + if (post_timA_min_high > + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_min_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_min_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + if (post_timA_max_high < + ((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) >> 6)) + post_timA_max_high = + ((ctrl->timings[channel][slotrank]. + lanes[lane].timA + shift) >> 6); + if (pre_timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6)) + pre_timA_max_high = + (ctrl->timings[channel][slotrank]. + lanes[lane].timA >> 6); + } + + if (pre_timA_max_high - pre_timA_min_high < + post_timA_max_high - post_timA_min_high) + shift_402x = +1; + else if (pre_timA_max_high - pre_timA_min_high > + post_timA_max_high - post_timA_min_high) + shift_402x = -1; + + reg_4028 |= + (ctrl->timings[channel][slotrank].val_4028 + shift_402x - + post_timA_min_high) << (4 * slotrank); + reg_4024 |= + (ctrl->timings[channel][slotrank].val_4024 + + shift_402x) << (8 * slotrank); + + FOR_ALL_LANES { + MCHBAR32(lane_registers[lane] + 0x10 + 0x100 * channel + + 4 * slotrank) + = + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift) & 0x3f) + | + ((ctrl->timings[channel][slotrank].lanes[lane]. + rising + shift) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timA + shift - + (post_timA_min_high << 6)) & 0x1c0) << 10) + | (ctrl->timings[channel][slotrank].lanes[lane]. + falling << 20)); + + MCHBAR32(lane_registers[lane] + 0x20 + 0x100 * channel + + 4 * slotrank) + = + ((clamp + (ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift, 0, 127) & 0x3f) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x3f) << 8) + | + (((ctrl->timings[channel][slotrank].lanes[lane]. + timB + shift) & 0x1c0) << 9) + | + ((clamp + (ctrl->timings[channel][slotrank].lanes[lane]. + timC + shift, 0, 127) & 0x40) << 13)); + } + } + MCHBAR32(0x4024 + 0x400 * channel) = reg_4024; + MCHBAR32(0x4028 + 0x400 * channel) = reg_4028; +} + +static void test_timA(ramctr_timing * ctrl, int channel, int slotrank) +{ + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x4040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x100f | ((ctrl->CAS + 36) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); +} + +static int does_lane_work(ramctr_timing * ctrl, int channel, int slotrank, + int lane) +{ + u32 timA = ctrl->timings[channel][slotrank].lanes[lane].timA; + return ((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + channel * 0x100 + 4 + + ((timA / 32) & 1) * 4) + >> (timA % 32)) & 1); +} + +struct run { + int middle; + int end; + int start; + int all; +}; + +static struct run get_longest_zero_run(int *seq, int sz) +{ + int i, ls; + int bl = 0, bs = 0; + struct run ret; + + ls = 0; + for (i = 0; i < 2 * sz; i++) + if (seq[i % sz]) { + if (i - ls > bl) { + bl = i - ls; + bs = ls; + } + ls = i + 1; + } + if (bl == 0) { + ret.middle = sz / 2; + ret.start = 0; + ret.end = sz; + ret.all = 1; + return ret; + } + + ret.start = bs % sz; + ret.end = (bs + bl - 1) % sz; + ret.middle = (bs + (bl - 1) / 2) % sz; + ret.all = 0; + + return ret; +} + +static void discover_timA_coarse(ramctr_timing * ctrl, int channel, + int slotrank, int *upperA) +{ + int timA; + int statistics[NUM_LANES][128]; + int lane; + + for (timA = 0; timA < 128; timA++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = timA; + program_timings(ctrl, channel); + + test_timA(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timA] = + !does_lane_work(ctrl, channel, slotrank, lane); + printk(BIOS_SPEW, "Astat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timA, + statistics[lane][timA]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timA = rn.middle; + upperA[lane] = rn.end; + if (upperA[lane] < rn.middle) + upperA[lane] += 128; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + printk(BIOS_SPEW, "Aend: %d, %d, %d, %x\n", channel, slotrank, + lane, upperA[lane]); + } +} + +static void discover_timA_fine(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int timA_delta; + int statistics[NUM_LANES][51]; + int lane, i; + + memset(statistics, 0, sizeof(statistics)); + + for (timA_delta = -25; timA_delta <= 25; timA_delta++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA = upperA[lane] + timA_delta + 0x40; + program_timings(ctrl, channel); + + for (i = 0; i < 100; i++) { + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + statistics[lane][timA_delta + 25] += + does_lane_work(ctrl, channel, slotrank, + lane); + } + } + FOR_ALL_LANES { + printk(BIOS_SPEW, "A+stat: %d, %d, %d, %d (%x), %x\n", + channel, slotrank, lane, timA_delta, + upperA[lane] + timA_delta + 0x40, + statistics[lane][timA_delta + 25]); + } + } + FOR_ALL_LANES { + int last_zero, first_all; + + for (last_zero = -25; last_zero <= 25; last_zero++) + if (statistics[lane][last_zero + 25]) + break; + last_zero--; + for (first_all = -25; first_all <= 25; first_all++) + if (statistics[lane][first_all + 25] == 100) + break; + + printk(BIOS_SPEW, "lane %d: %d, %d\n", lane, last_zero, + first_all); + + ctrl->timings[channel][slotrank].lanes[lane].timA = + (last_zero + first_all) / 2 + upperA[lane]; + printk(BIOS_SPEW, "Aval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timA); + } +} + +static void discover_402x(ramctr_timing * ctrl, int channel, int slotrank, + int *upperA) +{ + int works[NUM_LANES]; + int lane; + while (1) { + int all_works = 1, some_works = 0; + program_timings(ctrl, channel); + test_timA(ctrl, channel, slotrank); + FOR_ALL_LANES { + works[lane] = + !does_lane_work(ctrl, channel, slotrank, lane); + if (works[lane]) + some_works = 1; + else + all_works = 0; + } + if (all_works) + return; + if (!some_works) { + if (ctrl->timings[channel][slotrank].val_4024 < 2) + die("402x discovery failed"); + ctrl->timings[channel][slotrank].val_4024 -= 2; + printk(BIOS_SPEW, "4024 -= 2;\n"); + continue; + } + ctrl->timings[channel][slotrank].val_4028 += 2; + printk(BIOS_SPEW, "4028 += 2;\n"); + if (ctrl->timings[channel][slotrank].val_4028 >= 0x10) + die("402x discovery failed"); + FOR_ALL_LANES if (works[lane]) { + ctrl->timings[channel][slotrank].lanes[lane].timA += + 128; + upperA[lane] += 128; + printk(BIOS_SPEW, "increment %d, %d, %d\n", channel, + slotrank, lane); + } + } +} + +struct timA_minmax { + int timA_min_high, timA_max_high; +}; + +static void pre_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + int lane; + mnmx->timA_min_high = 7; + mnmx->timA_max_high = 0; + + FOR_ALL_LANES { + if (mnmx->timA_min_high > + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_min_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + if (mnmx->timA_max_high < + (ctrl->timings[channel][slotrank].lanes[lane].timA >> 6)) + mnmx->timA_max_high = + (ctrl->timings[channel][slotrank].lanes[lane]. + timA >> 6); + } +} + +static void post_timA_change(ramctr_timing * ctrl, int channel, int slotrank, + struct timA_minmax *mnmx) +{ + struct timA_minmax post; + int shift_402x = 0; + + /* Get changed maxima. */ + pre_timA_change(ctrl, channel, slotrank, &post); + + if (mnmx->timA_max_high - mnmx->timA_min_high < + post.timA_max_high - post.timA_min_high) + shift_402x = +1; + else if (mnmx->timA_max_high - mnmx->timA_min_high > + post.timA_max_high - post.timA_min_high) + shift_402x = -1; + else + shift_402x = 0; + + ctrl->timings[channel][slotrank].val_4028 += shift_402x; + ctrl->timings[channel][slotrank].val_4024 += shift_402x; + printk(BIOS_SPEW, "4024 += %d;\n", shift_402x); + printk(BIOS_SPEW, "4028 += %d;\n", shift_402x); +} + +static void read_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + u32 r32; + int all_high, some_high; + int upperA[NUM_LANES]; + struct timA_minmax mnmx; + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + write32(DEFAULT_MCHBAR + 0x3400, (slotrank << 2) | 0x8001); + + ctrl->timings[channel][slotrank].val_4028 = 4; + ctrl->timings[channel][slotrank].val_4024 = 55; + program_timings(ctrl, channel); + + discover_timA_coarse(ctrl, channel, slotrank, upperA); + + all_high = 1; + some_high = 0; + FOR_ALL_LANES if (ctrl->timings[channel][slotrank].lanes[lane]. + timA >= 0x40) + some_high = 1; + else + all_high = 0; + if (all_high) { + ctrl->timings[channel][slotrank].val_4028--; + printk(BIOS_SPEW, "4028--;\n"); + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + timA -= 0x40; + upperA[lane] -= 0x40; + + } + } else if (some_high) { + ctrl->timings[channel][slotrank].val_4024++; + ctrl->timings[channel][slotrank].val_4028++; + printk(BIOS_SPEW, "4024++;\n"); + printk(BIOS_SPEW, "4028++;\n"); + } + + program_timings(ctrl, channel); + + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_402x(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + discover_timA_fine(ctrl, channel, slotrank, upperA); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + pre_timA_change(ctrl, channel, slotrank, &mnmx); + + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timA -= mnmx.timA_min_high * 0x40; + ctrl->timings[channel][slotrank].val_4028 -= mnmx.timA_min_high; + printk(BIOS_SPEW, "4028 -= %d;\n", mnmx.timA_min_high); + + post_timA_change(ctrl, channel, slotrank, &mnmx); + + printk(BIOS_SPEW, "4/8: %d, %d, %x, %x\n", channel, slotrank, + ctrl->timings[channel][slotrank].val_4024, + ctrl->timings[channel][slotrank].val_4028); + + FOR_ALL_LANES + printk(BIOS_SPEW, "%d, %d, %d, %x\n", channel, slotrank, + lane, + ctrl->timings[channel][slotrank].lanes[lane].timA); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + } + + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + 4 * lane, 0); +} + +static void test_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int lane; + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x4140 + 0x400 * channel + 4 * lane); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) + | 4 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | (6 << 16)); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8041001); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x80411f4); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << 10) + | 8 | (ctrl->CAS << 16)); + + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x40011f4 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timC; + int statistics[NUM_LANES][MAX_TIMC + 1]; + int lane; + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + + for (timC = 0; timC <= MAX_TIMC; timC++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timC = timC; + program_timings(ctrl, channel); + + test_timC(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timC] = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + printk(BIOS_SPEW, "Cstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timC, + statistics[lane][timC]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_TIMC + 1); + ctrl->timings[channel][slotrank].lanes[lane].timC = rn.middle; + if (rn.all) + die("timC discovery failed"); + printk(BIOS_SPEW, "Cval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timC); + } +} + +static int get_precedening_channels(ramctr_timing * ctrl, int target_channel) +{ + int channel, ret = 0; + FOR_ALL_POPULATED_CHANNELS if (channel < target_channel) + ret++; + return ret; +} + +static void fill_pattern0(ramctr_timing * ctrl, int channel, u32 a, u32 b) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + printk(BIOS_SPEW, "channel_offset=%x\n", channel_offset); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + 4 * j, j & 2 ? b : a); + sfence(); +} + +static int num_of_channels(const ramctr_timing * ctrl) +{ + int ret = 0; + int channel; + FOR_ALL_POPULATED_CHANNELS ret++; + return ret; +} + +static void fill_pattern1(ramctr_timing * ctrl, int channel) +{ + unsigned j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + j * 4, 0xffffffff); + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + channel_step + j * 4, 0); + sfence(); +} + +static void precharge(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + } +} + +static void test_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + write_mrreg(ctrl, channel, slotrank, 1, + 0x80 | make_mr1(ctrl, slotrank)); + + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->delay2) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + 8 | (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f107); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4000c01 | ((ctrl->CAS + 38) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 4); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x400 * channel + 0x4284, 0x40001); + wait_428c(channel); + + write_mrreg(ctrl, channel, slotrank, 1, + 0x1080 | make_mr1(ctrl, slotrank)); +} + +static void discover_timB(ramctr_timing * ctrl, int channel, int slotrank) +{ + int timB; + int statistics[NUM_LANES][128]; + int lane; + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052 | (slotrank << 2)); + + for (timB = 0; timB < 128; timB++) { + FOR_ALL_LANES ctrl->timings[channel][slotrank].lanes[lane]. + timB = timB; + program_timings(ctrl, channel); + + test_timB(ctrl, channel, slotrank); + + FOR_ALL_LANES { + statistics[lane][timB] = + !((read32 + (DEFAULT_MCHBAR + lane_registers[lane] + + channel * 0x100 + 4 + ((timB / 32) & 1) * 4) + >> (timB % 32)) & 1); + printk(BIOS_SPEW, "Bstat: %d, %d, %d, %x, %x\n", + channel, slotrank, lane, timB, + statistics[lane][timB]); + } + } + FOR_ALL_LANES { + struct run rn = get_longest_zero_run(statistics[lane], 128); + ctrl->timings[channel][slotrank].lanes[lane].timB = rn.end; + if (rn.all) + die("timB discovery failed"); + printk(BIOS_SPEW, "Bval: %d, %d, %d, %x\n", channel, slotrank, + lane, ctrl->timings[channel][slotrank].lanes[lane].timB); + } +} + +static int get_timB_high_adjust(u64 val) +{ + int i; + if (val >= 0xfffffffffff00000LL) + return -1; + if (val >= 0xfffffff000000000LL) + return -2; + if (val >= 0xfff0000000000000LL) + return -3; + + for (i = 0; i < 8; i++) + if (val >> (8 * (7 - i) + 4)) + return i; + return 8; +} + +static void adjust_high_timB(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + write32(DEFAULT_MCHBAR + 0x3400, 0x200); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern1(ctrl, channel); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 1); + } + FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS { + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x10001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->tRCD << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x8040c01); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, 0x8041003); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x3e2); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f207); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0x8000c01 | ((ctrl->CWL + ctrl->tWTR + 5) << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x8); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | ((ctrl->tRP) << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0xc01 | ((ctrl->tRCD) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x3f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4000c01 | + ((ctrl->tRP + + ctrl->timings[channel][slotrank].val_4024 + + ctrl->timings[channel][slotrank].val_4028) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60008); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0x80001); + wait_428c(channel); + FOR_ALL_LANES { + u64 res = + read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 4); + res |= + ((u64) read32(DEFAULT_MCHBAR + lane_registers[lane] + + 0x100 * channel + 8)) << 32; + ctrl->timings[channel][slotrank].lanes[lane].timB += + get_timB_high_adjust(res) * 64; + printk(BIOS_SPEW, "Bval+: %d, %d, %d, %x\n", channel, + slotrank, lane, + ctrl->timings[channel][slotrank].lanes[lane]. + timB); + } + } + write32(DEFAULT_MCHBAR + 0x3400, 0); +} + +static void write_op(ramctr_timing * ctrl, int channel) +{ + int slotrank; + + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); +} + +static void write_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS + write32(DEFAULT_MCHBAR + 0x4008 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4008 + + 0x400 * channel) | 0x8000000); + + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + write_op(ctrl, channel); + } + + FOR_ALL_CHANNELS + FOR_ALL_POPULATED_RANKS write_mrreg(ctrl, channel, slotrank, 1, + make_mr1(ctrl, + slotrank) | 0x1080); + + write32(DEFAULT_MCHBAR + 0x3400, 0x108052); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS discover_timB(ctrl, channel, + slotrank); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS write_mrreg(ctrl, channel, + slotrank, 1, + make_mr1(ctrl, + slotrank)); + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + FOR_ALL_POPULATED_CHANNELS wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) | 8); + + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + ~0x00200000 & read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel)); + read32(DEFAULT_MCHBAR + 0x428c + 0x400 * channel); + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x659001); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + printk(BIOS_SPEW, "CPE\n"); + precharge(ctrl); + printk(BIOS_SPEW, "CPF\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0xaaaaaaaa, 0x55555555); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS + discover_timC(ctrl, channel, slotrank); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + adjust_high_timB(ctrl); + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane); + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static int test_320c(ramctr_timing * ctrl, int channel, int slotrank) +{ + struct ram_rank_timings saved_rt = ctrl->timings[channel][slotrank]; + int timC_delta; + int lanes_ok = 0; + int ctr = 0; + int lane; + + for (timC_delta = -5; timC_delta <= 5; timC_delta++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].timC = + saved_rt.lanes[lane].timC + timC_delta; + } + program_timings(ctrl, channel); + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 4 * lane + 0x4f40, 0); + + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + ((max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1)) << 10) + | 8 | (ctrl->tRCD << 16)); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | ctr | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8001020 | ((ctrl->CWL + ctrl->tWTR + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4244 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4001020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4248 + 0x400 * channel, 0x389abcd); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0x20e42); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, 0xf1001); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0x240); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + u32 r32 = + read32(DEFAULT_MCHBAR + 0x4340 + 4 * lane + + 0x400 * channel); + + if (r32 == 0) + lanes_ok |= 1 << lane; + } + ctr++; + if (lanes_ok == ((1 << NUM_LANES) - 1)) + break; + } + + ctrl->timings[channel][slotrank] = saved_rt; + return lanes_ok != ((1 << NUM_LANES) - 1); +} + +const u32 pattern[][16] = { + {0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff, + 0x00000000, 0x00000000, 0xffffffff, 0xffffffff}, + {0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, + 0xffffffff, 0xffffffff, 0x00000000, 0x00000000}, + {0xe62d6424, 0x9277e09e, 0x8f43dc3f, 0x76eae589, + 0x0010fdc6, 0xdc55e01c, 0x5effb0ab, 0x6cba5d29, + 0xa43d1e64, 0xab5c2e0f, 0x7796ed16, 0x96023bf4, + 0xa74c831d, 0x90f138c0, 0x17830a8a, 0x5ac17c47}, + {0x359ebbeb, 0x2b9b4512, 0xef584d98, 0x106bf7cb, + 0x363525ad, 0xb3a4dfdc, 0xa6b9fcd8, 0xd21689ec, + 0x84a3695b, 0xbd9c2e27, 0xdb3d0f44, 0x988158f1, + 0xcca91d3f, 0xb62a6d12, 0xe905e4cf, 0x7f1fa626}, + {0xe58efeae, 0xcd006081, 0xa9119403, 0xbcfbd35f, + 0x213b3bf7, 0x7bfcb773, 0xc85143f9, 0x0bdbff50, + 0xa3053c90, 0x51d66cb7, 0x296f4387, 0xb715f99e, + 0xfaddc989, 0xbb1de8a7, 0x39206b4d, 0x80174a57}, + {0xa1622ac1, 0xb4f4a5f0, 0x16dc2bc3, 0x50fb0954, + 0x2e261721, 0x52b82c3c, 0x821902b8, 0x0d4b6c38, + 0x1f618631, 0x047956f3, 0xd4337f5a, 0x591f8002, + 0x27f28db2, 0xfae37369, 0xb3f27580, 0x3cdb6397}, + {0x3dee23be, 0x19f36408, 0x227f4a6a, 0x024603c5, + 0xd5e062db, 0x6d8d4c5c, 0x7ff693b0, 0x76641be9, + 0x9e74f41c, 0xe7bc7f33, 0x2636f2e9, 0x70279750, + 0xce2355aa, 0x32d230ef, 0x22f9b468, 0xadd4e7a2}, + {0x936c0fed, 0xba0612d5, 0xa97c1ea7, 0x10e29d67, + 0x1c4c5dc8, 0x83645621, 0xcd8b521c, 0xb8301817, + 0xac7d6571, 0xcc41d200, 0x4ebdefdd, 0xd2917bde, + 0x60f75acc, 0x7791534b, 0x26ea2a83, 0x6b74513a}, + {0xd1957b85, 0xc6f8f9ca, 0xf04fb4be, 0xfeb786fb, + 0xa1dea3aa, 0x67fe7db6, 0x25d49c87, 0xe3d54870, + 0x93dc1f86, 0x7d0c1a18, 0x9272e128, 0x68e1b876, + 0xce284c9e, 0x8fa18792, 0x5785a340, 0xb6fcf198}, + {0xff7d8e4a, 0x0c21ee43, 0xe820b388, 0xb4443c0e, + 0xa1e6e498, 0x5c426110, 0x1b434ef3, 0xbef05b91, + 0xa6907968, 0x53662ac3, 0x6defac32, 0x2c11c29c, + 0x6175cced, 0xb17dd3ad, 0x6e6a1076, 0x1372b1fa}, + {0x4408ed06, 0x49460ffd, 0xb49d26cb, 0x6a3662a5, + 0x5e857047, 0xa387cd4a, 0x04edc81e, 0xfd94d8d4, + 0x2fe48d91, 0x9d2356bc, 0x96131878, 0xaca3fce4, + 0xbb312c6c, 0x5023b090, 0x3614be70, 0xa14dfabb}, + {0xd4cc1e83, 0x757a1930, 0xc3d16a61, 0x9e0d6681, + 0x8a081fa9, 0xbd11c888, 0x1672f010, 0xa083f71c, + 0x1ec02eef, 0xc4586ca8, 0x6d322b35, 0x56054679, + 0x1552a0ff, 0x5cb7707e, 0xdfb55d4a, 0xcc76cc07}, + {0x507cf71f, 0x2166421a, 0x54be4af0, 0xfd42158c, + 0x417b1f7f, 0x9466860b, 0x3a0075bf, 0x2055575c, + 0xcedfe7ab, 0xbe85aa5f, 0x39d0c2e3, 0x851c19df, + 0x39a35a3f, 0x3fb10d7d, 0x20b14899, 0x703b7f08}, + {0x8a7d9dd1, 0x33235565, 0xbd3d2e57, 0xa48c2726, + 0x0d5e2e13, 0xae421ff9, 0x8784a224, 0xf66c1510, + 0x057627aa, 0x8fb0cb41, 0x4289975a, 0xb181adfa, + 0x59f2059a, 0xe86feb05, 0x84222fc1, 0x319b3ce9}, + {0xe1e243b8, 0x3b0bcc1a, 0x70396f00, 0x5caff44d, + 0xe96961b3, 0xad73f692, 0x8b841a2d, 0xf5838839, + 0xec9c9d04, 0xcc2b5562, 0xf8ca2549, 0xa9c52ff8, + 0x3b2fde68, 0x3d4dc7f0, 0xa57387d0, 0x051199ad}, + {0x5f0ce4fc, 0xd830fbb7, 0x90abeb8f, 0x96d9cdbb, + 0x58f80a80, 0x0baaca36, 0x81a23623, 0x77127614, + 0xaa8382cd, 0x0922fbca, 0xd84d37e1, 0x721297df, + 0x160f3b3a, 0x10a1ecdc, 0x151c92f4, 0xc1fdcdab}, + {0x261c45cc, 0xfeddd2da, 0xfc3cb1c1, 0x6639641f, + 0x2c011892, 0x7108bee2, 0x8545e0b9, 0x7dd36dab, + 0x07d91950, 0x1520adcb, 0xf84aa939, 0x07d9bb2d, + 0xdf1ed826, 0xaee3c814, 0x1dca1e81, 0xc8e9f486}, + {0x933d306a, 0xaab7103d, 0xa8be37be, 0x49612f3a, + 0xb0cf28e5, 0xf9648902, 0x106d7c11, 0xf32e1813, + 0x21af36ef, 0xe695e4c4, 0x7ee1831d, 0x2aeda467, + 0x99d0c655, 0x3f0691ab, 0xcd68f7c1, 0xb469a20e}, + {0x8557aef0, 0x3eb0e373, 0x0853ac31, 0xe5bded62, + 0x3eddb0dd, 0x6bbf1caf, 0x2119c3d9, 0xe1732350, + 0x55456c75, 0xf6119375, 0x498dd1ad, 0x13f80916, + 0xb97f9f5e, 0x921d9f4c, 0xabdee367, 0x1d6bb8bf}, + {0xd165a3be, 0xd8b41598, 0xa20e1809, 0xefd5c8ce, + 0x18935c80, 0xdf1911f9, 0xc9e449eb, 0xb887a4d7, + 0x4a324f6f, 0x533e8031, 0x1c21c074, 0xa95f1ea5, + 0x765b320a, 0x839d7dfb, 0xc7d3aa93, 0xe534ae3d}, + {0xbe8592c8, 0x068457e6, 0x89b94fa3, 0xd522ad02, + 0x7e7db0b7, 0x2c5b896f, 0x9f8ecb37, 0x05b983ff, + 0x3fe9b25f, 0x34a6215b, 0x0592ba34, 0xd564f85a, + 0x156c426d, 0x25ad5460, 0xe7b5e8b7, 0xa73285c6}, + {0x5ad8d838, 0x27b42d36, 0xcc806ad1, 0x157a058a, + 0x7297735a, 0xffd6df8d, 0xff96f7a2, 0x155b27ea, + 0x84708101, 0x979fd78b, 0x49797d0c, 0x0dc93e3c, + 0x20287332, 0xed759f88, 0xe5068529, 0xb83aa781}, + {0xc38b302c, 0x57b54075, 0xac810692, 0xb0d493e7, + 0x4adda486, 0x0665ce2e, 0xb2a9c003, 0xafacc4ce, + 0x4d5e906d, 0xb3d52fab, 0xe6962c6b, 0x850f4dd1, + 0x5021656c, 0x5df6c06b, 0x9255125b, 0x2363c478}, + {0x188b715c, 0xe8b884b0, 0x5e6d0b9a, 0x1f0051e1, + 0xd2d35d4c, 0xbfeaecbe, 0xc84bb0ad, 0x67a232d6, + 0x99001587, 0xbf4313e1, 0x74f64061, 0x2c1fc562, + 0xb6fe8ca6, 0x5226a239, 0xf5198574, 0x61b51dca}, + {0x51dcecd3, 0xbadbe596, 0xebe3e84a, 0x772bfdfc, + 0x03656ac5, 0xa7c36e91, 0x6cd32cf0, 0xc3f699dd, + 0x7d5aba01, 0x51e38e82, 0x23103a98, 0x20298b9d, + 0x19436510, 0x63ad7e6c, 0x8bc2b33f, 0x27079917}, + {0x8bd5be78, 0xf2403bfa, 0x780ebdb6, 0x94c53b64, + 0x6241c2e2, 0x5bfb081e, 0x6799e88f, 0xc997b7d1, + 0x466ac8b1, 0xbf5909da, 0x497ea39f, 0x402ffb48, + 0xd7470c2d, 0x8510aba9, 0x6c52a1c9, 0x812ca967}, + {0x031f7ab4, 0xd32fe890, 0x36ae6de5, 0x083dcde4, + 0x99a7f12f, 0xe44864a7, 0x02b75fff, 0xf25dda35, + 0x7679ff4f, 0xed421e01, 0xd9c2cfa1, 0xd36b4e82, + 0x5315d908, 0xc7ebcb2a, 0xb6f3e4c1, 0xf5bfbae9}, + {0x3f4a2a96, 0x64d8bd5a, 0x19acd70d, 0xf62fcdd9, + 0x5de99cdf, 0x32f3b7cb, 0x2c020578, 0x4e9bafb8, + 0x74919a08, 0xaba33e91, 0xa6bd2254, 0x2435a9b9, + 0x47e2a1b4, 0xe837a28e, 0xe113f1b0, 0x7654bd79}, + {0x05537a6c, 0x77be1a5c, 0x4c7492c9, 0x9086bfb0, + 0x257adc18, 0xf4787fc1, 0xe3fb6d53, 0x9525e589, + 0x445a65bc, 0x833f7d08, 0x69cf1f7e, 0x9a6372e1, + 0xceedb52e, 0x31032997, 0xd1c36828, 0x132772d6}, + {0x0a166972, 0x89beaf3b, 0x8d780fbc, 0x8aea5392, + 0x58347a41, 0x1e381ec2, 0xcc6280c8, 0xee0863e1, + 0x976e2dd2, 0x8c6ee6e2, 0xa0ca57cd, 0x95114a7d, + 0x3c096704, 0xa941769d, 0x2de20c05, 0x0bf8f812}, + {0x22779d6c, 0x94e12e8f, 0x5ce40299, 0xea1b55b0, + 0x9ebec05d, 0xe076cd2b, 0x8fef5648, 0x6a284c65, + 0xa790b705, 0xf0b19997, 0x0d8ca8af, 0x17440419, + 0xef4f702f, 0x33cbcbb1, 0x83d60f26, 0x48988397}, + {0x0fed7f53, 0xb5acbb67, 0xc031c73f, 0x5364d9ef, + 0xa6dbd12d, 0x82174a6c, 0xccf8e7ab, 0xc473c036, + 0xcff493d8, 0xad9afc3b, 0x316a24e8, 0x1842bea4, + 0x4cc0c82e, 0x28ccd91e, 0xd7311b5d, 0x50a89860}, +}; + +static void fill_pattern5(ramctr_timing * ctrl, int channel) +{ + unsigned i, j; + unsigned channel_offset = + get_precedening_channels(ctrl, channel) * 0x40; + unsigned channel_step = 0x40 * num_of_channels(ctrl); + for (i = 0; i < sizeof(pattern) / sizeof(pattern[0]); i++) { + for (j = 0; j < 16; j++) + write32(0x04000000 + channel_offset + i * channel_step + + j * 4, pattern[i][j]); + } + sfence(); +} + +static void reprogram_320c(ramctr_timing * ctrl) +{ + int channel, slotrank; + u32 r32; + + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4020 + 0x400 * channel, + read32(DEFAULT_MCHBAR + 0x4020 + + 0x400 * channel) | 0x200000); + } + write32(DEFAULT_MCHBAR + 0x5030, read32(DEFAULT_MCHBAR + 0x5030) & ~8); + FOR_ALL_POPULATED_CHANNELS { + wait_428c(channel); + + /* choose an existing rank. */ + slotrank = !(ctrl->rankmap[channel] & 1) ? 2 : 0; + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x0f003); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, 0x41001); + + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x3e0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 1); + wait_428c(channel); + } + + /* jedec reset */ + dram_jedecreset(ctrl); + /* mrs commands. */ + dram_mrscommands(ctrl); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); +} + +static void command_training(ramctr_timing * ctrl) +{ + int channel; + int slotrank; + u32 reg_4004_b30; + int delta = 0; + int c320c; + int stat[NUM_SLOTRANKS][256]; + + /* FIXME: vendor BIOS discovers this by trying 0 and 2. Apparently 2 should work for + all systems but 0 is slightly more efficient for the systems that can tolerate it. + */ + reg_4004_b30 = 2; + + FOR_ALL_POPULATED_CHANNELS + MCHBAR32(0x4004 + 0x400 * channel) = + ctrl->tRRD + | (ctrl->tRTP << 4) + | (ctrl->tCKE << 8) + | (ctrl->tWTR << 12) + | (ctrl->tFAW << 16) + | (ctrl->tWR << 24) + | (reg_4004_b30 << 30); + + if (reg_4004_b30 == 2) + delta = 2; + else if (reg_4004_b30 == 0) + delta = 4; + + FOR_ALL_CHANNELS { + FOR_ALL_POPULATED_RANKS ctrl->timings[channel][slotrank]. + val_4024 -= delta; + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + FOR_ALL_POPULATED_CHANNELS { + for (c320c = -127; c320c <= 127; c320c++) { + FOR_ALL_POPULATED_RANKS ctrl-> + timings[channel][slotrank].val_320c = c320c; + program_timings(ctrl, channel); + reprogram_320c(ctrl); + FOR_ALL_POPULATED_RANKS { + stat[slotrank][c320c + 127] = + test_320c(ctrl, channel, slotrank); + printk(BIOS_SPEW, "3stat: %d, %d, %d: %d\n", + channel, slotrank, c320c, + stat[slotrank][c320c + 127]); + } + } + FOR_ALL_POPULATED_RANKS { + struct run rn = + get_longest_zero_run(stat[slotrank], 255); + ctrl->timings[channel][slotrank].val_320c = + rn.middle - 127; + printk(BIOS_SPEW, "3val: %d, %d: %d\n", channel, + slotrank, + ctrl->timings[channel][slotrank].val_320c); + if (rn.all) + die("c320c discovery failed"); + } + } + + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + + reprogram_320c(ctrl); +} + +static void discover_edges_real(ramctr_timing * ctrl, int channel, int slotrank, + int *edges) +{ + int edge; + int statistics[NUM_LANES][MAX_EDGE_TIMING + 1]; + int lane; + + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].rising = + edge; + ctrl->timings[channel][slotrank].lanes[lane].falling = + edge; + } + printk(BIOS_SPEW, "edge %02x\n", edge); + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + 4 * lane + + 0x4140); + } + + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, 0x40411f4); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + (0xc01 | (ctrl->delay1 << 16))); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, 0xc0001); + + wait_428c(channel); + + FOR_ALL_LANES { + statistics[lane][edge] = + read32(DEFAULT_MCHBAR + 0x4340 + 0x400 * channel + + lane * 4); + printk(BIOS_SPEW, "estat %d, %d, %d, %d %02x\n", + channel, slotrank, lane, edge, + statistics[lane][edge]); + } + } + FOR_ALL_LANES { + struct run rn = + get_longest_zero_run(statistics[lane], MAX_EDGE_TIMING + 1); + edges[lane] = rn.middle; + if (rn.all) + die("edge discovery failed"); + printk(BIOS_SPEW, "eval %d, %d, %d, %02x\n", channel, slotrank, + lane, edges[lane]); + } +} + +static void discover_edges(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + u32 r32; + + write32(DEFAULT_MCHBAR + 0x3400, 0); + + r32 = read32(DEFAULT_MCHBAR + 0x5030); + write32(DEFAULT_MCHBAR + 0x5030, r32 | 0x20); + udelay(1); + + write32(DEFAULT_MCHBAR + 0x5030, r32 & ~0x20); + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS { + FOR_ALL_LANES write32(DEFAULT_MCHBAR + 4 * lane + + 0x400 * channel + 0x4080, 0); + } + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0, 0); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + FOR_ALL_LANES read32(DEFAULT_MCHBAR + 0x400 * channel + + lane * 4 + 0x4140); + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 16; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 16; + } program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + + wait_428c(channel); + } + + FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + 48; + ctrl->timings[channel][slotrank].lanes[lane].rising = + 48; + } + + program_timings(ctrl, channel); + + FOR_ALL_POPULATED_RANKS { + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x360004); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x4041003); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24) | 0); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x1001 | ((ctrl->CAS + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f000); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->delay1 << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x360000); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + } + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + + lane * 4, + ~read32(DEFAULT_MCHBAR + 0x4040 + + 0x400 * channel + lane * 4) & 0xff); + } + + fill_pattern0(ctrl, channel, 0, 0xffffffff); + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void discover_edges_write_real(ramctr_timing * ctrl, int channel, + int slotrank, int *edges) +{ + int edge; + u32 raw_statistics[MAX_EDGE_TIMING + 1]; + int statistics[MAX_EDGE_TIMING + 1]; + const int reg3000b24[] = { 0, 0xc, 0x2c }; + int lane, i; + int lower[NUM_LANES]; + int upper[NUM_LANES]; + + FOR_ALL_LANES { + lower[lane] = 0; + upper[lane] = MAX_EDGE_TIMING; + } + + for (i = 0; i < 3; i++) { + /* FIXME: trace shows that vendor BIOS also tests with other patterns. + I'm not sure whether it's really needed. + */ + write32(DEFAULT_MCHBAR + 0x3000 + 0x100 * channel, + reg3000b24[i] << 24); + printk(BIOS_SPEW, "patterned\n"); + printk(BIOS_SPEW, "[%x] = 0x%08x\n(%d, %d)\n", + 0x3000 + 0x100 * channel, reg3000b24[i] << 24, channel, + slotrank); + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) { + FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane]. + rising = edge; + ctrl->timings[channel][slotrank].lanes[lane]. + falling = edge; + } + program_timings(ctrl, channel); + + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + 4 * lane, 0); + read32(DEFAULT_MCHBAR + 0x400 * channel + + 4 * lane + 0x4140); + } + wait_428c(channel); + + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, + 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + 0x4 | (ctrl->tRCD << 16) + | (max(ctrl->tRRD, (ctrl->tFAW >> 2) + 1) << + 10)); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, + 0x240); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, + 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x8005020 | ((ctrl->tWTR + ctrl->CWL + 8) << + 16)); + write32(DEFAULT_MCHBAR + 0x4204 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + 0x400 * channel, + 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + 0x400 * channel, + 0x4005020 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + 0x400 * channel, + (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + 0x400 * channel, + 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + 0x400 * channel, + 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + 0x400 * channel, + 0xc01 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + 0x400 * channel, + 0xc0001); + wait_428c(channel); + FOR_ALL_LANES { + read32(DEFAULT_MCHBAR + 0x4340 + + 0x400 * channel + lane * 4); + } + + raw_statistics[edge] = + MCHBAR32(0x436c + 0x400 * channel); + } + FOR_ALL_LANES { + struct run rn; + for (edge = 0; edge <= MAX_EDGE_TIMING; edge++) + statistics[edge] = + ! !(raw_statistics[edge] & (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_EDGE_TIMING + 1); + printk(BIOS_SPEW, + "edges: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, rn.middle, + rn.end, rn.start + ctrl->edge_offset[i], + rn.end - ctrl->edge_offset[i]); + lower[lane] = + max(rn.start + ctrl->edge_offset[i], lower[lane]); + upper[lane] = + min(rn.end - ctrl->edge_offset[i], upper[lane]); + edges[lane] = (lower[lane] + upper[lane]) / 2; + + } + } + + write32(DEFAULT_MCHBAR + 0x3000, 0); + printk(BIOS_SPEW, "CPA\n"); +} + +static void discover_edges_write(ramctr_timing * ctrl) +{ + int falling_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int rising_edges[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS { + fill_pattern5(ctrl, channel); + write32(DEFAULT_MCHBAR + 0x4288 + 0x400 * channel, 0x1f); + } + + /* FIXME: under some conditions (older chipsets?) vendor BIOS sets both edges to the same value. */ + write32(DEFAULT_MCHBAR + 0x4eb0, 0x300); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + falling_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0x200); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + discover_edges_write_real(ctrl, channel, slotrank, + rising_edges[channel][slotrank]); + } + + write32(DEFAULT_MCHBAR + 0x4eb0, 0); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + ctrl->timings[channel][slotrank].lanes[lane].falling = + falling_edges[channel][slotrank][lane]; + ctrl->timings[channel][slotrank].lanes[lane].rising = + rising_edges[channel][slotrank][lane]; + } + + FOR_ALL_POPULATED_CHANNELS + program_timings(ctrl, channel); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + write32(DEFAULT_MCHBAR + 0x4080 + 0x400 * channel + 4 * lane, + 0); + } +} + +static void test_timC_write(ramctr_timing *ctrl, int channel, int slotrank) +{ + wait_428c(channel); + write32(DEFAULT_MCHBAR + 0x4220 + 0x400 * channel, 0x1f006); + write32(DEFAULT_MCHBAR + 0x4230 + 0x400 * channel, + (max((ctrl->tFAW >> 2) + 1, ctrl->tRRD) + << 10) | (ctrl->tRCD << 16) | 4); + write32(DEFAULT_MCHBAR + 0x4200 + 0x400 * channel, + (slotrank << 24) | 0x60000); + write32(DEFAULT_MCHBAR + 0x4210 + 0x400 * channel, 0x244); + + write32(DEFAULT_MCHBAR + 0x4224 + 0x400 * channel, 0x1f201); + write32(DEFAULT_MCHBAR + 0x4234 + 0x400 * channel, + 0x80011e0 | + ((ctrl->tWTR + ctrl->CWL + 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4204 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4214 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x4228 + + 0x400 * channel, 0x1f105); + write32(DEFAULT_MCHBAR + 0x4238 + + 0x400 * channel, + 0x40011e0 | (max(ctrl->tRTP, 8) << 16)); + write32(DEFAULT_MCHBAR + 0x4208 + + 0x400 * channel, (slotrank << 24)); + write32(DEFAULT_MCHBAR + 0x4218 + + 0x400 * channel, 0x242); + + write32(DEFAULT_MCHBAR + 0x422c + + 0x400 * channel, 0x1f002); + write32(DEFAULT_MCHBAR + 0x423c + + 0x400 * channel, + 0x1001 | (ctrl->tRP << 16)); + write32(DEFAULT_MCHBAR + 0x420c + + 0x400 * channel, + (slotrank << 24) | 0x60400); + write32(DEFAULT_MCHBAR + 0x421c + + 0x400 * channel, 0); + + write32(DEFAULT_MCHBAR + 0x4284 + + 0x400 * channel, 0xc0001); + wait_428c(channel); +} + +static void discover_timC_write(ramctr_timing * ctrl) +{ + const u8 rege3c_b24[3] = { 0, 0xf, 0x2f }; + int i; + + int lower[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int upper[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; + int channel, slotrank, lane; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + lower[channel][slotrank][lane] = 0; + upper[channel][slotrank][lane] = MAX_TIMC; + } + + write32(DEFAULT_MCHBAR + 0x4ea8, 1); + + for (i = 0; i < 3; i++) + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100), + (rege3c_b24[i] << 24) + | (read32(DEFAULT_MCHBAR + 0xe3c + (channel * 0x100)) + & ~0x3f000000)); + udelay(2); + FOR_ALL_POPULATED_RANKS { + int timC; + u32 raw_statistics[MAX_TIMC + 1]; + int statistics[MAX_TIMC + 1]; + + /* FIXME: trace shows that vendor BIOS also tests with other patterns. + I'm not sure whether it's really needed. + */ + fill_pattern5(ctrl, channel); + for (timC = 0; timC < MAX_TIMC + 1; timC++) { + FOR_ALL_LANES + ctrl->timings[channel][slotrank].lanes[lane].timC = timC; + program_timings(ctrl, channel); + + test_timC_write (ctrl, channel, slotrank); + + raw_statistics[timC] = + MCHBAR32(0x436c + 0x400 * channel); + printk(BIOS_SPEW, "Cstat %02x %02x\n", timC, + raw_statistics[timC]); + } + FOR_ALL_LANES { + struct run rn; + for (timC = 0; timC <= MAX_TIMC; timC++) + statistics[timC] = + !!(raw_statistics[timC] & + (1 << lane)); + rn = get_longest_zero_run(statistics, + MAX_TIMC + 1); + if (rn.all) + die("timC write discovery failed"); + printk(BIOS_SPEW, + "timC: %d, %d, %d: 0x%x-0x%x-0x%x, 0x%x-0x%x\n", + channel, slotrank, i, rn.start, + rn.middle, rn.end, + rn.start + ctrl->timC_offset[i], + rn.end - ctrl->timC_offset[i]); + lower[channel][slotrank][lane] = + max(rn.start + ctrl->timC_offset[i], + lower[channel][slotrank][lane]); + upper[channel][slotrank][lane] = + min(rn.end - ctrl->timC_offset[i], + upper[channel][slotrank][lane]); + + } + } + } + + write32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c, + 0 | (read32(DEFAULT_MCHBAR + (channel * 0x100) + 0xe3c) & + ~0x3f000000)); + udelay(2); + + write32(DEFAULT_MCHBAR + 0x4ea8, 0); + + printk(BIOS_SPEW, "CPB\n"); + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS FOR_ALL_LANES { + printk(BIOS_SPEW, "timC [%d, %d, %d] = 0x%x\n", channel, + slotrank, lane, + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2); + ctrl->timings[channel][slotrank].lanes[lane].timC = + (lower[channel][slotrank][lane] + + upper[channel][slotrank][lane]) / 2; + } + FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); +} + +static void normalize_training(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + int mat = 0; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + int delta; + FOR_ALL_LANES mat = + max(ctrl->timings[channel][slotrank].lanes[lane].timA, mat); + delta = (mat >> 6) - ctrl->timings[channel][slotrank].val_4028; + ctrl->timings[channel][slotrank].val_4024 += delta; + ctrl->timings[channel][slotrank].val_4028 += delta; + } FOR_ALL_POPULATED_CHANNELS program_timings(ctrl, channel); +} + +static void write_controller_mr(ramctr_timing * ctrl) +{ + int channel, slotrank; + + FOR_ALL_CHANNELS FOR_ALL_POPULATED_RANKS { + write32(DEFAULT_MCHBAR | 0x0004 | (channel << 8) | + lane_registers[slotrank], make_mr0(ctrl, slotrank)); + write32(DEFAULT_MCHBAR | 0x0008 | (channel << 8) | + lane_registers[slotrank], make_mr1(ctrl, slotrank)); + } +} + +static void channel_test(ramctr_timing * ctrl) +{ + int channel, slotrank, lane; + + FOR_ALL_POPULATED_CHANNELS + if (read32(DEFAULT_MCHBAR | 0x42a0 | (channel << 10)) & 0xa000) + die("Mini channel test failed (1)\n"); + FOR_ALL_POPULATED_CHANNELS { + fill_pattern0(ctrl, channel, 0x12345678, 0x98765432); + + write32(DEFAULT_MCHBAR | 0x4288 | (channel << 10), 0); + } + + for (slotrank = 0; slotrank < 4; slotrank++) + FOR_ALL_CHANNELS + if (ctrl->rankmap[channel] & (1 << slotrank)) { + FOR_ALL_LANES { + write32(DEFAULT_MCHBAR | (0x4f40 + 4 * lane), 0); + write32(DEFAULT_MCHBAR | (0x4d40 + 4 * lane), 0); + } + wait_428c(channel); + write32(DEFAULT_MCHBAR | 0x4220 | (channel << 10), 0x0001f006); + write32(DEFAULT_MCHBAR | 0x4230 | (channel << 10), 0x0028a004); + write32(DEFAULT_MCHBAR | 0x4200 | (channel << 10), + 0x00060000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4210 | (channel << 10), 0x00000244); + write32(DEFAULT_MCHBAR | 0x4224 | (channel << 10), 0x0001f201); + write32(DEFAULT_MCHBAR | 0x4234 | (channel << 10), 0x08281064); + write32(DEFAULT_MCHBAR | 0x4204 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4214 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x4228 | (channel << 10), 0x0001f105); + write32(DEFAULT_MCHBAR | 0x4238 | (channel << 10), 0x04281064); + write32(DEFAULT_MCHBAR | 0x4208 | (channel << 10), + 0x00000000 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x4218 | (channel << 10), 0x00000242); + write32(DEFAULT_MCHBAR | 0x422c | (channel << 10), 0x0001f002); + write32(DEFAULT_MCHBAR | 0x423c | (channel << 10), 0x00280c01); + write32(DEFAULT_MCHBAR | 0x420c | (channel << 10), + 0x00060400 | (slotrank << 24)); + write32(DEFAULT_MCHBAR | 0x421c | (channel << 10), 0x00000240); + write32(DEFAULT_MCHBAR | 0x4284 | (channel << 10), 0x000c0001); + wait_428c(channel); + FOR_ALL_LANES + if (read32(DEFAULT_MCHBAR | 0x4340 | (channel << 10))) + die("Mini channel test failed (2)\n"); + } +} + +static void set_scrambling_seed(ramctr_timing * ctrl) +{ + int channel; + + /* FIXME: we hardcode seeds. Do we need to use some PRNG for them? + I don't think so. */ + static u32 seeds[NUM_CHANNELS][3] = { + {0x00009a36, 0xbafcfdcf, 0x46d1ab68}, + {0x00028bfa, 0x53fe4b49, 0x19ed5483} + }; + FOR_ALL_POPULATED_CHANNELS { + MCHBAR32(0x4020 + 0x400 * channel) &= ~0x10000000; + write32(DEFAULT_MCHBAR | 0x4034, seeds[channel][0]); + write32(DEFAULT_MCHBAR | 0x403c, seeds[channel][1]); + write32(DEFAULT_MCHBAR | 0x4038, seeds[channel][2]); + } +} + +static void set_4f8c(void) +{ + struct cpuid_result cpures; + u32 cpu; + + cpures = cpuid(0); + cpu = (cpures.eax); + if (IS_SANDY_CPU(cpu) && (IS_SANDY_CPU_D0(cpu) || IS_SANDY_CPU_D1(cpu))) { + MCHBAR32(0x4f8c) = 0x141D1519; + } else { + MCHBAR32(0x4f8c) = 0x551D1519; + } +} + +static void prepare_training(ramctr_timing * ctrl) +{ + int channel; + + FOR_ALL_POPULATED_CHANNELS { + // Always drive command bus + MCHBAR32(0x4004 + 0x400 * channel) |= 0x20000000; + } + + udelay(1); + + FOR_ALL_POPULATED_CHANNELS wait_428c(channel); +} + +static void hardcode1(ramctr_timing * ctrl) +{ + int channel; + u32 reg; + FOR_ALL_POPULATED_CHANNELS { + reg = read32(DEFAULT_MCHBAR | 0x400c | (channel << 10)); + write32(DEFAULT_MCHBAR | 0x400c | (channel << 10), + (reg & 0xFFF0FFFF) + | (ctrl->ref_card_offset[channel] << 16) + | (ctrl->ref_card_offset[channel] << 18)); + write32(DEFAULT_MCHBAR | 0x4008 | (channel << 10), 0x0a042220); // FIXME: hardcoded + } +} + +static void set_42a0(ramctr_timing * ctrl) +{ + int channel; + FOR_ALL_POPULATED_CHANNELS { + write32(DEFAULT_MCHBAR | (0x42a0 + 0x400 * channel), + 0x00001000 | ctrl->rankmap[channel]); + MCHBAR32(0x4004 + 0x400 * channel) &= ~0x20000000; // OK + } +} + +void init_dram_ddr3(spd_raw_data * spds, int mobile, int min_tck) +{ + int me_uma_size; + + MCHBAR32(0x5f00) |= 1; + + report_platform_info(); + + /* Wait for ME to be ready */ + intel_early_me_init(); + me_uma_size = intel_early_me_uma_size(); + + printk(BIOS_DEBUG, "Starting native Platform init\n"); + + u32 reg_5d10; + + wait_txt_clear(); + + wrmsr(0x000002e6, (msr_t) { + .lo = 0,.hi = 0}); + + reg_5d10 = read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + if ((pcie_read_config16(SOUTHBRIDGE, 0xa2) & 0xa0) == 0x20 /* 0x0004 */ + && reg_5d10) { + /* Need reset. */ + outb(0x6, 0xcf9); + + while (1) ; + } + + ramctr_timing ctrl; + + dimm_info info; + + early_pch_init(); + early_thermal_init(); + + ctrl.mobile = mobile; + ctrl.tCK = min_tck; + + /* Get DDR3 SPD data */ + dram_find_spds_ddr3(spds, &info, &ctrl); + + /* Find fastest common supported parameters */ + dram_find_common_params(&info, &ctrl); + + /* Calculate timings */ + dram_timing(&ctrl); + + /* Set MCU frequency */ + dram_freq(&ctrl); + + /* Set version register */ + MCHBAR32(0x5034) = 0xC04EB002; + + /* Enable crossover */ + dram_xover(&ctrl); + + /* Set timing and refresh registers */ + dram_timing_regs(&ctrl); + + /* Power mode preset */ + MCHBAR32(0x4e80) = 0x5500; + + /* Set scheduler parameters */ + MCHBAR32(0x4c20) = 0x10100005; + + /* Set cpu specific register */ + set_4f8c(); + + /* Clear IO reset bit */ + MCHBAR32(0x5030) &= ~0x20; + + /* Set MAD-DIMM registers */ + dram_dimm_mapping(&info, &ctrl); + printk(BIOS_DEBUG, "Done dimm mapping\n"); + + /* Zone config */ + dram_zones(&info, &ctrl, 1); + + /* Set memory map */ + dram_memorymap(&info, me_uma_size); + printk(BIOS_DEBUG, "Done memory map\n"); + + /* Set IO registers */ + dram_ioregs(&ctrl); + printk(BIOS_DEBUG, "Done io registers\n"); + + udelay(1); + + /* Do jedec ddr3 reset sequence */ + dram_jedecreset(&ctrl); + printk(BIOS_DEBUG, "Done jedec reset\n"); + + /* MRS commands */ + dram_mrscommands(&ctrl); + printk(BIOS_DEBUG, "Done MRS commands\n"); + dram_mrscommands(&ctrl); + + /* Prepare for memory training */ + prepare_training(&ctrl); + + read_training(&ctrl); + write_training(&ctrl); + + printk(BIOS_SPEW, "CP5a\n"); + + discover_edges(&ctrl); + + printk(BIOS_SPEW, "CP5b\n"); + + command_training(&ctrl); + + printk(BIOS_SPEW, "CP5c\n"); + + discover_edges_write(&ctrl); + + discover_timC_write(&ctrl); + + normalize_training(&ctrl); + + hardcode1(&ctrl); + + write_controller_mr(&ctrl); + + channel_test(&ctrl); + + write32(DEFAULT_MCHBAR | 0x5024, 0x00a030ce); // FIXME: hardcoded + + set_scrambling_seed(&ctrl); + + set_42a0(&ctrl); + + write32(DEFAULT_MCHBAR | 0x4cd4, 0x00000046); // FIXME: hardcoded + + write32(DEFAULT_MCHBAR | 0x400c, (read32(DEFAULT_MCHBAR | 0x400c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x440c, (read32(DEFAULT_MCHBAR | 0x440c) & 0xFFFFCFFF) | 0x1000); // OK + write32(DEFAULT_MCHBAR | 0x4cb0, 0x00000740); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x4380, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4780, 0x00000aaa); // OK + write32(DEFAULT_MCHBAR | 0x4f88, 0x5f7003ff); // OK + write32(DEFAULT_MCHBAR | 0x5064, 0x00073000 | ctrl.reg_5064b0); // OK + write32(DEFAULT_MCHBAR | 0x4384, 0x009b6ea1); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x4784, 0x009b6ea1); // FIXME: hardcoded + write32(DEFAULT_MCHBAR | 0x5880, 0xca9171e5); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x5888); // !!! = 0x00e4d5d0 + write32(DEFAULT_MCHBAR | 0x5888, 0x00e4d5d0); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x58a8); // !!! = 0x00000000 + write32(DEFAULT_MCHBAR | 0x58a8, 0x00000000); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x4294); // !!! = 0x000098ff + write32(DEFAULT_MCHBAR | 0x4294, 0x000198ff); // FIXME: hardcoded + read32(DEFAULT_MCHBAR | 0x4694); // !!! = 0x000098ff + write32(DEFAULT_MCHBAR | 0x4694, 0x000198ff); // FIXME: hardcoded + + MCHBAR32(0x5030) |= 1; // OK + MCHBAR32(0x5030) |= 0x80; // OK + MCHBAR32(0x5f18) = 0xfa; // OK + read32(DEFAULT_MCHBAR | 0x5d10); // !!! = 0x00000000 + write32(DEFAULT_MCHBAR | 0x5d10, 0x2010040c); // FIXME: hardcoded + + /* Zone config */ + dram_zones(&info, &ctrl, 0); + + intel_early_me_status(); + intel_early_me_init_done(ME_INIT_STATUS_SUCCESS); + intel_early_me_status(); + + post_system_agent_init(); + report_memory_config(); +} diff --git a/src/northbridge/intel/sandybridge/raminit_native.h b/src/northbridge/intel/sandybridge/raminit_native.h new file mode 100644 index 0000000..ff7d7c1 --- /dev/null +++ b/src/northbridge/intel/sandybridge/raminit_native.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RAMINIT_H +#define RAMINIT_H + +#include <device/dram/ddr3.h> + +/* The order is ch0dimmA, ch0dimmB, ch1dimmA, ch1dimmB. */ +void init_dram_ddr3(spd_raw_data *spds, int mobile, int min_tck); +void read_spd(spd_raw_data *spd, u8 addr); + +#endif /* RAMINIT_H */ diff --git a/src/southbridge/intel/bd82x6x/Makefile.inc b/src/southbridge/intel/bd82x6x/Makefile.inc index 64038e5..fa21277 100644 --- a/src/southbridge/intel/bd82x6x/Makefile.inc +++ b/src/southbridge/intel/bd82x6x/Makefile.inc @@ -45,10 +45,14 @@ smm-$(CONFIG_SPI_FLASH_SMM) += ../common/spi.c ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c me.c me_8.x.c finalize.c pch.c -romstage-y += early_usb.c early_smbus.c early_me.c me_status.c gpio.c +romstage-y += early_usb.c early_smbus.c me_status.c gpio.c romstage-y += reset.c romstage-y += early_spi.c early_pch.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE) += early_me.c +romstage-$(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) += early_thermal.c early_pch_native.c early_me_native.c + ifeq ($(CONFIG_BUILD_WITH_FAKE_IFD),y) IFD_BIN_PATH := $(objgenerated)/ifdfake.bin IFD_SECTIONS := $(addprefix -b ,$(CONFIG_IFD_BIOS_SECTION:"%"=%)) \ diff --git a/src/southbridge/intel/bd82x6x/early_me_native.c b/src/southbridge/intel/bd82x6x/early_me_native.c new file mode 100644 index 0000000..ab54ffd --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_me_native.c @@ -0,0 +1,272 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <arch/hlt.h> +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> +#include <device/pci_ids.h> +#include <string.h> +#include "me.h" +#include "pch.h" + +static const char *me_ack_values[] = { + [ME_HFS_ACK_NO_DID] = "No DID Ack received", + [ME_HFS_ACK_RESET] = "Non-power cycle reset", + [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset", + [ME_HFS_ACK_S3] = "Go to S3", + [ME_HFS_ACK_S4] = "Go to S4", + [ME_HFS_ACK_S5] = "Go to S5", + [ME_HFS_ACK_GBL_RESET] = "Global Reset", + [ME_HFS_ACK_CONTINUE] = "Continue to boot" +}; + +static inline void pci_read_dword_ptr(void *ptr, int offset) +{ + u32 dword = pci_read_config32(PCH_ME_DEV, offset); + memcpy(ptr, &dword, sizeof(dword)); +} + +static inline void pci_write_dword_ptr(void *ptr, int offset) +{ + u32 dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + pci_write_config32(PCH_ME_DEV, offset, dword); +} + +void intel_early_me_status(void) +{ + struct me_hfs hfs; + struct me_gmes gmes; + + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + pci_read_dword_ptr(&gmes, PCI_ME_GMES); + + intel_me_status(&hfs, &gmes); +} + +int intel_early_me_init(void) +{ + int count; + struct me_uma uma; + struct me_hfs hfs; + + printk(BIOS_INFO, "Intel ME early init\n"); + + /* Wait for ME UMA SIZE VALID bit to be set */ + for (count = ME_RETRY; count > 0; --count) { + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) + break; + udelay(ME_DELAY); + } + if (!count) { + printk(BIOS_ERR, "ERROR: ME is not ready!\n"); + return -1; + } + + /* Check for valid firmware */ + pci_read_dword_ptr(&hfs, PCI_ME_HFS); + if (hfs.fpt_bad) { + printk(BIOS_WARNING, "WARNING: ME has bad firmware\n"); + return -1; + } + + printk(BIOS_INFO, "Intel ME firmware is ready\n"); + return 0; +} + +int intel_early_me_uma_size(void) +{ + struct me_uma uma; + + pci_read_dword_ptr(&uma, PCI_ME_UMA); + if (uma.valid) { + printk(BIOS_DEBUG, "ME: Requested %uMB UMA\n", uma.size); + return uma.size; + } + + printk(BIOS_DEBUG, "ME: Invalid UMA size\n"); + return 0; +} + +static inline void set_global_reset(int enable) +{ + u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3); + + /* Clear CF9 Without Resume Well Reset Enable */ + etr3 &= ~ETR3_CWORWRE; + + /* CF9GR indicates a Global Reset */ + if (enable) + etr3 |= ETR3_CF9GR; + else + etr3 &= ~ETR3_CF9GR; + + pci_write_config32(PCH_LPC_DEV, ETR3, etr3); +} + +int intel_early_me_init_done(u8 status) +{ + u8 reset, errorcode, opmode; + u16 reg16; + u32 mebase_l, mebase_h; + u32 millisec; + u32 hfs, me_fws2; + struct me_did did = { + .init_done = ME_INIT_DONE, + .status = status + }; + u32 meDID; + + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xff000) >> 12; + + opmode = (hfs & 0xf0) >> 4; + errorcode = hfs & 0xf; + + if (opmode != ME_HFS_MODE_NORMAL) { + printk(BIOS_NOTICE, "ME: Wrong mode : %d\n", opmode); + //return 0; + } + if (errorcode) { + printk(BIOS_NOTICE, "ME: HFS error : %d\n", errorcode); + //return 0; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + // Poll cpu replaced for 50ms + millisec = 0; + while ((((me_fws2 & 0x100) >> 8) == 0) && millisec < 50) { + udelay(1000); + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + millisec++; + } + if (millisec >= 50 || ((me_fws2 & 0x100) >> 8) == 0x0) { + printk(BIOS_NOTICE, "Waited long enough, or CPU was not replaced, continue...\n"); + } else if ((me_fws2 & 0x100) == 0x100) { + if ((me_fws2 & 0x80) == 0x80) { + printk(BIOS_NOTICE, "CPU was replaced & warm reset required...\n"); + reg16 = pcie_read_config16(PCI_DEV(0, 31, 0), 0xa2) & ~0x80; + pcie_write_config16(PCI_DEV(0, 31, 0), 0xa2, reg16); + set_global_reset(0); + outb(0x6, 0xcf9); + hlt(); + } + + if (((me_fws2 & 0x10) == 0x10) && (me_fws2 & 0x80) == 0x00) { + printk(BIOS_NOTICE, "Full training required\n"); + } + } + + printk(BIOS_NOTICE, "PASSED! Tell ME that DRAM is ready\n"); + + /* MEBASE from MESEG_BASE[35:20] */ + mebase_l = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_L); + mebase_h = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_H) & 0xf; + did.uma_base = (mebase_l >> 20) | (mebase_h << 12); + + meDID = did.uma_base | (1 << 28);// | (1 << 23); + pci_write_config32(PCI_DEV(0, 0x16, 0), PCI_ME_H_GS, meDID); + + udelay(1100); + + /* Must wait for ME acknowledgement */ + millisec = 0; + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + while ((((hfs & 0xf0) >> 4) != ME_HFS_BIOS_DRAM_ACK) && (millisec < 5000)) { + udelay(1000); + hfs = (pci_read_config32(PCI_DEV(0, 0x16, 0), PCI_ME_HFS) & 0xfe000000) >> 24; + millisec++; + } + + me_fws2 = pci_read_config32(PCI_DEV(0, 0x16, 0), 0x48); + printk(BIOS_NOTICE, "ME: FWS2: 0x%x\n", me_fws2); + printk(BIOS_NOTICE, "ME: Bist in progress: 0x%x\n", me_fws2 & 0x1); + printk(BIOS_NOTICE, "ME: ICC Status : 0x%x\n", (me_fws2 & 0x6) >> 1); + printk(BIOS_NOTICE, "ME: Invoke MEBx : 0x%x\n", (me_fws2 & 0x8) >> 3); + printk(BIOS_NOTICE, "ME: CPU replaced : 0x%x\n", (me_fws2 & 0x10) >> 4); + printk(BIOS_NOTICE, "ME: MBP ready : 0x%x\n", (me_fws2 & 0x20) >> 5); + printk(BIOS_NOTICE, "ME: MFS failure : 0x%x\n", (me_fws2 & 0x40) >> 6); + printk(BIOS_NOTICE, "ME: Warm reset req : 0x%x\n", (me_fws2 & 0x80) >> 7); + printk(BIOS_NOTICE, "ME: CPU repl valid : 0x%x\n", (me_fws2 & 0x100) >> 8); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0x600) >> 9); + printk(BIOS_NOTICE, "ME: FW update req : 0x%x\n", (me_fws2 & 0x800) >> 11); + printk(BIOS_NOTICE, "ME: (Reserved) : 0x%x\n", (me_fws2 & 0xf000) >> 12); + printk(BIOS_NOTICE, "ME: Current state : 0x%x\n", (me_fws2 & 0xff0000) >> 16); + printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); + printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28); + + + /* Return the requested BIOS action */ + printk(BIOS_NOTICE, "ME: Requested BIOS Action: %s\n", + me_ack_values[(hfs & 0xe) >> 1]); + + reset = inb(0xcf9); + reset &= 0xf1; + switch ((hfs & 0xe) >> 1) { + case ME_HFS_ACK_NO_DID: + case ME_HFS_ACK_CONTINUE: + /* Continue to boot */ + return 0; + case ME_HFS_ACK_RESET: + /* Non-power cycle reset */ + set_global_reset(0); + reset |= 0x06; + break; + case ME_HFS_ACK_PWR_CYCLE: + /* Power cycle reset */ + set_global_reset(0); + reset |= 0x0e; + break; + case ME_HFS_ACK_GBL_RESET: + /* Global reset */ + set_global_reset(1); + reset |= 0x0e; + break; + case ME_HFS_ACK_S3: + case ME_HFS_ACK_S4: + case ME_HFS_ACK_S5: + break; + } + + /* Perform the requested reset */ + if (reset) { + outb(reset, 0xcf9); + hlt(); + } + return -1; +} diff --git a/src/southbridge/intel/bd82x6x/early_pch_native.c b/src/southbridge/intel/bd82x6x/early_pch_native.c new file mode 100644 index 0000000..d7f3a2e --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_pch_native.c @@ -0,0 +1,375 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko <phcoder(a)gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <string.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <cbmem.h> +#include <arch/cbfs.h> +#include <cbfs.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> +#include <device/pci_def.h> +#include <delay.h> + +#include "pch.h" +/* For DMI bar. */ +#include "northbridge/intel/sandybridge/sandybridge.h" + +#define SOUTHBRIDGE PCI_DEV(0, 0x1f, 0) + +static void +wait_2338 (void) +{ + while (read8 (DEFAULT_RCBA | 0x2338) & 1); +} + +static u32 +read_2338 (u32 edx) +{ + u32 ret; + + write32 (DEFAULT_RCBA | 0x2330, edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + ret = read32 (DEFAULT_RCBA | 0x2334); + wait_2338 (); + read8 (DEFAULT_RCBA | 0x2338); + return ret; +} + +static void +write_2338 (u32 edx, u32 val) +{ + read_2338 (edx); + write16 (DEFAULT_RCBA | 0x2338, (read16 (DEFAULT_RCBA | 0x2338) + & 0x1ff) | 0x600); + wait_2338 (); + + write32 (DEFAULT_RCBA | 0x2334, val); + wait_2338 (); + write16 (DEFAULT_RCBA | 0x2338, + (read16 (DEFAULT_RCBA | 0x2338) & 0x1ff) | 0x600); + read8 (DEFAULT_RCBA | 0x2338); +} + + +static void +init_dmi (void) +{ + int i; + + write32 (DEFAULT_DMIBAR | 0x0914, + read32 (DEFAULT_DMIBAR | 0x0914) | 0x80000000); + write32 (DEFAULT_DMIBAR | 0x0934, + read32 (DEFAULT_DMIBAR | 0x0934) | 0x80000000); + for (i = 0; i < 4; i++) + { + write32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a00 | (i << 4)) & 0xf3ffffff); + write32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4), + read32 (DEFAULT_DMIBAR | 0x0a04 | (i << 4)) | 0x800); + } + write32 (DEFAULT_DMIBAR | 0x0c30, (read32 (DEFAULT_DMIBAR | 0x0c30) + & 0xfffffff) | 0x40000000); + for (i = 0; i < 2; i++) + { + write32 (DEFAULT_DMIBAR | 0x0904 | (i << 5), + read32 (DEFAULT_DMIBAR | 0x0904 | (i << 5)) & 0xfe3fffff); + write32 (DEFAULT_DMIBAR | 0x090c | (i << 5), + read32 (DEFAULT_DMIBAR | 0x090c | (i << 5)) & 0xfff1ffff); + } + write32 (DEFAULT_DMIBAR | 0x090c, + read32 (DEFAULT_DMIBAR | 0x090c) & 0xfe1fffff); + write32 (DEFAULT_DMIBAR | 0x092c, + read32 (DEFAULT_DMIBAR | 0x092c) & 0xfe1fffff); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x090c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x090c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x7a1842ec); + read32 (DEFAULT_DMIBAR | 0x092c); // !!! = 0x00000208 + write32 (DEFAULT_DMIBAR | 0x092c, 0x00000128); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0c04); // !!! = 0x2e680008 + write32 (DEFAULT_DMIBAR | 0x0c04, 0x2e680008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x7a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1842ec); + read32 (DEFAULT_DMIBAR | 0x0910); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0910, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0930); // !!! = 0x00006300 + write32 (DEFAULT_DMIBAR | 0x0930, 0x00004300); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042010 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0a04); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a04, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a14); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a14, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a24); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a24, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0a34); // !!! = 0x0c0708f0 + write32 (DEFAULT_DMIBAR | 0x0a34, 0x0c0718f0); + read32 (DEFAULT_DMIBAR | 0x0900); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0900, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0920); // !!! = 0x50000000 + write32 (DEFAULT_DMIBAR | 0x0920, 0x50000000); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46139008); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1842ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x3a1846ec); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03042018); + read32 (DEFAULT_DMIBAR | 0x0908); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0908, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0928); // !!! = 0x51ffffff + write32 (DEFAULT_DMIBAR | 0x0928, 0x51ffffff); + read32 (DEFAULT_DMIBAR | 0x0c00); // !!! = 0x29700c08 + write32 (DEFAULT_DMIBAR | 0x0c00, 0x29700c08); + read32 (DEFAULT_DMIBAR | 0x0c0c); // !!! = 0x16063400 + write32 (DEFAULT_DMIBAR | 0x0c0c, 0x00063400); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46139008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x46339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x46339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45339008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45339008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x453b9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x453b9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45bb9008); + read32 (DEFAULT_DMIBAR | 0x0700); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0700, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0720); // !!! = 0x45bb9008 + write32 (DEFAULT_DMIBAR | 0x0720, 0x45fb9008); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a080 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9021a280); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9021a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x9821a280); + read32 (DEFAULT_DMIBAR | 0x0a00); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a00, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a10); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a10, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a20); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a20, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0a30); // !!! = 0x03042018 + write32 (DEFAULT_DMIBAR | 0x0a30, 0x03242018); + read32 (DEFAULT_DMIBAR | 0x0258); // !!! = 0x40000600 + write32 (DEFAULT_DMIBAR | 0x0258, 0x60000600); + read32 (DEFAULT_DMIBAR | 0x0904); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0904, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0914); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0914, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x0924); // !!! = 0x3a1846ec + write32 (DEFAULT_DMIBAR | 0x0924, 0x2a1846ec); + read32 (DEFAULT_DMIBAR | 0x0934); // !!! = 0x9821a280 + write32 (DEFAULT_DMIBAR | 0x0934, 0x98200280); + read32 (DEFAULT_DMIBAR | 0x022c); // !!! = 0x00c26460 + write32 (DEFAULT_DMIBAR | 0x022c, 0x00c2403c); + read8 (DEFAULT_RCBA | 0x21a4); // !!! = 0x42 + + read32 (DEFAULT_RCBA | 0x21a4); // !!! = 0x00012c42 + read32 (DEFAULT_RCBA | 0x2340); // !!! = 0x0013001b + write32 (DEFAULT_RCBA | 0x2340, 0x003a001b); + read8 (DEFAULT_RCBA | 0x21b0); // !!! = 0x01 + write8 (DEFAULT_RCBA | 0x21b0, 0x02); + read32 (DEFAULT_DMIBAR | 0x0084); // !!! = 0x0041ac41 + write32 (DEFAULT_DMIBAR | 0x0084, 0x0041ac42); + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0041 + read8 (DEFAULT_DMIBAR | 0x0088); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0088, 0x20); + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + read16 (DEFAULT_DMIBAR | 0x008a); // !!! = 0x0042 + + read32 (DEFAULT_DMIBAR | 0x0014); // !!! = 0x8000007f + write32 (DEFAULT_DMIBAR | 0x0014, 0x80000019); + read32 (DEFAULT_DMIBAR | 0x0020); // !!! = 0x01000000 + write32 (DEFAULT_DMIBAR | 0x0020, 0x81000022); + read32 (DEFAULT_DMIBAR | 0x002c); // !!! = 0x02000000 + write32 (DEFAULT_DMIBAR | 0x002c, 0x82000044); + read32 (DEFAULT_DMIBAR | 0x0038); // !!! = 0x07000080 + write32 (DEFAULT_DMIBAR | 0x0038, 0x87000080); + read8 (DEFAULT_DMIBAR | 0x0004); // !!! = 0x00 + write8 (DEFAULT_DMIBAR | 0x0004, 0x01); + + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x01200654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x01200654 + write32 (DEFAULT_RCBA | 0x0050, 0x012a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + read8 (DEFAULT_RCBA | 0x1114); // !!! = 0x00 + write8 (DEFAULT_RCBA | 0x1114, 0x05); + read32 (DEFAULT_RCBA | 0x2014); // !!! = 0x80000011 + write32 (DEFAULT_RCBA | 0x2014, 0x80000019); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2020, 0x81000022); + read32 (DEFAULT_RCBA | 0x2020); // !!! = 0x81000022 + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2030, 0x82000044); + read32 (DEFAULT_RCBA | 0x2030); // !!! = 0x82000044 + read32 (DEFAULT_RCBA | 0x2040); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x2040, 0x87000080); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x012a0654 + write32 (DEFAULT_RCBA | 0x0050, 0x812a0654); + read32 (DEFAULT_RCBA | 0x0050); // !!! = 0x812a0654 + read16 (DEFAULT_RCBA | 0x201a); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2026); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2036); // !!! = 0x0000 + read16 (DEFAULT_RCBA | 0x2046); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x001a); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0026); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x0032); // !!! = 0x0000 + read16 (DEFAULT_DMIBAR | 0x003e); // !!! = 0x0000 +} + +void +early_pch_init (void) +{ + pcie_write_config8 (SOUTHBRIDGE, 0xa6, + pcie_read_config8 (SOUTHBRIDGE, 0xa6) | 2); + + write32 (DEFAULT_RCBA | 0x2088, 0x00109000); + read32 (DEFAULT_RCBA | 0x20ac); // !!! = 0x00000000 + write32 (DEFAULT_RCBA | 0x20ac, 0x40000000); + write32 (DEFAULT_RCBA | 0x100c, 0x01110000); + write8 (DEFAULT_RCBA | 0x2340, 0x1b); + read32 (DEFAULT_RCBA | 0x2314); // !!! = 0x0a080000 + write32 (DEFAULT_RCBA | 0x2314, 0x0a280000); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xc809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + write32 (DEFAULT_RCBA | 0x2324, 0x00854c74); + read8 (DEFAULT_RCBA | 0x0400); // !!! = 0x00 + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + read32 (DEFAULT_RCBA | 0x2310); // !!! = 0xa809605b + write32 (DEFAULT_RCBA | 0x2310, 0xa809605b); + + write_2338 (0xea007f62, 0x00590133); + write_2338 (0xec007f62, 0x00590133); + write_2338 (0xec007f64, 0x59555588); + write_2338 (0xea0040b9, 0x0001051c); + write_2338 (0xeb0040a1, 0x800084ff); + write_2338 (0xec0040a1, 0x800084ff); + write_2338 (0xea004001, 0x00008400); + write_2338 (0xeb004002, 0x40201758); + write_2338 (0xec004002, 0x40201758); + write_2338 (0xea004002, 0x00601758); + write_2338 (0xea0040a1, 0x810084ff); + write_2338 (0xeb0040b1, 0x0001c598); + write_2338 (0xec0040b1, 0x0001c598); + write_2338 (0xeb0040b6, 0x0001c598); + write_2338 (0xea0000a9, 0x80ff969f); + write_2338 (0xea0001a9, 0x80ff969f); + write_2338 (0xeb0040b2, 0x0001c396); + write_2338 (0xeb0040b3, 0x0001c396); + write_2338 (0xec0040b2, 0x0001c396); + write_2338 (0xea0001a9, 0x80ff94ff); + write_2338 (0xea000151, 0x0088037f); + write_2338 (0xea0000a9, 0x80ff94ff); + write_2338 (0xea000051, 0x0088037f); + + write_2338 (0xea007f05, 0x00010642); + write_2338 (0xea0040b7, 0x0001c91c); + write_2338 (0xea0040b8, 0x0001c91c); + write_2338 (0xeb0040a1, 0x820084ff); + write_2338 (0xec0040a1, 0x820084ff); + write_2338 (0xea007f0a, 0xc2480000); + + write_2338 (0xec00404d, 0x1ff177f); + write_2338 (0xec000084, 0x5a600000); + write_2338 (0xec000184, 0x5a600000); + write_2338 (0xec000284, 0x5a600000); + write_2338 (0xec000384, 0x5a600000); + write_2338 (0xec000094, 0x000f0501); + write_2338 (0xec000194, 0x000f0501); + write_2338 (0xec000294, 0x000f0501); + write_2338 (0xec000394, 0x000f0501); + write_2338 (0xec000096, 0x00000001); + write_2338 (0xec000196, 0x00000001); + write_2338 (0xec000296, 0x00000001); + write_2338 (0xec000396, 0x00000001); + write_2338 (0xec000001, 0x00008c08); + write_2338 (0xec000101, 0x00008c08); + write_2338 (0xec000201, 0x00008c08); + write_2338 (0xec000301, 0x00008c08); + write_2338 (0xec0040b5, 0x0001c518); + write_2338 (0xec000087, 0x06077597); + write_2338 (0xec000187, 0x06077597); + write_2338 (0xec000287, 0x06077597); + write_2338 (0xec000387, 0x06077597); + write_2338 (0xea000050, 0x00bb0157); + write_2338 (0xea000150, 0x00bb0157); + write_2338 (0xec007f60, 0x77777d77); + write_2338 (0xea00008d, 0x01320000); + write_2338 (0xea00018d, 0x01320000); + write_2338 (0xec0007b2, 0x04514b5e); + write_2338 (0xec00078c, 0x40000200); + write_2338 (0xec000780, 0x02000020); + + init_dmi(); +} diff --git a/src/southbridge/intel/bd82x6x/early_thermal.c b/src/southbridge/intel/bd82x6x/early_thermal.c new file mode 100644 index 0000000..02ec9a7 --- /dev/null +++ b/src/southbridge/intel/bd82x6x/early_thermal.c @@ -0,0 +1,70 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include "pch.h" +#include "cpu/intel/model_206ax/model_206ax.h" +#include <cpu/x86/msr.h> + +/* Early thermal init, must be done prior to giving ME its memory + which is done at the end of raminit. */ +void early_thermal_init(void) +{ + device_t dev; + msr_t msr; + + dev = PCI_DEV(0x0, 0x1f, 0x6); + + /* Program address for temporary BAR. */ + pci_write_config32(dev, 0x40, 0x40000000); + pci_write_config32(dev, 0x44, 0x0); + + /* Activate temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) | 5); + + + write16 (0x40000004, 0x3a2b); + write8 (0x4000000c, 0xff); + write8 (0x4000000d, 0x00); + write8 (0x4000000e, 0x40); + write8 (0x40000082, 0x00); + write8 (0x40000001, 0xba); + + /* Perform init. */ + /* Configure TJmax. */ + msr = rdmsr(MSR_TEMPERATURE_TARGET); + write16(0x40000012, ((msr.lo >> 16) & 0xff) << 6); + /* Northbridge temperature slope and offset. */ + write16(0x40000016, 0x808c); + + write16 (0x40000014, 0xde87); + + /* Enable thermal data reporting, processor, PCH and northbridge. */ + write16(0x4000001a, (read16(0x4000001a) & ~0xf) | 0x10f0); + + /* Disable temporary BAR. */ + pci_write_config32(dev, 0x40, + pci_read_config32(dev, 0x40) & ~1); + pci_write_config32(dev, 0x40, 0); + + write32 (DEFAULT_RCBA | 0x38b0, + (read32 (DEFAULT_RCBA | 0x38b0) & 0xffff8003) | 0x403c); +} diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h index 90de855..8b3e663 100644 --- a/src/southbridge/intel/bd82x6x/pch.h +++ b/src/southbridge/intel/bd82x6x/pch.h @@ -74,6 +74,8 @@ void enable_smbus(void); void enable_usb_bar(void); int smbus_read_byte(unsigned device, unsigned address); int early_spi_read(u32 offset, u32 size, u8 *buffer); +void early_thermal_init(void); +void early_pch_init(void); #endif #endif diff --git a/src/southbridge/intel/bd82x6x/smi.c b/src/southbridge/intel/bd82x6x/smi.c index 0166edf..a20232e 100644 --- a/src/southbridge/intel/bd82x6x/smi.c +++ b/src/southbridge/intel/bd82x6x/smi.c @@ -29,10 +29,7 @@ #include <cpu/x86/smm.h> #include <string.h> #include "pch.h" - -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE #include "northbridge/intel/sandybridge/sandybridge.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/bd82x6x/usb_ehci.c b/src/southbridge/intel/bd82x6x/usb_ehci.c index 78f92d9..97f20bd 100644 --- a/src/southbridge/intel/bd82x6x/usb_ehci.c +++ b/src/southbridge/intel/bd82x6x/usb_ehci.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include <kconfig.h> #include <console/console.h> #include <device/device.h> #include <device/pci.h> @@ -36,11 +37,43 @@ static void usb_ehci_init(struct device *dev) RCBA32(0x35b0) = reg32; printk(BIOS_DEBUG, "EHCI: Setting up controller.. "); + + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + pci_write_config32(dev, 0x84, 0x930c8811); + pci_write_config32(dev, 0x88, 0x24000d30); + pci_write_config32(dev, 0xf4, 0x80408588); + pci_write_config32(dev, 0xf4, 0x80808588); + pci_write_config32(dev, 0xf4, 0x00808588); + pci_write_config32(dev, 0xfc, 0x205b1708); +#endif + reg32 = pci_read_config32(dev, PCI_COMMAND); reg32 |= PCI_COMMAND_MASTER; //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + /* For others, done in MRC. */ +#if IS_ENABLED(CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE_NATIVE) + struct resource *res; + u8 access_cntl; + + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); +#endif + printk(BIOS_DEBUG, "done.\n"); } diff --git a/src/southbridge/intel/ibexpeak/me.c b/src/southbridge/intel/ibexpeak/me.c index bc56012..7efb687 100644 --- a/src/southbridge/intel/ibexpeak/me.c +++ b/src/southbridge/intel/ibexpeak/me.c @@ -377,125 +377,7 @@ static int mkhi_end_of_post(void) printk(BIOS_INFO, "ME: END OF POST message successful\n"); return 0; } -#endif - -#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && !defined(__SMM__) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) -/* Get ME firmware version */ -static int mkhi_get_fw_version(void) -{ - struct me_fw_version version; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_GEN, - .command = MKHI_GET_FW_VERSION, - }; - struct mei_header mei = { - .is_complete = 1, - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - .length = sizeof(mkhi), - }; - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) { - printk(BIOS_ERR, "ME: GET FW VERSION message failed\n"); - return -1; - } - - printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) " - "%u.%u.%u.%u (recovery)\n", - version.code_major, version.code_minor, - version.code_build_number, version.code_hot_fix, - version.recovery_major, version.recovery_minor, - version.recovery_build_number, version.recovery_hot_fix); - - return 0; -} - -static inline void print_cap(const char *name, int state) -{ - printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n", - name, state ? "en" : "dis"); -} - -/* Get ME Firmware Capabilities */ -static int mkhi_get_fwcaps(void) -{ - u32 rule_id = 0; - struct me_fwcaps cap; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_FWCAPS, - .command = MKHI_FWCAPS_GET_RULE, - }; - struct mei_header mei = { - .is_complete = 1, - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - .length = sizeof(mkhi) + sizeof(rule_id), - }; - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) { - printk(BIOS_ERR, "ME: GET FWCAPS message failed\n"); - return -1; - } - - print_cap("Full Network manageability", cap.caps_sku.full_net); - print_cap("Regular Network manageability", cap.caps_sku.std_net); - print_cap("Manageability", cap.caps_sku.manageability); - print_cap("Small business technology", cap.caps_sku.small_business); - print_cap("Level III manageability", cap.caps_sku.l3manageability); - print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at); - print_cap("IntelR Capability Licensing Service (CLS)", - cap.caps_sku.intel_cls); - print_cap("IntelR Power Sharing Technology (MPC)", - cap.caps_sku.intel_mpc); - print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking); - print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp); - print_cap("IPV6", cap.caps_sku.ipv6); - print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm); - print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och); - print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan); - print_cap("TLS", cap.caps_sku.tls); - print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan); - - return 0; -} -#endif - -#if CONFIG_CHROMEOS && 0 /* DISABLED */ -/* Tell ME to issue a global reset */ -int mkhi_global_reset(void) -{ - struct me_global_reset reset = { - .request_origin = GLOBAL_RESET_BIOS_POST, - .reset_type = CBM_RR_GLOBAL_RESET, - }; - struct mkhi_header mkhi = { - .group_id = MKHI_GROUP_ID_CBM, - .command = MKHI_GLOBAL_RESET, - }; - struct mei_header mei = { - .is_complete = 1, - .length = sizeof(mkhi) + sizeof(reset), - .host_address = MEI_HOST_ADDRESS, - .client_address = MEI_ADDRESS_MKHI, - }; - printk(BIOS_NOTICE, "ME: Requesting global reset\n"); - - /* Send request and wait for response */ - if (mei_sendrecv(&mei, &mkhi, &reset, NULL, 0) < 0) { - /* No response means reset will happen shortly... */ - hlt(); - } - - /* If the ME responded it rejected the reset request */ - printk(BIOS_ERR, "ME: Global Reset failed\n"); - return -1; -} -#endif - -#ifdef __SMM__ static void intel_me7_finalize_smm(void) { struct me_hfs hfs; @@ -723,7 +605,7 @@ static void intel_me_init(device_t dev) if (intel_mei_setup(dev) < 0) break; -#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) +#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) /* Print ME firmware version */ mkhi_get_fw_version(); /* Print ME firmware capabilities */ diff --git a/src/southbridge/intel/ibexpeak/smi.c b/src/southbridge/intel/ibexpeak/smi.c index 981be3b..2ce9072 100644 --- a/src/southbridge/intel/ibexpeak/smi.c +++ b/src/southbridge/intel/ibexpeak/smi.c @@ -31,13 +31,7 @@ #include <string.h> #include "pch.h" -#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE -#include "northbridge/intel/sandybridge/sandybridge.h" -#endif - -#if CONFIG_NORTHBRIDGE_INTEL_NEHALEM #include "northbridge/intel/nehalem/nehalem.h" -#endif extern unsigned char _binary_smm_start; extern unsigned char _binary_smm_end; diff --git a/src/southbridge/intel/ibexpeak/usb_ehci.c b/src/southbridge/intel/ibexpeak/usb_ehci.c index 21a257f..ea767c4 100644 --- a/src/southbridge/intel/ibexpeak/usb_ehci.c +++ b/src/southbridge/intel/ibexpeak/usb_ehci.c @@ -30,6 +30,8 @@ static void usb_ehci_init(struct device *dev) { u32 reg32; + struct resource *res; + u8 access_cntl; /* Disable Wake on Disconnect in RMH */ reg32 = RCBA32(0x35b0); @@ -50,6 +52,21 @@ static void usb_ehci_init(struct device *dev) //reg32 |= PCI_COMMAND_SERR; pci_write_config32(dev, PCI_COMMAND, reg32); + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (res) { + /* Number of ports and companion controllers. */ + reg32 = read32(res->base + 4); + write32(res->base + 4, (reg32 & 0xfff00000) | 3); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); + printk(BIOS_DEBUG, "done.\n"); }
1
0
0
0
Patch set updated for coreboot: d6b692e x230: Deploy VBT
by Vladimir Serbinenko
31 May '14
31 May '14
Vladimir Serbinenko (phcoder(a)gmail.com) just uploaded a new patch set to gerrit, which you can find at
http://review.coreboot.org/5396
-gerrit commit d6b692ea053682db877cd4941f89564746a303b4 Author: Vladimir Serbinenko <phcoder(a)gmail.com> Date: Sun Feb 23 00:13:56 2014 +0100 x230: Deploy VBT Change-Id: Ide31a56bfdbc31cd3b87993dfb4ed8ef0107cdba Signed-off-by: Vladimir Serbinenko <phcoder(a)gmail.com> --- src/mainboard/lenovo/x230/Makefile.inc | 1 + src/mainboard/lenovo/x230/gma.c | 297 ++++++++++++++++++++++++++++++++ src/northbridge/intel/sandybridge/gma.c | 30 ++++ src/northbridge/intel/sandybridge/gma.h | 2 + 4 files changed, 330 insertions(+) diff --git a/src/mainboard/lenovo/x230/Makefile.inc b/src/mainboard/lenovo/x230/Makefile.inc index d514d4b..4ce3d61 100644 --- a/src/mainboard/lenovo/x230/Makefile.inc +++ b/src/mainboard/lenovo/x230/Makefile.inc @@ -18,3 +18,4 @@ ## smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c +ramstage-y += gma.c diff --git a/src/mainboard/lenovo/x230/gma.c b/src/mainboard/lenovo/x230/gma.c new file mode 100644 index 0000000..20a461b --- /dev/null +++ b/src/mainboard/lenovo/x230/gma.c @@ -0,0 +1,297 @@ +#include <arch/io.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> + +#include "northbridge/intel/sandybridge/sandybridge.h" + +/* This array contains the information on flat panel. When using native + graphics init coreboot copies it to where VGA Option ROM would be so + that OS can find it and able to use internal display. This contains no + executable code and is just information on the panel. + */ + +unsigned char fake_vbt[8192] = +{ +0x24, 0x56, 0x42, 0x54, 0x20, 0x53, 0x4e, 0x42, 0x2f, 0x49, 0x56, 0x42, 0x2d, 0x4d, 0x4f, 0x42, +0x49, 0x4c, 0x45, 0x20, 0x64, 0x00, 0x30, 0x00, 0x6b, 0x11, 0xc6, 0x00, 0x30, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x42, 0x49, 0x4f, 0x53, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x20, +0xa8, 0x00, 0x16, 0x00, 0x3b, 0x11, 0xfe, 0xea, 0x00, 0x00, 0x64, 0x01, 0x01, 0x14, 0x0d, 0x32, +0x31, 0x36, 0x31, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x28, 0x52, 0x29, 0x20, 0x53, 0x61, 0x6e, 0x64, +0x79, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2f, 0x49, 0x76, 0x79, 0x62, 0x72, 0x69, 0x64, 0x67, +0x65, 0x20, 0x50, 0x43, 0x49, 0x20, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x65, 0x72, 0x61, 0x74, 0x65, +0x64, 0x20, 0x53, 0x56, 0x47, 0x41, 0x20, 0x42, 0x49, 0x4f, 0x53, 0x0d, 0x0a, 0x42, 0x75, 0x69, +0x6c, 0x64, 0x20, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x3a, 0x20, 0x32, 0x31, 0x36, 0x31, 0x5f, +0x52, 0x59, 0x61, 0x6e, 0x20, 0x50, 0x43, 0x20, 0x31, 0x34, 0x2e, 0x33, 0x34, 0x20, 0x20, 0x31, +0x32, 0x2f, 0x30, 0x37, 0x2f, 0x32, 0x30, 0x31, 0x32, 0x20, 0x20, 0x30, 0x38, 0x3a, 0x31, 0x38, +0x3a, 0x33, 0x31, 0x0d, 0x0a, 0x44, 0x45, 0x43, 0x4f, 0x4d, 0x50, 0x49, 0x4c, 0x41, 0x54, 0x49, +0x4f, 0x4e, 0x20, 0x4f, 0x52, 0x20, 0x44, 0x49, 0x53, 0x41, 0x53, 0x53, 0x45, 0x4d, 0x42, 0x4c, +0x59, 0x20, 0x50, 0x52, 0x4f, 0x48, 0x49, 0x42, 0x49, 0x54, 0x45, 0x44, 0x0d, 0x0a, 0x43, 0x6f, +0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, 0x32, 0x30, 0x30, 0x30, +0x2d, 0x32, 0x30, 0x31, 0x31, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, +0x2e, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x52, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x52, 0x65, 0x73, +0x65, 0x72, 0x76, 0x65, 0x64, 0x2e, 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x00, 0xc0, 0x03, 0x08, 0x04, +0x00, 0x00, 0x00, 0x01, 0x05, 0x00, 0x07, 0x03, 0x40, 0x01, 0x09, 0xfd, 0x32, 0x00, 0x44, 0x04, +0x40, 0x06, 0x04, 0x02, 0x09, 0x01, 0x00, 0x0a, 0x02, 0x08, 0x0c, 0x04, 0x08, 0x03, 0x01, 0x02, +0x05, 0x01, 0x04, 0x0d, 0x01, 0x04, 0x0b, 0x01, 0x02, 0x07, 0x01, 0x04, 0x15, 0x01, 0x04, 0x45, +0x01, 0x04, 0x0e, 0x04, 0x08, 0x46, 0x04, 0x40, 0x28, 0x20, 0x08, 0x48, 0x40, 0x08, 0x10, 0x00, +0x02, 0x0d, 0x01, 0x02, 0x04, 0x00, 0x00, 0x21, 0x08, 0x00, 0x22, 0x10, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xcb, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xd6, 0x60, 0x00, 0x10, 0x10, +0x01, 0xb6, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, +0x00, 0x07, 0x10, 0x01, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0xd6, 0x60, 0x00, 0x10, +0x10, 0x01, 0xc8, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, +0x00, 0x00, 0x07, 0x20, 0x01, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0xd6, 0x60, 0x00, +0x10, 0x10, 0x01, 0xda, 0x14, 0x00, 0x20, 0x00, 0x00, 0x40, 0xde, 0x09, 0x00, 0x00, 0x06, 0x00, +0x00, 0x00, 0x00, 0x07, 0x30, 0x01, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x01, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x40, 0x42, +0x44, 0x46, 0x48, 0x4a, 0x4c, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x80, 0x81, 0x82, 0x83, +0x84, 0x1a, 0x00, 0xfc, 0xc2, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x30, 0x00, 0x51, 0x07, 0x1e, 0x00, +0x48, 0x0a, 0x0f, 0x00, 0x86, 0x03, 0x28, 0x00, 0xb3, 0x03, 0x02, 0x00, 0xbd, 0x03, 0x38, 0x00, +0x61, 0x04, 0xc8, 0x00, 0xb1, 0x06, 0x30, 0x00, 0xe4, 0x06, 0x18, 0x00, 0xff, 0x06, 0x18, 0x00, +0x1a, 0x07, 0x18, 0x00, 0x12, 0x0a, 0x10, 0x00, 0x25, 0x0a, 0x08, 0x00, 0x30, 0x0a, 0x08, 0x00, +0x3b, 0x0a, 0x08, 0x00, 0x1d, 0x08, 0x08, 0x00, 0x61, 0x08, 0x12, 0x00, 0x73, 0x08, 0x12, 0x00, +0x85, 0x08, 0x12, 0x00, 0x97, 0x08, 0x12, 0x00, 0xac, 0x08, 0x0a, 0x00, 0xb6, 0x08, 0x0a, 0x00, +0xc0, 0x08, 0x0a, 0x00, 0xca, 0x08, 0x0a, 0x00, 0xd7, 0x08, 0x0a, 0x00, 0xe1, 0x08, 0x0a, 0x00, +0xeb, 0x08, 0x0a, 0x00, 0xf5, 0x08, 0x0a, 0x00, 0x07, 0x09, 0x0a, 0x00, 0x11, 0x09, 0x0a, 0x00, +0x1b, 0x09, 0x0a, 0x00, 0x25, 0x09, 0x0a, 0x00, 0x2f, 0x09, 0x0a, 0x00, 0x39, 0x09, 0x0a, 0x00, +0x43, 0x09, 0x0a, 0x00, 0x4d, 0x09, 0x0a, 0x00, 0x57, 0x09, 0x0a, 0x00, 0x61, 0x09, 0x0a, 0x00, +0x6b, 0x09, 0x0a, 0x00, 0x75, 0x09, 0x0a, 0x00, 0x7f, 0x09, 0x0a, 0x00, 0x89, 0x09, 0x0a, 0x00, +0x93, 0x09, 0x0a, 0x00, 0x9d, 0x09, 0x0a, 0x00, 0x06, 0x75, 0x00, 0xfc, 0xff, 0x02, 0x80, 0x00, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x8e, 0x29, 0x00, 0x80, 0x9c, 0x01, +0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x11, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xf0, +0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xf0, +0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, +0x07, 0x07, 0x00, 0xfe, 0xff, 0xce, 0x18, 0x00, 0xff, 0xff, 0x08, 0x3d, 0x00, 0xfc, 0xff, 0x02, +0x40, 0xf0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x01, 0x44, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, +0x48, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x03, 0x03, +0x50, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xf0, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, +0x58, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x09, 0x60, 0x00, 0x00, 0x00, 0x32, +0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, +0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, +0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, +0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, +0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, +0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x00, 0x32, 0x00, 0x0a, 0xcb, 0x00, +0x0a, 0x80, 0x04, 0x60, 0x03, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x05, 0x58, 0x02, 0xff, +0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x05, 0xc0, 0x03, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, +0x07, 0x40, 0x05, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x40, 0x07, 0x70, 0x05, 0xff, 0xff, 0xff, +0xbf, 0xff, 0xff, 0x80, 0x07, 0xa0, 0x05, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x0b, 0xc7, 0x00, 0x21, 0x80, +0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, +0x05, 0x70, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x02, 0x05, 0x72, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x13, 0x00, 0x4a, 0x00, 0x04, 0x00, 0x03, 0x08, 0x3c, 0x84, +0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0xef, 0x87, 0x0d, 0x03, 0x00, 0xf7, 0x03, +0xc8, 0x0e, 0x09, 0x00, 0x01, 0x22, 0x06, 0x5a, 0x00, 0x7e, 0x06, 0x2d, 0x00, 0x0f, 0x8b, 0x00, +0x09, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x84, 0x00, 0x10, 0x00, +0x03, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, +0x40, 0x00, 0x00, 0x40, 0x08, 0x00, 0x20, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x08, 0x00, 0x03, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, 0x04, 0x00, 0x00, +0x04, 0x08, 0x00, 0x40, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, +0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, +0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x01, 0x00, 0x00, 0x01, 0x08, 0x00, +0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, +0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0c, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x20, 0x00, 0x0f, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, +0x9e, 0x00, 0x06, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3c, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x16, 0x4b, 0x00, 0x00, 0x01, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x84, 0x0e, 0x00, 0x00, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, +0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, +0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, +0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x17, 0x48, +0x00, 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x18, 0x88, 0x36, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x18, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00, 0x30, 0x40, 0x30, 0x70, 0x13, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1e, 0xa8, 0x2f, 0x78, 0xe0, 0x51, 0x1a, 0x26, 0x40, 0x58, 0x98, 0x13, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x48, 0x3f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, +0xc0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x18, 0x28, 0x00, 0x36, 0x7f, 0x03, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x36, 0x7f, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, +0x36, 0x7f, 0x01, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x36, 0x7f, 0x06, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x19, 0x28, 0x00, 0x19, 0x00, 0xfa, 0x00, 0xfa, 0x00, 0x19, 0x00, 0x90, +0x01, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0xc8, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, +0x00, 0x40, 0x00, 0x2c, 0x01, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x2c, 0x01, 0x1a, +0x02, 0x00, 0x00, 0x40, 0x1b, 0xcc, 0x00, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, +0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, +0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, +0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, +0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, +0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, +0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, +0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, +0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, +0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, 0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0xd0, 0x07, 0x0a, +0x00, 0xd0, 0x07, 0xf4, 0x01, 0x88, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xff, 0xff, 0x1c, 0x36, 0x00, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, 0x08, 0x60, +0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, +0x08, 0x60, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, +0x1d, 0x10, 0x08, 0x60, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1d, 0x34, 0x00, 0x10, +0x00, 0x01, 0x08, 0x01, 0x09, 0x04, 0x0c, 0x40, 0x48, 0x20, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x08, 0x00, 0x01, 0x08, 0x01, 0x09, 0x04, 0x0c, 0x40, 0x48, 0x00, 0x08, 0x00, 0x01, +0x01, 0x09, 0x08, 0x02, 0x05, 0x04, 0x0c, 0x00, 0x08, 0x00, 0x01, 0x01, 0x09, 0x08, 0x02, 0x05, +0x04, 0x0c, 0x00, 0x1e, 0x11, 0x00, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x18, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, +0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, +0x00, 0x00, 0x29, 0x94, 0x00, 0x03, 0x0c, 0x0b, 0x20, 0x32, 0x0b, 0x12, 0x44, 0x0b, 0x0a, 0x4e, +0x0b, 0x20, 0x74, 0x0b, 0x12, 0x86, 0x0b, 0x0a, 0x90, 0x0b, 0x20, 0xb6, 0x0b, 0x12, 0xc8, 0x0b, +0x0a, 0xd2, 0x0b, 0x20, 0xf8, 0x0b, 0x12, 0x0a, 0x0c, 0x0a, 0x14, 0x0c, 0x20, 0x3a, 0x0c, 0x12, +0x4c, 0x0c, 0x0a, 0x56, 0x0c, 0x20, 0x7c, 0x0c, 0x12, 0x8e, 0x0c, 0x0a, 0x98, 0x0c, 0x20, 0xbe, +0x0c, 0x12, 0xd0, 0x0c, 0x0a, 0xda, 0x0c, 0x20, 0x00, 0x0d, 0x12, 0x12, 0x0d, 0x0a, 0x1c, 0x0d, +0x20, 0x42, 0x0d, 0x12, 0x54, 0x0d, 0x0a, 0x5e, 0x0d, 0x20, 0x84, 0x0d, 0x12, 0x96, 0x0d, 0x0a, +0xa0, 0x0d, 0x20, 0xc6, 0x0d, 0x12, 0xd8, 0x0d, 0x0a, 0xe2, 0x0d, 0x20, 0x08, 0x0e, 0x12, 0x1a, +0x0e, 0x0a, 0x24, 0x0e, 0x20, 0x4a, 0x0e, 0x12, 0x5c, 0x0e, 0x0a, 0x66, 0x0e, 0x20, 0x8c, 0x0e, +0x12, 0x9e, 0x0e, 0x0a, 0xa8, 0x0e, 0x20, 0xce, 0x0e, 0x12, 0xe0, 0x0e, 0x0a, 0xea, 0x0e, 0x20, +0x10, 0x0f, 0x12, 0x22, 0x0f, 0x0a, 0x2c, 0x0f, 0x0d, 0x2a, 0xf0, 0x04, 0x80, 0x02, 0xe0, 0x01, +0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, +0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, +0xff, 0xff, 0xd6, 0x09, 0x80, 0x90, 0x20, 0xe0, 0x1d, 0x10, 0x08, 0x60, 0x22, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x03, +0x58, 0x02, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, +0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, +0x27, 0x00, 0xff, 0xff, 0xa0, 0x0f, 0x20, 0x00, 0x31, 0x58, 0x1c, 0x20, 0x28, 0x80, 0x14, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x0c, +0x56, 0x05, 0x00, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x20, 0x00, 0x08, 0x72, 0x0c, 0x00, +0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, +0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x60, 0x1d, 0x56, 0xd8, 0x50, 0x00, 0x18, 0x30, 0x30, 0x40, +0x47, 0x00, 0x15, 0x9c, 0x10, 0x00, 0x00, 0x1b, 0x36, 0x7f, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, +0x00, 0x0c, 0x00, 0x05, 0x00, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, +0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, +0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x00, 0x98, 0x51, 0x00, 0x30, 0x40, +0x30, 0x70, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x05, 0x00, 0x04, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x78, 0x05, 0x1a, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, +0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, +0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x78, 0x20, 0x51, 0x1a, +0x10, 0x40, 0x10, 0x70, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x01, 0x90, +0x05, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x78, 0x05, 0x1a, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, +0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, +0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0xa8, 0x2f, 0x78, 0xe0, +0x51, 0x1a, 0x26, 0x40, 0x58, 0x98, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, +0x01, 0x90, 0x06, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x40, 0x06, 0xb0, 0x04, 0x80, 0x11, 0x0e, 0x00, +0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, +0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x48, 0x3f, +0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, +0x36, 0x7f, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x56, 0x05, 0x00, 0x03, 0x80, 0x11, +0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, +0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, +0x66, 0x21, 0x56, 0xaa, 0x51, 0x00, 0x1e, 0x30, 0x46, 0x90, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x18, 0x36, 0x7f, 0x03, 0x90, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x90, 0x06, 0x1a, 0x04, +0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, +0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, +0xff, 0xff, 0x7c, 0x2e, 0x90, 0xa0, 0x60, 0x1a, 0x1e, 0x40, 0x30, 0x20, 0x36, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x04, 0x90, 0x09, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, +0xb0, 0x04, 0x80, 0x11, 0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, +0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, +0x27, 0x00, 0xff, 0xff, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0, 0x23, 0x40, 0x30, 0x20, 0x2a, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x05, 0x90, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x0c, +0xa0, 0x05, 0x84, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, +0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, +0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x9a, 0x29, 0xa0, 0xd0, 0x51, 0x84, 0x22, 0x30, 0x50, 0x99, +0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, +0x00, 0x0c, 0x40, 0x06, 0x84, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x72, +0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, +0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x30, 0x2a, 0x40, 0xc8, 0x60, 0x84, 0x64, 0x30, +0x18, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x0c, 0x00, +0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, 0x00, 0x00, +0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, +0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x64, 0x19, 0x00, 0x40, 0x41, 0x00, +0x26, 0x30, 0x18, 0x88, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, +0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x05, 0x20, 0x03, 0x80, 0x11, 0x0e, 0x00, 0x00, 0x03, +0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, 0xd0, 0x07, +0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0xea, 0x1a, 0x00, 0xa0, +0x50, 0x20, 0x17, 0x30, 0x0c, 0x30, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x36, 0x7f, +0x03, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x80, 0x07, 0x38, 0x04, 0x80, 0x11, 0x0e, 0x00, +0x00, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, 0x0c, 0x00, +0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, 0x02, 0x3a, +0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2d, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, +0x36, 0x7f, 0x03, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x06, 0x80, 0x11, +0x0e, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x08, 0x72, 0x0c, 0x00, 0xb8, 0x0b, 0x2c, 0x01, 0x0c, 0x72, +0x0c, 0x00, 0xd0, 0x07, 0x2c, 0x01, 0x10, 0x72, 0x0c, 0x00, 0x06, 0x0f, 0x27, 0x00, 0xff, 0xff, +0x29, 0x40, 0x00, 0x60, 0x80, 0x00, 0x13, 0x60, 0x10, 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x1e, 0x36, 0x7f, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x4c, 0x46, 0x50, 0x5f, +0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, +0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, +0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, +0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, +0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, +0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, +0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, +0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, +0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, +0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, +0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, +0x6d, 0x65, 0x4c, 0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x4c, +0x46, 0x50, 0x5f, 0x50, 0x61, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x2b, 0x71, 0x00, 0x06, +0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4a, 0xdc, 0x00, 0x03, +0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, +0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, +0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, +0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, +0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, 0x4e, 0xdc, 0x00, 0x03, 0x58, 0xaa, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0x2c, 0x15, 0x00, 0x89, 0x46, 0x00, 0x00, 0x00, 0x49, 0x00, 0x0a, 0x00, 0x55, 0x00, 0x50, 0x00, +0x64, 0x00, 0x2c, 0x01, 0x96, 0x00, 0xe8, 0x03, 0x2e, 0xb0, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, +0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, +0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, +0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00 +}; diff --git a/src/northbridge/intel/sandybridge/gma.c b/src/northbridge/intel/sandybridge/gma.c index a82a284..b7b4da5 100644 --- a/src/northbridge/intel/sandybridge/gma.c +++ b/src/northbridge/intel/sandybridge/gma.c @@ -21,6 +21,7 @@ #include <console/console.h> #include <bootmode.h> #include <delay.h> +#include <string.h> #include <device/device.h> #include <device/pci.h> #include <device/pci_ids.h> @@ -652,6 +653,35 @@ static void gma_func0_init(struct device *dev) i915lightup(conf, physbase, iobase, mmiobase, graphics_base); #endif + + /* Linux relies on VBT for panel info. */ + if (read16(0xc0000) != 0xaa55) { + optionrom_header_t *oh = (void *)0xc0000; + optionrom_pcir_t *pcir; + int sz; + + memset(oh->reserved, 0, 8192); + + sz = (0x80 + sizeof(fake_vbt) + 511) / 512; + oh->signature = 0xaa55; + oh->size = sz; + oh->pcir_offset = 0x40; + oh->vbt_offset = 0x80; + + pcir = (void *)0xc0040; + pcir->signature = 0x52494350; // PCIR + pcir->vendor = dev->vendor; + pcir->device = dev->device; + pcir->length = sizeof(*pcir); + pcir->revision = dev->class; + pcir->classcode[0] = dev->class >> 8; + pcir->classcode[1] = dev->class >> 16; + pcir->classcode[2] = dev->class >> 24; + pcir->imagelength = sz; + pcir->indicator = 0x80; + + memcpy((void *)0xc0080, fake_vbt, sizeof(fake_vbt)); + } } static void gma_set_subsystem(device_t dev, unsigned vendor, unsigned device) diff --git a/src/northbridge/intel/sandybridge/gma.h b/src/northbridge/intel/sandybridge/gma.h index f128412..0248473 100644 --- a/src/northbridge/intel/sandybridge/gma.h +++ b/src/northbridge/intel/sandybridge/gma.h @@ -166,6 +166,8 @@ typedef struct { #define VBT_SIGNATURE 0x54425624 +extern u8 fake_vbt[8192]; + struct northbridge_intel_sandybridge_config; void i915lightup(const struct northbridge_intel_sandybridge_config *info,
1
0
0
0
← Newer
1
2
3
4
5
6
7
8
9
...
107
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
Results per page:
10
25
50
100
200