[coreboot-gerrit] Patch set updated for coreboot: 38ff532 NOTFORMERGE: Experimental i965 northbridge, ICH8 southbridge, T61 romstage (untested!)
Damien Zammit (damien@zamaudio.com)
gerrit at coreboot.org
Fri May 23 08:20:26 CEST 2014
Damien Zammit (damien at zamaudio.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/5807
-gerrit
commit 38ff5323f2ffd1178ff8339d3a8ec6e91bf159e1
Author: Damien Zammit <damien at zamaudio.com>
Date: Thu May 22 07:01:46 2014 +1000
NOTFORMERGE: Experimental i965 northbridge, ICH8 southbridge, T61 romstage
(untested!)
Change-Id: I4541ab385dd349349c2b615995f5a1202662fa03
Signed-off-by: Damien Zammit <damien at zamaudio.com>
---
src/cpu/x86/smm/smmrelocate.S | 2 +
src/mainboard/lenovo/Kconfig | 4 +
src/mainboard/lenovo/t61/Kconfig | 61 +
src/mainboard/lenovo/t61/Makefile.inc | 21 +
src/mainboard/lenovo/t61/devicetree.cb | 191 ++
src/mainboard/lenovo/t61/dock.c | 238 ++
src/mainboard/lenovo/t61/dock.h | 30 +
src/mainboard/lenovo/t61/romstage.c | 349 +++
src/northbridge/intel/Kconfig | 1 +
src/northbridge/intel/Makefile.inc | 1 +
src/northbridge/intel/i965/Kconfig | 76 +
src/northbridge/intel/i965/Makefile.inc | 31 +
src/northbridge/intel/i965/bootblock.c | 24 +
src/northbridge/intel/i965/debug.c | 120 +
src/northbridge/intel/i965/early_init.c | 909 ++++++
src/northbridge/intel/i965/errata.c | 34 +
src/northbridge/intel/i965/i965.h | 388 +++
src/northbridge/intel/i965/ram_calc.c | 57 +
src/northbridge/intel/i965/raminit.c | 3186 ++++++++++++++++++++
src/northbridge/intel/i965/raminit.h | 78 +
src/northbridge/intel/i965/rcven.c | 338 +++
src/northbridge/intel/i965/udelay.c | 82 +
src/southbridge/intel/Kconfig | 1 +
src/southbridge/intel/Makefile.inc | 1 +
src/southbridge/intel/i82801hx/Kconfig | 44 +
src/southbridge/intel/i82801hx/Makefile.inc | 38 +
src/southbridge/intel/i82801hx/acpi/audio.asl | 36 +
src/southbridge/intel/i82801hx/acpi/globalnvs.asl | 171 ++
src/southbridge/intel/i82801hx/acpi/irqlinks.asl | 493 +++
src/southbridge/intel/i82801hx/acpi/lpc.asl | 290 ++
src/southbridge/intel/i82801hx/acpi/pata.asl | 80 +
src/southbridge/intel/i82801hx/acpi/pci.asl | 77 +
src/southbridge/intel/i82801hx/acpi/pcie.asl | 186 ++
src/southbridge/intel/i82801hx/acpi/sata.asl | 83 +
.../intel/i82801hx/acpi/sleepstates.asl | 27 +
src/southbridge/intel/i82801hx/acpi/smbus.asl | 242 ++
src/southbridge/intel/i82801hx/acpi/usb.asl | 217 ++
src/southbridge/intel/i82801hx/azalia.c | 342 +++
src/southbridge/intel/i82801hx/bootblock.c | 54 +
src/southbridge/intel/i82801hx/chip.h | 76 +
src/southbridge/intel/i82801hx/early_lpc.c | 33 +
src/southbridge/intel/i82801hx/early_smbus.c | 62 +
src/southbridge/intel/i82801hx/i82801hx.c | 43 +
src/southbridge/intel/i82801hx/i82801hx.h | 386 +++
src/southbridge/intel/i82801hx/ide.c | 127 +
src/southbridge/intel/i82801hx/lpc.c | 538 ++++
src/southbridge/intel/i82801hx/nic.c | 46 +
src/southbridge/intel/i82801hx/nvs.h | 138 +
src/southbridge/intel/i82801hx/pci.c | 152 +
src/southbridge/intel/i82801hx/pcie.c | 135 +
src/southbridge/intel/i82801hx/reset.c | 41 +
src/southbridge/intel/i82801hx/sata.c | 230 ++
src/southbridge/intel/i82801hx/smbus.c | 271 ++
src/southbridge/intel/i82801hx/smbus.h | 100 +
src/southbridge/intel/i82801hx/smi.c | 393 +++
src/southbridge/intel/i82801hx/smihandler.c | 666 ++++
src/southbridge/intel/i82801hx/usb.c | 108 +
src/southbridge/intel/i82801hx/usb_ehci.c | 110 +
src/southbridge/intel/i82801hx/watchdog.c | 53 +
59 files changed, 12311 insertions(+)
diff --git a/src/cpu/x86/smm/smmrelocate.S b/src/cpu/x86/smm/smmrelocate.S
index bdc9771..decfdc9 100644
--- a/src/cpu/x86/smm/smmrelocate.S
+++ b/src/cpu/x86/smm/smmrelocate.S
@@ -32,6 +32,8 @@
// here.
#if CONFIG_SOUTHBRIDGE_INTEL_I82801GX
#include "../../../southbridge/intel/i82801gx/i82801gx.h"
+#elif CONFIG_SOUTHBRIDGE_INTEL_I82801HX
+#include "../../../southbridge/intel/i82801hx/i82801hx.h"
#elif CONFIG_SOUTHBRIDGE_INTEL_I82801DX
#include "../../../southbridge/intel/i82801dx/i82801dx.h"
#elif CONFIG_SOUTHBRIDGE_INTEL_SCH
diff --git a/src/mainboard/lenovo/Kconfig b/src/mainboard/lenovo/Kconfig
index c1dec85..a00bb96 100644
--- a/src/mainboard/lenovo/Kconfig
+++ b/src/mainboard/lenovo/Kconfig
@@ -40,6 +40,9 @@ config BOARD_LENOVO_T60
Thinkpad T60p (Model 2007)
+config BOARD_LENOVO_T61
+ bool "ThinkPad T61"
+
endchoice
source "src/mainboard/lenovo/x60/Kconfig"
@@ -48,6 +51,7 @@ source "src/mainboard/lenovo/x230/Kconfig"
source "src/mainboard/lenovo/t520/Kconfig"
source "src/mainboard/lenovo/t530/Kconfig"
source "src/mainboard/lenovo/t60/Kconfig"
+source "src/mainboard/lenovo/t61/Kconfig"
config MAINBOARD_VENDOR
string
diff --git a/src/mainboard/lenovo/t61/Kconfig b/src/mainboard/lenovo/t61/Kconfig
new file mode 100644
index 0000000..a544497
--- /dev/null
+++ b/src/mainboard/lenovo/t61/Kconfig
@@ -0,0 +1,61 @@
+if BOARD_LENOVO_T61
+
+config BOARD_SPECIFIC_OPTIONS # dummy
+ def_bool y
+ select CPU_INTEL_SOCKET_MFCPGA478
+ select NORTHBRIDGE_INTEL_I965
+ select NORTHBRIDGE_INTEL_SUBTYPE_I965GM
+ select SOUTHBRIDGE_INTEL_I82801HX
+ select SUPERIO_NSC_PC87382
+ select SUPERIO_NSC_PC87384
+ select SOUTHBRIDGE_TI_PCI1X2X
+ select EC_LENOVO_PMH7
+ select EC_LENOVO_H8
+ select DRIVERS_ICS_954309
+ #select HAVE_OPTION_TABLE
+ #select HAVE_PIRQ_TABLE
+ #select HAVE_MP_TABLE
+ select BOARD_ROMSIZE_KB_4096
+ #select CHANNEL_XOR_RANDOMIZATION
+ #select HAVE_ACPI_TABLES
+ #select HAVE_ACPI_RESUME
+ select EARLY_CBMEM_INIT
+ select H8_DOCK_EARLY_INIT
+ #select HAVE_CMOS_DEFAULT
+config MAINBOARD_DIR
+ string
+ default lenovo/t61
+
+config DCACHE_RAM_BASE
+ hex
+ default 0xffdf8000
+
+config DCACHE_RAM_SIZE
+ hex
+ default 0x8000
+
+config MAINBOARD_PART_NUMBER
+ string
+ default "ThinkPad T61"
+
+config MMCONF_BASE_ADDRESS
+ hex
+ default 0xf0000000
+
+config IRQ_SLOT_COUNT
+ int
+ default 18
+
+config MAX_CPUS
+ int
+ default 2
+
+config MAINBOARD_SMBIOS_MANUFACTURER
+ string
+ default "LENOVO"
+
+config SEABIOS_PS2_TIMEOUT
+ int
+ default 3000
+
+endif
diff --git a/src/mainboard/lenovo/t61/Makefile.inc b/src/mainboard/lenovo/t61/Makefile.inc
new file mode 100644
index 0000000..5e09e99
--- /dev/null
+++ b/src/mainboard/lenovo/t61/Makefile.inc
@@ -0,0 +1,21 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2007-2008 coresystems GmbH
+##
+## 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
+##
+
+smm-$(CONFIG_HAVE_SMI_HANDLER) += dock.c
+romstage-y += dock.c
diff --git a/src/mainboard/lenovo/t61/devicetree.cb b/src/mainboard/lenovo/t61/devicetree.cb
new file mode 100644
index 0000000..78b95f1
--- /dev/null
+++ b/src/mainboard/lenovo/t61/devicetree.cb
@@ -0,0 +1,191 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2007-2009 coresystems GmbH
+## Copyright (C) 2011 Sven Schnelle <svens at stackframe.org>
+##
+## 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
+##
+
+chip northbridge/intel/i965
+
+ device cpu_cluster 0 on
+ chip cpu/intel/socket_mFCPGA478
+ device lapic 0 on end
+ end
+ end
+
+ device domain 0 on
+ device pci 00.0 on # Host bridge
+ subsystemid 0x17aa 0x20b3
+ end
+ device pci 01.0 off # PCI-e
+ end
+
+ device pci 02.0 on # GMA Graphics controller
+ subsystemid 0x17aa 0x20b5
+ end
+ device pci 02.1 on # display controller
+ subsystemid 0x17aa 0x20b5
+ end
+
+ chip southbridge/intel/i82801hx
+ register "pirqa_routing" = "0x0b"
+ register "pirqb_routing" = "0x0b"
+ register "pirqc_routing" = "0x0b"
+ register "pirqd_routing" = "0x0b"
+ register "pirqe_routing" = "0x0b"
+ register "pirqf_routing" = "0x0b"
+ register "pirqg_routing" = "0x0b"
+ register "pirqh_routing" = "0x0b"
+
+ # GPI routing
+ # 0 No effect (default)
+ # 1 SMI# (if corresponding ALT_GPI_SMI_EN bit is also set)
+ # 2 SCI (if corresponding GPIO_EN bit is also set)
+ register "gpi13_routing" = "2"
+ register "gpi12_routing" = "2"
+ register "gpi8_routing" = "2"
+
+ device pci 1e.0 on # PCI Bridge
+ chip southbridge/ti/pci1x2x
+ device pci 00.0 on
+ subsystemid 0x17aa 0x20ae
+ end
+ register "scr" = "0x0844d070"
+ register "mrr" = "0x01d01002"
+
+ end
+ end
+ device pci 1f.0 on # PCI-LPC bridge
+ subsystemid 0x17aa 0x20b6
+ chip ec/lenovo/pmh7
+ device pnp ff.1 on # dummy
+ end
+
+ register "backlight_enable" = "0x01"
+ register "dock_event_enable" = "0x01"
+ end
+ chip ec/lenovo/h8
+ device pnp ff.2 on # dummy
+ io 0x60 = 0x62
+ io 0x62 = 0x66
+ io 0x64 = 0x1600
+ io 0x66 = 0x1604
+ end
+
+
+ register "config0" = "0xa6"
+ register "config1" = "0x05"
+ register "config2" = "0xa0"
+ register "config3" = "0x01"
+
+ register "beepmask0" = "0xfe"
+ register "beepmask1" = "0x96"
+ register "has_power_management_beeps" = "1"
+
+ register "event2_enable" = "0xff"
+ register "event3_enable" = "0xff"
+ register "event4_enable" = "0xf4"
+ register "event5_enable" = "0x3f"
+ register "event6_enable" = "0x80"
+ register "event7_enable" = "0x01"
+ register "event8_enable" = "0x01"
+ register "event9_enable" = "0xff"
+ register "eventa_enable" = "0xff"
+ register "eventb_enable" = "0xff"
+ register "eventc_enable" = "0xff"
+ register "eventd_enable" = "0xff"
+ register "eventc_enable" = "0x3c"
+
+ end
+ chip superio/nsc/pc87382
+ device pnp 164e.2 on # IR
+ io 0x60 = 0x2f8
+ end
+
+ device pnp 164e.3 off # Serial Port
+ io 0x60 = 0x3f8
+ end
+
+ device pnp 164e.7 on # GPIO
+ io 0x60 = 0x1680
+ end
+
+ device pnp 164e.19 on # DLPC
+ io 0x60 = 0x164c
+ end
+ end
+
+ chip superio/nsc/pc87384
+ device pnp 2e.0 off #FDC
+ end
+
+ device pnp 2e.1 on # Parallel Port
+ io 0x60 = 0x3bc
+ irq 0x70 = 7
+ end
+
+ device pnp 2e.2 off # Serial Port / IR
+ io 0x60 = 0x2f8
+ irq 0x70 = 4
+ end
+
+ device pnp 2e.3 on # Serial Port
+ io 0x60 = 0x3f8
+ irq 0x70 = 4
+ end
+
+ device pnp 2e.7 on # GPIO
+ io 0x60 = 0x1620
+ end
+
+ device pnp 2e.a off # WDT
+ end
+ end
+ end
+ device pci 1f.3 on # SMBUS
+ subsystemid 0x17aa 0x20a9
+ chip drivers/ics/954309
+ register "reg0" = "0x2e"
+ register "reg1" = "0xf7"
+ register "reg2" = "0x3c"
+ register "reg3" = "0x20"
+ register "reg4" = "0x01"
+ register "reg5" = "0x00"
+ register "reg6" = "0x1b"
+ register "reg7" = "0x01"
+ register "reg8" = "0x54"
+ register "reg9" = "0xff"
+ register "reg10" = "0xff"
+ register "reg11" = "0x07"
+ device i2c 69 on end
+ end
+ # eeprom, 8 virtual devices, same chip
+ chip drivers/i2c/at24rf08c
+ device i2c 54 on end
+ device i2c 55 on end
+ device i2c 56 on end
+ device i2c 57 on end
+ device i2c 5c on end
+ device i2c 5d on end
+ device i2c 5e on end
+ device i2c 5f on end
+ end
+ end
+ end
+ end
+end
diff --git a/src/mainboard/lenovo/t61/dock.c b/src/mainboard/lenovo/t61/dock.c
new file mode 100644
index 0000000..b01f8e8
--- /dev/null
+++ b/src/mainboard/lenovo/t61/dock.c
@@ -0,0 +1,238 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Sven Schnelle <svens at stackframe.org>
+ *
+ * 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 <device/device.h>
+#include <arch/io.h>
+#include <delay.h>
+#include "dock.h"
+#include "superio/nsc/pc87384/pc87384.h"
+#include "ec/acpi/ec.h"
+#include "ec/lenovo/pmh7/pmh7.h"
+#include "southbridge/intel/i82801gx/i82801gx.h"
+
+#define DLPC_CONTROL 0x164c
+
+static void dlpc_write_register(int reg, int value)
+{
+ outb(reg, 0x164e);
+ outb(value, 0x164f);
+}
+
+static u8 dlpc_read_register(int reg)
+{
+ outb(reg, 0x164e);
+ return inb(0x164f);
+}
+
+static void dock_write_register(int reg, int value)
+{
+ outb(reg, 0x2e);
+ outb(value, 0x2f);
+}
+
+static u8 dock_read_register(int reg)
+{
+ outb(reg, 0x2e);
+ return inb(0x2f);
+}
+
+static void dlpc_gpio_set_mode(int port, int mode)
+{
+ dlpc_write_register(0xf0, port);
+ dlpc_write_register(0xf1, mode);
+}
+
+static void dock_gpio_set_mode(int port, int mode, int irq)
+{
+ dock_write_register(0xf0, port);
+ dock_write_register(0xf1, mode);
+ dock_write_register(0xf2, irq);
+}
+
+static void dlpc_gpio_init(void)
+{
+ /* Select GPIO module */
+ dlpc_write_register(0x07, 0x07);
+ /* GPIO Base Address 0x1680 */
+ dlpc_write_register(0x60, 0x16);
+ dlpc_write_register(0x61, 0x80);
+
+ /* Activate GPIO */
+ dlpc_write_register(0x30, 0x01);
+
+ dlpc_gpio_set_mode(0x00, 3);
+ dlpc_gpio_set_mode(0x01, 3);
+ dlpc_gpio_set_mode(0x02, 0);
+ dlpc_gpio_set_mode(0x03, 3);
+ dlpc_gpio_set_mode(0x04, 4);
+ dlpc_gpio_set_mode(0x20, 4);
+ dlpc_gpio_set_mode(0x21, 4);
+ dlpc_gpio_set_mode(0x23, 4);
+}
+
+int dlpc_init(void)
+{
+ int timeout = 1000;
+
+ /* Enable 14.318MHz CLK on CLKIN */
+ dlpc_write_register(0x29, 0xa0);
+ while(!(dlpc_read_register(0x29) & 0x10) && timeout--)
+ udelay(1000);
+
+ if (!timeout)
+ return 1;
+
+ /* Select DLPC module */
+ dlpc_write_register(0x07, 0x19);
+ /* DLPC Base Address */
+ dlpc_write_register(0x60, (DLPC_CONTROL >> 8) & 0xff);
+ dlpc_write_register(0x61, DLPC_CONTROL & 0xff);
+ /* Activate DLPC */
+ dlpc_write_register(0x30, 0x01);
+
+ /* Reset docking state */
+ outb(0x00, DLPC_CONTROL);
+
+ dlpc_gpio_init();
+ return 0;
+}
+
+static int dock_superio_init(void)
+{
+ int timeout = 1000;
+ /* startup 14.318MHz Clock */
+ dock_write_register(0x29, 0xa0);
+ /* wait until clock is settled */
+ while(!(dock_read_register(0x29) & 0x10) && timeout--)
+ udelay(1000);
+
+ if (!timeout)
+ return 1;
+
+ /* set GPIO pins to Serial/Parallel Port
+ * functions
+ */
+ dock_write_register(0x22, 0xa9);
+
+ /* enable serial port */
+ dock_write_register(0x07, PC87384_SP1);
+ dock_write_register(0x30, 0x01);
+
+ dock_write_register(0x07, PC87384_GPIO);
+ dock_write_register(0x60, 0x16);
+ dock_write_register(0x61, 0x20);
+ /* enable GPIO */
+ dock_write_register(0x30, 0x01);
+
+ dock_gpio_set_mode(0x00, PC87384_GPIO_PIN_DEBOUNCE |
+ PC87384_GPIO_PIN_PULLUP, 0x00);
+
+ dock_gpio_set_mode(0x01, PC87384_GPIO_PIN_TYPE_PUSH_PULL |
+ PC87384_GPIO_PIN_OE, 0x00);
+
+ dock_gpio_set_mode(0x02, PC87384_GPIO_PIN_TYPE_PUSH_PULL |
+ PC87384_GPIO_PIN_OE, 0x00);
+
+ dock_gpio_set_mode(0x03, PC87384_GPIO_PIN_DEBOUNCE |
+ PC87384_GPIO_PIN_PULLUP, 0x00);
+
+ dock_gpio_set_mode(0x04, PC87384_GPIO_PIN_DEBOUNCE |
+ PC87384_GPIO_PIN_PULLUP, 0x00);
+
+ dock_gpio_set_mode(0x05, PC87384_GPIO_PIN_DEBOUNCE |
+ PC87384_GPIO_PIN_PULLUP, 0x00);
+
+ dock_gpio_set_mode(0x06, PC87384_GPIO_PIN_DEBOUNCE |
+ PC87384_GPIO_PIN_PULLUP, 0x00);
+
+ dock_gpio_set_mode(0x07, PC87384_GPIO_PIN_DEBOUNCE |
+ PC87384_GPIO_PIN_PULLUP, 0x00);
+
+
+ /* no GPIO events enabled for PORT0 */
+ outb(0x00, 0x1622);
+ /* clear GPIO events on PORT0 */
+ outb(0xff, 0x1623);
+ outb(0xff, 0x1624);
+ /* no GPIO events enabled for PORT1 */
+ outb(0x00, 0x1626);
+
+ /* clear GPIO events on PORT1*/
+ outb(0xff, 0x1627);
+ outb(0x1F, 0x1628);
+ outb(0xfd, 0x1620);
+ return 0;
+}
+
+int dock_connect(void)
+{
+ int timeout = 1000;
+
+ outb(0x07, DLPC_CONTROL);
+
+ timeout = 1000;
+
+ while(!(inb(DLPC_CONTROL) & 8) && timeout--)
+ udelay(1000);
+
+ if (!timeout) {
+ /* docking failed, disable DLPC switch */
+ outb(0x00, DLPC_CONTROL);
+ dlpc_write_register(0x30, 0x00);
+ return 1;
+ }
+
+ /* Assert D_PLTRST# */
+ outb(0xfe, 0x1680);
+ udelay(1000);
+ /* Deassert D_PLTRST# */
+ outb(0xff, 0x1680);
+ udelay(10000);
+
+ return dock_superio_init();
+}
+
+void dock_disconnect(void)
+{
+ /* disconnect LPC bus */
+ outb(0x00, DLPC_CONTROL);
+ /* Assert PLTRST and DLPCPD */
+ outb(0xfc, 0x1680);
+}
+
+int dock_present(void)
+{
+ return pmh7_register_read(0x61) & 1;
+}
+
+int legacy_io_present(void)
+{
+ return !(inb(DEFAULT_GPIOBASE + 0x0c) & 0x40);
+}
+
+void legacy_io_init(void)
+{
+ /* Enable Power for Ultrabay slot */
+ pmh7_ultrabay_power_enable(1);
+ udelay(100000);
+ dock_superio_init();
+}
diff --git a/src/mainboard/lenovo/t61/dock.h b/src/mainboard/lenovo/t61/dock.h
new file mode 100644
index 0000000..631f007
--- /dev/null
+++ b/src/mainboard/lenovo/t61/dock.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (c) 2011 Sven Schnelle <svens at stackframe.org>
+ *
+ * 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 THINKPAD_X60_DOCK_H
+#define THINKPAD_X60_DOCK_H
+
+extern int dock_connect(void);
+extern void dock_disconnect(void);
+extern int dock_present(void);
+extern int dlpc_init(void);
+
+extern int legacy_io_present(void);
+extern void legacy_io_init(void);
+#endif
diff --git a/src/mainboard/lenovo/t61/romstage.c b/src/mainboard/lenovo/t61/romstage.c
new file mode 100644
index 0000000..99394d3
--- /dev/null
+++ b/src/mainboard/lenovo/t61/romstage.c
@@ -0,0 +1,349 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ * Copyright (C) 2011 Sven Schnelle <svens at stackframe.org>
+ *
+ * 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
+ */
+
+// __PRE_RAM__ means: use "unsigned" for device, not a struct.
+
+#include <stdint.h>
+#include <string.h>
+#include <arch/io.h>
+#include <device/pci_def.h>
+#include <device/pnp_def.h>
+#include <cpu/x86/lapic.h>
+#include <lib.h>
+#include <cbmem.h>
+#include <timestamp.h>
+#include <pc80/mc146818rtc.h>
+#include <console/console.h>
+#include <cpu/x86/bist.h>
+#include "northbridge/intel/i965/i965.h"
+#include "northbridge/intel/i965/raminit.h"
+#include "southbridge/intel/i82801gx/i82801gx.h"
+#include "dock.h"
+
+void setup_ich7_gpios(void)
+{
+ printk(BIOS_DEBUG, " GPIOS...");
+
+ /* T60 GPIO:
+ 6: LEGACYIO#
+ 7: BDC_PRESENCE#
+ 8: H8_WAKE#
+ 10: MDI_DETECT
+ 12: H8SCI#
+ 14: CPUSB#
+ 15: CPPE#
+ 25: MDC_KILL#
+ 27: EXC_PWR_CTRL
+ 28: EXC_AUX_CTRL
+ 35: CLKREQ_SATA#
+ 36: PLANARID0
+ 37: PLANARID1
+ 38: PLANARID2
+ 39: PLANARID3
+ */
+ outl(0x1f48f7c2, DEFAULT_GPIOBASE + 0x00); /* GPIO_USE_SEL */
+ outl(0xe0e0ffc3, DEFAULT_GPIOBASE + 0x04); /* GP_IO_SEL */
+ outl(0xfbfefb7d, DEFAULT_GPIOBASE + 0x0c); /* GP_LVL */
+ /* Output Control Registers */
+ outl(0x00040000, DEFAULT_GPIOBASE + 0x18); /* GPO_BLINK */
+ /* Input Control Registers */
+ outl(0x000039ff, DEFAULT_GPIOBASE + 0x2c); /* GPI_INV */
+ outl(0x000100f0, DEFAULT_GPIOBASE + 0x30); /* GPIO_USE_SEL2 */
+ outl(0x000000f1, DEFAULT_GPIOBASE + 0x34); /* GP_IO_SEL2 */
+ outl(0x000300a3, DEFAULT_GPIOBASE + 0x38); /* GP_LVL2 */
+}
+
+static void ich7_enable_lpc(void)
+{
+ // Enable Serial IRQ
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0x64, 0xd0);
+ // decode range
+ pci_write_config16(PCI_DEV(0, 0x1f, 0), 0x80, 0x0210);
+ // decode range
+ pci_write_config16(PCI_DEV(0, 0x1f, 0), 0x82, 0x1f0d);
+
+ /* range 0x1600 - 0x167f */
+ pci_write_config16(PCI_DEV(0, 0x1f, 0), 0x84, 0x1601);
+ pci_write_config16(PCI_DEV(0, 0x1f, 0), 0x86, 0x007c);
+
+ /* range 0x15e0 - 0x10ef */
+ pci_write_config16(PCI_DEV(0, 0x1f, 0), 0x88, 0x15e1);
+ pci_write_config16(PCI_DEV(0, 0x1f, 0), 0x8a, 0x000c);
+
+ /* range 0x1680 - 0x169f */
+ pci_write_config16(PCI_DEV(0, 0x1f, 0), 0x8c, 0x1681);
+ pci_write_config16(PCI_DEV(0, 0x1f, 0), 0x8e, 0x001c);
+}
+
+static void early_superio_config(void)
+{
+ int timeout = 100000;
+ device_t dev = PNP_DEV(0x2e, 3);
+
+ pnp_write_config(dev, 0x29, 0xa0);
+
+ while(!(pnp_read_config(dev, 0x29) & 0x10) && timeout--)
+ udelay(1000);
+
+ /* Enable COM1 */
+ pnp_set_logical_device(dev);
+ pnp_set_iobase(dev, PNP_IDX_IO0, 0x3f8);
+ pnp_set_enable(dev, 1);
+}
+
+static void rcba_config(void)
+{
+ /* Set up virtual channel 0 */
+ RCBA32(0x0014) = 0x80000001;
+ RCBA32(0x001c) = 0x03128010;
+
+ /* Device 1f interrupt pin register */
+ RCBA32(0x3100) = 0x00001230;
+ RCBA32(0x3108) = 0x40004321;
+
+ /* PCIe Interrupts */
+ RCBA32(0x310c) = 0x00004321;
+ /* HD Audio Interrupt */
+ RCBA32(0x3110) = 0x00000002;
+
+ /* dev irq route register */
+ RCBA16(0x3140) = 0x1007;
+ RCBA16(0x3142) = 0x0076;
+ RCBA16(0x3144) = 0x3210;
+ RCBA16(0x3146) = 0x7654;
+ RCBA16(0x3148) = 0x0010;
+
+ /* Enable IOAPIC */
+ RCBA8(0x31ff) = 0x03;
+
+ /* Enable upper 128bytes of CMOS */
+ RCBA32(0x3400) = (1 << 2);
+
+ /* Disable unused devices */
+ RCBA32(0x3418) = FD_PCIE6 | FD_PCIE5 | FD_INTLAN | FD_ACMOD | FD_ACAUD;
+ RCBA32(0x3418) |= (1 << 0); // Required.
+
+ /* Set up I/O Trap #0 for 0xfe00 (SMIC) */
+ RCBA32(0x1e84) = 0x00020001;
+ RCBA32(0x1e80) = 0x0000fe01;
+
+ /* Set up I/O Trap #3 for 0x800-0x80c (Trap) */
+ RCBA32(0x1e9c) = 0x000200f0;
+ RCBA32(0x1e98) = 0x000c0801;
+}
+
+static void early_ich7_init(void)
+{
+ uint8_t reg8;
+ uint32_t reg32;
+
+ // program secondary mlt XXX byte?
+ pci_write_config8(PCI_DEV(0, 0x1e, 0), 0x1b, 0x20);
+
+ // reset rtc power status
+ reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xa4);
+ reg8 &= ~(1 << 2);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa4, reg8);
+
+ // usb transient disconnect
+ reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xad);
+ reg8 |= (3 << 0);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xad, reg8);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x1d, 7), 0xfc);
+ reg32 |= (1 << 29) | (1 << 17);
+ pci_write_config32(PCI_DEV(0, 0x1d, 7), 0xfc, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x1d, 7), 0xdc);
+ reg32 |= (1 << 31) | (1 << 27);
+ pci_write_config32(PCI_DEV(0, 0x1d, 7), 0xdc, reg32);
+
+ RCBA32(0x0088) = 0x0011d000;
+ RCBA16(0x01fc) = 0x060f;
+ RCBA32(0x01f4) = 0x86000040;
+ RCBA32(0x0214) = 0x10030549;
+ RCBA32(0x0218) = 0x00020504;
+ RCBA8(0x0220) = 0xc5;
+ reg32 = RCBA32(0x3410);
+ reg32 |= (1 << 6);
+ RCBA32(0x3410) = reg32;
+ reg32 = RCBA32(0x3430);
+ reg32 &= ~(3 << 0);
+ reg32 |= (1 << 0);
+ RCBA32(0x3430) = reg32;
+ RCBA32(0x3418) |= (1 << 0);
+ RCBA16(0x0200) = 0x2008;
+ RCBA8(0x2027) = 0x0d;
+ RCBA16(0x3e08) |= (1 << 7);
+ RCBA16(0x3e48) |= (1 << 7);
+ RCBA32(0x3e0e) |= (1 << 7);
+ RCBA32(0x3e4e) |= (1 << 7);
+
+ // next step only on ich7m b0 and later:
+ reg32 = RCBA32(0x2034);
+ reg32 &= ~(0x0f << 16);
+ reg32 |= (5 << 16);
+ RCBA32(0x2034) = reg32;
+}
+
+void main(unsigned long bist)
+{
+ u32 reg32;
+ int boot_mode = 0, dock_err;
+ int cbmem_was_initted;
+ const u8 spd_addrmap[2 * DIMM_SOCKETS] = { 0x50, 0x52, 0x51, 0x53 };
+
+
+ timestamp_init(get_initial_timestamp());
+ timestamp_add_now(TS_START_ROMSTAGE);
+
+ if (bist == 0)
+ enable_lapic();
+
+ /* Force PCIRST# */
+ pci_write_config16(PCI_DEV(0, 0x1e, 0), BCTRL, SBR);
+ udelay(200 * 1000);
+ pci_write_config16(PCI_DEV(0, 0x1e, 0), BCTRL, 0);
+
+ ich7_enable_lpc();
+
+ /* We want early GPIO setup, to be able to detect legacy I/O module */
+ pci_write_config32(PCI_DEV(0, 0x1f, 0), GPIOBASE, DEFAULT_GPIOBASE | 1);
+ /* Enable GPIOs */
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0x4c /* GC */ , 0x10);
+ setup_ich7_gpios();
+
+ dock_err = dlpc_init();
+
+ /* We prefer Legacy I/O module over docking */
+ if (legacy_io_present()) {
+ legacy_io_init();
+ early_superio_config();
+ } else if (!dock_err && dock_present()) {
+ dock_connect();
+ early_superio_config();
+ }
+
+ /* Setup the console */
+ console_init();
+ printk(BIOS_EMERG, "READY CONSOLE!\n");
+
+ /* Halt if there was a built in self test failure */
+ report_bist_failure(bist);
+
+ if (MCHBAR16(SSKPD) == 0xCAFE) {
+ printk(BIOS_DEBUG, "soft reset detected, rebooting properly\n");
+ outb(0x6, 0xcf9);
+ while (1) asm("hlt");
+ }
+
+ /* Perform some early chipset initialization required
+ * before RAM initialization can work
+ */
+ i965_early_initialization();
+
+ /* Read PM1_CNT */
+ reg32 = inl(DEFAULT_PMBASE + 0x04);
+ printk(BIOS_DEBUG, "PM1_CNT: %08x\n", reg32);
+ if (((reg32 >> 10) & 7) == 5) {
+#if CONFIG_HAVE_ACPI_RESUME
+ printk(BIOS_DEBUG, "Resume from S3 detected.\n");
+ boot_mode = 2;
+ /* Clear SLP_TYPE. This will break stage2 but
+ * we care for that when we get there.
+ */
+ outl(reg32 & ~(7 << 10), DEFAULT_PMBASE + 0x04);
+
+#else
+ printk(BIOS_DEBUG, "Resume from S3 detected, but disabled.\n");
+#endif
+ }
+
+ /* Enable SPD ROMs and DDR-II DRAM */
+ enable_smbus();
+
+#if CONFIG_DEFAULT_CONSOLE_LOGLEVEL > 8
+ dump_spd_registers();
+#endif
+
+ timestamp_add_now(TS_BEFORE_INITRAM);
+ sdram_initialize(boot_mode, spd_addrmap);
+ timestamp_add_now(TS_AFTER_INITRAM);
+
+ /* Perform some initialization that must run before stage2 */
+ early_ich7_init();
+
+ /* This should probably go away. Until now it is required
+ * and mainboard specific
+ */
+ rcba_config();
+
+ /* Chipset Errata! */
+ fixup_i965_errata();
+
+ /* Initialize the internal PCIe links before we go into stage2 */
+ i965_late_initialization();
+
+#if !CONFIG_HAVE_ACPI_RESUME
+#if CONFIG_DEFAULT_CONSOLE_LOGLEVEL > 8
+#if CONFIG_DEBUG_RAM_SETUP
+ sdram_dump_mchbar_registers();
+
+ {
+ /* This will not work if TSEG is in place! */
+ u32 tom = pci_read_config32(PCI_DEV(0,2,0), 0x5c);
+
+ printk(BIOS_DEBUG, "TOM: 0x%08x\n", tom);
+ ram_check(0x00000000, 0x000a0000);
+ ram_check(0x00100000, tom);
+ }
+#endif
+#endif
+#endif
+
+ MCHBAR16(SSKPD) = 0xCAFE;
+
+ cbmem_was_initted = !cbmem_recovery(boot_mode==2);
+
+#if CONFIG_HAVE_ACPI_RESUME
+ /* If there is no high memory area, we didn't boot before, so
+ * this is not a resume. In that case we just create the cbmem toc.
+ */
+ if ((boot_mode == 2) && cbmem_was_initted) {
+ void *resume_backup_memory = cbmem_find(CBMEM_ID_RESUME);
+
+ /* copy 1MB - 64K to high tables ram_base to prevent memory corruption
+ * through stage 2. We could keep stuff like stack and heap in high tables
+ * memory completely, but that's a wonderful clean up task for another
+ * day.
+ */
+ if (resume_backup_memory)
+ memcpy(resume_backup_memory, (void *)CONFIG_RAMBASE, HIGH_MEMORY_SAVE);
+
+ /* Magic for S3 resume */
+ pci_write_config32(PCI_DEV(0, 0x00, 0), SKPAD, SKPAD_ACPI_S3_MAGIC);
+ }
+#endif
+
+ timestamp_add_now(TS_END_ROMSTAGE);
+
+}
diff --git a/src/northbridge/intel/Kconfig b/src/northbridge/intel/Kconfig
index b9ee5c6..1a4ac7c 100644
--- a/src/northbridge/intel/Kconfig
+++ b/src/northbridge/intel/Kconfig
@@ -9,6 +9,7 @@ source src/northbridge/intel/i82810/Kconfig
source src/northbridge/intel/i82830/Kconfig
source src/northbridge/intel/i855/Kconfig
source src/northbridge/intel/i945/Kconfig
+source src/northbridge/intel/i965/Kconfig
source src/northbridge/intel/gm45/Kconfig
source src/northbridge/intel/sch/Kconfig
source src/northbridge/intel/i5000/Kconfig
diff --git a/src/northbridge/intel/Makefile.inc b/src/northbridge/intel/Makefile.inc
index 808a1b2..40ffe1f 100644
--- a/src/northbridge/intel/Makefile.inc
+++ b/src/northbridge/intel/Makefile.inc
@@ -9,6 +9,7 @@ subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I82810) += i82810
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I82830) += i82830
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I855) += i855
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I945) += i945
+subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I965) += i965
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_GM45) += gm45
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_SCH) += sch
subdirs-$(CONFIG_NORTHBRIDGE_INTEL_I5000) += i5000
diff --git a/src/northbridge/intel/i965/Kconfig b/src/northbridge/intel/i965/Kconfig
new file mode 100644
index 0000000..8a4435c
--- /dev/null
+++ b/src/northbridge/intel/i965/Kconfig
@@ -0,0 +1,76 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2007-2009 coresystems GmbH
+##
+## 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
+##
+
+config NORTHBRIDGE_INTEL_I965
+ bool
+
+if NORTHBRIDGE_INTEL_I965
+
+config NORTHBRIDGE_SPECIFIC_OPTIONS # dummy
+ def_bool y
+ select MMCONF_SUPPORT
+ select MMCONF_SUPPORT_DEFAULT
+ select HAVE_DEBUG_RAM_SETUP
+ select LAPIC_MONOTONIC_TIMER
+
+config NORTHBRIDGE_INTEL_SUBTYPE_I965GC
+ def_bool n
+config NORTHBRIDGE_INTEL_SUBTYPE_I965GM
+ def_bool n
+
+config BOOTBLOCK_NORTHBRIDGE_INIT
+ string
+ default "northbridge/intel/i965/bootblock.c"
+
+config VGA_BIOS_ID
+ string
+ default "8086,2a02"
+
+config CHANNEL_XOR_RANDOMIZATION
+ bool
+ default n
+
+config OVERRIDE_CLOCK_DISABLE
+ bool
+ default n
+ help
+ Usually system firmware turns off system memory clock
+ signals to unused SO-DIMM slots to reduce EMI and power
+ consumption.
+ However, some boards do not like unused clock signals to
+ be disabled.
+
+config MAXIMUM_SUPPORTED_FREQUENCY
+ int
+ default 0
+ help
+ If non-zero, this designates the maximum DDR frequency
+ the board supports, despite what the chipset should be
+ capable of.
+
+config CHECK_SLFRCS_ON_RESUME
+ def_bool n
+ help
+ On some boards it may be neccessary to hard reset early
+ during resume from S3 if the SLFRCS register indicates that
+ a memory channel is not guaranteed to be in self-refresh.
+ On other boards the check always creates a false positive,
+ effectively making it impossible to resume.
+
+endif
diff --git a/src/northbridge/intel/i965/Makefile.inc b/src/northbridge/intel/i965/Makefile.inc
new file mode 100644
index 0000000..5acf33e
--- /dev/null
+++ b/src/northbridge/intel/i965/Makefile.inc
@@ -0,0 +1,31 @@
+#
+# This file is part of the coreboot project.
+#
+# Copyright (C) 2007-2009 coresystems GmbH
+#
+# 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
+#
+
+#ramstage-y += ram_calc.c
+#ramstage-y += northbridge.c
+#ramstage-y += gma.c
+#ramstage-$(CONFIG_GENERATE_ACPI_TABLES) += acpi.c
+
+romstage-y += ram_calc.c
+romstage-y += raminit.c
+romstage-y += early_init.c
+romstage-y += errata.c
+romstage-y += debug.c
+
+smm-y += udelay.c
diff --git a/src/northbridge/intel/i965/bootblock.c b/src/northbridge/intel/i965/bootblock.c
new file mode 100644
index 0000000..7458f1e
--- /dev/null
+++ b/src/northbridge/intel/i965/bootblock.c
@@ -0,0 +1,24 @@
+#include <arch/io.h>
+
+/* Just re-define this instead of including i945.h. It blows up romcc. */
+#define PCIEXBAR 0x60
+
+static void bootblock_northbridge_init(void)
+{
+ uint32_t reg;
+
+ /*
+ * The "io" variant of the config access is explicitly used to
+ * setup the PCIEXBAR because CONFIG_MMCONF_SUPPORT_DEFAULT is set to
+ * to true. That way all subsequent non-explicit config accesses use
+ * MCFG. This code also assumes that bootblock_northbridge_init() is
+ * the first thing called in the non-asm boot block code. The final
+ * assumption is that no assembly code is using the
+ * CONFIG_MMCONF_SUPPORT_DEFAULT option to do PCI config acceses.
+ *
+ * The PCIEXBAR is assumed to live in the memory mapped IO space under
+ * 4GiB.
+ */
+ reg = CONFIG_MMCONF_BASE_ADDRESS | 4 | 1; /* 64MiB - 0-63 buses. */
+ pci_io_write_config32(PCI_DEV(0,0,0), PCIEXBAR, reg);
+}
diff --git a/src/northbridge/intel/i965/debug.c b/src/northbridge/intel/i965/debug.c
new file mode 100644
index 0000000..79004ee
--- /dev/null
+++ b/src/northbridge/intel/i965/debug.c
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 coresystems GmbH
+ *
+ * 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 <spd.h>
+#include <lib.h>
+#include <arch/io.h>
+#include <device/pci_def.h>
+#include <console/console.h>
+#include "i965.h"
+
+void print_pci_devices(void)
+{
+ device_t dev;
+ for(dev = PCI_DEV(0, 0, 0);
+ dev <= PCI_DEV(0, 0x1f, 0x7);
+ dev += PCI_DEV(0,0,1)) {
+ uint32_t id;
+ id = pci_read_config32(dev, PCI_VENDOR_ID);
+ if (((id & 0xffff) == 0x0000) || ((id & 0xffff) == 0xffff) ||
+ (((id >> 16) & 0xffff) == 0xffff) ||
+ (((id >> 16) & 0xffff) == 0x0000)) {
+ continue;
+ }
+ printk(BIOS_DEBUG, "PCI: %02x:%02x.%02x", (dev >> 20) & 0xff,
+ (dev >> 15) & 0x1f, (dev >> 12) & 7);
+ printk(BIOS_DEBUG, " [%04x:%04x]\n", id &0xffff, id >> 16);
+ }
+}
+
+void dump_pci_device(unsigned dev)
+{
+ int i;
+
+ printk(BIOS_DEBUG, "PCI: %02x:%02x.%02x\n", (dev >> 20) & 0xff, (dev >> 15) & 0x1f, (dev >> 12) & 7);
+
+ for(i = 0; i <= 255; i++) {
+ unsigned char val;
+ if ((i & 0x0f) == 0) {
+ printk(BIOS_DEBUG, "%02x:", i);
+ }
+ val = pci_read_config8(dev, i);
+ printk(BIOS_DEBUG, " %02x", val);
+ if ((i & 0x0f) == 0x0f) {
+ printk(BIOS_DEBUG, "\n");
+ }
+ }
+}
+
+void dump_pci_devices(void)
+{
+ device_t dev;
+ for(dev = PCI_DEV(0, 0, 0);
+ dev <= PCI_DEV(0, 0x1f, 0x7);
+ dev += PCI_DEV(0,0,1)) {
+ uint32_t id;
+ id = pci_read_config32(dev, PCI_VENDOR_ID);
+ if (((id & 0xffff) == 0x0000) || ((id & 0xffff) == 0xffff) ||
+ (((id >> 16) & 0xffff) == 0xffff) ||
+ (((id >> 16) & 0xffff) == 0x0000)) {
+ continue;
+ }
+ dump_pci_device(dev);
+ }
+}
+
+void dump_spd_registers(void)
+{
+ unsigned device;
+ device = DIMM0;
+ while(device <= DIMM3) {
+ int status = 0;
+ int i;
+ printk(BIOS_DEBUG, "\ndimm %02x", device);
+
+ for(i = 0; (i < 256) ; i++) {
+ if ((i % 16) == 0) {
+ printk(BIOS_DEBUG, "\n%02x: ", i);
+ }
+ status = smbus_read_byte(device, i);
+ if (status < 0) {
+ printk(BIOS_DEBUG, "bad device: %02x\n", -status);
+ break;
+ }
+ printk(BIOS_DEBUG, "%02x ", status);
+ }
+ device++;
+ printk(BIOS_DEBUG, "\n");
+ }
+}
+
+void dump_mem(unsigned start, unsigned end)
+{
+ unsigned i;
+ print_debug("dump_mem:");
+ for(i=start;i<end;i++) {
+ if((i & 0xf)==0) {
+ printk(BIOS_DEBUG, "\n%08x:", i);
+ }
+ printk(BIOS_DEBUG, " %02x", (unsigned char)*((unsigned char *)i));
+ }
+ print_debug("\n");
+ }
diff --git a/src/northbridge/intel/i965/early_init.c b/src/northbridge/intel/i965/early_init.c
new file mode 100644
index 0000000..97bd115
--- /dev/null
+++ b/src/northbridge/intel/i965/early_init.c
@@ -0,0 +1,909 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2010 coresystems GmbH
+ *
+ * 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 <stdint.h>
+#include <stdlib.h>
+#include <console/console.h>
+#include <arch/io.h>
+#include <device/pci_def.h>
+#include "i965.h"
+
+int i965_silicon_revision(void)
+{
+ return pci_read_config8(PCI_DEV(0, 0x00, 0), PCI_CLASS_REVISION);
+}
+
+static void i965m_detect_chipset(void)
+{
+ u8 reg8;
+
+ printk(BIOS_INFO, "\n");
+ reg8 = (pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe7) & 0x70) >> 4;
+ switch (reg8) {
+ case 1:
+ printk(BIOS_INFO, "Mobile Intel(R) 82945GM/GME Express");
+ break;
+ case 2:
+ printk(BIOS_INFO, "Mobile Intel(R) 82945GMS/GU/GSE Express");
+ break;
+ case 3:
+ printk(BIOS_INFO, "Mobile Intel(R) 82945PM Express");
+ break;
+ case 5:
+ printk(BIOS_INFO, "Intel(R) 82945GT Express");
+ break;
+ case 6:
+ printk(BIOS_INFO, "Mobile Intel(R) 82943/82940GML Express");
+ break;
+ default:
+ printk(BIOS_INFO, "Unknown (%02x)", reg8); /* Others reserved. */
+ }
+ printk(BIOS_INFO, " Chipset\n");
+
+ printk(BIOS_DEBUG, "(G)MCH capable of up to FSB ");
+ reg8 = (pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe3) & 0xe0) >> 5;
+ switch (reg8) {
+ case 2:
+ printk(BIOS_DEBUG, "800 MHz"); /* According to 965 spec */
+ break;
+ case 3:
+ printk(BIOS_DEBUG, "667 MHz");
+ break;
+ case 4:
+ printk(BIOS_DEBUG, "533 MHz");
+ break;
+ default:
+ printk(BIOS_DEBUG, "N/A MHz (%02x)", reg8);
+ }
+ printk(BIOS_DEBUG, "\n");
+
+ printk(BIOS_DEBUG, "(G)MCH capable of ");
+ reg8 = (pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe4) & 0x07);
+ switch (reg8) {
+ case 2:
+ printk(BIOS_DEBUG, "up to DDR2-667");
+ break;
+ case 3:
+ printk(BIOS_DEBUG, "up to DDR2-533");
+ break;
+ case 4:
+ printk(BIOS_DEBUG, "DDR2-400");
+ break;
+ default:
+ printk(BIOS_INFO, "unknown max. RAM clock (%02x).", reg8); /* Others reserved. */
+ }
+ printk(BIOS_DEBUG, "\n");
+#if CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GC
+ printk(BIOS_ERR, "coreboot is compiled for the wrong chipset.\n");
+#endif
+}
+
+static void i965_detect_chipset(void)
+{
+ u8 reg8;
+
+ printk(BIOS_INFO, "\nIntel(R) ");
+
+ reg8 = ((pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe7) >> 5) & 4) | ((pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe4) >> 4) & 3);
+ switch (reg8) {
+ case 0:
+ case 1:
+ printk(BIOS_INFO, "82945G");
+ break;
+ case 2:
+ case 3:
+ printk(BIOS_INFO, "82945P");
+ break;
+ case 4:
+ printk(BIOS_INFO, "82945GC");
+ break;
+ case 5:
+ printk(BIOS_INFO, "82945GZ");
+ break;
+ case 6:
+ case 7:
+ printk(BIOS_INFO, "82945PL");
+ break;
+ default:
+ break;
+ }
+ printk(BIOS_INFO, " Chipset\n");
+
+ printk(BIOS_DEBUG, "(G)MCH capable of ");
+ reg8 = (pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe4) & 0x07);
+ switch (reg8) {
+ case 0:
+ printk(BIOS_DEBUG, "up to DDR2-667");
+ break;
+ case 3:
+ printk(BIOS_DEBUG, "up to DDR2-533");
+ break;
+ default:
+ printk(BIOS_INFO, "unknown max. RAM clock (%02x).", reg8); /* Others reserved. */
+ }
+ printk(BIOS_DEBUG, "\n");
+#if CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GM
+ printk(BIOS_ERR, "coreboot is compiled for the wrong chipset.\n");
+#endif
+}
+
+static void i965_setup_bars(void)
+{
+ u8 reg8;
+
+ /* As of now, we don't have all the A0 workarounds implemented */
+ if (i965_silicon_revision() == 0)
+ printk(BIOS_INFO, "Warning: i945 silicon revision A0 might not work correctly.\n");
+
+ /* Setting up Southbridge. In the northbridge code. */
+ printk(BIOS_DEBUG, "Setting up static southbridge registers...");
+ pci_write_config32(PCI_DEV(0, 0x1f, 0), RCBA, DEFAULT_RCBA | 1);
+
+ pci_write_config32(PCI_DEV(0, 0x1f, 0), PMBASE, DEFAULT_PMBASE | 1);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0x44 /* ACPI_CNTL */ , 0x80); /* Enable ACPI BAR */
+
+ pci_write_config32(PCI_DEV(0, 0x1f, 0), GPIOBASE, DEFAULT_GPIOBASE | 1);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0x4c /* GC */ , 0x10); /* Enable GPIOs */
+ setup_ich7_gpios();
+ printk(BIOS_DEBUG, " done.\n");
+
+ printk(BIOS_DEBUG, "Disabling Watchdog reboot...");
+ RCBA32(GCS) = RCBA32(GCS) | (1 << 5); /* No reset */
+ outw((1 << 11), DEFAULT_PMBASE | 0x60 | 0x08); /* halt timer */
+ printk(BIOS_DEBUG, " done.\n");
+
+ printk(BIOS_DEBUG, "Setting up static northbridge registers...");
+ /* Set up all hardcoded northbridge BARs */
+ pci_write_config32(PCI_DEV(0, 0x00, 0), EPBAR, DEFAULT_EPBAR | 1);
+ pci_write_config32(PCI_DEV(0, 0x00, 0), MCHBAR, DEFAULT_MCHBAR | 1);
+ pci_write_config32(PCI_DEV(0, 0x00, 0), DMIBAR, DEFAULT_DMIBAR | 1);
+ //pci_write_config32(PCI_DEV(0, 0x00, 0), X60BAR, DEFAULT_X60BAR | 1);
+
+ /* Hardware default is 8MB UMA. If someone wants to make this a
+ * CMOS or compile time option, send a patch.
+ * pci_write_config16(PCI_DEV(0, 0x00, 0), GGC, 0x30);
+ */
+
+ /* Set C0000-FFFFF to access RAM on both reads and writes */
+ pci_write_config8(PCI_DEV(0, 0x00, 0), PAM0, 0x30);
+ pci_write_config8(PCI_DEV(0, 0x00, 0), PAM1, 0x33);
+ pci_write_config8(PCI_DEV(0, 0x00, 0), PAM2, 0x33);
+ pci_write_config8(PCI_DEV(0, 0x00, 0), PAM3, 0x33);
+ pci_write_config8(PCI_DEV(0, 0x00, 0), PAM4, 0x33);
+ pci_write_config8(PCI_DEV(0, 0x00, 0), PAM5, 0x33);
+ pci_write_config8(PCI_DEV(0, 0x00, 0), PAM6, 0x33);
+
+ pci_write_config32(PCI_DEV(0, 0x00, 0), SKPAD, SKPAD_NORMAL_BOOT_MAGIC);
+ printk(BIOS_DEBUG, " done.\n");
+
+ /* Wait for MCH BAR to come up */
+ printk(BIOS_DEBUG, "Waiting for MCHBAR to come up...");
+ if ((pci_read_config8(PCI_DEV(0, 0x0f, 0), 0xe6) & 0x2) == 0x00) { /* Bit 49 of CAPID0 */
+ do {
+ reg8 = *(volatile u8 *)0xfed40000;
+ } while (!(reg8 & 0x80));
+ }
+ printk(BIOS_DEBUG, "ok\n");
+}
+
+static void i965_setup_egress_port(void)
+{
+ u32 reg32;
+ u32 timeout;
+
+ printk(BIOS_DEBUG, "Setting up Egress Port RCRB\n");
+
+ /* Egress Port Virtual Channel 0 Configuration */
+
+ /* map only TC0 to VC0 */
+ reg32 = EPBAR32(EPVC0RCTL);
+ reg32 &= 0xffffff01;
+ EPBAR32(EPVC0RCTL) = reg32;
+
+ reg32 = EPBAR32(EPPVCCAP1);
+ reg32 &= ~(7 << 0);
+ reg32 |= 1;
+ EPBAR32(EPPVCCAP1) = reg32;
+
+ /* Egress Port Virtual Channel 1 Configuration */
+ reg32 = EPBAR32(0x2c);
+ reg32 &= 0xffffff00;
+ if ((MCHBAR32(CLKCFG) & 7) == 1)
+ reg32 |= 0x0d; /* 533MHz */
+ if ((MCHBAR32(CLKCFG) & 7) == 3)
+ reg32 |= 0x10; /* 667MHz */
+ EPBAR32(0x2c) = reg32;
+
+ EPBAR32(EPVC1MTS) = 0x0a0a0a0a;
+
+ reg32 = EPBAR32(EPVC1RCAP);
+ reg32 &= ~(0x7f << 16);
+ reg32 |= (0x0a << 16);
+ EPBAR32(EPVC1RCAP) = reg32;
+
+ if ((MCHBAR32(CLKCFG) & 7) == 1) { /* 533MHz */
+ EPBAR32(EPVC1IST + 0) = 0x009c009c;
+ EPBAR32(EPVC1IST + 4) = 0x009c009c;
+ }
+
+ if ((MCHBAR32(CLKCFG) & 7) == 3) { /* 667MHz */
+ EPBAR32(EPVC1IST + 0) = 0x00c000c0;
+ EPBAR32(EPVC1IST + 4) = 0x00c000c0;
+ }
+
+ /* Is internal graphics enabled? */
+ if (pci_read_config8(PCI_DEV(0, 0x0, 0), 0x54) & ((1 << 4) | (1 << 3))) { /* DEVEN */
+ MCHBAR32(MMARB1) |= (1 << 17);
+ }
+
+ /* Assign Virtual Channel ID 1 to VC1 */
+ reg32 = EPBAR32(EPVC1RCTL);
+ reg32 &= ~(7 << 24);
+ reg32 |= (1 << 24);
+ EPBAR32(EPVC1RCTL) = reg32;
+
+ reg32 = EPBAR32(EPVC1RCTL);
+ reg32 &= 0xffffff01;
+ reg32 |= (1 << 7);
+ EPBAR32(EPVC1RCTL) = reg32;
+
+ EPBAR32(PORTARB + 0x00) = 0x01000001;
+ EPBAR32(PORTARB + 0x04) = 0x00040000;
+ EPBAR32(PORTARB + 0x08) = 0x00001000;
+ EPBAR32(PORTARB + 0x0c) = 0x00000040;
+ EPBAR32(PORTARB + 0x10) = 0x01000001;
+ EPBAR32(PORTARB + 0x14) = 0x00040000;
+ EPBAR32(PORTARB + 0x18) = 0x00001000;
+ EPBAR32(PORTARB + 0x1c) = 0x00000040;
+
+ EPBAR32(EPVC1RCTL) |= (1 << 16);
+ EPBAR32(EPVC1RCTL) |= (1 << 16);
+
+ printk(BIOS_DEBUG, "Loading port arbitration table ...");
+ /* Loop until bit 0 becomes 0 */
+ timeout = 0x7fffff;
+ while ((EPBAR16(EPVC1RSTS) & 1) && --timeout) ;
+ if (!timeout)
+ printk(BIOS_DEBUG, "timeout!\n");
+ else
+ printk(BIOS_DEBUG, "ok\n");
+
+ /* Now enable VC1 */
+ EPBAR32(EPVC1RCTL) |= (1 << 31);
+
+ printk(BIOS_DEBUG, "Wait for VC1 negotiation ...");
+ /* Wait for VC1 negotiation pending */
+ timeout = 0x7fff;
+ while ((EPBAR16(EPVC1RSTS) & (1 << 1)) && --timeout) ;
+ if (!timeout)
+ printk(BIOS_DEBUG, "timeout!\n");
+ else
+ printk(BIOS_DEBUG, "ok\n");
+
+}
+
+static void ich7_setup_dmi_rcrb(void)
+{
+ u16 reg16;
+ u32 reg32;
+
+ reg16 = RCBA16(LCTL);
+ reg16 &= ~(3 << 0);
+ reg16 |= 3;
+ RCBA16(LCTL) = reg16;
+
+ RCBA32(V0CTL) = 0x80000001;
+ RCBA32(V1CAP) = 0x03128010;
+ RCBA32(ESD) = 0x00000810;
+ RCBA32(RP1D) = 0x01000003;
+ RCBA32(RP2D) = 0x02000002;
+ RCBA32(RP3D) = 0x03000002;
+ RCBA32(RP4D) = 0x04000002;
+ RCBA32(HDD) = 0x0f000003;
+ RCBA32(RP5D) = 0x05000002;
+
+ RCBA32(RPFN) = 0x00543210;
+
+ pci_write_config16(PCI_DEV(0, 0x1c, 0), 0x42, 0x0141);
+ pci_write_config16(PCI_DEV(0, 0x1c, 4), 0x42, 0x0141);
+ pci_write_config16(PCI_DEV(0, 0x1c, 5), 0x42, 0x0141);
+
+ pci_write_config32(PCI_DEV(0, 0x1c, 4), 0x54, 0x00480ce0);
+ pci_write_config32(PCI_DEV(0, 0x1c, 5), 0x54, 0x00500ce0);
+
+ reg32 = RCBA32(V1CTL);
+ reg32 &= ~( (0x7f << 1) | (7 << 17) | (7 << 24) );
+ reg32 |= (0x40 << 1) | (4 << 17) | (1 << 24) | (1 << 31);
+ RCBA32(V1CTL) = reg32;
+
+ RCBA32(ESD) |= (2 << 16);
+
+ RCBA32(ULD) |= (1 << 24) | (1 << 16);
+
+ RCBA32(ULBA) = DEFAULT_DMIBAR;
+
+ RCBA32(RP1D) |= (2 << 16);
+ RCBA32(RP2D) |= (2 << 16);
+ RCBA32(RP3D) |= (2 << 16);
+ RCBA32(RP4D) |= (2 << 16);
+ RCBA32(HDD) |= (2 << 16);
+ RCBA32(RP5D) |= (2 << 16);
+ RCBA32(RP6D) |= (2 << 16);
+
+ RCBA32(LCAP) |= (3 << 10);
+}
+
+static void i965_setup_dmi_rcrb(void)
+{
+ u32 reg32;
+ u32 timeout;
+ int activate_aspm = 1; /* hardcode ASPM for now */
+
+ printk(BIOS_DEBUG, "Setting up DMI RCRB\n");
+
+ /* Virtual Channel 0 Configuration */
+ reg32 = DMIBAR32(DMIVC0RCTL0);
+ reg32 &= 0xffffff01;
+ DMIBAR32(DMIVC0RCTL0) = reg32;
+
+ reg32 = DMIBAR32(DMIPVCCAP1);
+ reg32 &= ~(7 << 0);
+ reg32 |= 1;
+ DMIBAR32(DMIPVCCAP1) = reg32;
+
+ reg32 = DMIBAR32(DMIVC1RCTL);
+ reg32 &= ~(7 << 24);
+ reg32 |= (1 << 24); /* NOTE: This ID must match ICH7 side */
+ DMIBAR32(DMIVC1RCTL) = reg32;
+
+ reg32 = DMIBAR32(DMIVC1RCTL);
+ reg32 &= 0xffffff01;
+ reg32 |= (1 << 7);
+ DMIBAR32(DMIVC1RCTL) = reg32;
+
+ /* Now enable VC1 */
+ DMIBAR32(DMIVC1RCTL) |= (1 << 31);
+
+ printk(BIOS_DEBUG, "Wait for VC1 negotiation ...");
+ /* Wait for VC1 negotiation pending */
+ timeout = 0x7ffff;
+ while ((DMIBAR16(DMIVC1RSTS) & (1 << 1)) && --timeout) ;
+ if (!timeout)
+ printk(BIOS_DEBUG, "timeout!\n");
+ else
+ printk(BIOS_DEBUG, "done..\n");
+#if 1
+ /* Enable Active State Power Management (ASPM) L0 state */
+
+ reg32 = DMIBAR32(DMILCAP);
+ reg32 &= ~(7 << 12);
+ reg32 |= (2 << 12);
+
+ reg32 &= ~(7 << 15);
+
+ reg32 |= (2 << 15);
+ DMIBAR32(DMILCAP) = reg32;
+
+ reg32 = DMIBAR32(DMICC);
+ reg32 &= 0x00ffffff;
+ reg32 &= ~(3 << 0);
+ reg32 |= (1 << 0);
+ reg32 &= ~(3 << 20);
+ reg32 |= (1 << 20);
+
+ DMIBAR32(DMICC) = reg32;
+
+ if (activate_aspm) {
+ DMIBAR32(DMILCTL) |= (3 << 0);
+ }
+#endif
+
+ /* Last but not least, some additional steps */
+ reg32 = MCHBAR32(FSBSNPCTL);
+ reg32 &= ~(0xff << 2);
+ reg32 |= (0xaa << 2);
+ MCHBAR32(FSBSNPCTL) = reg32;
+
+ DMIBAR32(0x2c) = 0x86000040;
+
+ reg32 = DMIBAR32(0x204);
+ reg32 &= ~0x3ff;
+#if 1
+ reg32 |= 0x13f; /* for x4 DMI only */
+#else
+ reg32 |= 0x1e4; /* for x2 DMI only */
+#endif
+ DMIBAR32(0x204) = reg32;
+
+ if (pci_read_config8(PCI_DEV(0, 0x0, 0), 0x54) & ((1 << 4) | (1 << 3))) { /* DEVEN */
+ printk(BIOS_DEBUG, "Internal graphics: enabled\n");
+ DMIBAR32(0x200) |= (1 << 21);
+ } else {
+ printk(BIOS_DEBUG, "Internal graphics: disabled\n");
+ DMIBAR32(0x200) &= ~(1 << 21);
+ }
+
+ reg32 = DMIBAR32(0x204);
+ reg32 &= ~((1 << 11) | (1 << 10));
+ DMIBAR32(0x204) = reg32;
+
+ reg32 = DMIBAR32(0x204);
+ reg32 &= ~(0xff << 12);
+ reg32 |= (0x0d << 12);
+ DMIBAR32(0x204) = reg32;
+
+ DMIBAR32(DMICTL1) |= (3 << 24);
+
+ reg32 = DMIBAR32(0x200);
+ reg32 &= ~(0x3 << 26);
+ reg32 |= (0x02 << 26);
+ DMIBAR32(0x200) = reg32;
+
+ DMIBAR32(DMIDRCCFG) &= ~(1 << 31);
+ DMIBAR32(DMICTL2) |= (1 << 31);
+
+ if (i965_silicon_revision() >= 3) {
+ reg32 = DMIBAR32(0xec0);
+ reg32 &= 0x0fffffff;
+ reg32 |= (2 << 28);
+ DMIBAR32(0xec0) = reg32;
+
+ reg32 = DMIBAR32(0xed4);
+ reg32 &= 0x0fffffff;
+ reg32 |= (2 << 28);
+ DMIBAR32(0xed4) = reg32;
+
+ reg32 = DMIBAR32(0xee8);
+ reg32 &= 0x0fffffff;
+ reg32 |= (2 << 28);
+ DMIBAR32(0xee8) = reg32;
+
+ reg32 = DMIBAR32(0xefc);
+ reg32 &= 0x0fffffff;
+ reg32 |= (2 << 28);
+ DMIBAR32(0xefc) = reg32;
+ }
+
+ /* wait for bit toggle to 0 */
+ printk(BIOS_DEBUG, "Waiting for DMI hardware...");
+ timeout = 0x7fffff;
+ while ((DMIBAR8(0x32) & (1 << 1)) && --timeout) ;
+ if (!timeout)
+ printk(BIOS_DEBUG, "timeout!\n");
+ else
+ printk(BIOS_DEBUG, "ok\n");
+
+ /* Clear Error Status Bits! */
+ DMIBAR32(0x1c4) = 0xffffffff;
+ DMIBAR32(0x1d0) = 0xffffffff;
+ DMIBAR32(0x228) = 0xffffffff;
+
+ /* Program Read-Only Write-Once Registers */
+ DMIBAR32(0x308) = DMIBAR32(0x308);
+ DMIBAR32(0x314) = DMIBAR32(0x314);
+ DMIBAR32(0x324) = DMIBAR32(0x324);
+ DMIBAR32(0x328) = DMIBAR32(0x328);
+ DMIBAR32(0x338) = DMIBAR32(0x334);
+ DMIBAR32(0x338) = DMIBAR32(0x338);
+
+ if (i965_silicon_revision() == 1 && ((MCHBAR8(0xe08) & (1 << 5)) == 1)) {
+ if ((MCHBAR32(0x214) & 0xf) != 0x3) {
+ printk(BIOS_INFO, "DMI link requires A1 stepping workaround. Rebooting.\n");
+ reg32 = DMIBAR32(0x224);
+ reg32 &= ~(7 << 0);
+ reg32 |= (3 << 0);
+ DMIBAR32(0x224) = reg32;
+ outb(0x06, 0xcf9);
+ for (;;) asm("hlt"); /* wait for reset */
+ }
+ }
+}
+
+static void i965_setup_pci_express_x16(void)
+{
+ u32 timeout;
+ u32 reg32;
+ u16 reg16;
+
+ u8 reg8;
+
+ printk(BIOS_DEBUG, "Enabling PCI Express x16 Link\n");
+
+ reg16 = pci_read_config16(PCI_DEV(0, 0x00, 0), DEVEN);
+ reg16 |= DEVEN_D1F0;
+ pci_write_config16(PCI_DEV(0, 0x00, 0), DEVEN, reg16);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x208);
+ reg32 &= ~(1 << 8);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x208, reg32);
+
+ /* We have no success with querying the usual PCIe registers
+ * for link setup success on the i945. Hence we assign a temporary
+ * PCI bus 0x0a and check whether we find a device on 0:a.0
+ */
+
+ /* First we reset the secondary bus */
+ reg16 = pci_read_config16(PCI_DEV(0, 0x01, 0), 0x3e);
+ reg16 |= (1 << 6); /* SRESET */
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0x3e, reg16);
+ /* Read back and clear reset bit. */
+ reg16 = pci_read_config16(PCI_DEV(0, 0x01, 0), 0x3e);
+ reg16 &= ~(1 << 6); /* SRESET */
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0x3e, reg16);
+
+ reg16 = pci_read_config16(PCI_DEV(0, 0x01, 0), 0xba);
+ printk(BIOS_DEBUG, "SLOTSTS: %04x\n", reg16);
+ if (!(reg16 & 0x48)) {
+ goto disable_pciexpress_x16_link;
+ }
+ reg16 |= (1 << 4) | (1 << 0);
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0xba, reg16);
+
+ pci_write_config8(PCI_DEV(0, 0x01, 0), 0x19, 0x00);
+ pci_write_config8(PCI_DEV(0, 0x01, 0), 0x1a, 0x00);
+ pci_write_config8(PCI_DEV(0, 0x01, 0), 0x19, 0x0a);
+ pci_write_config8(PCI_DEV(0, 0x01, 0), 0x1a, 0x0a);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x224);
+ reg32 &= ~(1 << 8);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x224, reg32);
+
+ MCHBAR16(UPMC1) &= ~( (1 << 5) | (1 << 0) );
+
+ /* Initialze PEG_CAP */
+ reg16 = pci_read_config16(PCI_DEV(0, 0x01, 0), 0xa2);
+ reg16 |= (1 << 8);
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0xa2, reg16);
+
+ /* Setup SLOTCAP */
+ /* TODO: These values are mainboard dependent and should
+ * be set from devicetree.cb.
+ */
+ /* NOTE: SLOTCAP becomes RO after the first write! */
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xb4);
+ reg32 &= 0x0007ffff;
+
+ reg32 &= 0xfffe007f;
+
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0xb4, reg32);
+
+ /* Wait for training to succeed */
+ printk(BIOS_DEBUG, "PCIe link training ...");
+ timeout = 0x7ffff;
+ while ((((pci_read_config32(PCI_DEV(0, 0x01, 0), 0x214) >> 16) & 4) != 3) && --timeout) ;
+
+ reg32 = pci_read_config32(PCI_DEV(0x0a, 0x0, 0), 0);
+ if (reg32 != 0x00000000 && reg32 != 0xffffffff) {
+ printk(BIOS_DEBUG, " Detected PCIe device %04x:%04x\n",
+ reg32 & 0xffff, reg32 >> 16);
+ } else {
+ printk(BIOS_DEBUG, " timeout!\n");
+
+ printk(BIOS_DEBUG, "Restrain PCIe port to x1\n");
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x214);
+ reg32 &= ~(0xf << 1);
+ reg32 |=1;
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x214, reg32);
+
+ reg16 = pci_read_config16(PCI_DEV(0, 0x01, 0), 0x3e);
+
+ reg16 |= (1 << 6);
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0x3e, reg16);
+ reg16 &= ~(1 << 6);
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0x3e, reg16);
+
+ printk(BIOS_DEBUG, "PCIe link training ...");
+ timeout = 0x7ffff;
+ while ((((pci_read_config32(PCI_DEV(0, 0x01, 0), 0x214) >> 16) & 4) != 3) && --timeout) ;
+
+ reg32 = pci_read_config32(PCI_DEV(0xa, 0x00, 0), 0);
+ if (reg32 != 0x00000000 && reg32 != 0xffffffff) {
+ printk(BIOS_DEBUG, " Detected PCIe x1 device %04x:%04x\n",
+ reg32 & 0xffff, reg32 >> 16);
+ } else {
+ printk(BIOS_DEBUG, " timeout!\n");
+ printk(BIOS_DEBUG, "Disabling PCIe x16 port completely.\n");
+ goto disable_pciexpress_x16_link;
+ }
+ }
+
+ reg16 = pci_read_config16(PCI_DEV(0, 0x01, 0), 0xb2);
+ reg16 >>= 4;
+ reg16 &= 0x3f;
+ /* reg16 == 1 -> x1; reg16 == 16 -> x16 */
+ printk(BIOS_DEBUG, "PCIe x%d link training succeeded.\n", reg16);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x204);
+ reg32 &= 0xfffffc00; /* clear [9:0] */
+ if (reg16 == 1) {
+ reg32 |= 0x32b;
+ // TODO
+ /* pci_write_config32(PCI_DEV(0, 0x01, 0), 0x204, reg32); */
+ } else if (reg16 == 16) {
+ reg32 |= 0x0f4;
+ // TODO
+ /* pci_write_config32(PCI_DEV(0, 0x01, 0), 0x204, reg32); */
+ }
+
+ reg32 = (pci_read_config32(PCI_DEV(0xa, 0, 0), 0x8) >> 8);
+ printk(BIOS_DEBUG, "PCIe device class: %06x\n", reg32);
+ if (reg32 == 0x030000) {
+ printk(BIOS_DEBUG, "PCIe device is VGA. Disabling IGD.\n");
+ reg16 = (1 << 1);
+ pci_write_config16(PCI_DEV(0, 0x0, 0), 0x52, reg16);
+
+ /* DEVEN */
+ reg32 = pci_read_config32(PCI_DEV(0, 0x0, 0), 0x54);
+ reg32 &= ~((1 << 3) | (1 << 4));
+ pci_write_config32(PCI_DEV(0, 0x0, 0), 0x54, reg32);
+
+ /* Set VGA enable bit in PCIe bridge */
+ reg16 = pci_read_config16(PCI_DEV(0, 0x1, 0), 0x3e);
+ reg16 |= (1 << 3);
+ pci_write_config16(PCI_DEV(0, 0x1, 0), 0x3e, reg16);
+ }
+
+ /* Enable GPEs */
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xec);
+ reg32 |= (1 << 2) | (1 << 1) | (1 << 0); /* PMEGPE, HPGPE, GENGPE */
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x114, reg32);
+
+ /* Virtual Channel Configuration: Only VC0 on PCIe x16 */
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x114);
+ reg32 &= 0xffffff01;
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x114, reg32);
+
+ /* Extended VC count */
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x104);
+ reg32 &= ~(7 << 0);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x104, reg32);
+
+ /* Active State Power Management ASPM */
+
+ /* TODO */
+
+ /* Clear error bits */
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0x06, 0xffff);
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0x1e, 0xffff);
+ pci_write_config16(PCI_DEV(0, 0x01, 0), 0xaa, 0xffff);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x1c4, 0xffffffff);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x1d0, 0xffffffff);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x1f0, 0xffffffff);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x228, 0xffffffff);
+
+ /* Program R/WO registers */
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x308);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x308, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x314);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x314, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x324);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x324, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x328);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x328, reg32);
+
+ reg8 = pci_read_config8(PCI_DEV(0, 0x01, 0), 0xb4);
+ pci_write_config8(PCI_DEV(0, 0x01, 0), 0xb4, reg8);
+
+ /* Additional PCIe graphics setup */
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xf0);
+ reg32 |= (3 << 26);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0xf0, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xf0);
+ reg32 |= (3 << 24);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0xf0, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xf0);
+ reg32 |= (1 << 5);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0xf0, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x200);
+ reg32 &= ~(3 << 26);
+ reg32 |= (2 << 26);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x200, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xe80);
+ if (i965_silicon_revision() >= 2) {
+ reg32 |= (1 << 12);
+ } else {
+ reg32 &= ~(1 << 12);
+ }
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0xe80, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xeb4);
+ reg32 &= ~(1 << 31);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0xeb4, reg32);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xfc);
+ reg32 |= (1 << 31);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0xfc, reg32);
+
+ if (i965_silicon_revision() >= 3) {
+ static const u32 reglist[] = {
+ 0xec0, 0xed4, 0xee8, 0xefc, 0xf10, 0xf24,
+ 0xf38, 0xf4c, 0xf60, 0xf74, 0xf88, 0xf9c,
+ 0xfb0, 0xfc4, 0xfd8, 0xfec
+ };
+
+ int i;
+ for (i=0; i<ARRAY_SIZE(reglist); i++) {
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), reglist[i]);
+ reg32 &= 0x0fffffff;
+ reg32 |= (2 << 28);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), reglist[i], reg32);
+ }
+ }
+
+ if (i965_silicon_revision() <= 2 ) {
+ /* Set voltage specific parameters */
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0xe80);
+ reg32 &= (0xf << 4); /* Default case 1.05V */
+ if ((MCHBAR32(0xe08) & (1 << 20)) == 0) { /* 1.50V */
+ reg32 |= (7 << 4);
+ }
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0xe80, reg32);
+ }
+
+ return;
+
+disable_pciexpress_x16_link:
+ /* For now we just disable the x16 link */
+ printk(BIOS_DEBUG, "Disabling PCI Express x16 Link\n");
+
+ MCHBAR16(UPMC1) |= (1 << 5) | (1 << 0);
+
+ reg16 = pci_read_config16(PCI_DEV(0, 0x01, 0), BCTRL1);
+ reg16 |= (1 << 6);
+ pci_write_config16(PCI_DEV(0, 0x01, 0), BCTRL1, reg16);
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x224);
+ reg32 |= (1 << 8);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x224, reg32);
+
+ reg16 = pci_read_config16(PCI_DEV(0, 0x01, 0), BCTRL1);
+ reg16 &= ~(1 << 6);
+ pci_write_config16(PCI_DEV(0, 0x01, 0), BCTRL1, reg16);
+
+ printk(BIOS_DEBUG, "Wait for link to enter detect state... ");
+ timeout = 0x7fffff;
+ for (reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x214);
+ (reg32 & 0x000f0000) && --timeout;) ;
+ if (!timeout)
+ printk(BIOS_DEBUG, "timeout!\n");
+ else
+ printk(BIOS_DEBUG, "ok\n");
+
+ /* Finally: Disable the PCI config header */
+ reg16 = pci_read_config16(PCI_DEV(0, 0x00, 0), DEVEN);
+ reg16 &= ~DEVEN_D1F0;
+ pci_write_config16(PCI_DEV(0, 0x00, 0), DEVEN, reg16);
+}
+
+static void i965_setup_root_complex_topology(void)
+{
+ u32 reg32;
+
+ printk(BIOS_DEBUG, "Setting up Root Complex Topology\n");
+ /* Egress Port Root Topology */
+
+ reg32 = EPBAR32(EPESD);
+ reg32 &= 0xff00ffff;
+ reg32 |= (1 << 16);
+ EPBAR32(EPESD) = reg32;
+
+ EPBAR32(EPLE1D) |= (1 << 16) | (1 << 0);
+
+ EPBAR32(EPLE1A) = DEFAULT_DMIBAR;
+
+ EPBAR32(EPLE2D) |= (1 << 16) | (1 << 0);
+
+ /* DMI Port Root Topology */
+
+ reg32 = DMIBAR32(DMILE1D);
+ reg32 &= 0x00ffffff;
+
+ reg32 &= 0xff00ffff;
+ reg32 |= (2 << 16);
+
+ reg32 |= (1 << 0);
+ DMIBAR32(DMILE1D) = reg32;
+
+ DMIBAR32(DMILE1A) = DEFAULT_RCBA;
+
+ DMIBAR32(DMILE2D) |= (1 << 16) | (1 << 0);
+
+ DMIBAR32(DMILE2A) = DEFAULT_EPBAR;
+
+ /* PCI Express x16 Port Root Topology */
+ if (pci_read_config8(PCI_DEV(0, 0x00, 0), DEVEN) & DEVEN_D1F0) {
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x158, DEFAULT_EPBAR);
+ reg32 = pci_read_config32(PCI_DEV(0, 0x01, 0), 0x150);
+ reg32 |= (1 << 0);
+ pci_write_config32(PCI_DEV(0, 0x01, 0), 0x150, reg32);
+ }
+}
+
+static void ich7_setup_root_complex_topology(void)
+{
+ RCBA32(0x104) = 0x00000802;
+ RCBA32(0x110) = 0x00000001;
+ RCBA32(0x114) = 0x00000000;
+ RCBA32(0x118) = 0x00000000;
+}
+
+static void ich7_setup_pci_express(void)
+{
+ RCBA32(CG) |= (1 << 0);
+
+ /* Initialize slot power limit for root ports */
+ pci_write_config32(PCI_DEV(0, 0x1c, 0), 0x54, 0x00000060);
+#if 0
+ pci_write_config32(PCI_DEV(0, 0x1c, 4), 0x54, 0x00480ce0);
+ pci_write_config32(PCI_DEV(0, 0x1c, 5), 0x54, 0x00500ce0);
+#endif
+
+ pci_write_config32(PCI_DEV(0, 0x1c, 0), 0xd8, 0x00110000);
+}
+
+void i965_early_initialization(void)
+{
+ /* Print some chipset specific information */
+ switch (pci_read_config32(PCI_DEV(0, 0x00, 0), 0)) {
+ case 0x27708086: /* 82945G/GZ/GC/P/PL */
+ i965_detect_chipset();
+ break;
+ case 0x27a08086: /* 945GME/GSE */
+ case 0x27ac8086: /* 945GM/PM/GMS/GU/GT, 943/940GML */
+
+ case 0x2a008086: /* 965PM */
+ i965m_detect_chipset();
+ break;
+ }
+
+ /* Setup all BARs required for early PCIe and raminit */
+ i965_setup_bars();
+
+ /* Change port80 to LPC */
+ RCBA32(GCS) &= (~0x04);
+
+ /* Just do it that way */
+ RCBA32(0x2010) |= (1 << 10);
+}
+
+void i965_late_initialization(void)
+{
+ i965_setup_egress_port();
+
+ ich7_setup_root_complex_topology();
+
+ ich7_setup_pci_express();
+
+ ich7_setup_dmi_rcrb();
+
+ i965_setup_dmi_rcrb();
+
+ i965_setup_pci_express_x16();
+
+ i965_setup_root_complex_topology();
+}
+
diff --git a/src/northbridge/intel/i965/errata.c b/src/northbridge/intel/i965/errata.c
new file mode 100644
index 0000000..7900f90
--- /dev/null
+++ b/src/northbridge/intel/i965/errata.c
@@ -0,0 +1,34 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 coresystems GmbH
+ *
+ * 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 <stdint.h>
+#include "i965.h"
+#include "raminit.h"
+
+int fixup_i965_errata(void)
+{
+ u32 reg32;
+
+ /* Mobile Intel 965 Express only */
+ reg32 = MCHBAR32(FSBPMC3);
+ reg32 &= ~((1 << 13) | (1 << 29));
+ MCHBAR32(FSBPMC3) = reg32;
+
+ return 0;
+}
diff --git a/src/northbridge/intel/i965/i965.h b/src/northbridge/intel/i965/i965.h
new file mode 100644
index 0000000..830ec47
--- /dev/null
+++ b/src/northbridge/intel/i965/i965.h
@@ -0,0 +1,388 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Damien Zammit <damien at zamaudio.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
+ */
+
+#ifndef __NORTHBRIDGE_INTEL_I965_I965_H__
+#define __NORTHBRIDGE_INTEL_I965_I965_H__ 1
+
+/* Northbridge BARs */
+#define DEFAULT_PCIEXBAR CONFIG_MMCONF_BASE_ADDRESS /* 4 KB per PCIe device */
+#define DEFAULT_MCHBAR 0xfed14000 /* 16 KB */
+#define DEFAULT_DMIBAR 0xfed18000 /* 4 KB */
+#define DEFAULT_EPBAR 0xfed19000 /* 4 KB */
+
+#include "../../../southbridge/intel/i82801gx/i82801gx.h"
+
+/* Everything below this line is ignored in the DSDT */
+#ifndef __ACPI__
+
+/* Device 0:0.0 PCI configuration space (Host Bridge) */
+
+#define EPBAR 0x40
+#define MCHBAR 0x48
+#define PCIEXBAR 0x60
+#define DMIBAR 0x68
+#define PMIOBAR 0x78
+
+#define GGC 0x52 /* GMCH Graphics Control */
+
+#define DEVEN 0x54 /* Device Enable */
+#define DEVEN_D0F0 (1 << 0)
+#define DEVEN_D1F0 (1 << 1)
+#define DEVEN_D2F0 (1 << 3)
+#define DEVEN_D2F1 (1 << 4)
+#ifndef BOARD_DEVEN
+#define BOARD_DEVEN ( DEVEN_D0F0 | DEVEN_D2F0 | DEVEN_D2F1 )
+#endif
+
+#define PAM0 0x90
+#define PAM1 0x91
+#define PAM2 0x92
+#define PAM3 0x93
+#define PAM4 0x94
+#define PAM5 0x95
+#define PAM6 0x96
+
+#define LAC 0x97 /* Legacy Access Control */
+#define REMAPBASE 0x98
+#define REMAPLIMIT 0x9a
+
+#define TOM 0xa0
+#define TOUUD 0xa2
+#define TOLUD 0xb0 /* Top of Low Used Memory */
+#define SMRAM 0x9d /* System Management RAM Control */
+#define ESMRAM 0x9e /* Extended System Management RAM Control */
+
+#define CAPPTR 0x34
+#define CAPID0 0xe0
+#define CRLT 0xf0 /* ME lock */
+#define ERRSTS 0xc8
+#define ERRCMD 0xca
+
+#define SKPAD 0xdc /* Scratchpad Data */
+
+/* Device 0:1.0 PCI configuration space (PCI Express) */
+
+#define BCTRL1 0x3e /* 16bit */
+
+
+/* Device 0:2.0 PCI configuration space (Graphics Device) */
+
+#define GCFC 0xf0 /* Graphics Clock Frequency & Gating Control */
+
+
+/*
+ * MCHBAR
+ */
+
+#define MCHBAR8(x) *((volatile u8 *)(DEFAULT_MCHBAR + x))
+#define MCHBAR16(x) *((volatile u16 *)(DEFAULT_MCHBAR + x))
+#define MCHBAR32(x) *((volatile u32 *)(DEFAULT_MCHBAR + x))
+
+/* Chipset Control Registers */
+#define FSBPMC3 0x40 /* 32bit */
+#define FSBPMC4 0x44 /* 32bit */
+#define FSBSNPCTL 0x48 /* 32bit */
+#define SLPCTL 0x90 /* 32bit */
+#define FSBPMC5 0x94 /* 32bit */
+
+#define C0DRB01 0x1200 /* 32bit */
+#define C0DRA 0x1208 /* 32bit */
+#define C0DCLKDIS 0x120c /* 32bit */
+#define C0DRT0 0x1210 /* 32bit */
+#define C0DRT1 0x1214 /* 32bit */
+#define C0DRT2 0x1218 /* 32bit */
+#define C0DRT3 0x121c /* 32bit */
+#define C0DRT4 0x1220 /* 32bit */
+#define C0DRT5 0x1224 /* 32bit */
+#define C0DRC0 0x1230 /* 32bit */
+#define C0DRC1 0x1234 /* 32bit */
+#define C0DRC2 0x1238 /* 32bit */
+#define C0AIT 0x1250 /* 64bit */
+#define C0DCCFT 0x1258 /* 64bit */
+#define C0DTEW1 0x126c /* 32bit */
+#define C0GTEW 0x1270 /* 32bit */
+#define C0GTC 0x1274 /* 32bit */
+#define C0DTPEW 0x1278 /* 64bit */
+#define C0DTAEW 0x1280 /* 64bit */
+#define C0DTC 0x1288 /* 32bit */
+#define C0DMC 0x1294 /* 32bit */
+#define C0ODT 0x1298 /* 64bit */
+
+#define C1DRB01 0x1300 /* 32bit */
+#define C1DRA 0x1308 /* 32bit */
+#define C1DCLKDIS 0x130c /* 32bit */
+#define C1DRT0 0x1310 /* 32bit */
+#define C1DRT1 0x1314 /* 32bit */
+#define C1DRT2 0x1318 /* 32bit */
+#define C1DRT3 0x131c /* 32bit */
+#define C1DRT4 0x1320 /* 32bit */
+#define C1DRT5 0x1324 /* 32bit */
+#define C1DRC0 0x1330 /* 32bit */
+#define C1DRC1 0x1334 /* 32bit */
+#define C1DRC2 0x1338 /* 32bit */
+#define C1AIT 0x1350 /* 64bit */
+#define C1DCCFT 0x1358 /* 64bit */
+#define C1DTEW1 0x136c /* 32bit */
+#define C1GTEW 0x1370 /* 32bit */
+#define C1GTC 0x1374 /* 32bit */
+#define C1DTPEW 0x1378 /* 64bit */
+#define C1DTAEW 0x1380 /* 64bit */
+#define C1DTC 0x1388 /* 32bit */
+#define C1DMC 0x1394 /* 32bit */
+#define C1ODT 0x1398 /* 64bit */
+
+/** 965 MCH register controlling DRAM channel configuration */
+#define C0DRB3 0x10206
+#define C1DRB3 0x10606
+
+#define DCC 0x200 /* 32bit */
+#define DCC2 0x204 /* 32bit */
+#define CCCFT 0x208 /* 64bit */
+#define WCC 0x218 /* 32bit */
+#define MMARB0 0x220 /* 32bit */
+#define MMARB1 0x224 /* 32bit */
+#define SBTEST 0x230 /* 32bit */
+
+#define SBOCC 0x238 /* 32bit */
+#define ODTC 0x284 /* 32bit */
+#define SMVREFC 0x2a0 /* 32bit */
+#define DRTST 0x2a8 /* 32bit */
+#define REPC 0x2e0 /* 32bit */
+#define DQSMT 0x2f4 /* 16bit */
+#define RCVENMT 0x2f8 /* 32bit */
+
+#define C0R0B00DQST 0x300 /* 64bit */
+
+#define C0WL0REOST 0x340 /* 8bit */
+#define C0WL1REOST 0x341 /* 8bit */
+#define C0WL2REOST 0x342 /* 8bit */
+#define C0WL3REOST 0x343 /* 8bit */
+#define WDLLBYPMODE 0x360 /* 16bit */
+#define C0WDLLCMC 0x36c /* 32bit */
+#define C0HCTC 0x37c /* 8bit */
+
+#define C1R0B00DQST 0x380 /* 64bit */
+
+#define C1WL0REOST 0x3c0 /* 8bit */
+#define C1WL1REOST 0x3c1 /* 8bit */
+#define C1WL2REOST 0x3c2 /* 8bit */
+#define C1WL3REOST 0x3c3 /* 8bit */
+#define C1WDLLCMC 0x3ec /* 32bit */
+#define C1HCTC 0x3fc /* 8bit */
+
+#define GBRCOMPCTL 0x400 /* 32bit */
+
+#define SMSRCTL 0x408 /* XXX who knows */
+#define C0DRAMW 0x40c /* 16bit */
+#define G1SC 0x410 /* 8bit */
+#define G2SC 0x418 /* 8bit */
+#define G3SC 0x420 /* 8bit */
+#define G4SC 0x428 /* 8bit */
+#define G5SC 0x430 /* 8bit */
+#define G6SC 0x438 /* 8bit */
+
+#define C1DRAMW 0x48c /* 16bit */
+#define G7SC 0x490 /* 8bit */
+#define G8SC 0x498 /* 8bit */
+
+#define G1SRPUT 0x500 /* 256bit */
+#define G1SRPDT 0x520 /* 256bit */
+#define G2SRPUT 0x540 /* 256bit */
+#define G2SRPDT 0x560 /* 256bit */
+#define G3SRPUT 0x580 /* 256bit */
+#define G3SRPDT 0x5a0 /* 256bit */
+#define G4SRPUT 0x5c0 /* 256bit */
+#define G4SRPDT 0x5e0 /* 256bit */
+#define G5SRPUT 0x600 /* 256bit */
+#define G5SRPDT 0x620 /* 256bit */
+#define G6SRPUT 0x640 /* 256bit */
+#define G6SRPDT 0x660 /* 256bit */
+#define G7SRPUT 0x680 /* 256bit */
+#define G7SRPDT 0x6a0 /* 256bit */
+#define G8SRPUT 0x6c0 /* 256bit */
+#define G8SRPDT 0x6e0 /* 256bit */
+
+/* Clock Controls */
+#define CLKCFG 0xc00 /* 32bit */
+#define UPMC1 0xc14 /* 16bit */
+#define CPCTL 0xc16 /* 16bit */
+#define SSKPD 0xc1c /* 16bit (scratchpad) */
+#define UPMC2 0xc20 /* 16bit */
+#define UPMC4 0xc30 /* 32bit */
+#define HGIPMC1 0xc34 /* 32bit */
+#define HGIPMC2 0xc38 /* 32bit */
+//#define PLLMON
+
+#define DFT_STRAP1 0xe08 /* 32bit */
+
+/* Thermal Management Controls */
+#define TSC1 0x1001 /* 16bit */
+#define TSS1 0x1004 /* 16bit */
+#define TR1 0x1006 /* 8bit */
+#define TOF1 0x1007 /* 8bit */
+#define RTR1 0x1008 /* 8bit */
+#define TIC1 0x100b /* 8bit */
+#define TMAC1 0x100c /* 8bit */
+#define TMA1 0x100d /* 8bit */
+#define TSI1 0x100e /* 8bit */
+#define TSPM1 0x100f /* 8bit */
+#define TSTTPA1 0x1010 /* 32bit */
+#define TSTTPB1 0x1014 /* 32bit */
+#define TCO1 0x1018 /* 8bit */
+#define HWTHROTCTRL1 0x101c /* 8bit */
+#define TCOFUSE1 0x101d /* 8bit */
+#define TIS1 0x101e /* 16bit */
+
+#define TSC2 0x1041 /* 16bit */
+#define TSS2 0x1044 /* 16bit */
+#define TR2 0x1046 /* 8bit */
+#define TOF2 0x1047 /* 8bit */
+#define RTR2 0x1048 /* 8bit */
+#define TIC2 0x104b /* 8bit */
+#define TMAC2 0x104c /* 8bit */
+#define TMA2 0x104d /* 8bit */
+#define TSI2 0x104e /* 8bit */
+#define TSPM2 0x104f /* 8bit */
+#define TSTTPA2 0x1050 /* 32bit */
+#define TSTTPB2 0x1054 /* 32bit */
+#define TCO2 0x1058 /* 8bit */
+#define HWTHROTCTRL2 0x105c /* 8bit */
+#define TCOFUSE2 0x105d /* 8bit */
+#define TIS2 0x105e /* 16bit */
+
+#define TERATE 0x1070 /* 8bit */
+#define TSRCTRL 0x1080 /* 8bit */
+#define IUB 0x10e0 /* 32bit */
+#define TERRCMD 0x10e4 /* 8bit */
+#define TSMICMD 0x10e5 /* 8bit */
+#define TSCICMD 0x10e6 /* 8bit */
+#define TINTRCMD 0x10e7 /* 8bit */
+#define EXTTSCS 0x10ef /* 8bit */
+
+/* ACPI Power Management Controls */
+
+#define MIPMC3 0xbd8 /* 32bit */
+
+#define C2C3TT 0xf00 /* 32bit */
+#define C3C4TT 0xf04 /* 32bit */
+
+#define MIPMC4 0xf08 /* 16bit */
+#define MIPMC5 0xf0a /* 16bit */
+#define MIPMC6 0xf0c /* 16bit */
+#define MIPMC7 0xf0e /* 16bit */
+
+#define PMCFG 0xf10 /* 32bit */
+#define SLFRCS 0xf14 /* 32bit */
+#define GIPMC1 0xfb0 /* 32bit */
+#define FSBPMC1 0xfb8 /* 32bit */
+#define UPMC3 0xfc0 /* 32bit */
+#define ECO 0xffc /* 32bit */
+
+/*
+ * EPBAR - Egress Port Root Complex Register Block
+ */
+
+#define EPBAR8(x) *((volatile u8 *)(DEFAULT_EPBAR + x))
+#define EPBAR16(x) *((volatile u16 *)(DEFAULT_EPBAR + x))
+#define EPBAR32(x) *((volatile u32 *)(DEFAULT_EPBAR + x))
+
+#define EPPVCCAP1 0x004 /* 32bit */
+#define EPPVCCAP2 0x008 /* 32bit */
+
+#define EPVC0RCAP 0x010 /* 32bit */
+#define EPVC0RCTL 0x014 /* 32bit */
+#define EPVC0RSTS 0x01a /* 16bit */
+
+#define EPVC1RCAP 0x01c /* 32bit */
+#define EPVC1RCTL 0x020 /* 32bit */
+#define EPVC1RSTS 0x026 /* 16bit */
+
+#define EPVC1MTS 0x028 /* 32bit */
+#define EPVC1ITC 0x02c /* 32bit */ /* VC1 Isoch timing control */
+#define EPVC1IWT 0x030 /* 64bit */ /* Reserved */
+#define EPVC1IST 0x038 /* 64bit */
+
+#define EPESD 0x044 /* 32bit */
+
+#define EPLE1D 0x050 /* 32bit */
+#define EPLE1A 0x058 /* 64bit */
+#define EPLE2D 0x060 /* 32bit */
+#define EPLE2A 0x068 /* 64bit */
+
+#define PORTARB 0x100 /* 256bit */
+
+/*
+ * DMIBAR
+ */
+
+#define DMIBAR8(x) *((volatile u8 *)(DEFAULT_DMIBAR + x))
+#define DMIBAR16(x) *((volatile u16 *)(DEFAULT_DMIBAR + x))
+#define DMIBAR32(x) *((volatile u32 *)(DEFAULT_DMIBAR + x))
+
+#define DMIVCECH 0x000 /* 32bit */
+#define DMIPVCCAP1 0x004 /* 32bit */
+#define DMIPVCCAP2 0x008 /* 32bit */
+
+#define DMIPVCCCTL 0x00c /* 16bit */
+
+#define DMIVC0RCAP 0x010 /* 32bit */
+#define DMIVC0RCTL0 0x014 /* 32bit */
+#define DMIVC0RSTS 0x01a /* 16bit */
+
+#define DMIVC1RCAP 0x01c /* 32bit */
+#define DMIVC1RCTL 0x020 /* 32bit */
+#define DMIVC1RSTS 0x026 /* 16bit */
+
+#define DMIRCLDECH 0x040 /* 32bit */ /* DMI Root complex link declaration */
+#define DMIESD 0x044 /* 32bit */ /* DMI element self description */
+
+#define DMILE1D 0x050 /* 32bit */
+#define DMILE1A 0x058 /* 64bit */
+#define DMILE2D 0x060 /* 32bit */
+#define DMILE2A 0x068 /* 64bit */
+
+#define DMILCAP 0x084 /* 32bit */
+#define DMILCTL 0x088 /* 16bit */
+#define DMILSTS 0x08a /* 16bit */
+
+#define DMICTL1 0x0f0 /* 32bit */
+#define DMICTL2 0x0fc /* 32bit */
+
+#define DMICC 0x208 /* 32bit */
+
+#define DMIDRCCFG 0xeb4 /* 32bit */
+
+static inline void barrier(void) { asm("" ::: "memory"); }
+
+int i965_silicon_revision(void);
+void i965_early_initialization(void);
+void i965_late_initialization(void);
+
+/* provided by mainboard code */
+void setup_ich7_gpios(void);
+
+/* debugging functions */
+void print_pci_devices(void);
+void dump_pci_device(unsigned dev);
+void dump_pci_devices(void);
+void dump_spd_registers(void);
+void dump_mem(unsigned start, unsigned end);
+
+#endif
+#endif
diff --git a/src/northbridge/intel/i965/ram_calc.c b/src/northbridge/intel/i965/ram_calc.c
new file mode 100644
index 0000000..410280d
--- /dev/null
+++ b/src/northbridge/intel/i965/ram_calc.c
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+// Use simple device model for this file even in ramstage
+#define __SIMPLE_DEVICE__
+
+#include <arch/io.h>
+#include <cbmem.h>
+#include "i965.h"
+
+unsigned long get_top_of_ram(void)
+{
+ u32 tom;
+
+ if (pci_read_config8(PCI_DEV(0, 0x0, 0), DEVEN) & ((1 << 4) | (1 << 3))) {
+ /* IGD enabled, get top of Memory from BSM register */
+ tom = pci_read_config32(PCI_DEV(0,2,0), 0x5c);
+ } else {
+ tom = (pci_read_config8(PCI_DEV(0,0,0), TOLUD) & 0xf7) << 24;
+ }
+
+ /* if TSEG enabled subtract size */
+ switch(pci_read_config8(PCI_DEV(0, 0, 0), ESMRAM)) {
+ case 0x01:
+ /* 1MB TSEG */
+ tom -= 0x10000;
+ break;
+ case 0x03:
+ /* 2MB TSEG */
+ tom -= 0x20000;
+ break;
+ case 0x05:
+ /* 8MB TSEG */
+ tom -= 0x80000;
+ break;
+ default:
+ /* TSEG either disabled or invalid */
+ break;
+ }
+ return (unsigned long) tom;
+}
diff --git a/src/northbridge/intel/i965/raminit.c b/src/northbridge/intel/i965/raminit.c
new file mode 100644
index 0000000..aa42272
--- /dev/null
+++ b/src/northbridge/intel/i965/raminit.c
@@ -0,0 +1,3186 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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 <cpu/x86/mtrr.h>
+#include <cpu/x86/cache.h>
+#include <pc80/mc146818rtc.h>
+#include <spd.h>
+#include <string.h>
+#include <arch/io.h>
+#include <lib.h>
+#include "raminit.h"
+#include "i965.h"
+#include <cbmem.h>
+
+/* Debugging macros. */
+#if CONFIG_DEBUG_RAM_SETUP
+#define PRINTK_DEBUG(x...) printk(BIOS_DEBUG, x)
+#else
+#define PRINTK_DEBUG(x...)
+#endif
+
+#define RAM_INITIALIZATION_COMPLETE (1 << 19)
+
+#define RAM_COMMAND_SELF_REFRESH (0x0 << 16)
+#define RAM_COMMAND_NOP (0x1 << 16)
+#define RAM_COMMAND_PRECHARGE (0x2 << 16)
+#define RAM_COMMAND_MRS (0x3 << 16)
+#define RAM_COMMAND_EMRS (0x4 << 16)
+#define RAM_COMMAND_CBR (0x6 << 16)
+#define RAM_COMMAND_NORMAL (0x7 << 16)
+
+#define RAM_EMRS_1 (0x0 << 21)
+#define RAM_EMRS_2 (0x1 << 21)
+#define RAM_EMRS_3 (0x2 << 21)
+
+static int get_dimm_spd_address(struct sys_info *sysinfo, int device)
+{
+ if (sysinfo->spd_addresses)
+ return sysinfo->spd_addresses[device];
+ else
+ return DIMM0 + device;
+
+}
+
+static inline int spd_read_byte(unsigned device, unsigned address)
+{
+ return smbus_read_byte(device, address);
+}
+
+static __attribute__((noinline)) void do_ram_command(u32 command)
+{
+ u32 reg32;
+
+ reg32 = MCHBAR32(DCC);
+ reg32 &= ~( (3<<21) | (1<<20) | (1<<19) | (7 << 16) );
+ reg32 |= command;
+
+ /* Also set Init Complete */
+ if (command == RAM_COMMAND_NORMAL)
+ reg32 |= RAM_INITIALIZATION_COMPLETE;
+
+ PRINTK_DEBUG(" Sending RAM command 0x%08x", reg32);
+
+ MCHBAR32(DCC) = reg32; /* This is the actual magic */
+
+ PRINTK_DEBUG("...done\n");
+
+ udelay(1);
+}
+
+static void ram_read32(u32 offset)
+{
+ PRINTK_DEBUG(" ram read: %08x\n", offset);
+
+ read32(offset);
+}
+
+#if CONFIG_DEBUG_RAM_SETUP
+void sdram_dump_mchbar_registers(void)
+{
+ int i;
+ printk(BIOS_DEBUG, "Dumping MCHBAR Registers\n");
+
+ for (i=0; i<0xfff; i+=4) {
+ if (MCHBAR32(i) == 0)
+ continue;
+ printk(BIOS_DEBUG, "0x%04x: 0x%08x\n", i, MCHBAR32(i));
+ }
+}
+#endif
+
+static int memclk(void)
+{
+ int offset = 0;
+#if CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GM
+ offset++;
+#endif
+ switch (((MCHBAR32(CLKCFG) >> 4) & 7) - offset) {
+ case 1: return 400;
+ case 2: return 533;
+ case 3: return 667;
+ default: printk(BIOS_DEBUG, "memclk: unknown register value %x\n", ((MCHBAR32(CLKCFG) >> 4) & 7) - offset);
+ }
+ return -1;
+}
+
+#if CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GM
+static u16 fsbclk(void)
+{
+ switch (MCHBAR32(CLKCFG) & 7) {
+ case 0: return 400;
+ case 1: return 533;
+ case 3: return 667;
+ default: printk(BIOS_DEBUG, "fsbclk: unknown register value %x\n", MCHBAR32(CLKCFG) & 7);
+ }
+ return 0xffff;
+}
+#elif CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GC
+static u16 fsbclk(void)
+{
+ switch (MCHBAR32(CLKCFG) & 7) {
+ case 0: return 1066;
+ case 1: return 533;
+ case 2: return 800;
+ default: printk(BIOS_DEBUG, "fsbclk: unknown register value %x\n", MCHBAR32(CLKCFG) & 7);
+ }
+ return 0xffff;
+}
+#endif
+
+static int sdram_capabilities_max_supported_memory_frequency(void)
+{
+ u32 reg32;
+
+#if CONFIG_MAXIMUM_SUPPORTED_FREQUENCY
+ return CONFIG_MAXIMUM_SUPPORTED_FREQUENCY;
+#endif
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x00, 0), 0xe4); /* CAPID0 + 4 */
+ reg32 &= (7 << 0);
+
+ switch (reg32) {
+ case 4: return 400;
+ case 3: return 533;
+ case 2: return 667;
+ }
+ /* Newer revisions of this chipset rather support faster memory clocks,
+ * so if it's a reserved value, return the fastest memory clock that we
+ * know of and can handle
+ */
+ return 667;
+}
+
+/**
+ * @brief determine whether chipset is capable of dual channel interleaved mode
+ *
+ * @return 1 if interleaving is supported, 0 otherwise
+ */
+static int sdram_capabilities_interleave(void)
+{
+ u32 reg32;
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x00,0), 0xe4); /* CAPID0 + 4 */
+ reg32 >>= 25;
+ reg32 &= 1;
+
+ return (!reg32);
+}
+
+/**
+ * @brief determine whether chipset is capable of two memory channels
+ *
+ * @return 1 if dual channel operation is supported, 0 otherwise
+ */
+static int sdram_capabilities_dual_channel(void)
+{
+ u32 reg32;
+
+ reg32 = pci_read_config32(PCI_DEV(0, 0x00,0), 0xe4); /* CAPID0 + 4 */
+ reg32 >>= 24;
+ reg32 &= 1;
+
+ return (!reg32);
+}
+
+static int sdram_capabilities_enhanced_addressing_xor(void)
+{
+ u8 reg8;
+
+ reg8 = pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe5); /* CAPID0 + 5 */
+ reg8 &= (1 << 7);
+
+ return (!reg8);
+}
+
+static int sdram_capabilities_two_dimms_per_channel(void)
+{
+ u8 reg8;
+
+ reg8 = pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe8); /* CAPID0 + 8 */
+ reg8 &= (1 << 0);
+
+ return (reg8 != 0);
+}
+
+// TODO check if we ever need this function
+#if 0
+static int sdram_capabilities_MEM4G_disable(void)
+{
+ u8 reg8;
+
+ reg8 = pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe5); /* CAPID0 + 5 */
+ reg8 &= (1 << 0);
+
+ return (reg8 != 0);
+}
+#endif
+
+#define GFX_FREQUENCY_CAP_166MHZ 0x04
+#define GFX_FREQUENCY_CAP_200MHZ 0x03
+#define GFX_FREQUENCY_CAP_250MHZ 0x02
+#define GFX_FREQUENCY_CAP_ALL 0x00
+
+static int sdram_capabilities_core_frequencies(void)
+{
+ u8 reg8;
+
+ reg8 = pci_read_config8(PCI_DEV(0, 0x00, 0), 0xe5); /* CAPID0 + 5 */
+ reg8 &= (1 << 3) | (1 << 2) | (1 << 1);
+ reg8 >>= 1;
+
+ return (reg8);
+}
+
+static void sdram_detect_errors(struct sys_info *sysinfo)
+{
+ u8 reg8;
+ u8 do_reset = 0;
+
+ reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xa2);
+
+ if (reg8 & ((1<<7)|(1<<2))) {
+ if (reg8 & (1<<2)) {
+ printk(BIOS_DEBUG, "SLP S4# Assertion Width Violation.\n");
+ /* Write back clears bit 2 */
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, reg8);
+ do_reset = 1;
+
+ }
+
+ if (reg8 & (1<<7)) {
+ printk(BIOS_DEBUG, "DRAM initialization was interrupted.\n");
+ reg8 &= ~(1<<7);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, reg8);
+ do_reset = 1;
+ }
+
+ /* Set SLP_S3# Assertion Stretch Enable */
+ reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xa4); /* GEN_PMCON_3 */
+ reg8 |= (1 << 3);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa4, reg8);
+
+ if (do_reset) {
+ printk(BIOS_DEBUG, "Reset required.\n");
+ outb(0x00, 0xcf9);
+ outb(0x0e, 0xcf9);
+ for (;;) asm("hlt"); /* Wait for reset! */
+ }
+ }
+
+ /* Set DRAM initialization bit in ICH7 */
+ reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xa2);
+ reg8 |= (1<<7);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, reg8);
+
+ /* clear self refresh status if check is disabled or not a resume */
+ if (!CONFIG_CHECK_SLFRCS_ON_RESUME || sysinfo->boot_path != BOOT_PATH_RESUME) {
+ MCHBAR8(SLFRCS) |= 3;
+ } else {
+ /* Validate self refresh config */
+ if (((sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED) ||
+ (sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)) &&
+ !(MCHBAR8(SLFRCS) & (1<<0))) {
+ do_reset = 1;
+ }
+ if (((sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED) ||
+ (sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)) &&
+ !(MCHBAR8(SLFRCS) & (1<<1))) {
+ do_reset = 1;
+ }
+ }
+
+ if (do_reset) {
+ printk(BIOS_DEBUG, "Reset required.\n");
+ outb(0x00, 0xcf9);
+ outb(0x0e, 0xcf9);
+ for (;;) asm("hlt"); /* Wait for reset! */
+ }
+}
+
+/**
+ * @brief Get generic DIMM parameters.
+ * @param sysinfo Central memory controller information structure
+ *
+ * This function gathers several pieces of information for each system DIMM:
+ * o DIMM width (x8 / x16)
+ * o DIMM sides (single sided / dual sided)
+ *
+ * Also, some non-supported scenarios are detected.
+ */
+
+static void sdram_get_dram_configuration(struct sys_info *sysinfo)
+{
+ u32 dimm_mask = 0;
+ int i;
+
+ /**
+ * i965 supports two DIMMs, in two configurations:
+ *
+ * - single channel with two DIMMs
+ * - dual channel with one DIMM per channel
+ *
+ * In practice dual channel mainboards have their SPD at 0x50/0x52
+ * whereas single channel configurations have their SPD at 0x50/0x51.
+ *
+ * The capability register knows a lot about the channel configuration
+ * but for now we stick with the information we gather via SPD.
+ */
+
+ if (sdram_capabilities_dual_channel()) {
+ sysinfo->dual_channel = 1;
+ printk(BIOS_DEBUG, "This mainboard supports Dual Channel Operation.\n");
+ } else {
+ sysinfo->dual_channel = 0;
+ printk(BIOS_DEBUG, "This mainboard supports only Single Channel Operation.\n");
+ }
+
+ /**
+ * Since we only support two DIMMs in total, there is a limited number
+ * of combinations. This function returns the type of DIMMs.
+ * return value:
+ * [0:7] lower DIMM population
+ * [8-15] higher DIMM population
+ * [16] dual channel?
+ *
+ * There are 5 different possible populations for a DIMM socket:
+ * 1. x16 double sided (X16DS)
+ * 2. x8 double sided (X8DS)
+ * 3. x16 single sided (X16SS)
+ * 4. x8 double stacked (X8DDS)
+ * 5. not populated (NC)
+ *
+ * For the return value we start counting at zero.
+ *
+ */
+
+ for (i=0; i<(2 * DIMM_SOCKETS); i++) {
+ int device = get_dimm_spd_address(sysinfo, i);
+ u8 reg8;
+
+ /* Initialize the socket information with a sane value */
+ sysinfo->dimm[i] = SYSINFO_DIMM_NOT_POPULATED;
+
+ /* Dual Channel not supported, but Channel 1? Bail out */
+ if (!sdram_capabilities_dual_channel() && (i >> 1))
+ continue;
+
+ /* Two DIMMs per channel not supported, but odd DIMM number? */
+ if (!sdram_capabilities_two_dimms_per_channel() && (i& 1))
+ continue;
+
+ printk(BIOS_DEBUG, "DDR II Channel %d Socket %d: ", (i >> 1), (i & 1));
+
+ if (spd_read_byte(device, SPD_MEMORY_TYPE) != SPD_MEMORY_TYPE_SDRAM_DDR2) {
+ printk(BIOS_DEBUG, "N/A\n");
+ continue;
+ }
+
+ reg8 = spd_read_byte(device, SPD_DIMM_CONFIG_TYPE);
+ if (reg8 == ERROR_SCHEME_ECC)
+ die("Error: ECC memory not supported by this chipset\n");
+
+ reg8 = spd_read_byte(device, SPD_MODULE_ATTRIBUTES);
+ if (reg8 & MODULE_BUFFERED)
+ die("Error: Buffered memory not supported by this chipset\n");
+ if (reg8 & MODULE_REGISTERED)
+ die("Error: Registered memory not supported by this chipset\n");
+
+ switch (spd_read_byte(device, SPD_PRIMARY_SDRAM_WIDTH)) {
+ case 0x08:
+ switch (spd_read_byte(device, SPD_NUM_DIMM_BANKS) & 0x0f) {
+ case 1:
+ printk(BIOS_DEBUG, "x8DDS\n");
+ sysinfo->dimm[i] = SYSINFO_DIMM_X8DDS;
+ break;
+ case 0:
+ printk(BIOS_DEBUG, "x8DS\n");
+ sysinfo->dimm[i] = SYSINFO_DIMM_X8DS;
+ break;
+ default:
+ printk(BIOS_DEBUG, "Unsupported.\n");
+ }
+ break;
+ case 0x10:
+ switch (spd_read_byte(device, SPD_NUM_DIMM_BANKS) & 0x0f) {
+ case 1:
+ printk(BIOS_DEBUG, "x16DS\n");
+ sysinfo->dimm[i] = SYSINFO_DIMM_X16DS;
+ break;
+ case 0:
+ printk(BIOS_DEBUG, "x16SS\n");
+ sysinfo->dimm[i] = SYSINFO_DIMM_X16SS;
+ break;
+ default:
+ printk(BIOS_DEBUG, "Unsupported.\n");
+ }
+ break;
+ default:
+ die("Unsupported DDR-II memory width.\n");
+ }
+
+ dimm_mask |= (1 << i);
+ }
+
+ if (!dimm_mask) {
+ die("No memory installed.\n");
+ }
+
+ if (!(dimm_mask & ((1 << DIMM_SOCKETS) - 1))) {
+ printk(BIOS_INFO, "Channel 0 has no memory populated.\n");
+ }
+}
+
+/**
+ * @brief determine if any DIMMs are stacked
+ *
+ * @param sysinfo central sysinfo data structure.
+ */
+static void sdram_verify_package_type(struct sys_info * sysinfo)
+{
+ int i;
+
+ /* Assume no stacked DIMMs are available until we find one */
+ sysinfo->package = 0;
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ /* Is the current DIMM a stacked DIMM? */
+ if (spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_NUM_DIMM_BANKS) & (1 << 4))
+ sysinfo->package = 1;
+ }
+}
+
+static u8 sdram_possible_cas_latencies(struct sys_info * sysinfo)
+{
+ int i;
+ u8 cas_mask;
+
+ /* Setup CAS mask with all supported CAS Latencies */
+ cas_mask = SPD_CAS_LATENCY_DDR2_3 |
+ SPD_CAS_LATENCY_DDR2_4 |
+ SPD_CAS_LATENCY_DDR2_5;
+
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ if (sysinfo->dimm[i] != SYSINFO_DIMM_NOT_POPULATED)
+ cas_mask &= spd_read_byte(get_dimm_spd_address(sysinfo, i),
+ SPD_ACCEPTABLE_CAS_LATENCIES);
+ }
+
+ if(!cas_mask) {
+ die("No DDR-II modules with accepted CAS latencies found.\n");
+ }
+
+ return cas_mask;
+}
+
+static void sdram_detect_cas_latency_and_ram_speed(struct sys_info * sysinfo, u8 cas_mask)
+{
+ int i, j, idx;
+ int lowest_common_cas = 0;
+ int max_ram_speed = 0;
+
+ const u8 ddr2_speeds_table[] = {
+ 0x50, 0x60, /* DDR2 400: tCLK = 5.0ns tAC = 0.6ns */
+ 0x3d, 0x50, /* DDR2 533: tCLK = 3.75ns tAC = 0.5ns */
+ 0x30, 0x45, /* DDR2 667: tCLK = 3.0ns tAC = 0.45ns */
+ };
+
+ const u8 spd_lookup_table[] = {
+ SPD_MIN_CYCLE_TIME_AT_CAS_MAX, SPD_ACCESS_TIME_FROM_CLOCK,
+ SPD_SDRAM_CYCLE_TIME_2ND, SPD_ACCESS_TIME_FROM_CLOCK_2ND,
+ SPD_SDRAM_CYCLE_TIME_3RD, SPD_ACCESS_TIME_FROM_CLOCK_3RD
+ };
+
+ switch (sdram_capabilities_max_supported_memory_frequency()) {
+ case 400: max_ram_speed = 0; break;
+ case 533: max_ram_speed = 1; break;
+ case 667: max_ram_speed = 2; break;
+ }
+
+ if (fsbclk() == 533)
+ max_ram_speed = 1;
+
+ sysinfo->memory_frequency = 0;
+ sysinfo->cas = 0;
+
+ if (cas_mask & SPD_CAS_LATENCY_DDR2_3) {
+ lowest_common_cas = 3;
+ } else if (cas_mask & SPD_CAS_LATENCY_DDR2_4) {
+ lowest_common_cas = 4;
+ } else if (cas_mask & SPD_CAS_LATENCY_DDR2_5) {
+ lowest_common_cas = 5;
+ }
+ PRINTK_DEBUG("lowest common cas = %d\n", lowest_common_cas);
+
+ for (j = max_ram_speed; j>=0; j--) {
+ int freq_cas_mask = cas_mask;
+
+ PRINTK_DEBUG("Probing Speed %d\n", j);
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ int device = get_dimm_spd_address(sysinfo, i);
+ int current_cas_mask;
+
+ PRINTK_DEBUG(" DIMM: %d\n", i);
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED) {
+ continue;
+ }
+
+ current_cas_mask = spd_read_byte(device, SPD_ACCEPTABLE_CAS_LATENCIES);
+
+ while (current_cas_mask) {
+ int highest_supported_cas = 0, current_cas = 0;
+ PRINTK_DEBUG(" Current CAS mask: %04x; ", current_cas_mask);
+ if (current_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
+ highest_supported_cas = 5;
+ } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
+ highest_supported_cas = 4;
+ } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
+ highest_supported_cas = 3;
+ } else {
+ die("Invalid max. CAS.\n");
+ }
+ if (current_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
+ current_cas = 3;
+ } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
+ current_cas = 4;
+ } else if (current_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
+ current_cas = 5;
+ } else {
+ die("Invalid CAS.\n");
+ }
+
+ idx = highest_supported_cas - current_cas;
+ PRINTK_DEBUG("idx=%d, ", idx);
+ PRINTK_DEBUG("tCLK=%x, ", spd_read_byte(device, spd_lookup_table[2*idx]));
+ PRINTK_DEBUG("tAC=%x", spd_read_byte(device, spd_lookup_table[(2*idx)+1]));
+
+ if (spd_read_byte(device, spd_lookup_table[2*idx]) <= ddr2_speeds_table[2*j] &&
+ spd_read_byte(device, spd_lookup_table[(2*idx)+1]) <= ddr2_speeds_table[(2*j)+1]) {
+ PRINTK_DEBUG(": OK\n");
+ break;
+ }
+
+ PRINTK_DEBUG(": Not fast enough!\n");
+
+ current_cas_mask &= ~(1 << (current_cas));
+ }
+
+ freq_cas_mask &= current_cas_mask;
+ if (!current_cas_mask) {
+ PRINTK_DEBUG(" No valid CAS for this speed on DIMM %d\n", i);
+ break;
+ }
+ }
+ PRINTK_DEBUG(" freq_cas_mask for speed %d: %04x\n", j, freq_cas_mask);
+ if (freq_cas_mask) {
+ switch (j) {
+ case 0: sysinfo->memory_frequency = 400; break;
+ case 1: sysinfo->memory_frequency = 533; break;
+ case 2: sysinfo->memory_frequency = 667; break;
+ }
+ if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_3) {
+ sysinfo->cas = 3;
+ } else if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_4) {
+ sysinfo->cas = 4;
+ } else if (freq_cas_mask & SPD_CAS_LATENCY_DDR2_5) {
+ sysinfo->cas = 5;
+ }
+ break;
+ }
+ }
+
+ if (sysinfo->memory_frequency && sysinfo->cas) {
+ printk(BIOS_DEBUG, "Memory will be driven at %dMHz with CAS=%d clocks\n",
+ sysinfo->memory_frequency, sysinfo->cas);
+ } else {
+ die("Could not find common memory frequency and CAS\n");
+ }
+}
+
+static void sdram_detect_smallest_tRAS(struct sys_info * sysinfo)
+{
+ int i;
+ int tRAS_time;
+ int tRAS_cycles;
+ int freq_multiplier = 0;
+
+ switch (sysinfo->memory_frequency) {
+ case 400: freq_multiplier = 0x14; break; /* 5ns */
+ case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
+ case 667: freq_multiplier = 0x0c; break; /* 3ns */
+ }
+
+ tRAS_cycles = 4; /* 4 clocks minimum */
+ tRAS_time = tRAS_cycles * freq_multiplier;
+
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ u8 reg8;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_ACTIVE_TO_PRECHARGE_DELAY);
+ if (!reg8) {
+ die("Invalid tRAS value.\n");
+ }
+
+ while ((tRAS_time >> 2) < reg8) {
+ tRAS_time += freq_multiplier;
+ tRAS_cycles++;
+ }
+ }
+ if(tRAS_cycles > 0x18) {
+ die("DDR-II Module does not support this frequency (tRAS error)\n");
+ }
+
+ printk(BIOS_DEBUG, "tRAS = %d cycles\n", tRAS_cycles);
+ sysinfo->tras = tRAS_cycles;
+}
+
+static void sdram_detect_smallest_tRP(struct sys_info * sysinfo)
+{
+ int i;
+ int tRP_time;
+ int tRP_cycles;
+ int freq_multiplier = 0;
+
+ switch (sysinfo->memory_frequency) {
+ case 400: freq_multiplier = 0x14; break; /* 5ns */
+ case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
+ case 667: freq_multiplier = 0x0c; break; /* 3ns */
+ }
+
+ tRP_cycles = 2; /* 2 clocks minimum */
+ tRP_time = tRP_cycles * freq_multiplier;
+
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ u8 reg8;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_ROW_PRECHARGE_TIME);
+ if (!reg8) {
+ die("Invalid tRP value.\n");
+ }
+
+ while (tRP_time < reg8) {
+ tRP_time += freq_multiplier;
+ tRP_cycles++;
+ }
+ }
+
+ if(tRP_cycles > 6) {
+ die("DDR-II Module does not support this frequency (tRP error)\n");
+ }
+
+ printk(BIOS_DEBUG, "tRP = %d cycles\n", tRP_cycles);
+ sysinfo->trp = tRP_cycles;
+}
+
+static void sdram_detect_smallest_tRCD(struct sys_info * sysinfo)
+{
+ int i;
+ int tRCD_time;
+ int tRCD_cycles;
+ int freq_multiplier = 0;
+
+ switch (sysinfo->memory_frequency) {
+ case 400: freq_multiplier = 0x14; break; /* 5ns */
+ case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
+ case 667: freq_multiplier = 0x0c; break; /* 3ns */
+ }
+
+ tRCD_cycles = 2; /* 2 clocks minimum */
+ tRCD_time = tRCD_cycles * freq_multiplier;
+
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ u8 reg8;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_MIN_RAS_TO_CAS_DELAY);
+ if (!reg8) {
+ die("Invalid tRCD value.\n");
+ }
+
+ while (tRCD_time < reg8) {
+ tRCD_time += freq_multiplier;
+ tRCD_cycles++;
+ }
+ }
+ if(tRCD_cycles > 6) {
+ die("DDR-II Module does not support this frequency (tRCD error)\n");
+ }
+
+ printk(BIOS_DEBUG, "tRCD = %d cycles\n", tRCD_cycles);
+ sysinfo->trcd = tRCD_cycles;
+}
+
+static void sdram_detect_smallest_tWR(struct sys_info * sysinfo)
+{
+ int i;
+ int tWR_time;
+ int tWR_cycles;
+ int freq_multiplier = 0;
+
+ switch (sysinfo->memory_frequency) {
+ case 400: freq_multiplier = 0x14; break; /* 5ns */
+ case 533: freq_multiplier = 0x0f; break; /* 3.75ns */
+ case 667: freq_multiplier = 0x0c; break; /* 3ns */
+ }
+
+ tWR_cycles = 2; /* 2 clocks minimum */
+ tWR_time = tWR_cycles * freq_multiplier;
+
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ u8 reg8;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ reg8 = spd_read_byte(get_dimm_spd_address(sysinfo, i), SPD_WRITE_RECOVERY_TIME);
+ if (!reg8) {
+ die("Invalid tWR value.\n");
+ }
+
+ while (tWR_time < reg8) {
+ tWR_time += freq_multiplier;
+ tWR_cycles++;
+ }
+ }
+ if(tWR_cycles > 5) {
+ die("DDR-II Module does not support this frequency (tWR error)\n");
+ }
+
+ printk(BIOS_DEBUG, "tWR = %d cycles\n", tWR_cycles);
+ sysinfo->twr = tWR_cycles;
+}
+
+static void sdram_detect_smallest_tRFC(struct sys_info * sysinfo)
+{
+ int i, index = 0;
+
+ const u8 tRFC_cycles[] = {
+ /* 75 105 127.5 */
+ 15, 21, 26, /* DDR2-400 */
+ 20, 28, 34, /* DDR2-533 */
+ 25, 35, 43 /* DDR2-667 */
+ };
+
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ u8 reg8;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ reg8 = sysinfo->banksize[i*2];
+ switch (reg8) {
+ case 0x04: reg8 = 0; break;
+ case 0x08: reg8 = 1; break;
+ case 0x10: reg8 = 2; break;
+ case 0x20: reg8 = 3; break;
+ }
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_X16DS || sysinfo->dimm[i] == SYSINFO_DIMM_X16SS)
+ reg8++;
+
+ if (reg8 > 3) {
+ /* Can this happen? Go back to 127.5ns just to be sure
+ * we don't run out of the array. This may be wrong
+ */
+ printk(BIOS_DEBUG, "DIMM %d is 1Gb x16.. Please report.\n", i);
+ reg8 = 3;
+ }
+
+ if (reg8 > index)
+ index = reg8;
+
+ }
+ index--;
+ switch (sysinfo->memory_frequency) {
+ case 667: index += 3; /* Fallthrough */
+ case 533: index += 3; /* Fallthrough */
+ case 400: break;
+ }
+
+ sysinfo->trfc = tRFC_cycles[index];
+ printk(BIOS_DEBUG, "tRFC = %d cycles\n", tRFC_cycles[index]);
+}
+
+static void sdram_detect_smallest_refresh(struct sys_info * sysinfo)
+{
+ int i;
+
+ sysinfo->refresh = 0;
+
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ int refresh;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ refresh = spd_read_byte(get_dimm_spd_address(sysinfo, i),
+ SPD_REFRESH) & ~(1 << 7);
+
+ /* 15.6us */
+ if (!refresh)
+ continue;
+
+ /* Refresh is slower than 15.6us, use 15.6us */
+ if (refresh > 2)
+ continue;
+
+ if (refresh == 2) {
+ sysinfo->refresh = 1;
+ break;
+ }
+
+ die("DDR-II module has unsupported refresh value\n");
+ }
+ printk(BIOS_DEBUG, "Refresh: %s\n", sysinfo->refresh?"7.8us":"15.6us");
+}
+
+static void sdram_verify_burst_length(struct sys_info * sysinfo)
+{
+ int i;
+
+ for (i=0; i<2*DIMM_SOCKETS; i++) {
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ if (!(spd_read_byte(get_dimm_spd_address(sysinfo, i),
+ SPD_SUPPORTED_BURST_LENGTHS) & SPD_BURST_LENGTH_8))
+ die("Only DDR-II RAM with burst length 8 is supported by this chipset.\n");
+ }
+}
+
+static void sdram_program_dram_width(struct sys_info * sysinfo)
+{
+ u16 c0dramw=0, c1dramw=0;
+ int idx;
+
+ if (sysinfo->dual_channel)
+ idx = 2;
+ else
+ idx = 1;
+
+ switch (sysinfo->dimm[0]) {
+ case 0: c0dramw = 0x0000; break; /* x16DS */
+ case 1: c0dramw = 0x0001; break; /* x8DS */
+ case 2: c0dramw = 0x0000; break; /* x16SS */
+ case 3: c0dramw = 0x0005; break; /* x8DDS */
+ case 4: c0dramw = 0x0000; break; /* NC */
+ }
+
+ switch (sysinfo->dimm[idx]) {
+ case 0: c1dramw = 0x0000; break; /* x16DS */
+ case 1: c1dramw = 0x0010; break; /* x8DS */
+ case 2: c1dramw = 0x0000; break; /* x16SS */
+ case 3: c1dramw = 0x0050; break; /* x8DDS */
+ case 4: c1dramw = 0x0000; break; /* NC */
+ }
+
+ if ( !sdram_capabilities_dual_channel() ) {
+ /* Single Channel */
+ c0dramw |= c1dramw;
+ c1dramw = 0;
+ }
+
+ MCHBAR16(C0DRAMW) = c0dramw;
+ MCHBAR16(C1DRAMW) = c1dramw;
+}
+
+static void sdram_write_slew_rates(u32 offset, const u32 *slew_rate_table)
+{
+ int i;
+
+ for (i=0; i<16; i++)
+ MCHBAR32(offset+(i*4)) = slew_rate_table[i];
+}
+
+static const u32 dq2030[] = {
+ 0x08070706, 0x0a090908, 0x0d0c0b0a, 0x12100f0e,
+ 0x1a181614, 0x22201e1c, 0x2a282624, 0x3934302d,
+ 0x0a090908, 0x0c0b0b0a, 0x0e0d0d0c, 0x1211100f,
+ 0x19171513, 0x211f1d1b, 0x2d292623, 0x3f393531
+};
+
+static const u32 dq2330[] = {
+ 0x08070706, 0x0a090908, 0x0d0c0b0a, 0x12100f0e,
+ 0x1a181614, 0x22201e1c, 0x2a282624, 0x3934302d,
+ 0x0a090908, 0x0c0b0b0a, 0x0e0d0d0c, 0x1211100f,
+ 0x19171513, 0x211f1d1b, 0x2d292623, 0x3f393531
+};
+
+static const u32 cmd2710[] = {
+ 0x07060605, 0x0f0d0b09, 0x19171411, 0x1f1f1d1b,
+ 0x1f1f1f1f, 0x1f1f1f1f, 0x1f1f1f1f, 0x1f1f1f1f,
+ 0x1110100f, 0x0f0d0b09, 0x19171411, 0x1f1f1d1b,
+ 0x1f1f1f1f, 0x1f1f1f1f, 0x1f1f1f1f, 0x1f1f1f1f
+};
+
+static const u32 cmd3210[] = {
+ 0x0f0d0b0a, 0x17151311, 0x1f1d1b19, 0x1f1f1f1f,
+ 0x1f1f1f1f, 0x1f1f1f1f, 0x1f1f1f1f, 0x1f1f1f1f,
+ 0x18171615, 0x1f1f1c1a, 0x1f1f1f1f, 0x1f1f1f1f,
+ 0x1f1f1f1f, 0x1f1f1f1f, 0x1f1f1f1f, 0x1f1f1f1f
+};
+
+static const u32 clk2030[] = {
+ 0x0e0d0d0c, 0x100f0f0e, 0x100f0e0d, 0x15131211,
+ 0x1d1b1917, 0x2523211f, 0x2a282927, 0x32302e2c,
+ 0x17161514, 0x1b1a1918, 0x1f1e1d1c, 0x23222120,
+ 0x27262524, 0x2d2b2928, 0x3533312f, 0x3d3b3937
+};
+
+static const u32 ctl3215[] = {
+ 0x01010000, 0x03020101, 0x07060504, 0x0b0a0908,
+ 0x100f0e0d, 0x14131211, 0x18171615, 0x1c1b1a19,
+ 0x05040403, 0x07060605, 0x0a090807, 0x0f0d0c0b,
+ 0x14131211, 0x18171615, 0x1c1b1a19, 0x201f1e1d
+};
+
+static const u32 ctl3220[] = {
+ 0x05040403, 0x07060505, 0x0e0c0a08, 0x1a171411,
+ 0x2825221f, 0x35322f2b, 0x3e3e3b38, 0x3e3e3e3e,
+ 0x09080807, 0x0b0a0a09, 0x0f0d0c0b, 0x1b171311,
+ 0x2825221f, 0x35322f2b, 0x3e3e3b38, 0x3e3e3e3e
+};
+
+static const u32 nc[] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+enum {
+ DQ2030,
+ DQ2330,
+ CMD2710,
+ CMD3210,
+ CLK2030,
+ CTL3215,
+ CTL3220,
+ NC,
+};
+
+static const u8 dual_channel_slew_group_lookup[] = {
+ DQ2030, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD3210, NC, CTL3215, NC, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD2710,
+ DQ2030, CMD3210, NC, CTL3215, NC, CLK2030, NC, NC,
+
+ DQ2030, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD3210, CTL3215, NC, CLK2030, NC, DQ2030, CMD3210,
+ DQ2030, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD3210, CTL3215, NC, CLK2030, NC, DQ2030, CMD2710,
+ DQ2030, CMD3210, CTL3215, NC, CLK2030, NC, NC, NC,
+
+ DQ2030, CMD3210, NC, CTL3215, NC, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD3210, NC, CTL3215, NC, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD2710,
+ DQ2030, CMD3210, NC, CTL3215, NC, CLK2030, NC, NC,
+
+ DQ2030, CMD2710, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD2710, CTL3215, NC, CLK2030, NC, DQ2030, CMD3210,
+ DQ2030, CMD2710, CTL3215, CTL3215, CLK2030, CLK2030, DQ2030, CMD3210,
+ DQ2030, CMD2710, CTL3215, NC, CLK2030, NC, DQ2030, CMD2710,
+ DQ2030, CMD2710, CTL3215, NC, CLK2030, NC, NC, NC,
+
+ NC, NC, NC, CTL3215, NC, CLK2030, DQ2030, CMD3210,
+ NC, NC, CTL3215, NC, CLK2030, NC, DQ2030, CMD3210,
+ NC, NC, NC, CTL3215, NC, CLK2030, DQ2030, CMD3210,
+ NC, NC, CTL3215, NC, CLK2030, CLK2030, DQ2030, CMD2710
+};
+
+static const u8 single_channel_slew_group_lookup[] = {
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, NC, CTL3215, NC, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, NC, CTL3215, NC, CLK2030, NC, NC,
+
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, NC, CLK2030, NC, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, NC, CLK2030, NC, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, NC, CLK2030, NC, NC, NC,
+
+ DQ2330, CMD3210, NC, CTL3215, NC, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, NC, CTL3215, NC, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, NC, CTL3215, NC, CLK2030, NC, NC,
+
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, NC, CLK2030, NC, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, CTL3215, CLK2030, CLK2030, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, NC, CLK2030, NC, DQ2330, CMD3210,
+ DQ2330, CMD3210, CTL3215, NC, CLK2030, NC, NC, NC,
+
+ DQ2330, NC, NC, CTL3215, NC, CLK2030, DQ2030, CMD3210,
+ DQ2330, NC, CTL3215, NC, CLK2030, NC, DQ2030, CMD3210,
+ DQ2330, NC, NC, CTL3215, NC, CLK2030, DQ2030, CMD3210,
+ DQ2330, NC, CTL3215, NC, CLK2030, CLK2030, DQ2030, CMD3210
+};
+
+static const u32 *slew_group_lookup(int dual_channel, int index)
+{
+ const u8 *slew_group;
+ /* Dual Channel needs different tables. */
+ if (dual_channel)
+ slew_group = dual_channel_slew_group_lookup;
+ else
+ slew_group = single_channel_slew_group_lookup;
+
+ switch (slew_group[index]) {
+ case DQ2030: return dq2030;
+ case DQ2330: return dq2330;
+ case CMD2710: return cmd2710;
+ case CMD3210: return cmd3210;
+ case CLK2030: return clk2030;
+ case CTL3215: return ctl3215;
+ case CTL3220: return ctl3220;
+ case NC: return nc;
+ }
+
+ return nc;
+}
+
+#if CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GM
+/* Strength multiplier tables */
+static const u8 dual_channel_strength_multiplier[] = {
+ 0x44, 0x11, 0x11, 0x11, 0x44, 0x44, 0x44, 0x11,
+ 0x44, 0x11, 0x11, 0x11, 0x44, 0x44, 0x44, 0x11,
+ 0x44, 0x11, 0x00, 0x11, 0x00, 0x44, 0x44, 0x11,
+ 0x44, 0x11, 0x11, 0x11, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x11, 0x00, 0x11, 0x00, 0x44, 0x00, 0x00,
+ 0x44, 0x11, 0x11, 0x11, 0x44, 0x44, 0x44, 0x11,
+ 0x44, 0x11, 0x11, 0x00, 0x44, 0x00, 0x44, 0x11,
+ 0x44, 0x11, 0x11, 0x11, 0x44, 0x44, 0x44, 0x11,
+ 0x44, 0x11, 0x11, 0x00, 0x44, 0x00, 0x44, 0x22,
+ 0x44, 0x11, 0x11, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x44, 0x11, 0x00, 0x11, 0x00, 0x44, 0x44, 0x11,
+ 0x44, 0x11, 0x11, 0x11, 0x44, 0x44, 0x44, 0x11,
+ 0x44, 0x11, 0x00, 0x11, 0x00, 0x44, 0x44, 0x11,
+ 0x44, 0x11, 0x11, 0x11, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x11, 0x00, 0x11, 0x00, 0x44, 0x00, 0x00,
+ 0x44, 0x22, 0x11, 0x11, 0x44, 0x44, 0x44, 0x11,
+ 0x44, 0x22, 0x11, 0x00, 0x44, 0x00, 0x44, 0x11,
+ 0x44, 0x22, 0x11, 0x11, 0x44, 0x44, 0x44, 0x11,
+ 0x44, 0x22, 0x11, 0x00, 0x44, 0x00, 0x44, 0x22,
+ 0x44, 0x22, 0x11, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x11, 0x00, 0x44, 0x44, 0x11,
+ 0x00, 0x00, 0x11, 0x00, 0x44, 0x00, 0x44, 0x11,
+ 0x00, 0x00, 0x00, 0x11, 0x00, 0x44, 0x44, 0x11,
+ 0x00, 0x00, 0x11, 0x00, 0x44, 0x44, 0x44, 0x22
+};
+
+static const u8 single_channel_strength_multiplier[] = {
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x00, 0x11, 0x00, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x00, 0x11, 0x00, 0x44, 0x00, 0x00,
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x00, 0x44, 0x00, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x00, 0x44, 0x00, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x33, 0x11, 0x00, 0x11, 0x00, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x00, 0x11, 0x00, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x00, 0x11, 0x00, 0x44, 0x00, 0x00,
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x00, 0x44, 0x00, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x11, 0x44, 0x44, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x00, 0x44, 0x00, 0x33, 0x11,
+ 0x33, 0x11, 0x11, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x33, 0x00, 0x00, 0x11, 0x00, 0x44, 0x33, 0x11,
+ 0x33, 0x00, 0x11, 0x00, 0x44, 0x00, 0x33, 0x11,
+ 0x33, 0x00, 0x00, 0x11, 0x00, 0x44, 0x33, 0x11,
+ 0x33, 0x00, 0x11, 0x00, 0x44, 0x44, 0x33, 0x11
+};
+#elif CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GC
+static const u8 dual_channel_strength_multiplier[] = {
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x33,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x33,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x33,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x33,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x22,
+ 0x44, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x33
+};
+
+static const u8 single_channel_strength_multiplier[] = {
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x44, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x55, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x44, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x55, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x44, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x88, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x44, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x55, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x55, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x88, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x55, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x88, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x22, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00,
+ 0x44, 0x33, 0x00, 0x00, 0x44, 0x44, 0x44, 0x00
+};
+#endif
+
+static void sdram_rcomp_buffer_strength_and_slew(struct sys_info *sysinfo)
+{
+ const u8 * strength_multiplier;
+ int idx, dual_channel;
+
+ /* Set Strength Multipliers */
+
+ /* Dual Channel needs different tables. */
+ if (sdram_capabilities_dual_channel()) {
+ printk(BIOS_DEBUG, "Programming Dual Channel RCOMP\n");
+ strength_multiplier = dual_channel_strength_multiplier;
+ dual_channel = 1;
+ idx = 5 * sysinfo->dimm[0] + sysinfo->dimm[2];
+ } else {
+ printk(BIOS_DEBUG, "Programming Single Channel RCOMP\n");
+ strength_multiplier = single_channel_strength_multiplier;
+ dual_channel = 0;
+ idx = 5 * sysinfo->dimm[0] + sysinfo->dimm[1];
+ }
+
+ printk(BIOS_DEBUG, "Table Index: %d\n", idx);
+
+ MCHBAR8(G1SC) = strength_multiplier[idx * 8 + 0];
+ MCHBAR8(G2SC) = strength_multiplier[idx * 8 + 1];
+ MCHBAR8(G3SC) = strength_multiplier[idx * 8 + 2];
+ MCHBAR8(G4SC) = strength_multiplier[idx * 8 + 3];
+ MCHBAR8(G5SC) = strength_multiplier[idx * 8 + 4];
+ MCHBAR8(G6SC) = strength_multiplier[idx * 8 + 5];
+ MCHBAR8(G7SC) = strength_multiplier[idx * 8 + 6];
+ MCHBAR8(G8SC) = strength_multiplier[idx * 8 + 7];
+
+ /* Channel 0 */
+ sdram_write_slew_rates(G1SRPUT, slew_group_lookup(dual_channel, idx * 8 + 0));
+ sdram_write_slew_rates(G2SRPUT, slew_group_lookup(dual_channel, idx * 8 + 1));
+ if ((slew_group_lookup(dual_channel, idx * 8 + 2) != nc) && (sysinfo->package == SYSINFO_PACKAGE_STACKED)) {
+
+ sdram_write_slew_rates(G3SRPUT, ctl3220);
+ } else {
+ sdram_write_slew_rates(G3SRPUT, slew_group_lookup(dual_channel, idx * 8 + 2));
+ }
+ sdram_write_slew_rates(G4SRPUT, slew_group_lookup(dual_channel, idx * 8 + 3));
+ sdram_write_slew_rates(G5SRPUT, slew_group_lookup(dual_channel, idx * 8 + 4));
+ sdram_write_slew_rates(G6SRPUT, slew_group_lookup(dual_channel, idx * 8 + 5));
+
+ /* Channel 1 */
+ if (sysinfo->dual_channel) {
+ sdram_write_slew_rates(G7SRPUT, slew_group_lookup(dual_channel, idx * 8 + 6));
+ sdram_write_slew_rates(G8SRPUT, slew_group_lookup(dual_channel, idx * 8 + 7));
+ } else {
+ sdram_write_slew_rates(G7SRPUT, nc);
+ sdram_write_slew_rates(G8SRPUT, nc);
+ }
+}
+
+static void sdram_enable_rcomp(void)
+{
+ u32 reg32;
+ /* Enable Global Periodic RCOMP */
+ udelay(300);
+ reg32 = MCHBAR32(GBRCOMPCTL);
+ reg32 &= ~(1 << 23);
+ MCHBAR32(GBRCOMPCTL) = reg32;
+}
+
+static void sdram_program_dll_timings(struct sys_info *sysinfo)
+{
+ u32 chan0dll = 0, chan1dll = 0;
+ int i;
+
+ printk(BIOS_DEBUG, "Programming DLL Timings... \n");
+
+ MCHBAR16(DQSMT) &= ~( (3 << 12) | (1 << 10) | ( 0xf << 0) );
+ MCHBAR16(DQSMT) |= (1 << 13) | (0xc << 0);
+
+ /* We drive both channels with the same speed */
+ switch (sysinfo->memory_frequency) {
+ case 400: chan0dll = 0x26262626; chan1dll=0x26262626; break; /* 400MHz */
+ case 533: chan0dll = 0x22222222; chan1dll=0x22222222; break; /* 533MHz */
+ case 667: chan0dll = 0x11111111; chan1dll=0x11111111; break; /* 667MHz */
+ }
+
+ for (i=0; i < 4; i++) {
+ MCHBAR32(C0R0B00DQST + (i * 0x10) + 0) = chan0dll;
+ MCHBAR32(C0R0B00DQST + (i * 0x10) + 4) = chan0dll;
+ MCHBAR32(C1R0B00DQST + (i * 0x10) + 0) = chan1dll;
+ MCHBAR32(C1R0B00DQST + (i * 0x10) + 4) = chan1dll;
+ }
+}
+
+static void sdram_force_rcomp(void)
+{
+ u32 reg32;
+ u8 reg8;
+
+ reg32 = MCHBAR32(ODTC);
+ reg32 |= (1 << 28);
+ MCHBAR32(ODTC) = reg32;
+
+ reg32 = MCHBAR32(SMSRCTL);
+ reg32 |= (1 << 0);
+ MCHBAR32(SMSRCTL) = reg32;
+
+ /* Start initial RCOMP */
+ reg32 = MCHBAR32(GBRCOMPCTL);
+ reg32 |= (1 << 8);
+ MCHBAR32(GBRCOMPCTL) = reg32;
+
+ reg8 = i965_silicon_revision();
+ if ((reg8 == 0 && (MCHBAR32(DCC) & (3 << 0)) == 0) || (reg8 == 1)) {
+
+ reg32 = MCHBAR32(GBRCOMPCTL);
+ reg32 |= (3 << 5);
+ MCHBAR32(GBRCOMPCTL) = reg32;
+ }
+}
+
+static void sdram_initialize_system_memory_io(struct sys_info *sysinfo)
+{
+ u8 reg8;
+ u32 reg32;
+
+ printk(BIOS_DEBUG, "Initializing System Memory IO... \n");
+ /* Enable Data Half Clock Pushout */
+ reg8 = MCHBAR8(C0HCTC);
+ reg8 &= ~0x1f;
+ reg8 |= ( 1 << 0);
+ MCHBAR8(C0HCTC) = reg8;
+
+ reg8 = MCHBAR8(C1HCTC);
+ reg8 &= ~0x1f;
+ reg8 |= ( 1 << 0);
+ MCHBAR8(C1HCTC) = reg8;
+
+ MCHBAR16(WDLLBYPMODE) &= ~( (1 << 9) | (1 << 6) | (1 << 4) | (1 << 3) | (1 << 1) );
+ MCHBAR16(WDLLBYPMODE) |= (1 << 8) | (1 << 7) | (1 << 5) | (1 << 2) | (1 << 0);
+
+ MCHBAR8(C0WDLLCMC) = 0;
+ MCHBAR8(C1WDLLCMC) = 0;
+
+ /* Program RCOMP Settings */
+ sdram_program_dram_width(sysinfo);
+
+ sdram_rcomp_buffer_strength_and_slew(sysinfo);
+
+ /* Indicate that RCOMP programming is done */
+ reg32 = MCHBAR32(GBRCOMPCTL);
+ reg32 &= ~( (1 << 29) | (1 << 26) | (3 << 21) | (3 << 2) );
+ reg32 |= (3 << 27) | (3 << 0);
+ MCHBAR32(GBRCOMPCTL) = reg32;
+
+ MCHBAR32(GBRCOMPCTL) |= (1 << 10);
+
+ /* Program DLL Timings */
+ sdram_program_dll_timings(sysinfo);
+
+ /* Force RCOMP cycle */
+ sdram_force_rcomp();
+}
+
+static void sdram_enable_system_memory_io(struct sys_info *sysinfo)
+{
+ u32 reg32;
+
+ printk(BIOS_DEBUG, "Enabling System Memory IO... \n");
+
+ reg32 = MCHBAR32(RCVENMT);
+ reg32 &= ~(0x3f << 6);
+ MCHBAR32(RCVENMT) = reg32; /* [11:6] = 0 */
+
+ reg32 |= (1 << 11) | (1 << 9);
+ MCHBAR32(RCVENMT) = reg32;
+
+ reg32 = MCHBAR32(DRTST);
+ reg32 |= (1 << 3) | (1 << 2);
+ MCHBAR32(DRTST) = reg32;
+
+ reg32 = MCHBAR32(DRTST);
+ reg32 |= (1 << 6) | (1 << 4);
+ MCHBAR32(DRTST) = reg32;
+
+ asm volatile ("nop; nop;" ::: "memory");
+
+ reg32 = MCHBAR32(DRTST);
+
+ /* Is channel 0 populated? */
+ if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED ||
+ sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)
+ reg32 |= (1 << 7) | (1 << 5);
+ else
+ reg32 |= (1 << 31);
+
+ /* Is channel 1 populated? */
+ if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED ||
+ sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)
+ reg32 |= (1 << 9) | (1 << 8);
+ else
+ reg32 |= (1 << 30);
+
+ MCHBAR32(DRTST) = reg32;
+
+ /* Activate DRAM Channel IO Buffers */
+ if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED ||
+ sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED) {
+ reg32 = MCHBAR32(C0DRC1);
+ reg32 |= (1 << 8);
+ MCHBAR32(C0DRC1) = reg32;
+ }
+ if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED ||
+ sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED) {
+ reg32 = MCHBAR32(C1DRC1);
+ reg32 |= (1 << 8);
+ MCHBAR32(C1DRC1) = reg32;
+ }
+}
+
+struct dimm_size {
+ unsigned long side1;
+ unsigned long side2;
+};
+
+static struct dimm_size sdram_get_dimm_size(struct sys_info *sysinfo, u16 dimmno)
+{
+ /* Calculate the log base 2 size of a DIMM in bits */
+ struct dimm_size sz;
+ int value, low, rows, columns, device;
+
+ device = get_dimm_spd_address(sysinfo, dimmno);
+ sz.side1 = 0;
+ sz.side2 = 0;
+
+ rows = spd_read_byte(device, SPD_NUM_ROWS); /* rows */
+ if (rows < 0) goto hw_err;
+ if ((rows & 0xf) == 0) goto val_err;
+ sz.side1 += rows & 0xf;
+
+ columns = spd_read_byte(device, SPD_NUM_COLUMNS); /* columns */
+ if (columns < 0) goto hw_err;
+ if ((columns & 0xf) == 0) goto val_err;
+ sz.side1 += columns & 0xf;
+
+ value = spd_read_byte(device, SPD_NUM_BANKS_PER_SDRAM); /* banks */
+ if (value < 0) goto hw_err;
+ if ((value & 0xff) == 0) goto val_err;
+ sz.side1 += log2(value & 0xff);
+
+ /* Get the module data width and convert it to a power of two */
+ value = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_MSB); /* (high byte) */
+ if (value < 0) goto hw_err;
+ value &= 0xff;
+ value <<= 8;
+
+ low = spd_read_byte(device, SPD_MODULE_DATA_WIDTH_LSB); /* (low byte) */
+ if (low < 0) goto hw_err;
+ value = value | (low & 0xff);
+ if ((value != 72) && (value != 64)) goto val_err;
+ sz.side1 += log2(value);
+
+ /* side 2 */
+ value = spd_read_byte(device, SPD_NUM_DIMM_BANKS); /* number of physical banks */
+
+ if (value < 0) goto hw_err;
+ value &= 7;
+ value++;
+ if (value == 1) goto out;
+ if (value != 2) goto val_err;
+
+ /* Start with the symmetrical case */
+ sz.side2 = sz.side1;
+
+ if ((rows & 0xf0) == 0) goto out; /* If symmetrical we are done */
+
+ /* Don't die here, I have not come across any of these to test what
+ * actually happens.
+ */
+ printk(BIOS_ERR, "Assymetric DIMMs are not supported by this chipset\n");
+
+ sz.side2 -= (rows & 0x0f); /* Subtract out rows on side 1 */
+ sz.side2 += ((rows >> 4) & 0x0f); /* Add in rows on side 2 */
+
+ sz.side2 -= (columns & 0x0f); /* Subtract out columns on side 1 */
+ sz.side2 += ((columns >> 4) & 0x0f); /* Add in columns on side 2 */
+
+ goto out;
+
+ val_err:
+ die("Bad SPD value\n");
+ hw_err:
+ /* If a hardware error occurs the spd rom probably does not exist.
+ * In this case report that there is no memory
+ */
+ sz.side1 = 0;
+ sz.side2 = 0;
+out:
+ return sz;
+}
+
+static void sdram_detect_dimm_size(struct sys_info * sysinfo)
+{
+ int i;
+
+ for(i = 0; i < 2 * DIMM_SOCKETS; i++) {
+ struct dimm_size sz;
+
+ sysinfo->banksize[i * 2] = 0;
+ sysinfo->banksize[(i * 2) + 1] = 0;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ sz = sdram_get_dimm_size(sysinfo, i);
+
+ sysinfo->banks[i] = spd_read_byte(get_dimm_spd_address(sysinfo, i),
+ SPD_NUM_BANKS_PER_SDRAM); /* banks */
+
+ if (sz.side1 < 30)
+ die("DDR-II rank size smaller than 128MB is not supported.\n");
+
+ sysinfo->banksize[i * 2] = 1 << (sz.side1 - 28);
+
+ printk(BIOS_DEBUG, "DIMM %d side 0 = %d MB\n", i, sysinfo->banksize[i * 2] * 32 );
+
+ if (!sz.side2)
+ continue;
+
+ /* If there is a second side, it has to have at least 128M, too */
+ if (sz.side2 < 30)
+ die("DDR-II rank size smaller than 128MB is not supported.\n");
+
+ sysinfo->banksize[(i * 2) + 1] = 1 << (sz.side2 - 28);
+
+ printk(BIOS_DEBUG, "DIMM %d side 1 = %d MB\n", i, sysinfo->banksize[(i * 2) + 1] * 32);
+ }
+}
+
+static int sdram_program_row_boundaries(struct sys_info *sysinfo)
+{
+ int i;
+ int cum0, cum1, tolud, tom;
+
+ printk(BIOS_DEBUG, "Setting RAM size... \n");
+
+ cum0 = 0;
+ for(i = 0; i < 2 * DIMM_SOCKETS; i++) {
+ cum0 += sysinfo->banksize[i];
+ MCHBAR8(C0DRB01+i) = cum0;
+ }
+
+ /* Assume we continue in Channel 1 where we stopped in Channel 0 */
+ cum1 = cum0;
+
+ /* Exception: Interleaved starts from the beginning */
+ if (sysinfo->interleaved)
+ cum1 = 0;
+
+#if 0
+ /* Exception: Channel 1 is not populated. C1DRB stays zero */
+ if (sysinfo->dimm[2] == SYSINFO_DIMM_NOT_POPULATED &&
+ sysinfo->dimm[3] == SYSINFO_DIMM_NOT_POPULATED)
+ cum1 = 0;
+#endif
+
+ for(i = 0; i < 2 * DIMM_SOCKETS; i++) {
+ cum1 += sysinfo->banksize[i + 4];
+ MCHBAR8(C1DRB01+i) = cum1;
+ }
+
+ /* Set TOLUD Top Of Low Usable DRAM */
+ if (sysinfo->interleaved)
+ tolud = (cum0 + cum1) << 1;
+ else
+ tolud = (cum1 ? cum1 : cum0) << 1;
+
+ /* The TOM register has a different format */
+ tom = tolud >> 3;
+
+ /* Limit the value of TOLUD to leave some space for PCI memory. */
+ if (tolud > 0xd0)
+ tolud = 0xd0; /* 3.25GB : 0.75GB */
+
+ pci_write_config8(PCI_DEV(0,0,0), TOLUD, tolud);
+
+ printk(BIOS_DEBUG, "C0DRB = 0x%08x\n", MCHBAR32(C0DRB01));
+ printk(BIOS_DEBUG, "C1DRB = 0x%08x\n", MCHBAR32(C1DRB01));
+ printk(BIOS_DEBUG, "TOLUD = 0x%04x\n", pci_read_config8(PCI_DEV(0,0,0), TOLUD));
+
+ pci_write_config16(PCI_DEV(0,0,0), TOM, tom);
+
+ return 0;
+}
+
+static int sdram_set_row_attributes(struct sys_info *sysinfo)
+{
+ int i, value;
+ u16 dra0=0, dra1=0, dra = 0;
+
+ printk(BIOS_DEBUG, "Setting row attributes... \n");
+ for(i=0; i < 2 * DIMM_SOCKETS; i++) {
+ u16 device;
+ u8 columnsrows;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED) {
+ continue;
+ }
+
+ device = get_dimm_spd_address(sysinfo, i);
+
+ value = spd_read_byte(device, SPD_NUM_ROWS); /* rows */
+ columnsrows = (value & 0x0f);
+
+ value = spd_read_byte(device, SPD_NUM_COLUMNS); /* columns */
+ columnsrows |= (value & 0xf) << 4;
+
+ switch (columnsrows) {
+ case 0x9d: dra = 2; break;
+ case 0xad: dra = 3; break;
+ case 0xbd: dra = 4; break;
+ case 0xae: dra = 3; break;
+ case 0xbe: dra = 4; break;
+ default: die("Unsupported Rows/Columns. (DRA)");
+ }
+
+ /* Double Sided DIMMs? */
+ if (sysinfo->banksize[(2 * i) + 1] != 0) {
+ dra = (dra << 4) | dra;
+ }
+
+ if (i < DIMM_SOCKETS)
+ dra0 |= (dra << (i*8));
+ else
+ dra1 |= (dra << ((i - DIMM_SOCKETS)*8));
+ }
+
+ MCHBAR16(C0DRA) = dra0;
+ MCHBAR16(C1DRA) = dra1;
+
+ printk(BIOS_DEBUG, "C0DRA = 0x%04x\n", dra0);
+ printk(BIOS_DEBUG, "C1DRA = 0x%04x\n", dra1);
+
+ return 0;
+}
+
+static void sdram_set_bank_architecture(struct sys_info *sysinfo)
+{
+/*
+ u32 off32;
+ int i;
+
+ MCHBAR16(C1BNKARC) &= 0xff00;
+ MCHBAR16(C0BNKARC) &= 0xff00;
+
+ off32 = C0BNKARC;
+ for (i=0; i < 2 * DIMM_SOCKETS; i++) {
+ if (i == DIMM_SOCKETS)
+ off32 = C1BNKARC;
+
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_NOT_POPULATED)
+ continue;
+
+ if (sysinfo->banks[i] != 8)
+ continue;
+
+ printk(BIOS_SPEW, "DIMM%d has 8 banks.\n", i);
+
+ if (i & 1)
+ MCHBAR16(off32) |= 0x50;
+ else
+ MCHBAR16(off32) |= 0x05;
+ }
+*/
+}
+
+#define REFRESH_7_8US 1
+#define REFRESH_15_6US 0
+static void sdram_program_refresh_rate(struct sys_info *sysinfo)
+{
+ u32 reg32;
+
+ if (sysinfo->refresh == REFRESH_7_8US) {
+ reg32 = (2 << 8); /* Refresh enabled at 7.8us */
+ } else {
+ reg32 = (1 << 8); /* Refresh enabled at 15.6us */
+ }
+
+ MCHBAR32(C0DRC0) &= ~(7 << 8);
+ MCHBAR32(C0DRC0) |= reg32;
+
+ MCHBAR32(C1DRC0) &= ~(7 << 8);
+ MCHBAR32(C1DRC0) |= reg32;
+}
+
+static void sdram_program_cke_tristate(struct sys_info *sysinfo)
+{
+ u32 reg32;
+ int i;
+
+ reg32 = MCHBAR32(C0DRC1);
+
+ for (i=0; i < 4; i++) {
+ if (sysinfo->banksize[i] == 0) {
+ reg32 |= (1 << (16 + i));
+ }
+ }
+
+ reg32 |= (1 << 12);
+
+ reg32 |= (1 << 11);
+ MCHBAR32(C0DRC1) = reg32;
+
+ /* Do we have to do this if we're in Single Channel Mode? */
+ reg32 = MCHBAR32(C1DRC1);
+
+ for (i=4; i < 8; i++) {
+ if (sysinfo->banksize[i] == 0) {
+ reg32 |= (1 << (12 + i));
+ }
+ }
+
+ reg32 |= (1 << 12);
+
+ reg32 |= (1 << 11);
+ MCHBAR32(C1DRC1) = reg32;
+}
+
+static void sdram_program_odt_tristate(struct sys_info *sysinfo)
+{
+ u32 reg32;
+ int i;
+
+ reg32 = MCHBAR32(C0DRC2);
+
+ for (i=0; i < 4; i++) {
+ if (sysinfo->banksize[i] == 0) {
+ reg32 |= (1 << (24 + i));
+ }
+ }
+ MCHBAR32(C0DRC2) = reg32;
+
+ reg32 = MCHBAR32(C1DRC2);
+
+ for (i=4; i < 8; i++) {
+ if (sysinfo->banksize[i] == 0) {
+ reg32 |= (1 << (20 + i));
+ }
+ }
+ MCHBAR32(C1DRC2) = reg32;
+}
+
+static void sdram_set_timing_and_control(struct sys_info *sysinfo)
+{
+ u32 reg32, off32;
+ u32 tWTR;
+ u32 temp_drt;
+ int i, page_size;
+
+ static const u8 const drt0_table[] = {
+ /* CL 3, 4, 5 */
+ 3, 4, 5, /* FSB533/400, DDR533/400 */
+ 4, 5, 6, /* FSB667, DDR533/400 */
+ 4, 5, 6, /* FSB667, DDR667 */
+ };
+
+ static const u8 const cas_table[] = {
+ 2, 1, 0, 3
+ };
+
+ reg32 = MCHBAR32(C0DRC0);
+ reg32 |= (1 << 2); /* Burst Length 8 */
+ reg32 &= ~( (1 << 13) | (1 << 12) );
+ MCHBAR32(C0DRC0) = reg32;
+
+ reg32 = MCHBAR32(C1DRC0);
+ reg32 |= (1 << 2); /* Burst Length 8 */
+ reg32 &= ~( (1 << 13) | (1 << 12) );
+ MCHBAR32(C1DRC0) = reg32;
+
+ if (!sysinfo->dual_channel && sysinfo->dimm[1] !=
+ SYSINFO_DIMM_NOT_POPULATED) {
+ reg32 = MCHBAR32(C0DRC0);
+ reg32 |= (1 << 15);
+ MCHBAR32(C0DRC0) = reg32;
+ }
+
+ sdram_program_refresh_rate(sysinfo);
+
+ sdram_program_cke_tristate(sysinfo);
+
+ sdram_program_odt_tristate(sysinfo);
+
+ /* Calculate DRT0 */
+
+ temp_drt = 0;
+
+ /* B2B Write Precharge (same bank) = CL-1 + BL/2 + tWR */
+ reg32 = (sysinfo->cas - 1) + (BURSTLENGTH / 2) + sysinfo->twr;
+ temp_drt |= (reg32 << 28);
+
+ /* Write Auto Precharge (same bank) = CL-1 + BL/2 + tWR + tRP */
+ reg32 += sysinfo->trp;
+ temp_drt |= (reg32 << 4);
+
+ if (sysinfo->memory_frequency == 667) {
+ tWTR = 3; /* 667MHz */
+ } else {
+ tWTR = 2; /* 400 and 533 */
+ }
+
+ /* B2B Write to Read Command Spacing */
+ reg32 = (sysinfo->cas - 1) + (BURSTLENGTH / 2) + tWTR;
+ temp_drt |= (reg32 << 24);
+
+ /* CxDRT0 [23:22], [21:20], [19:18] [16] have fixed values */
+ temp_drt |= ( (1 << 22) | (3 << 20) | (1 << 18) | (0 << 16) );
+
+ /* Program Write Auto Precharge to Activate */
+ off32 = 0;
+ if (sysinfo->fsb_frequency == 667) { /* 667MHz FSB */
+ off32 += 3;
+ }
+ if (sysinfo->memory_frequency == 667) {
+ off32 += 3;
+ }
+ off32 += sysinfo->cas - 3;
+ reg32 = drt0_table[off32];
+ temp_drt |= (reg32 << 11);
+
+ /* Read Auto Precharge to Activate */
+
+ temp_drt |= (8 << 0);
+
+ MCHBAR32(C0DRT0) = temp_drt;
+ MCHBAR32(C1DRT0) = temp_drt;
+
+ /* Calculate DRT1 */
+
+ temp_drt = MCHBAR32(C0DRT1) & 0x00020088;
+
+ /* DRAM RASB Precharge */
+ temp_drt |= (sysinfo->trp - 2) << 0;
+
+ /* DRAM RASB to CASB Delay */
+ temp_drt |= (sysinfo->trcd - 2) << 4;
+
+ /* CASB Latency */
+ temp_drt |= (cas_table[sysinfo->cas - 3]) << 8;
+
+ /* Refresh Cycle Time */
+ temp_drt |= (sysinfo->trfc) << 10;
+
+ /* Pre-All to Activate Delay */
+ temp_drt |= (0 << 16);
+
+ /* Precharge to Precharge Delay stays at 1 clock */
+ temp_drt |= (0 << 18);
+
+ /* Activate to Precharge Delay */
+ temp_drt |= (sysinfo->tras << 19);
+
+ /* Read to Precharge (tRTP) */
+ if (sysinfo->memory_frequency == 667) {
+ temp_drt |= (1 << 28);
+ } else {
+ temp_drt |= (0 << 28);
+ }
+
+ /* Determine page size */
+ reg32 = 0;
+ page_size = 1; /* Default: 1k pagesize */
+ for (i=0; i< 2*DIMM_SOCKETS; i++) {
+ if (sysinfo->dimm[i] == SYSINFO_DIMM_X16DS ||
+ sysinfo->dimm[i] == SYSINFO_DIMM_X16SS)
+ page_size = 2; /* 2k pagesize */
+ }
+
+ if (sysinfo->memory_frequency == 533 && page_size == 2) {
+ reg32 = 1;
+ }
+ if (sysinfo->memory_frequency == 667) {
+ reg32 = page_size;
+ }
+
+ temp_drt |= (reg32 << 30);
+
+ MCHBAR32(C0DRT1) = temp_drt;
+ MCHBAR32(C1DRT1) = temp_drt;
+
+ /* Program DRT2 */
+ reg32 = MCHBAR32(C0DRT2);
+ reg32 &= ~(1 << 8);
+ MCHBAR32(C0DRT2) = reg32;
+
+ reg32 = MCHBAR32(C1DRT2);
+ reg32 &= ~(1 << 8);
+ MCHBAR32(C1DRT2) = reg32;
+
+ /* Calculate DRT3 */
+ temp_drt = MCHBAR32(C0DRT3) & ~0x07ffffff;
+
+ /* Get old tRFC value */
+ reg32 = MCHBAR32(C0DRT1) >> 10;
+ reg32 &= 0x3f;
+
+ /* 788nS - tRFC */
+ switch (sysinfo->memory_frequency) {
+ case 400: /* 5nS */
+ reg32 = ((78800 / 500) - reg32) & 0x1ff;
+ reg32 |= (0x8c << 16) | (0x0c << 10); /* 1 us */
+ break;
+ case 533: /* 3.75nS */
+ reg32 = ((78800 / 375) - reg32) & 0x1ff;
+ reg32 |= (0xba << 16) | (0x10 << 10); /* 1 us */
+ break;
+ case 667: /* 3nS */
+ reg32 = ((78800 / 300) - reg32) & 0x1ff;
+ reg32 |= (0xe9 << 16) | (0x14 << 10); /* 1 us */
+ break;
+ }
+
+ temp_drt |= reg32;
+
+ MCHBAR32(C0DRT3) = temp_drt;
+ MCHBAR32(C1DRT3) = temp_drt;
+}
+
+static void sdram_set_channel_mode(struct sys_info *sysinfo)
+{
+ u32 reg32;
+
+ printk(BIOS_DEBUG, "Setting mode of operation for memory channels...");
+
+ if (sdram_capabilities_interleave() &&
+ ( ( sysinfo->banksize[0] + sysinfo->banksize[1] +
+ sysinfo->banksize[2] + sysinfo->banksize[3] ) ==
+ ( sysinfo->banksize[4] + sysinfo->banksize[5] +
+ sysinfo->banksize[6] + sysinfo->banksize[7] ) ) ) {
+ /* Both channels equipped with DIMMs of the same size */
+ sysinfo->interleaved = 1;
+ } else {
+ sysinfo->interleaved = 0;
+ }
+
+ reg32 = MCHBAR32(DCC);
+ reg32 &= ~(7 << 0);
+
+ if(sysinfo->interleaved) {
+ /* Dual Channel Interleaved */
+ printk(BIOS_DEBUG, "Dual Channel Interleaved.\n");
+ reg32 |= (1 << 1);
+ } else if (sysinfo->dimm[0] == SYSINFO_DIMM_NOT_POPULATED &&
+ sysinfo->dimm[1] == SYSINFO_DIMM_NOT_POPULATED) {
+ /* Channel 1 only */
+ printk(BIOS_DEBUG, "Single Channel 1 only.\n");
+ reg32 |= (1 << 2);
+ } else if (sdram_capabilities_dual_channel() && sysinfo->dimm[2] !=
+ SYSINFO_DIMM_NOT_POPULATED) {
+ /* Dual Channel Assymetric */
+ printk(BIOS_DEBUG, "Dual Channel Assymetric.\n");
+ reg32 |= (1 << 0);
+ } else {
+ /* All bits 0 means Single Channel 0 operation */
+ printk(BIOS_DEBUG, "Single Channel 0 only.\n");
+ }
+
+ /* Now disable channel XORing */
+ reg32 |= (1 << 10);
+
+ MCHBAR32(DCC) = reg32;
+
+ PRINTK_DEBUG("DCC=0x%08x\n", MCHBAR32(DCC));
+}
+
+static void sdram_program_pll_settings(struct sys_info *sysinfo)
+{
+// MCHBAR32(PLLMON) = 0x80800000;
+
+ sysinfo->fsb_frequency = fsbclk();
+ if (sysinfo->fsb_frequency == 0xffff)
+ die("Unsupported FSB speed");
+
+ /* Program CPCTL according to FSB speed */
+ /* Only write the lower byte */
+ switch (sysinfo->fsb_frequency) {
+ case 400: MCHBAR8(CPCTL) = 0x90; break; /* FSB400 */
+ case 533: MCHBAR8(CPCTL) = 0x95; break; /* FSB533 */
+ case 667: MCHBAR8(CPCTL) = 0x8d; break; /* FSB667 */
+ }
+
+ MCHBAR16(CPCTL) &= ~(1 << 11);
+
+ MCHBAR16(CPCTL); /* Read back register to activate settings */
+}
+
+static void sdram_program_graphics_frequency(struct sys_info *sysinfo)
+{
+ u8 reg8;
+ u16 reg16;
+ u8 freq, second_vco, voltage;
+
+#define CRCLK_166MHz 0x00
+#define CRCLK_200MHz 0x01
+#define CRCLK_250MHz 0x03
+#define CRCLK_400MHz 0x05
+
+#define CDCLK_200MHz 0x00
+#define CDCLK_320MHz 0x40
+
+#define VOLTAGE_1_05 0x00
+#define VOLTAGE_1_50 0x01
+
+ printk(BIOS_DEBUG, "Setting Graphics Frequency... \n");
+
+ printk(BIOS_DEBUG, "FSB: %d MHz ", sysinfo->fsb_frequency);
+
+ voltage = VOLTAGE_1_05;
+ if (MCHBAR32(DFT_STRAP1) & (1 << 20))
+ voltage = VOLTAGE_1_50;
+ printk(BIOS_DEBUG, "Voltage: %s ", (voltage==VOLTAGE_1_05)?"1.05V":"1.5V");
+
+ /* Gate graphics hardware for frequency change */
+ reg8 = pci_read_config16(PCI_DEV(0,2,0), GCFC + 1);
+ reg8 = (1<<3) | (1<<1); /* disable crclk, gate cdclk */
+ pci_write_config8(PCI_DEV(0,2,0), GCFC + 1, reg8);
+
+ /* Get graphics frequency capabilities */
+ reg8 = sdram_capabilities_core_frequencies();
+
+ freq = CRCLK_250MHz;
+ switch (reg8) {
+ case GFX_FREQUENCY_CAP_ALL:
+ if (voltage == VOLTAGE_1_05)
+ freq = CRCLK_250MHz;
+ else
+ freq = CRCLK_400MHz; /* 1.5V requires 400MHz */
+ break;
+ case GFX_FREQUENCY_CAP_250MHZ: freq = CRCLK_250MHz; break;
+ case GFX_FREQUENCY_CAP_200MHZ: freq = CRCLK_200MHz; break;
+ case GFX_FREQUENCY_CAP_166MHZ: freq = CRCLK_166MHz; break;
+ }
+
+ if (freq != CRCLK_400MHz) {
+ /* What chipset are we? Force 166MHz for GMS */
+ reg8 = (pci_read_config8(PCI_DEV(0, 0x00,0), 0xe7) & 0x70) >> 4;
+ if (reg8==2)
+ freq = CRCLK_166MHz;
+ }
+
+ printk(BIOS_DEBUG, "Render: ");
+ switch (freq) {
+ case CRCLK_166MHz: printk(BIOS_DEBUG, "166Mhz"); break;
+ case CRCLK_200MHz: printk(BIOS_DEBUG, "200Mhz"); break;
+ case CRCLK_250MHz: printk(BIOS_DEBUG, "250Mhz"); break;
+ case CRCLK_400MHz: printk(BIOS_DEBUG, "400Mhz"); break;
+ }
+
+ if (i965_silicon_revision() == 0) {
+ sysinfo->mvco4x = 1;
+ } else {
+ sysinfo->mvco4x = 0;
+ }
+
+ second_vco = 0;
+
+ if (voltage == VOLTAGE_1_50) {
+ second_vco = 1;
+ } else if ((i965_silicon_revision() > 0) && (freq == CRCLK_250MHz)) {
+ u16 mem = sysinfo->memory_frequency;
+ u16 fsb = sysinfo->fsb_frequency;
+
+ if ( (fsb == 667 && mem == 533) ||
+ (fsb == 533 && mem == 533) ||
+ (fsb == 533 && mem == 400)) {
+ second_vco = 1;
+ }
+
+ if (fsb == 667 && mem == 533)
+ sysinfo->mvco4x = 1;
+ }
+
+ if (second_vco) {
+ sysinfo->clkcfg_bit7=1;
+ } else {
+ sysinfo->clkcfg_bit7=0;
+ }
+
+ /* Graphics Core Render Clock */
+ reg16 = pci_read_config16(PCI_DEV(0,2,0), GCFC);
+ reg16 &= ~( (7 << 0) | (1 << 13) );
+ reg16 |= freq;
+ pci_write_config16(PCI_DEV(0,2,0), GCFC, reg16);
+
+ /* Graphics Core Display Clock */
+ reg8 = pci_read_config8(PCI_DEV(0,2,0), GCFC);
+ reg8 &= ~( (1<<7) | (7<<4) );
+
+ if (voltage == VOLTAGE_1_05) {
+ reg8 |= CDCLK_200MHz;
+ printk(BIOS_DEBUG, " Display: 200MHz\n");
+ } else {
+ reg8 |= CDCLK_320MHz;
+ printk(BIOS_DEBUG, " Display: 320MHz\n");
+ }
+ pci_write_config8(PCI_DEV(0,2,0), GCFC, reg8);
+
+ reg8 = pci_read_config8(PCI_DEV(0,2,0), GCFC + 1);
+
+ reg8 |= (1<<3) | (1<<1);
+ pci_write_config8(PCI_DEV(0,2,0), GCFC + 1, reg8);
+
+ reg8 |= 0x0f;
+ pci_write_config8(PCI_DEV(0,2,0), GCFC + 1, reg8);
+
+ /* Ungate core render and display clocks */
+ reg8 &= 0xf0;
+ pci_write_config8(PCI_DEV(0,2,0), GCFC + 1, reg8);
+}
+
+static void sdram_program_memory_frequency(struct sys_info *sysinfo)
+{
+ u32 clkcfg;
+ u8 reg8;
+
+ printk(BIOS_DEBUG, "Setting Memory Frequency... ");
+
+ clkcfg = MCHBAR32(CLKCFG);
+
+ printk(BIOS_DEBUG, "CLKCFG=0x%08x, ", clkcfg);
+
+ clkcfg &= ~( (1 << 12) | (1 << 7) | ( 7 << 4) );
+
+ if (sysinfo->mvco4x) {
+ printk(BIOS_DEBUG, "MVCO 4x, ");
+ clkcfg &= ~(1 << 12);
+ }
+
+ if (sysinfo->clkcfg_bit7) {
+ printk(BIOS_DEBUG, "second VCO, ");
+
+ clkcfg |= (1 << 7);
+ }
+
+ switch (sysinfo->memory_frequency) {
+ case 400: clkcfg |= (2 << 4); break;
+ case 533: clkcfg |= (3 << 4); break;
+ case 667: clkcfg |= (4 << 4); break;
+ default: die("Target Memory Frequency Error");
+ }
+
+ if (MCHBAR32(CLKCFG) == clkcfg) {
+ printk(BIOS_DEBUG, "ok (unchanged)\n");
+ return;
+ }
+
+ MCHBAR32(CLKCFG) = clkcfg;
+
+ /* Make sure the following code is in the
+ * cache before we execute it.
+ */
+ goto cache_code;
+vco_update:
+ reg8 = pci_read_config8(PCI_DEV(0,0x1f,0), 0xa2);
+ reg8 &= ~(1 << 7);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, reg8);
+
+ clkcfg &= ~(1 << 10);
+ MCHBAR32(CLKCFG) = clkcfg;
+ clkcfg |= (1 << 10);
+ MCHBAR32(CLKCFG) = clkcfg;
+
+ asm volatile (
+ " movl $0x100, %%ecx\n"
+ "delay_update:\n"
+ " nop\n"
+ " nop\n"
+ " nop\n"
+ " nop\n"
+ " loop delay_update\n"
+ : /* No outputs */
+ : /* No inputs */
+ : "%ecx", "memory"
+ );
+
+ clkcfg &= ~(1 << 10);
+ MCHBAR32(CLKCFG) = clkcfg;
+
+ goto out;
+cache_code:
+ goto vco_update;
+out:
+
+ printk(BIOS_DEBUG, "CLKCFG=0x%08x, ", MCHBAR32(CLKCFG));
+ printk(BIOS_DEBUG, "ok\n");
+}
+
+static void sdram_program_clock_crossing(void)
+{
+ int idx = 0;
+
+ /**
+ * We add the indices according to our clocks from CLKCFG.
+ */
+#if CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GM
+ static const u32 data_clock_crossing[] = {
+ 0x00100401, 0x00000000, /* DDR400 FSB400 */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0x08040120, 0x00000000, /* DDR400 FSB533 */
+ 0x00100401, 0x00000000, /* DDR533 FSB533 */
+ 0x00010402, 0x00000000, /* DDR667 FSB533 - fake values */
+
+ 0x04020120, 0x00000010, /* DDR400 FSB667 */
+ 0x10040280, 0x00000040, /* DDR533 FSB667 */
+ 0x00100401, 0x00000000, /* DDR667 FSB667 */
+
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ };
+
+ static const u32 command_clock_crossing[] = {
+ 0x04020208, 0x00000000, /* DDR400 FSB400 */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0x00060108, 0x00000000, /* DDR400 FSB533 */
+ 0x04020108, 0x00000000, /* DDR533 FSB533 */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0x00040318, 0x00000000, /* DDR400 FSB667 */
+ 0x04020118, 0x00000000, /* DDR533 FSB667 */
+ 0x02010804, 0x00000000, /* DDR667 FSB667 */
+
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ };
+
+#elif CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GC
+ /* i965 G/P */
+ static const u32 data_clock_crossing[] = {
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0x10080201, 0x00000000, /* DDR400 FSB533 */
+ 0x00100401, 0x00000000, /* DDR533 FSB533 */
+ 0x00010402, 0x00000000, /* DDR667 FSB533 - fake values */
+
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0x04020108, 0x00000000, /* DDR400 FSB800 */
+ 0x00020108, 0x00000000, /* DDR533 FSB800 */
+ 0x00080201, 0x00000000, /* DDR667 FSB800 */
+
+ 0x00010402, 0x00000000, /* DDR400 FSB1066 */
+ 0x04020108, 0x00000000, /* DDR533 FSB1066 */
+ 0x08040110, 0x00000000, /* DDR667 FSB1066 */
+ };
+
+ static const u32 command_clock_crossing[] = {
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0x00010800, 0x00000402, /* DDR400 FSB533 */
+ 0x01000400, 0x00000200, /* DDR533 FSB533 */
+ 0x00020904, 0x00000000, /* DDR667 FSB533 - fake values */
+
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+ 0xffffffff, 0xffffffff, /* nonexistant */
+
+ 0x02010804, 0x00000000, /* DDR400 FSB800 */
+ 0x00010402, 0x00000000, /* DDR533 FSB800 */
+ 0x04020180, 0x00000008, /* DDR667 FSB800 */
+
+ 0x00020904, 0x00000000, /* DDR400 FSB1066 */
+ 0x02010804, 0x00000000, /* DDR533 FSB1066 */
+ 0x180601c0, 0x00000020, /* DDR667 FSB1066 */
+ };
+#endif
+
+ printk(BIOS_DEBUG, "Programming Clock Crossing...");
+
+ printk(BIOS_DEBUG, "MEM=");
+ switch (memclk()) {
+ case 400: printk(BIOS_DEBUG, "400"); idx += 0; break;
+ case 533: printk(BIOS_DEBUG, "533"); idx += 2; break;
+ case 667: printk(BIOS_DEBUG, "667"); idx += 4; break;
+ default: printk(BIOS_DEBUG, "RSVD %x", memclk()); return;
+ }
+
+ printk(BIOS_DEBUG, " FSB=");
+ switch (fsbclk()) {
+ case 400: printk(BIOS_DEBUG, "400"); idx += 0; break;
+ case 533: printk(BIOS_DEBUG, "533"); idx += 6; break;
+ case 667: printk(BIOS_DEBUG, "667"); idx += 12; break;
+ case 800: printk(BIOS_DEBUG, "800"); idx += 18; break;
+ case 1066: printk(BIOS_DEBUG, "1066"); idx += 24; break;
+ default: printk(BIOS_DEBUG, "RSVD %x\n", fsbclk()); return;
+ }
+
+ if (command_clock_crossing[idx]==0xffffffff) {
+ printk(BIOS_DEBUG, "Invalid MEM/FSB combination!\n");
+ }
+
+ MCHBAR32(CCCFT + 0) = command_clock_crossing[idx];
+ MCHBAR32(CCCFT + 4) = command_clock_crossing[idx + 1];
+
+ MCHBAR32(C0DCCFT + 0) = data_clock_crossing[idx];
+ MCHBAR32(C0DCCFT + 4) = data_clock_crossing[idx + 1];
+ MCHBAR32(C1DCCFT + 0) = data_clock_crossing[idx];
+ MCHBAR32(C1DCCFT + 4) = data_clock_crossing[idx + 1];
+
+ printk(BIOS_DEBUG, "... ok\n");
+}
+
+static void sdram_disable_fast_dispatch(void)
+{
+ u32 reg32;
+
+ reg32 = MCHBAR32(FSBPMC3);
+ reg32 |= (1 << 1);
+ MCHBAR32(FSBPMC3) = reg32;
+
+ reg32 = MCHBAR32(SBTEST);
+ reg32 |= (3 << 1);
+ MCHBAR32(SBTEST) = reg32;
+}
+
+static void sdram_pre_jedec_initialization(void)
+{
+ u32 reg32;
+
+ reg32 = MCHBAR32(WCC);
+ reg32 &= 0x113ff3ff;
+ reg32 |= (4 << 29) | (3 << 25) | (1 << 10);
+ MCHBAR32(WCC) = reg32;
+
+ MCHBAR32(SMVREFC) |= (1 << 6);
+
+ MCHBAR32(MMARB0) &= ~(3 << 17);
+ MCHBAR32(MMARB0) |= (1 << 21) | (1 << 16);
+
+ MCHBAR32(MMARB1) &= ~(7 << 8);
+ MCHBAR32(MMARB1) |= (3 << 8);
+
+ /* Adaptive Idle Timer Control */
+ MCHBAR32(C0AIT) = 0x000006c4;
+ MCHBAR32(C0AIT+4) = 0x871a066d;
+
+ MCHBAR32(C1AIT) = 0x000006c4;
+ MCHBAR32(C1AIT+4) = 0x871a066d;
+}
+
+#define EA_DUALCHANNEL_XOR_BANK_RANK_MODE (0xd4 << 24)
+#define EA_DUALCHANNEL_XOR_BANK_MODE (0xf4 << 24)
+#define EA_DUALCHANNEL_BANK_RANK_MODE (0xc2 << 24)
+#define EA_DUALCHANNEL_BANK_MODE (0xe2 << 24)
+#define EA_SINGLECHANNEL_XOR_BANK_RANK_MODE (0x91 << 24)
+#define EA_SINGLECHANNEL_XOR_BANK_MODE (0xb1 << 24)
+#define EA_SINGLECHANNEL_BANK_RANK_MODE (0x80 << 24)
+#define EA_SINGLECHANNEL_BANK_MODE (0xa0 << 24)
+
+static void sdram_enhanced_addressing_mode(struct sys_info *sysinfo)
+{
+ u32 chan0 = 0, chan1 = 0;
+ int chan0_dualsided, chan1_dualsided, chan0_populated, chan1_populated;
+
+ chan0_populated = (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED ||
+ sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED);
+ chan1_populated = (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED ||
+ sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED);
+ chan0_dualsided = (sysinfo->banksize[1] || sysinfo->banksize[3]);
+ chan1_dualsided = (sysinfo->banksize[5] || sysinfo->banksize[7]);
+
+ if (sdram_capabilities_enhanced_addressing_xor()) {
+ if (!sysinfo->interleaved) {
+ /* Single Channel & Dual Channel Assymetric */
+ if (chan0_populated) {
+ if (chan0_dualsided) {
+ chan0 = EA_SINGLECHANNEL_XOR_BANK_RANK_MODE;
+ } else {
+ chan0 = EA_SINGLECHANNEL_XOR_BANK_MODE;
+ }
+ }
+ if (chan1_populated) {
+ if (chan1_dualsided) {
+ chan1 = EA_SINGLECHANNEL_XOR_BANK_RANK_MODE;
+ } else {
+ chan1 = EA_SINGLECHANNEL_XOR_BANK_MODE;
+ }
+ }
+ } else {
+ /* Interleaved has always both channels populated */
+ if (chan0_dualsided) {
+ chan0 = EA_DUALCHANNEL_XOR_BANK_RANK_MODE;
+ } else {
+ chan0 = EA_DUALCHANNEL_XOR_BANK_MODE;
+ }
+
+ if (chan1_dualsided) {
+ chan1 = EA_DUALCHANNEL_XOR_BANK_RANK_MODE;
+ } else {
+ chan1 = EA_DUALCHANNEL_XOR_BANK_MODE;
+ }
+ }
+ } else {
+ if (!sysinfo->interleaved) {
+ /* Single Channel & Dual Channel Assymetric */
+ if (chan0_populated) {
+ if (chan0_dualsided) {
+ chan0 = EA_SINGLECHANNEL_BANK_RANK_MODE;
+ } else {
+ chan0 = EA_SINGLECHANNEL_BANK_MODE;
+ }
+ }
+ if (chan1_populated) {
+ if (chan1_dualsided) {
+ chan1 = EA_SINGLECHANNEL_BANK_RANK_MODE;
+ } else {
+ chan1 = EA_SINGLECHANNEL_BANK_MODE;
+ }
+ }
+ } else {
+ /* Interleaved has always both channels populated */
+ if (chan0_dualsided) {
+ chan0 = EA_DUALCHANNEL_BANK_RANK_MODE;
+ } else {
+ chan0 = EA_DUALCHANNEL_BANK_MODE;
+ }
+
+ if (chan1_dualsided) {
+ chan1 = EA_DUALCHANNEL_BANK_RANK_MODE;
+ } else {
+ chan1 = EA_DUALCHANNEL_BANK_MODE;
+ }
+ }
+ }
+
+ MCHBAR32(C0DRC1) &= 0x00ffffff;
+ MCHBAR32(C0DRC1) |= chan0;
+ MCHBAR32(C1DRC1) &= 0x00ffffff;
+ MCHBAR32(C1DRC1) |= chan1;
+}
+
+static void sdram_post_jedec_initialization(struct sys_info *sysinfo)
+{
+ u32 reg32;
+
+ /* Enable Channel XORing for Dual Channel Interleave */
+ if (sysinfo->interleaved) {
+
+ reg32 = MCHBAR32(DCC);
+#if CONFIG_CHANNEL_XOR_RANDOMIZATION
+ reg32 &= ~(1 << 10);
+ reg32 |= (1 << 9);
+#else
+ reg32 &= ~(1 << 9);
+#endif
+ MCHBAR32(DCC) = reg32;
+ }
+
+ /* DRAM mode optimizations */
+ sdram_enhanced_addressing_mode(sysinfo);
+
+ reg32 = MCHBAR32(FSBPMC3);
+ reg32 &= ~(1 << 1);
+ MCHBAR32(FSBPMC3) = reg32;
+
+ reg32 = MCHBAR32(SBTEST);
+ reg32 &= ~(1 << 2);
+ MCHBAR32(SBTEST) = reg32;
+
+ reg32 = MCHBAR32(SBOCC);
+ reg32 &= 0xffbdb6ff;
+ reg32 |= (0xbdb6 << 8) | (1 << 0);
+ MCHBAR32(SBOCC) = reg32;
+}
+
+static void sdram_power_management(struct sys_info *sysinfo)
+{
+ u8 reg8;
+ u16 reg16;
+ u32 reg32;
+ int integrated_graphics = 1;
+ int i;
+
+ reg32 = MCHBAR32(C0DRT2);
+ reg32 &= 0xffffff00;
+ /* Idle timer = 8 clocks, CKE idle timer = 16 clocks */
+ reg32 |= (1 << 5) | (1 << 4);
+ MCHBAR32(C0DRT2) = reg32;
+
+ reg32 = MCHBAR32(C1DRT2);
+ reg32 &= 0xffffff00;
+ /* Idle timer = 8 clocks, CKE idle timer = 16 clocks */
+ reg32 |= (1 << 5) | (1 << 4);
+ MCHBAR32(C1DRT2) = reg32;
+
+ reg32 = MCHBAR32(C0DRC1);
+
+ reg32 |= (1 << 12) | (1 << 11);
+ MCHBAR32(C0DRC1) = reg32;
+
+ reg32 = MCHBAR32(C1DRC1);
+
+ reg32 |= (1 << 12) | (1 << 11);
+ MCHBAR32(C1DRC1) = reg32;
+
+ if (i965_silicon_revision()>1) {
+ /* FIXME bits 5 and 0 only if PCIe graphics is disabled */
+ u16 peg_bits = (1 << 5) | (1 << 0);
+
+ MCHBAR16(UPMC1) = 0x1010 | peg_bits;
+ } else {
+ /* FIXME bits 5 and 0 only if PCIe graphics is disabled */
+ u16 peg_bits = (1 << 5) | (1 << 0);
+
+ /* Rev 0 and 1 */
+ MCHBAR16(UPMC1) = 0x0010 | peg_bits;
+ }
+
+ reg16 = MCHBAR16(UPMC2);
+ reg16 &= 0xfc00;
+ reg16 |= 0x0100;
+ MCHBAR16(UPMC2) = reg16;
+
+ MCHBAR32(UPMC3) = 0x000f06ff;
+
+ for (i=0; i<5; i++) {
+ MCHBAR32(UPMC3) &= ~(1 << 16);
+ MCHBAR32(UPMC3) |= (1 << 16);
+ }
+
+ MCHBAR32(GIPMC1) = 0x8000000c;
+
+ reg16 = MCHBAR16(CPCTL);
+ reg16 &= ~(7 << 11);
+ if (i965_silicon_revision()>2) {
+ reg16 |= (6 << 11);
+ } else {
+ reg16 |= (4 << 11);
+ }
+ MCHBAR16(CPCTL) = reg16;
+
+#if 0
+ if ((MCHBAR32(ECO) & (1 << 16)) != 0) {
+#else
+ if (i965_silicon_revision() != 0) {
+#endif
+ switch (sysinfo->fsb_frequency) {
+ case 667: MCHBAR32(HGIPMC2) = 0x0d590d59; break;
+ case 533: MCHBAR32(HGIPMC2) = 0x155b155b; break;
+ }
+ } else {
+ switch (sysinfo->fsb_frequency) {
+ case 667: MCHBAR32(HGIPMC2) = 0x09c409c4; break;
+ case 533: MCHBAR32(HGIPMC2) = 0x0fa00fa0; break;
+ }
+ }
+
+ MCHBAR32(FSBPMC1) = 0x8000000c;
+
+ reg32 = MCHBAR32(C2C3TT);
+ reg32 &= 0xffff0000;
+ switch (sysinfo->fsb_frequency) {
+ case 667: reg32 |= 0x0600; break;
+ case 533: reg32 |= 0x0480; break;
+ }
+ MCHBAR32(C2C3TT) = reg32;
+
+ reg32 = MCHBAR32(C3C4TT);
+ reg32 &= 0xffff0000;
+ switch (sysinfo->fsb_frequency) {
+ case 667: reg32 |= 0x0b80; break;
+ case 533: reg32 |= 0x0980; break;
+ }
+ MCHBAR32(C3C4TT) = reg32;
+
+ if (i965_silicon_revision() == 0) {
+ MCHBAR32(ECO) &= ~(1 << 16);
+ } else {
+ MCHBAR32(ECO) |= (1 << 16);
+ }
+
+#if 0
+
+ if (i965_silicon_revision() == 0) {
+ MCHBAR32(FSBPMC3) &= ~(1 << 29);
+ } else {
+ MCHBAR32(FSBPMC3) |= (1 << 29);
+ }
+#endif
+ MCHBAR32(FSBPMC3) &= ~(1 << 29);
+
+ MCHBAR32(FSBPMC3) |= (1 << 21);
+
+ MCHBAR32(FSBPMC3) &= ~(1 << 19);
+
+ MCHBAR32(FSBPMC3) &= ~(1 << 13);
+
+ reg32 = MCHBAR32(FSBPMC4);
+ reg32 &= ~(3 << 24);
+ reg32 |= ( 2 << 24);
+ MCHBAR32(FSBPMC4) = reg32;
+
+ MCHBAR32(FSBPMC4) |= (1 << 21);
+
+ MCHBAR32(FSBPMC4) |= (1 << 5);
+
+ if ((i965_silicon_revision() < 2) /* || cpuid() = 0x6e8 */ ) {
+ /* stepping 0 and 1 or CPUID 6e8 */
+ MCHBAR32(FSBPMC4) &= ~(1 << 4);
+ } else {
+ MCHBAR32(FSBPMC4) |= (1 << 4);
+ }
+
+ reg8 = pci_read_config8(PCI_DEV(0,0x0,0), 0xfc);
+ reg8 |= (1 << 4);
+ pci_write_config8(PCI_DEV(0, 0x0, 0), 0xfc, reg8);
+
+ reg8 = pci_read_config8(PCI_DEV(0,0x2,0), 0xc1);
+ reg8 |= (1 << 2);
+ pci_write_config8(PCI_DEV(0, 0x2, 0), 0xc1, reg8);
+
+#ifdef C2_SELF_REFRESH_DISABLE
+
+ if (integrated_graphics) {
+ printk(BIOS_DEBUG, "C2 self-refresh with IGD\n");
+ MCHBAR16(MIPMC4) = 0x0468;
+ MCHBAR16(MIPMC5) = 0x046c;
+ MCHBAR16(MIPMC6) = 0x046c;
+ } else {
+ MCHBAR16(MIPMC4) = 0x6468;
+ MCHBAR16(MIPMC5) = 0x646c;
+ MCHBAR16(MIPMC6) = 0x646c;
+ }
+#else
+ if (integrated_graphics) {
+ MCHBAR16(MIPMC4) = 0x04f8;
+ MCHBAR16(MIPMC5) = 0x04fc;
+ MCHBAR16(MIPMC6) = 0x04fc;
+ } else {
+ MCHBAR16(MIPMC4) = 0x64f8;
+ MCHBAR16(MIPMC5) = 0x64fc;
+ MCHBAR16(MIPMC6) = 0x64fc;
+ }
+
+#endif
+
+ reg32 = MCHBAR32(PMCFG);
+ reg32 &= ~(3 << 17);
+ reg32 |= (2 << 17);
+ MCHBAR32(PMCFG) = reg32;
+
+ MCHBAR32(PMCFG) |= (1 << 4);
+
+ reg32 = MCHBAR32(0xc30);
+ reg32 &= 0xffffff00;
+ reg32 |= 0x01;
+ MCHBAR32(0xc30) = reg32;
+
+ MCHBAR32(0xb18) &= ~(1 << 21);
+}
+
+static void sdram_thermal_management(void)
+{
+
+ MCHBAR8(TCO1) = 0x00;
+ MCHBAR8(TCO2) = 0x00;
+
+ /* The Thermal Sensors for DIMMs at 0x50, 0x52 are at I2C addr
+ * 0x30/0x32.
+ */
+
+ /* TODO This is not implemented yet. Volunteers? */
+}
+
+static void sdram_save_receive_enable(void)
+{
+ int i;
+ u32 reg32;
+ u8 values[4];
+
+ /* The following values are stored to an unused CMOS
+ * area and restored instead of recalculated in case
+ * of an S3 resume.
+ *
+ * C0WL0REOST [7:0] -> 8 bit
+ * C1WL0REOST [7:0] -> 8 bit
+ * RCVENMT [11:8] [3:0] -> 8 bit
+ * C0DRT1 [27:24] -> 4 bit
+ * C1DRT1 [27:24] -> 4 bit
+ */
+
+ values[0] = MCHBAR8(C0WL0REOST);
+ values[1] = MCHBAR8(C1WL0REOST);
+
+ reg32 = MCHBAR32(RCVENMT);
+ values[2] = (u8)((reg32 >> (8 - 4)) & 0xf0) | (reg32 & 0x0f);
+
+ reg32 = MCHBAR32(C0DRT1);
+ values[3] = (reg32 >> 24) & 0x0f;
+ reg32 = MCHBAR32(C1DRT1);
+ values[3] |= (reg32 >> (24 - 4)) & 0xf0;
+
+ /* coreboot only uses bytes 0 - 127 for its CMOS values so far
+ * so we grab bytes 128 - 131 to save the receive enable values
+ */
+
+ for (i=0; i<4; i++)
+ cmos_write(values[i], 128 + i);
+}
+
+static void sdram_recover_receive_enable(void)
+{
+ int i;
+ u32 reg32;
+ u8 values[4];
+
+ for (i=0; i<4; i++)
+ values[i] = cmos_read(128 + i);
+
+ MCHBAR8(C0WL0REOST) = values[0];
+ MCHBAR8(C1WL0REOST) = values[1];
+
+ reg32 = MCHBAR32(RCVENMT);
+ reg32 &= ~((0x0f << 8) | (0x0f << 0));
+ reg32 |= ((u32)(values[2] & 0xf0) << (8 - 4)) | (values[2] & 0x0f);
+ MCHBAR32(RCVENMT) = reg32;
+
+ reg32 = MCHBAR32(C0DRT1) & ~(0x0f << 24);
+ reg32 |= (u32)(values[3] & 0x0f) << 24;
+ MCHBAR32(C0DRT1) = reg32;
+
+ reg32 = MCHBAR32(C1DRT1) & ~(0x0f << 24);
+ reg32 |= (u32)(values[3] & 0xf0) << (24 - 4);
+ MCHBAR32(C1DRT1) = reg32;
+}
+
+#include "rcven.c"
+
+static void sdram_program_receive_enable(struct sys_info *sysinfo)
+{
+ MCHBAR32(REPC) |= (1 << 0);
+
+ /* enable upper CMOS */
+ RCBA32(0x3400) = (1 << 2);
+
+ /* Program Receive Enable Timings */
+ if (sysinfo->boot_path == BOOT_PATH_RESUME) {
+ sdram_recover_receive_enable();
+ } else {
+ receive_enable_adjust(sysinfo);
+ sdram_save_receive_enable();
+ }
+
+ MCHBAR32(C0DRC1) |= (1 << 6);
+ MCHBAR32(C1DRC1) |= (1 << 6);
+ MCHBAR32(C0DRC1) &= ~(1 << 6);
+ MCHBAR32(C1DRC1) &= ~(1 << 6);
+
+ MCHBAR32(MIPMC3) |= (0x0f << 0);
+}
+
+/**
+ * @brief Enable On-Die Termination for DDR2.
+ *
+ */
+
+static void sdram_on_die_termination(struct sys_info *sysinfo)
+{
+ static const u32 odt[] = {
+ 0x00024911, 0xe0010000,
+ 0x00049211, 0xe0020000,
+ 0x0006db11, 0xe0030000,
+ };
+
+ u32 reg32;
+ int cas;
+
+ reg32 = MCHBAR32(ODTC);
+ reg32 &= ~(3 << 16);
+ reg32 |= (1 << 14) | (1 << 6) | (2 << 16);
+ MCHBAR32(ODTC) = reg32;
+
+ if ( !(sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED &&
+ sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED) ) {
+ printk(BIOS_DEBUG, "one dimm per channel config.. \n");
+
+ reg32 = MCHBAR32(C0ODT);
+ reg32 &= ~(7 << 28);
+ MCHBAR32(C0ODT) = reg32;
+ reg32 = MCHBAR32(C1ODT);
+ reg32 &= ~(7 << 28);
+ MCHBAR32(C1ODT) = reg32;
+ }
+
+ cas = sysinfo->cas;
+
+ reg32 = MCHBAR32(C0ODT) & 0xfff00000;
+ reg32 |= odt[(cas-3) * 2];
+ MCHBAR32(C0ODT) = reg32;
+
+ reg32 = MCHBAR32(C1ODT) & 0xfff00000;
+ reg32 |= odt[(cas-3) * 2];
+ MCHBAR32(C1ODT) = reg32;
+
+ reg32 = MCHBAR32(C0ODT + 4) & 0x1fc8ffff;
+ reg32 |= odt[((cas-3) * 2) + 1];
+ MCHBAR32(C0ODT + 4) = reg32;
+
+ reg32 = MCHBAR32(C1ODT + 4) & 0x1fc8ffff;
+ reg32 |= odt[((cas-3) * 2) + 1];
+ MCHBAR32(C1ODT + 4) = reg32;
+}
+
+/**
+ * @brief Enable clocks to populated sockets
+ */
+
+static void sdram_enable_memory_clocks(struct sys_info *sysinfo)
+{
+ u8 clocks[2] = { 0, 0 };
+
+#if CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GM
+#define CLOCKS_WIDTH 2
+#elif CONFIG_NORTHBRIDGE_INTEL_SUBTYPE_I965GC
+#define CLOCKS_WIDTH 3
+#endif
+ if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED)
+ clocks[0] |= (1 << CLOCKS_WIDTH)-1;
+
+ if (sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)
+ clocks[0] |= ((1 << CLOCKS_WIDTH)-1) << CLOCKS_WIDTH;
+
+ if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED)
+ clocks[1] |= (1 << CLOCKS_WIDTH)-1;
+
+ if (sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)
+ clocks[1] |= ((1 << CLOCKS_WIDTH)-1) << CLOCKS_WIDTH;
+
+#if CONFIG_OVERRIDE_CLOCK_DISABLE
+ /* Usually system firmware turns off system memory clock signals
+ * to unused SO-DIMM slots to reduce EMI and power consumption.
+ * However, the Kontron 986LCD-M does not like unused clock
+ * signals to be disabled.
+ */
+
+ clocks[0] = 0xf; /* force all clock gate pairs to enable */
+ clocks[1] = 0xf; /* force all clock gate pairs to enable */
+#endif
+
+ MCHBAR8(C0DCLKDIS) = clocks[0];
+ MCHBAR8(C1DCLKDIS) = clocks[1];
+}
+
+#define RTT_ODT_NONE 0
+#define RTT_ODT_50_OHM ( (1 << 9) | (1 << 5) )
+#define RTT_ODT_75_OHM (1 << 5)
+#define RTT_ODT_150_OHM (1 << 9)
+
+#define EMRS_OCD_DEFAULT ( (1 << 12) | (1 << 11) | (1 << 10) )
+
+#define MRS_CAS_3 (3 << 7)
+#define MRS_CAS_4 (4 << 7)
+#define MRS_CAS_5 (5 << 7)
+
+#define MRS_TWR_3 (2 << 12)
+#define MRS_TWR_4 (3 << 12)
+#define MRS_TWR_5 (4 << 12)
+
+#define MRS_BT (1 << 6)
+
+#define MRS_BL4 (2 << 3)
+#define MRS_BL8 (3 << 3)
+
+static void sdram_jedec_enable(struct sys_info *sysinfo)
+{
+ int i, nonzero;
+ u32 bankaddr = 0, tmpaddr, mrsaddr = 0;
+
+ for (i = 0, nonzero = -1; i < 8; i++) {
+ if (sysinfo->banksize[i] == 0) {
+ continue;
+ }
+
+ printk(BIOS_DEBUG, "jedec enable sequence: bank %d\n", i);
+ switch (i) {
+ case 0:
+ /* Start at address 0 */
+ bankaddr = 0;
+ break;
+ case 4:
+ if (sysinfo->interleaved) {
+ bankaddr = 0x40;
+ break;
+ }
+ default:
+ if (nonzero != -1) {
+ printk(BIOS_DEBUG, "bankaddr from bank size of rank %d\n", nonzero);
+ bankaddr += sysinfo->banksize[nonzero] <<
+ (sysinfo->interleaved ? 26 : 25);
+ break;
+ }
+ /* No populated bank hit before. Start at address 0 */
+ bankaddr = 0;
+ }
+
+ /* We have a bank with a non-zero size.. Remember it
+ * for the next offset we have to calculate
+ */
+ nonzero = i;
+
+ /* Get CAS latency set up */
+ switch (sysinfo->cas) {
+ case 5: mrsaddr = MRS_CAS_5; break;
+ case 4: mrsaddr = MRS_CAS_4; break;
+ case 3: mrsaddr = MRS_CAS_3; break;
+ default: die("Jedec Error (CAS).\n");
+ }
+
+ /* Get tWR set */
+ switch (sysinfo->twr) {
+ case 5: mrsaddr |= MRS_TWR_5; break;
+ case 4: mrsaddr |= MRS_TWR_4; break;
+ case 3: mrsaddr |= MRS_TWR_3; break;
+ default: die("Jedec Error (tWR).\n");
+ }
+
+ /* Set "Burst Type" */
+ mrsaddr |= MRS_BT;
+
+ /* Interleaved */
+ if (sysinfo->interleaved) {
+ mrsaddr = mrsaddr << 1;
+ }
+
+ /* Only burst length 8 supported */
+ mrsaddr |= MRS_BL8;
+
+ /* Apply NOP */
+ PRINTK_DEBUG("Apply NOP\n");
+ do_ram_command(RAM_COMMAND_NOP);
+ ram_read32(bankaddr);
+
+ /* Precharge all banks */
+ PRINTK_DEBUG("All Banks Precharge\n");
+ do_ram_command(RAM_COMMAND_PRECHARGE);
+ ram_read32(bankaddr);
+
+ /* Extended Mode Register Set (2) */
+ PRINTK_DEBUG("Extended Mode Register Set(2)\n");
+ do_ram_command(RAM_COMMAND_EMRS | RAM_EMRS_2);
+ ram_read32(bankaddr);
+
+ /* Extended Mode Register Set (3) */
+ PRINTK_DEBUG("Extended Mode Register Set(3)\n");
+ do_ram_command(RAM_COMMAND_EMRS | RAM_EMRS_3);
+ ram_read32(bankaddr);
+
+ /* Extended Mode Register Set */
+ PRINTK_DEBUG("Extended Mode Register Set\n");
+ do_ram_command(RAM_COMMAND_EMRS | RAM_EMRS_1);
+ tmpaddr = bankaddr;
+ if (!sdram_capabilities_dual_channel()) {
+ tmpaddr |= RTT_ODT_75_OHM;
+ } else if (sysinfo->interleaved) {
+ tmpaddr |= (RTT_ODT_150_OHM << 1);
+ } else {
+ tmpaddr |= RTT_ODT_150_OHM;
+ }
+ ram_read32(tmpaddr);
+
+ /* Mode Register Set: Reset DLLs */
+ PRINTK_DEBUG("MRS: Reset DLLs\n");
+ do_ram_command(RAM_COMMAND_MRS);
+ tmpaddr = bankaddr;
+ tmpaddr |= mrsaddr;
+ /* Set DLL reset bit */
+ if (sysinfo->interleaved)
+ tmpaddr |= (1 << 12);
+ else
+ tmpaddr |= (1 << 11);
+ ram_read32(tmpaddr);
+
+ /* Precharge all banks */
+ PRINTK_DEBUG("All Banks Precharge\n");
+ do_ram_command(RAM_COMMAND_PRECHARGE);
+ ram_read32(bankaddr);
+
+ /* CAS before RAS Refresh */
+ PRINTK_DEBUG("CAS before RAS\n");
+ do_ram_command(RAM_COMMAND_CBR);
+
+ /* CBR wants two READs */
+ ram_read32(bankaddr);
+ ram_read32(bankaddr);
+
+ /* Mode Register Set: Enable DLLs */
+ PRINTK_DEBUG("MRS: Enable DLLs\n");
+ do_ram_command(RAM_COMMAND_MRS);
+
+ tmpaddr = bankaddr;
+ tmpaddr |= mrsaddr;
+ ram_read32(tmpaddr);
+
+ /* Extended Mode Register Set */
+ PRINTK_DEBUG("Extended Mode Register Set: ODT/OCD\n");
+ do_ram_command(RAM_COMMAND_EMRS | RAM_EMRS_1);
+
+ tmpaddr = bankaddr;
+ if (!sdram_capabilities_dual_channel()) {
+
+ tmpaddr |= RTT_ODT_75_OHM | EMRS_OCD_DEFAULT;
+ } else if (sysinfo->interleaved) {
+ tmpaddr |= ((RTT_ODT_150_OHM | EMRS_OCD_DEFAULT) << 1);
+ } else {
+ tmpaddr |= RTT_ODT_150_OHM | EMRS_OCD_DEFAULT;
+ }
+ ram_read32(tmpaddr);
+
+ /* Extended Mode Register Set */
+ PRINTK_DEBUG("Extended Mode Register Set: OCD Exit\n");
+ do_ram_command(RAM_COMMAND_EMRS | RAM_EMRS_1);
+
+ tmpaddr = bankaddr;
+ if (!sdram_capabilities_dual_channel()) {
+ tmpaddr |= RTT_ODT_75_OHM;
+ } else if (sysinfo->interleaved) {
+ tmpaddr |= (RTT_ODT_150_OHM << 1);
+ } else {
+ tmpaddr |= RTT_ODT_150_OHM;
+ }
+ ram_read32(tmpaddr);
+ }
+}
+
+static void sdram_init_complete(void)
+{
+ PRINTK_DEBUG("Normal Operation\n");
+ do_ram_command(RAM_COMMAND_NORMAL);
+}
+
+static void sdram_setup_processor_side(void)
+{
+ if (i965_silicon_revision() == 0)
+ MCHBAR32(FSBPMC3) |= (1 << 2);
+
+ MCHBAR8(0xb00) |= 1;
+
+ if (i965_silicon_revision() == 0)
+ MCHBAR32(SLPCTL) |= (1 << 8);
+}
+
+/**
+ * @param boot_path: 0 = normal, 1 = reset, 2 = resume from s3
+ */
+void sdram_initialize(int boot_path, const u8 *spd_addresses)
+{
+ struct sys_info sysinfo;
+ u8 reg8, cas_mask;
+
+ printk(BIOS_DEBUG, "Setting up RAM controller.\n");
+
+ memset(&sysinfo, 0, sizeof(sysinfo));
+
+ sysinfo.boot_path = boot_path;
+ sysinfo.spd_addresses = spd_addresses;
+
+ /* Look at the type of DIMMs and verify all DIMMs are x8 or x16 width */
+ sdram_get_dram_configuration(&sysinfo);
+
+ /* If error, do cold boot */
+ sdram_detect_errors(&sysinfo);
+
+ /* Check whether we have stacked DIMMs */
+ sdram_verify_package_type(&sysinfo);
+
+ /* Determine common CAS */
+ cas_mask = sdram_possible_cas_latencies(&sysinfo);
+
+ /* Choose Common Frequency */
+ sdram_detect_cas_latency_and_ram_speed(&sysinfo, cas_mask);
+
+ /* Determine smallest common tRAS */
+ sdram_detect_smallest_tRAS(&sysinfo);
+
+ /* Determine tRP */
+ sdram_detect_smallest_tRP(&sysinfo);
+
+ /* Determine tRCD */
+ sdram_detect_smallest_tRCD(&sysinfo);
+
+ /* Determine smallest refresh period */
+ sdram_detect_smallest_refresh(&sysinfo);
+
+ /* Verify all DIMMs support burst length 8 */
+ sdram_verify_burst_length(&sysinfo);
+
+ /* determine tWR */
+ sdram_detect_smallest_tWR(&sysinfo);
+
+ /* Determine DIMM size parameters (rows, columns banks) */
+ sdram_detect_dimm_size(&sysinfo);
+
+ /* determine tRFC */
+ sdram_detect_smallest_tRFC(&sysinfo);
+
+ /* Program PLL settings */
+ sdram_program_pll_settings(&sysinfo);
+
+ /* Program Graphics Frequency */
+ sdram_program_graphics_frequency(&sysinfo);
+
+ /* Program System Memory Frequency */
+ sdram_program_memory_frequency(&sysinfo);
+
+ /* Determine Mode of Operation (Interleaved etc) */
+ sdram_set_channel_mode(&sysinfo);
+
+ /* Program Clock Crossing values */
+ sdram_program_clock_crossing();
+
+ /* Disable fast dispatch */
+ sdram_disable_fast_dispatch();
+
+ /* Enable WIODLL Power Down in ACPI states */
+ MCHBAR32(C0DMC) |= (1 << 24);
+ MCHBAR32(C1DMC) |= (1 << 24);
+
+ /* Program DRAM Row Boundary/Attribute Registers */
+
+ /* program row size DRB and set TOLUD */
+ sdram_program_row_boundaries(&sysinfo);
+
+ /* program page size DRA */
+ sdram_set_row_attributes(&sysinfo);
+
+ /* Program CxBNKARC */
+ sdram_set_bank_architecture(&sysinfo);
+
+ /* Program DRAM Timing and Control registers based on SPD */
+ sdram_set_timing_and_control(&sysinfo);
+
+ /* On-Die Termination Adjustment */
+ sdram_on_die_termination(&sysinfo);
+
+ /* Pre Jedec Initialization */
+ sdram_pre_jedec_initialization();
+
+ /* Perform System Memory IO Initialization */
+ sdram_initialize_system_memory_io(&sysinfo);
+
+ /* Perform System Memory IO Buffer Enable */
+ sdram_enable_system_memory_io(&sysinfo);
+
+ /* Enable System Memory Clocks */
+ sdram_enable_memory_clocks(&sysinfo);
+
+ if (boot_path == BOOT_PATH_NORMAL) {
+ /* Jedec Initialization sequence */
+ sdram_jedec_enable(&sysinfo);
+ }
+
+ /* Program Power Management Registers */
+ sdram_power_management(&sysinfo);
+
+ /* Post Jedec Init */
+ sdram_post_jedec_initialization(&sysinfo);
+
+ /* Program DRAM Throttling */
+ sdram_thermal_management();
+
+ /* Normal Operations */
+ sdram_init_complete();
+
+ /* Program Receive Enable Timings */
+ sdram_program_receive_enable(&sysinfo);
+
+ /* Enable Periodic RCOMP */
+ sdram_enable_rcomp();
+
+ /* Tell ICH7 that we're done */
+ reg8 = pci_read_config8(PCI_DEV(0,0x1f,0), 0xa2);
+ reg8 &= ~(1 << 7);
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, reg8);
+
+ printk(BIOS_DEBUG, "RAM initialization finished.\n");
+
+ sdram_setup_processor_side();
+}
diff --git a/src/northbridge/intel/i965/raminit.h b/src/northbridge/intel/i965/raminit.h
new file mode 100644
index 0000000..5388a21
--- /dev/null
+++ b/src/northbridge/intel/i965/raminit.h
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 coresystems GmbH
+ *
+ * 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
+
+#define DIMM_SOCKETS 2
+
+#define DIMM_TCO_BASE 0x30
+
+/* Burst length is always 8 */
+#define BURSTLENGTH 8
+
+struct sys_info {
+ u16 memory_frequency; /* 400, 533 or 667 */
+ u16 fsb_frequency; /* 400, 533 or 667 */
+
+ u8 trp; /* calculated by sdram_detect_smallest_tRP() */
+ u8 trcd; /* calculated by sdram_detect_smallest_tRCD() */
+ u8 tras; /* calculated by sdram_detect_smallest_tRAS() */
+ u8 trfc; /* calculated by sdram_detect_smallest_tRFC() */
+ u8 twr; /* calculated by sdram_detect_smallest_tWR() */
+
+ u8 cas; /* 3, 4 or 5 */
+ u8 refresh; /* 0 = 15.6us, 1 = 7.8us */
+
+ u8 dual_channel; /* 0 or 1 */
+ u8 interleaved;
+
+ u8 mvco4x; /* 0 (8x) or 1 (4x) */
+ u8 clkcfg_bit7;
+ u8 boot_path;
+#define BOOT_PATH_NORMAL 0
+#define BOOT_PATH_RESET 1
+#define BOOT_PATH_RESUME 2
+
+ u8 package; /* 0 = planar, 1 = stacked */
+#define SYSINFO_PACKAGE_PLANAR 0x00
+#define SYSINFO_PACKAGE_STACKED 0x01
+ u8 dimm[2 * DIMM_SOCKETS];
+#define SYSINFO_DIMM_X16DS 0x00
+#define SYSINFO_DIMM_X8DS 0x01
+#define SYSINFO_DIMM_X16SS 0x02
+#define SYSINFO_DIMM_X8DDS 0x03
+#define SYSINFO_DIMM_NOT_POPULATED 0x04
+
+ u8 banks[2 * DIMM_SOCKETS];
+
+ u8 banksize[2 * 2 * DIMM_SOCKETS];
+ const u8 *spd_addresses;
+
+} __attribute__ ((packed));
+
+void receive_enable_adjust(struct sys_info *sysinfo);
+void sdram_initialize(int boot_path, const u8 *sdram_addresses);
+int fixup_i965_errata(void);
+void udelay(u32 us);
+
+#if CONFIG_DEBUG_RAM_SETUP
+void sdram_dump_mchbar_registers(void);
+#endif
+#endif /* RAMINIT_H */
diff --git a/src/northbridge/intel/i965/rcven.c b/src/northbridge/intel/i965/rcven.c
new file mode 100644
index 0000000..15829c5
--- /dev/null
+++ b/src/northbridge/intel/i965/rcven.c
@@ -0,0 +1,338 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 coresystems GmbH
+ *
+ * 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 "raminit.h"
+
+/**
+ * sample the strobes signal
+ */
+static u32 sample_strobes(int channel_offset, struct sys_info *sysinfo)
+{
+ u32 reg32, addr;
+ int i;
+
+ MCHBAR32(C0DRC1 + channel_offset) |= (1 << 6);
+
+ MCHBAR32(C0DRC1 + channel_offset) &= ~(1 << 6);
+
+ addr = 0;
+
+ if (channel_offset != 0) { /* must be dual channel */
+ if (sysinfo->interleaved == 1) {
+ addr |= (1 << 6);
+ } else {
+ addr = ((u32)MCHBAR8(C0DRB3)) << 25;
+ }
+ }
+
+ for (i = 0; i < 28; i++) {
+ read32(addr);
+ read32(addr + 0x80);
+ }
+
+ reg32 = MCHBAR32(RCVENMT);
+ if (channel_offset == 0) {
+ reg32 = reg32 << 2;
+ }
+
+ /**
+ * [19] = 1: all bits are high
+ * [18] = 1: all bits are low
+ * [19:18] = 00: bits are mixed high, low
+ */
+ return reg32;
+}
+
+/**
+ * This function sets receive enable coarse and medium timing parameters
+ */
+
+static void set_receive_enable(int channel_offset, u8 medium, u8 coarse)
+{
+ u32 reg32;
+
+ printk(BIOS_SPEW, " set_receive_enable() medium=0x%x, coarse=0x%x\n", medium, coarse);
+
+ reg32 = MCHBAR32(C0DRT1 + channel_offset);
+ reg32 &= 0xf0ffffff;
+ reg32 |= ((u32)coarse & 0x0f) << 24;
+ MCHBAR32(C0DRT1 + channel_offset) = reg32;
+
+ /* This should never happen: */
+ if (coarse > 0x0f)
+ printk(BIOS_DEBUG, "set_receive_enable: coarse overflow: 0x%02x.\n", coarse);
+
+ /* medium control
+ *
+ * 00 - 1/4 clock
+ * 01 - 1/2 clock
+ * 10 - 3/4 clock
+ * 11 - 1 clock
+ */
+
+ reg32 = MCHBAR32(RCVENMT);
+ if (!channel_offset) {
+ /* Channel 0 */
+ reg32 &= ~(3 << 2);
+ reg32 |= medium << 2;
+ } else {
+ /* Channel 1 */
+ reg32 &= ~(3 << 0);
+ reg32 |= medium;
+ }
+ MCHBAR32(RCVENMT) = reg32;
+
+}
+
+static int normalize(int channel_offset, u8 * mediumcoarse, u8 * fine)
+{
+ printk(BIOS_SPEW, " normalize()\n");
+
+ if (*fine < 0x80)
+ return 0;
+
+ *fine -= 0x80;
+ *mediumcoarse += 1;
+
+ if (*mediumcoarse >= 0x40) {
+ printk(BIOS_DEBUG, "Normalize Error\n");
+ return -1;
+ }
+
+ set_receive_enable(channel_offset, *mediumcoarse & 3,
+ *mediumcoarse >> 2);
+
+ MCHBAR8(C0WL0REOST + channel_offset) = *fine;
+
+ return 0;
+}
+
+static int find_preamble(int channel_offset, u8 * mediumcoarse,
+ struct sys_info *sysinfo)
+{
+ /* find start of the data phase */
+ u32 reg32;
+
+ printk(BIOS_SPEW, " find_preamble()\n");
+
+ do {
+ if (*mediumcoarse < 4) {
+ printk(BIOS_DEBUG, "No Preamble found.\n");
+ return -1;
+ }
+ *mediumcoarse -= 4;
+
+ set_receive_enable(channel_offset, *mediumcoarse & 3,
+ *mediumcoarse >> 2);
+
+ reg32 = sample_strobes(channel_offset, sysinfo);
+
+ } while (reg32 & (1 << 19));
+
+ if (!(reg32 & (1 << 18))) {
+ printk(BIOS_DEBUG, "No Preamble found (neither high nor low).\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * add a quarter clock to the current receive enable settings
+ */
+
+static int add_quarter_clock(int channel_offset, u8 * mediumcoarse, u8 * fine)
+{
+ printk(BIOS_SPEW, " add_quarter_clock() mediumcoarse=%02x fine=%02x\n",
+ *mediumcoarse, *fine);
+ if (*fine >= 0x80) {
+ *fine -= 0x80;
+
+ *mediumcoarse += 2;
+ if (*mediumcoarse >= 0x40) {
+ printk(BIOS_DEBUG, "clocks at max.\n");
+ return -1;
+ }
+
+ set_receive_enable(channel_offset, *mediumcoarse & 3,
+ *mediumcoarse >> 2);
+ } else {
+ *fine += 0x80;
+ }
+
+ MCHBAR8(C0WL0REOST + channel_offset) = *fine;
+
+ return 0;
+}
+
+static int find_strobes_low(int channel_offset, u8 * mediumcoarse, u8 * fine,
+ struct sys_info *sysinfo)
+{
+ u32 rcvenmt;
+
+ printk(BIOS_SPEW, " find_strobes_low()\n");
+
+ for (;;) {
+ MCHBAR8(C0WL0REOST + channel_offset) = *fine;
+
+ set_receive_enable(channel_offset, *mediumcoarse & 3,
+ *mediumcoarse >> 2);
+
+ rcvenmt = sample_strobes(channel_offset, sysinfo);
+
+ if (((rcvenmt & (1 << 18)) != 0))
+ return 0;
+
+ *fine -= 0x80;
+ if (*fine == 0)
+ continue;
+
+ *mediumcoarse -= 2;
+ if (*mediumcoarse < 0xfe)
+ continue;
+
+ break;
+
+ }
+
+ printk(BIOS_DEBUG, "Could not find low strobe\n");
+ return 0;
+}
+
+static int find_strobes_edge(int channel_offset, u8 * mediumcoarse, u8 * fine,
+ struct sys_info *sysinfo)
+{
+
+ int counter;
+ u32 rcvenmt;
+
+ printk(BIOS_SPEW, " find_strobes_edge()\n");
+
+ counter = 8;
+ set_receive_enable(channel_offset, *mediumcoarse & 3,
+ *mediumcoarse >> 2);
+
+ for (;;) {
+ MCHBAR8(C0WL0REOST + channel_offset) = *fine;
+ rcvenmt = sample_strobes(channel_offset, sysinfo);
+
+ if ((rcvenmt & (1 << 19)) == 0) {
+ counter = 8;
+ } else {
+ counter--;
+ if (!counter)
+ break;
+ }
+
+ *fine = *fine + 1;
+ if (*fine < 0xf8) {
+ if (*fine & (1 << 3)) {
+ *fine &= ~(1 << 3);
+ *fine += 0x10;
+ }
+ continue;
+ }
+
+ *fine = 0;
+ *mediumcoarse += 2;
+ if (*mediumcoarse <= 0x40) {
+ set_receive_enable(channel_offset, *mediumcoarse & 3,
+ *mediumcoarse >> 2);
+ continue;
+ }
+
+ printk(BIOS_DEBUG, "Could not find rising edge.\n");
+ return -1;
+ }
+
+ *fine -= 7;
+ if (*fine >= 0xf9) {
+ *mediumcoarse -= 2;
+ set_receive_enable(channel_offset, *mediumcoarse & 3,
+ *mediumcoarse >> 2);
+ }
+
+ *fine &= ~(1 << 3);
+ MCHBAR8(C0WL0REOST + channel_offset) = *fine;
+
+ return 0;
+}
+
+/**
+ * Here we use a trick. The RCVEN channel 0 registers are all at an
+ * offset of 0x80 to the channel 0 registers. We don't want to waste
+ * a lot of if()s so let's just pass 0 or 0x80 for the channel offset.
+ */
+
+static int receive_enable_autoconfig(int channel_offset,
+ struct sys_info *sysinfo)
+{
+ u8 mediumcoarse;
+ u8 fine;
+
+ printk(BIOS_SPEW, "receive_enable_autoconfig() for channel %d\n",
+ channel_offset ? 1 : 0);
+
+ /* Set initial values */
+ mediumcoarse = (sysinfo->cas << 2) | 3;
+ fine = 0;
+
+ if (find_strobes_low(channel_offset, &mediumcoarse, &fine, sysinfo))
+ return -1;
+
+ if (find_strobes_edge(channel_offset, &mediumcoarse, &fine, sysinfo))
+ return -1;
+
+ if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
+ return -1;
+
+ if (find_preamble(channel_offset, &mediumcoarse, sysinfo))
+ return -1;
+
+ if (add_quarter_clock(channel_offset, &mediumcoarse, &fine))
+ return -1;
+
+ if (normalize(channel_offset, &mediumcoarse, &fine))
+ return -1;
+
+ /* This is a debug check to see if the rcven code is fully working.
+ * It can be removed when the output message is not printed anymore
+ */
+ if (MCHBAR8(C0WL0REOST + channel_offset) == 0) {
+ printk(BIOS_DEBUG, "Weird. No C%sWL0REOST\n", channel_offset?"1":"0");
+ }
+
+ return 0;
+}
+
+void receive_enable_adjust(struct sys_info *sysinfo)
+{
+ /* Is channel 0 populated? */
+ if (sysinfo->dimm[0] != SYSINFO_DIMM_NOT_POPULATED
+ || sysinfo->dimm[1] != SYSINFO_DIMM_NOT_POPULATED)
+ if (receive_enable_autoconfig(0, sysinfo))
+ return;
+
+ /* Is channel 1 populated? */
+ if (sysinfo->dimm[2] != SYSINFO_DIMM_NOT_POPULATED
+ || sysinfo->dimm[3] != SYSINFO_DIMM_NOT_POPULATED)
+ if (receive_enable_autoconfig(0x80, sysinfo))
+ return;
+}
+
diff --git a/src/northbridge/intel/i965/udelay.c b/src/northbridge/intel/i965/udelay.c
new file mode 100644
index 0000000..60ca9fb
--- /dev/null
+++ b/src/northbridge/intel/i965/udelay.c
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2008 coresystems GmbH
+ *
+ * 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 <delay.h>
+#include <stdint.h>
+#include <cpu/x86/tsc.h>
+#include <cpu/x86/msr.h>
+#include <cpu/intel/speedstep.h>
+
+/**
+ * Intel Core(tm) CPUs always run the TSC at the maximum possible CPU clock
+ */
+
+void udelay(u32 us)
+{
+ u32 dword;
+ tsc_t tsc, tsc1, tscd;
+ msr_t msr;
+ u32 fsb = 0, divisor;
+ u32 d; /* ticks per us */
+
+ msr = rdmsr(MSR_FSB_FREQ);
+ switch (msr.lo & 0x07) {
+ case 5:
+ fsb = 400;
+ break;
+ case 1:
+ fsb = 533;
+ break;
+ case 3:
+ fsb = 667;
+ break;
+ case 2:
+ fsb = 800;
+ break;
+ case 0:
+ fsb = 1067;
+ break;
+ case 4:
+ fsb = 1333;
+ break;
+ case 6:
+ fsb = 1600;
+ break;
+ }
+
+ msr = rdmsr(0x198);
+ divisor = (msr.hi >> 8) & 0x1f;
+
+ d = (fsb * divisor) / 4; /* CPU clock is always a quarter. */
+
+ multiply_to_tsc(&tscd, us, d);
+
+ tsc1 = rdtsc();
+ dword = tsc1.lo + tscd.lo;
+ if ((dword < tsc1.lo) || (dword < tscd.lo)) {
+ tsc1.hi++;
+ }
+ tsc1.lo = dword;
+ tsc1.hi += tscd.hi;
+
+ do {
+ tsc = rdtsc();
+ } while ((tsc.hi < tsc1.hi)
+ || ((tsc.hi == tsc1.hi) && (tsc.lo < tsc1.lo)));
+}
diff --git a/src/southbridge/intel/Kconfig b/src/southbridge/intel/Kconfig
index 6b46f8a..f940a4b 100644
--- a/src/southbridge/intel/Kconfig
+++ b/src/southbridge/intel/Kconfig
@@ -8,6 +8,7 @@ source src/southbridge/intel/i82801cx/Kconfig
source src/southbridge/intel/i82801dx/Kconfig
source src/southbridge/intel/i82801ex/Kconfig
source src/southbridge/intel/i82801gx/Kconfig
+source src/southbridge/intel/i82801hx/Kconfig
source src/southbridge/intel/i82801ix/Kconfig
source src/southbridge/intel/i82870/Kconfig
source src/southbridge/intel/pxhd/Kconfig
diff --git a/src/southbridge/intel/Makefile.inc b/src/southbridge/intel/Makefile.inc
index d9733f5..3d064d5 100644
--- a/src/southbridge/intel/Makefile.inc
+++ b/src/southbridge/intel/Makefile.inc
@@ -8,6 +8,7 @@ subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_I82801CX) += i82801cx
subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_I82801DX) += i82801dx
subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_I82801EX) += i82801ex
subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_I82801GX) += i82801gx
+subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_I82801HX) += i82801hx
subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_I82801IX) += i82801ix
subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_I82870) += i82870
subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_PXHD) += pxhd
diff --git a/src/southbridge/intel/i82801hx/Kconfig b/src/southbridge/intel/i82801hx/Kconfig
new file mode 100644
index 0000000..40f722d
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/Kconfig
@@ -0,0 +1,44 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2008-2009 coresystems GmbH
+##
+## 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
+##
+
+config SOUTHBRIDGE_INTEL_I82801HX
+ bool
+ select SOUTHBRIDGE_INTEL_COMMON
+ select IOAPIC
+ select HAVE_HARD_RESET
+ select HAVE_USBDEBUG
+ select USE_WATCHDOG_ON_BOOT
+ select HAVE_SMI_HANDLER
+
+if SOUTHBRIDGE_INTEL_I82801HX
+
+config EHCI_BAR
+ hex
+ default 0xfef00000
+
+config BOOTBLOCK_SOUTHBRIDGE_INIT
+ string
+ default "southbridge/intel/i82801hx/bootblock.c"
+
+config HPET_MIN_TICKS
+ hex
+ default 0x80
+
+endif
+
diff --git a/src/southbridge/intel/i82801hx/Makefile.inc b/src/southbridge/intel/i82801hx/Makefile.inc
new file mode 100644
index 0000000..757c62c
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/Makefile.inc
@@ -0,0 +1,38 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2008-2009 coresystems GmbH
+##
+## 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
+##
+
+ramstage-y += i82801hx.c
+ramstage-y += azalia.c
+ramstage-y += ide.c
+ramstage-y += lpc.c
+ramstage-y += nic.c
+ramstage-y += pci.c
+ramstage-y += pcie.c
+ramstage-y += sata.c
+ramstage-y += smbus.c
+ramstage-y += usb.c
+ramstage-y += usb_ehci.c
+
+ramstage-y += reset.c
+ramstage-y += watchdog.c
+
+ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c
+smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c
+
+romstage-y += early_smbus.c early_lpc.c
diff --git a/src/southbridge/intel/i82801hx/acpi/audio.asl b/src/southbridge/intel/i82801hx/acpi/audio.asl
new file mode 100644
index 0000000..fa9a76d
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/audio.asl
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+/* Intel i82801H HDA */
+
+// Intel High Definition Audio (Azalia) 0:1b.0
+
+Device (HDEF)
+{
+ Name (_ADR, 0x001b0000)
+
+ // Power Resources for Wake
+ Name (_PRW, Package(){
+ 5, // Bit 5 of GPE
+ 4 // Can wake from S4 state.
+ })
+}
+
diff --git a/src/southbridge/intel/i82801hx/acpi/globalnvs.asl b/src/southbridge/intel/i82801hx/acpi/globalnvs.asl
new file mode 100644
index 0000000..0384376
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/globalnvs.asl
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+/* Global Variables */
+
+Name(\PICM, 0) // IOAPIC/8259
+Name(\DSEN, 1) // Display Output Switching Enable
+
+/* Global ACPI memory region. This region is used for passing information
+ * between coreboot (aka "the system bios"), ACPI, and the SMI handler.
+ * Since we don't know where this will end up in memory at ACPI compile time,
+ * we have to fix it up in coreboot's ACPI creation phase.
+ */
+
+
+OperationRegion (GNVS, SystemMemory, 0xC0DEBABE, 0x100)
+Field (GNVS, ByteAcc, NoLock, Preserve)
+{
+ /* Miscellaneous */
+ Offset (0x00),
+ OSYS, 16, // 0x00 - Operating System
+ SMIF, 8, // 0x02 - SMI function
+ PRM0, 8, // 0x03 - SMI function parameter
+ PRM1, 8, // 0x04 - SMI function parameter
+ SCIF, 8, // 0x05 - SCI function
+ PRM2, 8, // 0x06 - SCI function parameter
+ PRM3, 8, // 0x07 - SCI function parameter
+ LCKF, 8, // 0x08 - Global Lock function for EC
+ PRM4, 8, // 0x09 - Lock function parameter
+ PRM5, 8, // 0x0a - Lock function parameter
+ P80D, 32, // 0x0b - Debug port (IO 0x80) value
+ LIDS, 8, // 0x0f - LID state (open = 1)
+ PWRS, 8, // 0x10 - Power State (AC = 1)
+ DBGS, 8, // 0x11 - Debug State
+ LINX, 8, // 0x12 - Linux OS
+ DCKN, 8, // 0x13 - PCIe docking state
+ /* Thermal policy */
+ Offset (0x14),
+ ACTT, 8, // 0x14 - active trip point
+ PSVT, 8, // 0x15 - passive trip point
+ TC1V, 8, // 0x16 - passive trip point TC1
+ TC2V, 8, // 0x17 - passive trip point TC2
+ TSPV, 8, // 0x18 - passive trip point TSP
+ CRTT, 8, // 0x19 - critical trip point
+ DTSE, 8, // 0x1a - Digital Thermal Sensor enable
+ DTS1, 8, // 0x1b - DT sensor 1
+ DTS2, 8, // 0x1c - DT sensor 2
+ /* Battery Support */
+ Offset (0x1e),
+ BNUM, 8, // 0x1e - number of batteries
+ B0SC, 8, // 0x1f - BAT0 stored capacity
+ B1SC, 8, // 0x20 - BAT1 stored capacity
+ B2SC, 8, // 0x21 - BAT2 stored capacity
+ B0SS, 8, // 0x22 - BAT0 stored status
+ B1SS, 8, // 0x23 - BAT1 stored status
+ B2SS, 8, // 0x24 - BAT2 stored status
+ /* Processor Identification */
+ Offset (0x28),
+ APIC, 8, // 0x28 - APIC Enabled by coreboot
+ MPEN, 8, // 0x29 - Multi Processor Enable
+ PCP0, 8, // 0x2a - PDC CPU/CORE 0
+ PCP1, 8, // 0x2b - PDC CPU/CORE 1
+ PPCM, 8, // 0x2c - Max. PPC state
+ /* Super I/O & CMOS config */
+ Offset (0x32),
+ NATP, 8, // 0x32 -
+ CMAP, 8, // 0x33 -
+ CMBP, 8, // 0x34 -
+ LPTP, 8, // 0x35 - LPT Port
+ FDCP, 8, // 0x36 - Floppy Disk Controller
+ RFDV, 8, // 0x37 -
+ HOTK, 8, // 0x38 -
+ RTCF, 8, // 0x39 -
+ UTIL, 8, // 0x3a -
+ ACIN, 8, // 0x3b -
+ /* Integrated Graphics Device */
+ Offset (0x3c),
+ IGDS, 8, // 0x3c - IGD state (primary = 1)
+ TLST, 8, // 0x3d - Display Toggle List pointer
+ CADL, 8, // 0x3e - Currently Attached Devices List
+ PADL, 8, // 0x3f - Previously Attached Devices List
+ CSTE, 16, // 0x40 - Current display state
+ NSTE, 16, // 0x42 - Next display state
+ SSTE, 16, // 0x44 - Set display state
+ Offset (0x46),
+ NDID, 8, // 0x46 - Number of Device IDs
+ DID1, 32, // 0x47 - Device ID 1
+ DID2, 32, // 0x4b - Device ID 2
+ DID3, 32, // 0x4f - Device ID 3
+ DID4, 32, // 0x53 - Device ID 4
+ DID5, 32, // 0x57 - Device ID 5
+ /* Backlight Control */
+ Offset (0x64),
+ BLCS, 8, // 0x64 - Backlight control possible?
+ BRTL, 8, // 0x65 - Brightness Level
+ ODDS, 8, // 0x66
+ /* Ambient Light Sensors */
+ Offset (0x6e),
+ ALSE, 8, // 0x6e - ALS enable
+ ALAF, 8, // 0x6f - Ambient light adjustment factor
+ LLOW, 8, // 0x70 - LUX Low
+ LHIH, 8, // 0x71 - LUX High
+ /* EMA */
+ Offset (0x78),
+ EMAE, 8, // 0x78 - EMA enable
+ EMAP, 16, // 0x79 - EMA pointer
+ EMAL, 16, // 0x7b - EMA length
+ /* MEF */
+ Offset (0x82),
+ MEFE, 8, // 0x82 - MEF enable
+ /* TPM support */
+ Offset (0x8c),
+ TPMP, 8, // 0x8c - TPM
+ TPME, 8, // 0x8d - TPM enable
+ /* SATA */
+ Offset (0x96),
+ GTF0, 56, // 0x96 - GTF task file buffer for port 0
+ GTF1, 56, // 0x9d - GTF task file buffer for port 1
+ GTF2, 56, // 0xa4 - GTF task file buffer for port 2
+ IDEM, 8, // 0xab - IDE mode (compatible / enhanced)
+ IDET, 8, // 0xac - IDE
+ /* IGD OpRegion */
+ Offset (0xb4),
+ ASLB, 32, // 0xb4 - IGD OpRegion Base Address
+ IBTT, 8, // 0xb8 - IGD boot panel device
+ IPAT, 8, // 0xb9 - IGD panel type cmos option
+ ITVF, 8, // 0xba - IGD TV format cmos option
+ ITVM, 8, // 0xbb - IGD TV minor format option
+ IPSC, 8, // 0xbc - IGD panel scaling
+ IBLC, 8, // 0xbd - IGD BLC config
+ IBIA, 8, // 0xbe - IGD BIA config
+ ISSC, 8, // 0xbf - IGD SSC config
+ I409, 8, // 0xc0 - IGD 0409 modified settings
+ I509, 8, // 0xc1 - IGD 0509 modified settings
+ I609, 8, // 0xc2 - IGD 0609 modified settings
+ I709, 8, // 0xc3 - IGD 0709 modified settings
+ IDMM, 8, // 0xc4 - IGD DVMT Mode
+ IDMS, 8, // 0xc5 - IGD DVMT memory size
+ IF1E, 8, // 0xc6 - IGD function 1 enable
+ HVCO, 8, // 0xc7 - IGD HPLL VCO
+ NXD1, 32, // 0xc8 - IGD _DGS next DID1
+ NXD2, 32, // 0xcc - IGD _DGS next DID2
+ NXD3, 32, // 0xd0 - IGD _DGS next DID3
+ NXD4, 32, // 0xd4 - IGD _DGS next DID4
+ NXD5, 32, // 0xd8 - IGD _DGS next DID5
+ NXD6, 32, // 0xdc - IGD _DGS next DID6
+ NXD7, 32, // 0xe0 - IGD _DGS next DID7
+ NXD8, 32, // 0xe4 - IGD _DGS next DID8
+ /* Mainboard Specific (TODO move elsewhere) */
+ Offset (0xf0),
+ DOCK, 8, // 0xf0 - Docking Status
+ BTEN, 8, // 0xf1 - Bluetooth Enable
+}
diff --git a/src/southbridge/intel/i82801hx/acpi/irqlinks.asl b/src/southbridge/intel/i82801hx/acpi/irqlinks.asl
new file mode 100644
index 0000000..5fcee45
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/irqlinks.asl
@@ -0,0 +1,493 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+Device (LNKA)
+{
+ Name (_HID, EISAID("PNP0C0F"))
+ Name (_UID, 1)
+
+ // Disable method
+ Method (_DIS, 0, Serialized)
+ {
+ Store (0x80, PRTA)
+ }
+
+ // Possible Resource Settings for this Link
+ Name (_PRS, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared)
+ { 1, 3, 4, 5, 6, 7, 10, 12, 14, 15 }
+ })
+
+ // Current Resource Settings for this link
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RTLA, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared) {}
+ })
+ CreateWordField(RTLA, 1, IRQ0)
+
+ // Clear the WordField
+ Store (Zero, IRQ0)
+
+ // Set the bit from PRTA
+ ShiftLeft(1, And(PRTA, 0x0f), IRQ0)
+
+ Return (RTLA)
+ }
+
+ // Set Resource Setting for this IRQ link
+ Method (_SRS, 1, Serialized)
+ {
+ CreateWordField(Arg0, 1, IRQ0)
+
+ // Which bit is set?
+ FindSetRightBit(IRQ0, Local0)
+
+ Decrement(Local0)
+ Store(Local0, PRTA)
+ }
+
+ // Status
+ Method (_STA, 0, Serialized)
+ {
+ If(And(PRTA, 0x80)) {
+ Return (0x9)
+ } Else {
+ Return (0xb)
+ }
+ }
+}
+
+Device (LNKB)
+{
+ Name (_HID, EISAID("PNP0C0F"))
+ Name (_UID, 2)
+
+ // Disable method
+ Method (_DIS, 0, Serialized)
+ {
+ Store (0x80, PRTB)
+ }
+
+ // Possible Resource Settings for this Link
+ Name (_PRS, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared)
+ { 1, 3, 4, 5, 6, 7, 11, 12, 14, 15 }
+ })
+
+ // Current Resource Settings for this link
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RTLB, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared) {}
+ })
+ CreateWordField(RTLB, 1, IRQ0)
+
+ // Clear the WordField
+ Store (Zero, IRQ0)
+
+ // Set the bit from PRTB
+ ShiftLeft(1, And(PRTB, 0x0f), IRQ0)
+
+ Return (RTLB)
+ }
+
+ // Set Resource Setting for this IRQ link
+ Method (_SRS, 1, Serialized)
+ {
+ CreateWordField(Arg0, 1, IRQ0)
+
+ // Which bit is set?
+ FindSetRightBit(IRQ0, Local0)
+
+ Decrement(Local0)
+ Store(Local0, PRTB)
+ }
+
+ // Status
+ Method (_STA, 0, Serialized)
+ {
+ If(And(PRTB, 0x80)) {
+ Return (0x9)
+ } Else {
+ Return (0xb)
+ }
+ }
+}
+
+Device (LNKC)
+{
+ Name (_HID, EISAID("PNP0C0F"))
+ Name (_UID, 3)
+
+ // Disable method
+ Method (_DIS, 0, Serialized)
+ {
+ Store (0x80, PRTC)
+ }
+
+ // Possible Resource Settings for this Link
+ Name (_PRS, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared)
+ { 1, 3, 4, 5, 6, 7, 10, 12, 14, 15 }
+ })
+
+ // Current Resource Settings for this link
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RTLC, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared) {}
+ })
+ CreateWordField(RTLC, 1, IRQ0)
+
+ // Clear the WordField
+ Store (Zero, IRQ0)
+
+ // Set the bit from PRTC
+ ShiftLeft(1, And(PRTC, 0x0f), IRQ0)
+
+ Return (RTLC)
+ }
+
+ // Set Resource Setting for this IRQ link
+ Method (_SRS, 1, Serialized)
+ {
+ CreateWordField(Arg0, 1, IRQ0)
+
+ // Which bit is set?
+ FindSetRightBit(IRQ0, Local0)
+
+ Decrement(Local0)
+ Store(Local0, PRTC)
+ }
+
+ // Status
+ Method (_STA, 0, Serialized)
+ {
+ If(And(PRTC, 0x80)) {
+ Return (0x9)
+ } Else {
+ Return (0xb)
+ }
+ }
+}
+
+Device (LNKD)
+{
+ Name (_HID, EISAID("PNP0C0F"))
+ Name (_UID, 4)
+
+ // Disable method
+ Method (_DIS, 0, Serialized)
+ {
+ Store (0x80, PRTD)
+ }
+
+ // Possible Resource Settings for this Link
+ Name (_PRS, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared)
+ { 1, 3, 4, 5, 6, 7, 11, 12, 14, 15 }
+ })
+
+ // Current Resource Settings for this link
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RTLD, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared) {}
+ })
+ CreateWordField(RTLD, 1, IRQ0)
+
+ // Clear the WordField
+ Store (Zero, IRQ0)
+
+ // Set the bit from PRTD
+ ShiftLeft(1, And(PRTD, 0x0f), IRQ0)
+
+ Return (RTLD)
+ }
+
+ // Set Resource Setting for this IRQ link
+ Method (_SRS, 1, Serialized)
+ {
+ CreateWordField(Arg0, 1, IRQ0)
+
+ // Which bit is set?
+ FindSetRightBit(IRQ0, Local0)
+
+ Decrement(Local0)
+ Store(Local0, PRTD)
+ }
+
+ // Status
+ Method (_STA, 0, Serialized)
+ {
+ If(And(PRTD, 0x80)) {
+ Return (0x9)
+ } Else {
+ Return (0xb)
+ }
+ }
+}
+
+Device (LNKE)
+{
+ Name (_HID, EISAID("PNP0C0F"))
+ Name (_UID, 5)
+
+ // Disable method
+ Method (_DIS, 0, Serialized)
+ {
+ Store (0x80, PRTE)
+ }
+
+ // Possible Resource Settings for this Link
+ Name (_PRS, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared)
+ { 1, 3, 4, 5, 6, 7, 10, 12, 14, 15 }
+ })
+
+ // Current Resource Settings for this link
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RTLE, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared) {}
+ })
+ CreateWordField(RTLE, 1, IRQ0)
+
+ // Clear the WordField
+ Store (Zero, IRQ0)
+
+ // Set the bit from PRTE
+ ShiftLeft(1, And(PRTE, 0x0f), IRQ0)
+
+ Return (RTLE)
+ }
+
+ // Set Resource Setting for this IRQ link
+ Method (_SRS, 1, Serialized)
+ {
+ CreateWordField(Arg0, 1, IRQ0)
+
+ // Which bit is set?
+ FindSetRightBit(IRQ0, Local0)
+
+ Decrement(Local0)
+ Store(Local0, PRTE)
+ }
+
+ // Status
+ Method (_STA, 0, Serialized)
+ {
+ If(And(PRTE, 0x80)) {
+ Return (0x9)
+ } Else {
+ Return (0xb)
+ }
+ }
+}
+
+Device (LNKF)
+{
+ Name (_HID, EISAID("PNP0C0F"))
+ Name (_UID, 6)
+
+ // Disable method
+ Method (_DIS, 0, Serialized)
+ {
+ Store (0x80, PRTF)
+ }
+
+ // Possible Resource Settings for this Link
+ Name (_PRS, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared)
+ { 1, 3, 4, 5, 6, 7, 11, 12, 14, 15 }
+ })
+
+ // Current Resource Settings for this link
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RTLF, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared) {}
+ })
+ CreateWordField(RTLF, 1, IRQ0)
+
+ // Clear the WordField
+ Store (Zero, IRQ0)
+
+ // Set the bit from PRTF
+ ShiftLeft(1, And(PRTF, 0x0f), IRQ0)
+
+ Return (RTLF)
+ }
+
+ // Set Resource Setting for this IRQ link
+ Method (_SRS, 1, Serialized)
+ {
+ CreateWordField(Arg0, 1, IRQ0)
+
+ // Which bit is set?
+ FindSetRightBit(IRQ0, Local0)
+
+ Decrement(Local0)
+ Store(Local0, PRTF)
+ }
+
+ // Status
+ Method (_STA, 0, Serialized)
+ {
+ If(And(PRTF, 0x80)) {
+ Return (0x9)
+ } Else {
+ Return (0xb)
+ }
+ }
+}
+
+Device (LNKG)
+{
+ Name (_HID, EISAID("PNP0C0F"))
+ Name (_UID, 7)
+
+ // Disable method
+ Method (_DIS, 0, Serialized)
+ {
+ Store (0x80, PRTG)
+ }
+
+ // Possible Resource Settings for this Link
+ Name (_PRS, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared)
+ { 1, 3, 4, 5, 6, 7, 10, 12, 14, 15 }
+ })
+
+ // Current Resource Settings for this link
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RTLG, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared) {}
+ })
+ CreateWordField(RTLG, 1, IRQ0)
+
+ // Clear the WordField
+ Store (Zero, IRQ0)
+
+ // Set the bit from PRTG
+ ShiftLeft(1, And(PRTG, 0x0f), IRQ0)
+
+ Return (RTLG)
+ }
+
+ // Set Resource Setting for this IRQ link
+ Method (_SRS, 1, Serialized)
+ {
+ CreateWordField(Arg0, 1, IRQ0)
+
+ // Which bit is set?
+ FindSetRightBit(IRQ0, Local0)
+
+ Decrement(Local0)
+ Store(Local0, PRTG)
+ }
+
+ // Status
+ Method (_STA, 0, Serialized)
+ {
+ If(And(PRTG, 0x80)) {
+ Return (0x9)
+ } Else {
+ Return (0xb)
+ }
+ }
+}
+
+Device (LNKH)
+{
+ Name (_HID, EISAID("PNP0C0F"))
+ Name (_UID, 8)
+
+ // Disable method
+ Method (_DIS, 0, Serialized)
+ {
+ Store (0x80, PRTH)
+ }
+
+ // Possible Resource Settings for this Link
+ Name (_PRS, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared)
+ { 1, 3, 4, 5, 6, 7, 11, 12, 14, 15 }
+ })
+
+ // Current Resource Settings for this link
+ Method (_CRS, 0, Serialized)
+ {
+ Name (RTLH, ResourceTemplate()
+ {
+ IRQ(Level, ActiveLow, Shared) {}
+ })
+ CreateWordField(RTLH, 1, IRQ0)
+
+ // Clear the WordField
+ Store (Zero, IRQ0)
+
+ // Set the bit from PRTH
+ ShiftLeft(1, And(PRTH, 0x0f), IRQ0)
+
+ Return (RTLH)
+ }
+
+ // Set Resource Setting for this IRQ link
+ Method (_SRS, 1, Serialized)
+ {
+ CreateWordField(Arg0, 1, IRQ0)
+
+ // Which bit is set?
+ FindSetRightBit(IRQ0, Local0)
+
+ Decrement(Local0)
+ Store(Local0, PRTH)
+ }
+
+ // Status
+ Method (_STA, 0, Serialized)
+ {
+ If(And(PRTH, 0x80)) {
+ Return (0x9)
+ } Else {
+ Return (0xb)
+ }
+ }
+}
+
diff --git a/src/southbridge/intel/i82801hx/acpi/lpc.asl b/src/southbridge/intel/i82801hx/acpi/lpc.asl
new file mode 100644
index 0000000..65995dd
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/lpc.asl
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+// Intel LPC Bus Device - 0:1f.0
+
+Device (LPCB)
+{
+ Name(_ADR, 0x001f0000)
+
+ OperationRegion(LPC0, PCI_Config, 0x00, 0x100)
+ Field (LPC0, AnyAcc, NoLock, Preserve)
+ {
+ Offset (0x40),
+ PMBS, 16, // PMBASE
+ Offset (0x60), // Interrupt Routing Registers
+ PRTA, 8,
+ PRTB, 8,
+ PRTC, 8,
+ PRTD, 8,
+ Offset (0x68),
+ PRTE, 8,
+ PRTF, 8,
+ PRTG, 8,
+ PRTH, 8,
+
+ Offset (0x80), // IO Decode Ranges
+ IOD0, 8,
+ IOD1, 8,
+
+ Offset (0xf0), // RCBA
+ RCEN, 1,
+ , 13,
+ RCBA, 18,
+ }
+
+ #include "irqlinks.asl"
+
+ #include "acpi/ec.asl"
+
+ Device (DMAC) // DMA Controller
+ {
+ Name(_HID, EISAID("PNP0200"))
+ Name(_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x00, 0x00, 0x01, 0x20)
+ IO (Decode16, 0x81, 0x81, 0x01, 0x11)
+ IO (Decode16, 0x93, 0x93, 0x01, 0x0d)
+ IO (Decode16, 0xc0, 0xc0, 0x01, 0x20)
+ DMA (Compatibility, NotBusMaster, Transfer8_16) { 4 }
+ })
+ }
+
+ Device (FWH) // Firmware Hub
+ {
+ Name (_HID, EISAID("INT0800"))
+ Name (_CRS, ResourceTemplate()
+ {
+ Memory32Fixed(ReadOnly, 0xff000000, 0x01000000)
+ })
+ }
+
+ Device (HPET)
+ {
+ Name (_HID, EISAID("PNP0103"))
+ Name (_CID, 0x010CD041)
+
+ Name(BUF0, ResourceTemplate()
+ {
+ Memory32Fixed(ReadOnly, 0xfed00000, 0x400, FED0)
+ })
+
+ Method (_STA, 0) // Device Status
+ {
+ If (HPTE) {
+ // Note: Ancient versions of Windows don't want
+ // to see the HPET in order to work right
+ If (LGreaterEqual(OSYS, 2001)) {
+ Return (0xf) // Enable and show device
+ } Else {
+ Return (0xb) // Enable and don't show device
+ }
+ }
+
+ Return (0x0) // Not enabled, don't show.
+ }
+
+ Method (_CRS, 0, Serialized) // Current resources
+ {
+ If (HPTE) {
+ CreateDWordField(BUF0, \_SB.PCI0.LPCB.HPET.FED0._BAS, HPT0)
+ If (Lequal(HPAS, 1)) {
+ Store(0xfed01000, HPT0)
+ }
+
+ If (Lequal(HPAS, 2)) {
+ Store(0xfed02000, HPT0)
+ }
+
+ If (Lequal(HPAS, 3)) {
+ Store(0xfed03000, HPT0)
+ }
+ }
+
+ Return (BUF0)
+ }
+ }
+
+ Device(PIC) // 8259 Interrupt Controller
+ {
+ Name(_HID,EISAID("PNP0000"))
+ Name(_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x20, 0x20, 0x01, 0x02)
+ IO (Decode16, 0x24, 0x24, 0x01, 0x02)
+ IO (Decode16, 0x28, 0x28, 0x01, 0x02)
+ IO (Decode16, 0x2c, 0x2c, 0x01, 0x02)
+ IO (Decode16, 0x30, 0x30, 0x01, 0x02)
+ IO (Decode16, 0x34, 0x34, 0x01, 0x02)
+ IO (Decode16, 0x38, 0x38, 0x01, 0x02)
+ IO (Decode16, 0x3c, 0x3c, 0x01, 0x02)
+ IO (Decode16, 0xa0, 0xa0, 0x01, 0x02)
+ IO (Decode16, 0xa4, 0xa4, 0x01, 0x02)
+ IO (Decode16, 0xa8, 0xa8, 0x01, 0x02)
+ IO (Decode16, 0xac, 0xac, 0x01, 0x02)
+ IO (Decode16, 0xb0, 0xb0, 0x01, 0x02)
+ IO (Decode16, 0xb4, 0xb4, 0x01, 0x02)
+ IO (Decode16, 0xb8, 0xb8, 0x01, 0x02)
+ IO (Decode16, 0xbc, 0xbc, 0x01, 0x02)
+ IO (Decode16, 0x4d0, 0x4d0, 0x01, 0x02)
+ IRQNoFlags () { 2 }
+ })
+ }
+
+ Device(MATH) // FPU
+ {
+ Name (_HID, EISAID("PNP0C04"))
+ Name (_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0xf0, 0xf0, 0x01, 0x01)
+ IRQNoFlags() { 13 }
+ })
+ }
+
+ Device(LDRC) // LPC device: Resource consumption
+ {
+ Name (_HID, EISAID("PNP0C02"))
+ Name (_UID, 2)
+ Name (_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x2e, 0x2e, 0x1, 0x02) // First SuperIO
+ IO (Decode16, 0x4e, 0x4e, 0x1, 0x02) // Second SuperIO
+ IO (Decode16, 0x61, 0x61, 0x1, 0x01) // NMI Status
+ IO (Decode16, 0x63, 0x63, 0x1, 0x01) // CPU Reserved
+ IO (Decode16, 0x65, 0x65, 0x1, 0x01) // CPU Reserved
+ IO (Decode16, 0x67, 0x67, 0x1, 0x01) // CPU Reserved
+ IO (Decode16, 0x80, 0x80, 0x1, 0x01) // Port 80 Post
+ IO (Decode16, 0x92, 0x92, 0x1, 0x01) // CPU Reserved
+ IO (Decode16, 0xb2, 0xb2, 0x1, 0x02) // SWSMI
+ IO (Decode16, 0x800, 0x800, 0x1, 0x10) // ACPI I/O trap
+ IO (Decode16, DEFAULT_PMBASE, DEFAULT_PMBASE, 0x1, 0x80) // ICH8-M ACPI
+ IO (Decode16, DEFAULT_GPIOBASE, DEFAULT_GPIOBASE, 0x1, 0x40) // ICH8-M GPIO
+ })
+ }
+
+ Device (RTC) // Real Time Clock
+ {
+ Name (_HID, EISAID("PNP0B00"))
+ Name (_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x70, 0x70, 1, 8)
+// Disable as Windows doesn't like it, and systems don't seem to use it.
+// IRQNoFlags() { 8 }
+ })
+ }
+
+ Device (TIMR) // Intel 8254 timer
+ {
+ Name(_HID, EISAID("PNP0100"))
+ Name(_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x40, 0x40, 0x01, 0x04)
+ IO (Decode16, 0x50, 0x50, 0x10, 0x04)
+ IRQNoFlags() {0}
+ })
+ }
+
+ #include "acpi/superio.asl"
+
+#ifdef ENABLE_TPM
+ Device (TPM) // Trusted Platform Module
+ {
+ Name(_HID, EISAID("IFX0102"))
+ Name(_CID, 0x310cd041)
+ Name(_UID, 1)
+
+ Method(_STA, 0)
+ {
+ If (TPMP) {
+ Return (0xf)
+ }
+ Return (0x0)
+ }
+
+ Name(_CRS, ResourceTemplate() {
+ IO (Decode16, 0x2e, 0x2e, 0x01, 0x02)
+ IO (Decode16, 0x6f0, 0x6f0, 0x01, 0x10)
+ Memory32Fixed (ReadWrite, 0xfed40000, 0x5000)
+ IRQ (Edge, Activehigh, Exclusive) { 6 }
+ })
+ }
+#endif
+
+ Device (PS2K) // Keyboard
+ {
+ Name(_HID, EISAID("PNP0303"))
+ Name(_CID, EISAID("PNP030B"))
+
+ Name(_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x60, 0x60, 0x01, 0x01)
+ IO (Decode16, 0x64, 0x64, 0x01, 0x01)
+ IRQ (Edge, ActiveHigh, Exclusive) { 0x01 } // IRQ 1
+ })
+
+ Method (_STA, 0)
+ {
+ Return (0xf)
+ }
+ }
+
+ Device (PS2M) // Mouse
+ {
+ Name(_HID, EISAID("PNP0F13"))
+ Name(_CRS, ResourceTemplate()
+ {
+ IRQ (Edge, ActiveHigh, Exclusive) { 0x0c } // IRQ 12
+ })
+
+ Method(_STA, 0)
+ {
+ Return (0xf)
+ }
+ }
+
+#ifdef ENABLE_FDC
+ Device (FDC0) // Floppy controller
+ {
+ Name (_HID, EisaId ("PNP0700"))
+ Method (_STA, 0, NotSerialized)
+ {
+ Return (0x0f) // FIXME
+ }
+
+ Name(_CRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x03F0, 0x03F0, 0x01, 0x06)
+ IO (Decode16, 0x03F7, 0x03F7, 0x01, 0x01)
+ IRQNoFlags () {6}
+ DMA (Compatibility, NotBusMaster, Transfer8) {2}
+ })
+
+ Name(_PRS, ResourceTemplate()
+ {
+ IO (Decode16, 0x03F0, 0x03F0, 0x01, 0x06)
+ IO (Decode16, 0x03F7, 0x03F7, 0x01, 0x01)
+ IRQNoFlags () {6}
+ DMA (Compatibility, NotBusMaster, Transfer8) {2}
+ })
+
+ }
+#endif
+}
diff --git a/src/southbridge/intel/i82801hx/acpi/pata.asl b/src/southbridge/intel/i82801hx/acpi/pata.asl
new file mode 100644
index 0000000..1905ed2
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/pata.asl
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+// Intel PATA Controller 0:1f.1
+
+Device (PATA)
+{
+ Name (_ADR, 0x001f0001)
+
+ Device (PRID)
+ {
+ Name (_ADR, 0)
+
+ // Get Timing Mode
+ Method (_GTM)
+ {
+ Name(PBUF, Buffer(20) {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 })
+
+ CreateDwordField (PBUF, 0, PIO0)
+ CreateDwordField (PBUF, 4, DMA0)
+ CreateDwordField (PBUF, 8, PIO1)
+ CreateDwordField (PBUF, 12, DMA1)
+ CreateDwordField (PBUF, 16, FLAG)
+
+ // TODO fill return structure
+
+ Return (PBUF)
+ }
+
+ // Set Timing Mode
+ Method (_STM, 3)
+ {
+ CreateDwordField (Arg0, 0, PIO0)
+ CreateDwordField (Arg0, 4, DMA0)
+ CreateDwordField (Arg0, 8, PIO1)
+ CreateDwordField (Arg0, 12, DMA1)
+ CreateDwordField (Arg0, 16, FLAG)
+
+ // TODO: Do the deed
+ }
+
+ Device (DSK0)
+ {
+ Name (_ADR, 0)
+ // TODO: _RMV ?
+ // TODO: _GTF ?
+ }
+
+ Device (DSK1)
+ {
+ Name (_ADR, 1)
+
+ // TODO: _RMV ?
+ // TODO: _GTF ?
+ }
+
+ }
+}
+
diff --git a/src/southbridge/intel/i82801hx/acpi/pci.asl b/src/southbridge/intel/i82801hx/acpi/pci.asl
new file mode 100644
index 0000000..d253d51
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/pci.asl
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+// Intel PCI to PCI bridge 0:1e.0
+
+Device (PCIB)
+{
+ Name (_ADR, 0x001e0000)
+
+ Device (SLT1)
+ {
+ Name (_ADR, 0x00000000)
+ Name (_PRW, Package(){ 11, 4 })
+ }
+
+ Device (SLT2)
+ {
+ Name (_ADR, 0x00010000)
+ Name (_PRW, Package(){ 11, 4 })
+ }
+
+ Device (SLT3)
+ {
+ Name (_ADR, 0x00020000)
+ Name (_PRW, Package(){ 11, 4 })
+ }
+
+ Device (SLT6)
+ {
+ Name (_ADR, 0x00050000)
+ Name (_PRW, Package(){ 11, 4 })
+ }
+
+ Device (LANC)
+ {
+ Name (_ADR, 0x00080000)
+ Name (_PRW, Package(){ 11, 3 })
+ }
+
+ Device (LANR)
+ {
+ Name (_ADR, 0x00000000)
+ Name (_PRW, Package(){ 11, 3 })
+ }
+
+ // TODO: How many slots, where?
+
+ // PCI Interrupt Routing.
+ // If PICM is set, interrupts are routed over the i8259, otherwise
+ // over the IOAPIC. (Really? If they're above 15 they need to be routed
+ // fixed over the IOAPIC?)
+
+ Method (_PRT)
+ {
+ #include "acpi/ich7_pci_irqs.asl"
+ }
+
+}
+
diff --git a/src/southbridge/intel/i82801hx/acpi/pcie.asl b/src/southbridge/intel/i82801hx/acpi/pcie.asl
new file mode 100644
index 0000000..f055f1a
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/pcie.asl
@@ -0,0 +1,186 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+/* Intel i82801H PCIe support */
+
+// PCI Express Ports
+
+Device (RP01)
+{
+ NAME(_ADR, 0x001c0000) // FIXME: Have a macro for PCI Devices -> ACPI notation?
+ //#include "pcie_port.asl"
+ Method(_PRT)
+ {
+ If (PICM) {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, 0, 16 },
+ Package() { 0x0000ffff, 1, 0, 17 },
+ Package() { 0x0000ffff, 2, 0, 18 },
+ Package() { 0x0000ffff, 3, 0, 19 }
+ })
+ } Else {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKA, 0 },
+ Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKB, 0 },
+ Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKC, 0 },
+ Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKD, 0 }
+ })
+
+ }
+
+ }
+}
+
+Device (RP02)
+{
+ NAME(_ADR, 0x001c0001) // FIXME: Have a macro for PCI Devices -> ACPI notation?
+ //#include "pcie_port.asl"
+ Method(_PRT)
+ {
+ If (PICM) {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, 0, 17 },
+ Package() { 0x0000ffff, 1, 0, 18 },
+ Package() { 0x0000ffff, 2, 0, 19 },
+ Package() { 0x0000ffff, 3, 0, 16 }
+ })
+ } Else {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKB, 0 },
+ Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKC, 0 },
+ Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKD, 0 },
+ Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKA, 0 }
+ })
+
+ }
+
+ }
+}
+
+
+Device (RP03)
+{
+ NAME(_ADR, 0x001c0002) // FIXME: Have a macro for PCI Devices -> ACPI notation?
+ //#include "pcie_port.asl"
+ Method(_PRT)
+ {
+ If (PICM) {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, 0, 18 },
+ Package() { 0x0000ffff, 1, 0, 19 },
+ Package() { 0x0000ffff, 2, 0, 16 },
+ Package() { 0x0000ffff, 3, 0, 17 }
+ })
+ } Else {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKC, 0 },
+ Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKD, 0 },
+ Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKA, 0 },
+ Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKB, 0 }
+ })
+
+ }
+
+ }
+}
+
+
+Device (RP04)
+{
+ NAME(_ADR, 0x001c0003) // FIXME: Have a macro for PCI Devices -> ACPI notation?
+ //#include "pcie_port.asl"
+ Method(_PRT)
+ {
+ If (PICM) {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, 0, 19 },
+ Package() { 0x0000ffff, 1, 0, 16 },
+ Package() { 0x0000ffff, 2, 0, 17 },
+ Package() { 0x0000ffff, 3, 0, 18 }
+ })
+ } Else {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKD, 0 },
+ Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKA, 0 },
+ Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKB, 0 },
+ Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKC, 0 }
+ })
+
+ }
+
+ }
+}
+
+
+Device (RP05)
+{
+ NAME(_ADR, 0x001c0004) // FIXME: Have a macro for PCI Devices -> ACPI notation?
+ //#include "pcie_port.asl"
+ Method(_PRT)
+ {
+ If (PICM) {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, 0, 16 },
+ Package() { 0x0000ffff, 1, 0, 17 },
+ Package() { 0x0000ffff, 2, 0, 18 },
+ Package() { 0x0000ffff, 3, 0, 19 }
+ })
+ } Else {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKA, 0 },
+ Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKB, 0 },
+ Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKC, 0 },
+ Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKD, 0 }
+ })
+
+ }
+
+ }
+}
+
+
+Device (RP06)
+{
+ NAME(_ADR, 0x001c0005) // FIXME: Have a macro for PCI Devices -> ACPI notation?
+ //#include "pcie_port.asl"
+ Method(_PRT)
+ {
+ If (PICM) {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, 0, 17 },
+ Package() { 0x0000ffff, 1, 0, 18 },
+ Package() { 0x0000ffff, 2, 0, 19 },
+ Package() { 0x0000ffff, 3, 0, 16 }
+ })
+ } Else {
+ Return (Package() {
+ Package() { 0x0000ffff, 0, \_SB.PCI0.LPCB.LNKB, 0 },
+ Package() { 0x0000ffff, 1, \_SB.PCI0.LPCB.LNKC, 0 },
+ Package() { 0x0000ffff, 2, \_SB.PCI0.LPCB.LNKD, 0 },
+ Package() { 0x0000ffff, 3, \_SB.PCI0.LPCB.LNKA, 0 }
+ })
+
+ }
+
+ }
+}
+
+
diff --git a/src/southbridge/intel/i82801hx/acpi/sata.asl b/src/southbridge/intel/i82801hx/acpi/sata.asl
new file mode 100644
index 0000000..e0c336a
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/sata.asl
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+// Intel SATA Controller 0:1f.2
+
+// Note: Some BIOSes put the S-ATA code into an SSDT to make it easily
+// pluggable
+
+Device (SATA)
+{
+ Name (_ADR, 0x001f0002)
+
+ Device (PRID)
+ {
+ Name (_ADR, 0)
+
+ // Get Timing Mode
+ Method (_GTM)
+ {
+ Name(PBUF, Buffer(20) {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 })
+
+ CreateDwordField (PBUF, 0, PIO0)
+ CreateDwordField (PBUF, 4, DMA0)
+ CreateDwordField (PBUF, 8, PIO1)
+ CreateDwordField (PBUF, 12, DMA1)
+ CreateDwordField (PBUF, 16, FLAG)
+
+ // TODO fill return structure
+
+ Return (PBUF)
+ }
+
+ // Set Timing Mode
+ Method (_STM, 3)
+ {
+ CreateDwordField (Arg0, 0, PIO0)
+ CreateDwordField (Arg0, 4, DMA0)
+ CreateDwordField (Arg0, 8, PIO1)
+ CreateDwordField (Arg0, 12, DMA1)
+ CreateDwordField (Arg0, 16, FLAG)
+
+ // TODO: Do the deed
+ }
+
+ Device (DSK0)
+ {
+ Name (_ADR, 0)
+ // TODO: _RMV ?
+ // TODO: _GTF ?
+ }
+
+ Device (DSK1)
+ {
+ Name (_ADR, 1)
+
+ // TODO: _RMV ?
+ // TODO: _GTF ?
+ }
+
+ }
+}
+
diff --git a/src/southbridge/intel/i82801hx/acpi/sleepstates.asl b/src/southbridge/intel/i82801hx/acpi/sleepstates.asl
new file mode 100644
index 0000000..06bfcb6
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/sleepstates.asl
@@ -0,0 +1,27 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+Name(\_S0, Package(){0x0,0x0,0x0,0x0})
+// Name(\_S1, Package(){0x1,0x1,0x0,0x0})
+Name(\_S3, Package(){0x5,0x5,0x0,0x0})
+Name(\_S4, Package(){0x6,0x6,0x0,0x0})
+Name(\_S5, Package(){0x7,0x7,0x0,0x0})
+
diff --git a/src/southbridge/intel/i82801hx/acpi/smbus.asl b/src/southbridge/intel/i82801hx/acpi/smbus.asl
new file mode 100644
index 0000000..b6d2d6a
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/smbus.asl
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+// Intel SMBus Controller 0:1f.3
+
+Device (SBUS)
+{
+ Name (_ADR, 0x001f0003)
+
+ OperationRegion (SMBP, PCI_Config, 0x00, 0x100)
+ Field(SMBP, DWordAcc, NoLock, Preserve)
+ {
+ Offset(0x40),
+ , 2,
+ I2CE, 1
+ }
+
+ OperationRegion (SMBI, SystemIO, 0x400, 0x20)
+ Field (SMBI, ByteAcc, NoLock, Preserve)
+ {
+ HSTS, 8, // Host Status
+ , 8,
+ HCNT, 8, // Host Control
+ HCMD, 8, // Host Command
+ TXSA, 8, // Transmit Slave Address
+ DAT0, 8, // Host Data 0
+ DAT1, 8, // Host Data 1
+ HBDB, 8, // Host Block Data Byte
+ PECK, 8, // Packet Error Check
+ RXSA, 8, // Receive Slave Address
+ RXDA, 16, // Receive Slave Data
+ AUXS, 8, // Auxiliary Status
+ AUXC, 8, // Auxiliary Control
+ SLPC, 8, // SMLink Pin Control
+ SBPC, 8, // SMBus Pin Control
+ SSTS, 8, // Slave Status
+ SCMD, 8, // Slave Command
+ NADR, 8, // Notify Device Address
+ NDLB, 8, // Notify Data Low Byte
+ NDLH, 8, // Notify Data High Byte
+ }
+
+#ifdef ENABLE_SMBUS_METHODS
+ // Kill all SMBus communication
+ Method (KILL, 0, Serialized)
+ {
+ Or (HCNT, 0x02, HCNT) // Send Kill
+ Or (HSTS, 0xff, HSTS) // Clean Status
+ }
+
+ // Check if last operation completed
+ // return Failure = 0, Success = 1
+ Method (CMPL, 0, Serialized)
+ {
+ Store (4000, Local0) // Timeout 200ms in 50us steps
+ While (Local0) {
+ If (And(HSTS, 0x02)) { // Completion Status?
+ Return (1) // Operation Completed
+ } Else {
+ Stall (50)
+ Decrement (Local0)
+ If (LEqual(Local0, 0)) {
+ KILL()
+ }
+ }
+ }
+
+ Return (0) // Failure
+ }
+
+
+ // Wait for SMBus to become ready
+ Method (SRDY, 0, Serialized)
+ {
+ Store (200, Local0) // Timeout 200ms
+ While (Local0) {
+ If (And(HSTS, 0x40)) { // IN_USE?
+ Sleep(1) // Wait 1ms
+ Decrement(Local0) // timeout--
+ If (LEqual(Local0, 0)) {
+ Return (1)
+ }
+ } Else {
+ Store (0, Local0) // We're ready
+ }
+ }
+
+ Store (4000, Local0) // Timeout 200ms (50us * 4000)
+ While (Local0) {
+ If (And (HSTS, 0x01)) { // Host Busy?
+ Stall(50) // Wait 50us
+ Decrement(Local0) // timeout--
+ If (LEqual(Local0, 0)) {
+ KILL()
+ }
+ } Else {
+ Return (0) // Success
+ }
+ }
+
+ Return (1) // Failure
+ }
+
+ // SMBus Send Byte
+ // Arg0: Address
+ // Arg1: Data
+ // Return: 1 = Success, 0=Failure
+
+ Method (SSXB, 2, Serialized)
+ {
+
+ // Is the SMBus Controller Ready?
+ If (SRDY()) {
+ Return (0)
+ }
+
+ // Send Byte
+ Store (0, I2CE) // SMBus Enable
+ Store (0xbf, HSTS)
+ Store (Arg0, TXSA) // Write Address
+ Store (Arg1, HCMD) // Write Data
+
+ Store (0x48, HCNT) // Start + Byte Data Protocol
+
+ If (CMPL()) {
+ Or (HSTS, 0xff, HSTS) // Clean up
+ Return (1) // Success
+ }
+
+ Return (0)
+ }
+
+
+ // SMBus Receive Byte
+ // Arg0: Address
+ // Return: 0xffff = Failure, Data (8bit) = Success
+
+ Method (SRXB, 2, Serialized)
+ {
+
+ // Is the SMBus Controller Ready?
+ If (SRDY()) {
+ Return (0xffff)
+ }
+
+ // Receive Byte
+ Store (0, I2CE) // SMBus Enable
+ Store (0xbf, HSTS)
+ Store (Or (Arg0, 1), TXSA) // Write Address
+
+ Store (0x44, HCNT) // Start
+
+ If (CMPL()) {
+ Or (HSTS, 0xff, HSTS) // Clean up
+ Return (DAT0) // Success
+ }
+
+ Return (0xffff)
+ }
+
+
+ // SMBus Write Byte
+ // Arg0: Address
+ // Arg1: Command
+ // Arg2: Data
+ // Return: 1 = Success, 0=Failure
+
+ Method (SWRB, 3, Serialized)
+ {
+
+ // Is the SMBus Controller Ready?
+ If (SRDY()) {
+ Return (0)
+ }
+
+ // Send Byte
+ Store (0, I2CE) // SMBus Enable
+ Store (0xbf, HSTS)
+ Store (Arg0, TXSA) // Write Address
+ Store (Arg1, HCMD) // Write Command
+ Store (Arg2, DAT0) // Write Data
+
+ Store (0x48, HCNT) // Start + Byte Protocol
+
+ If (CMPL()) {
+ Or (HSTS, 0xff, HSTS) // Clean up
+ Return (1) // Success
+ }
+
+ Return (0)
+ }
+
+
+ // SMBus Read Byte
+ // Arg0: Address
+ // Arg1: Command
+ // Return: 0xffff = Failure, Data (8bit) = Success
+
+ Method (SRDB, 2, Serialized)
+ {
+
+ // Is the SMBus Controller Ready?
+ If (SRDY()) {
+ Return (0xffff)
+ }
+
+ // Receive Byte
+ Store (0, I2CE) // SMBus Enable
+ Store (0xbf, HSTS)
+ Store (Or (Arg0, 1), TXSA) // Write Address
+ Store (Arg1, HCMD) // Command
+
+ Store (0x48, HCNT) // Start
+
+ If (CMPL()) {
+ Or (HSTS, 0xff, HSTS) // Clean up
+ Return (DAT0) // Success
+ }
+
+ Return (0xffff)
+ }
+#endif
+}
+
diff --git a/src/southbridge/intel/i82801hx/acpi/usb.asl b/src/southbridge/intel/i82801hx/acpi/usb.asl
new file mode 100644
index 0000000..c1f2073
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/acpi/usb.asl
@@ -0,0 +1,217 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+/* Intel i82801H USB support */
+
+// USB Controller 0:1d.0
+
+Device (USB1)
+{
+ Name(_ADR, 0x001d0000)
+
+ OperationRegion(U01P, PCI_Config, 0, 256)
+ Field(U01P, DWordAcc, NoLock, Preserve)
+ {
+ Offset(0xc4),
+ U1WE, 2 // USB Wake Enable
+ }
+
+ Name (_PRW, Package(){ 3, 4 }) // Power Resources for Wake
+
+ Method (_PSW, 1) // Power State Wake method
+ {
+ // USB Controller can wake OS from Sleep State
+ If (Arg0) {
+ Store (3, U1WE)
+ } Else {
+ Store (0, U1WE)
+ }
+ }
+
+ // Leave USB ports on for to allow Wake from USB
+
+ Method(_S3D,0) // Highest D State in S3 State
+ {
+ Return (2)
+ }
+
+ Method(_S4D,0) // Highest D State in S4 State
+ {
+ Return (2)
+ }
+}
+
+
+// USB Controller 0:1d.1
+
+Device (USB2)
+{
+ Name(_ADR, 0x001d0001)
+
+ OperationRegion(U02P, PCI_Config, 0, 256)
+ Field(U02P, DWordAcc, NoLock, Preserve)
+ {
+ Offset(0xc4),
+ U2WE, 2 // USB Wake Enable
+ }
+
+ Name (_PRW, Package(){ 3, 4 }) // Power Resources for Wake
+
+ Method (_PSW, 1) // Power State Wake method
+ {
+ // USB Controller can wake OS from Sleep State
+ If (Arg0) {
+ Store (3, U2WE)
+ } Else {
+ Store (0, U2WE)
+ }
+ }
+
+ // Leave USB ports on for to allow Wake from USB
+
+ Method(_S3D,0) // Highest D State in S3 State
+ {
+ Return (2)
+ }
+
+ Method(_S4D,0) // Highest D State in S4 State
+ {
+ Return (2)
+ }
+
+}
+
+
+// USB Controller 0:1d.2
+
+Device (USB3)
+{
+ Name(_ADR, 0x001d0002)
+
+ OperationRegion(U03P, PCI_Config, 0, 256)
+ Field(U03P, DWordAcc, NoLock, Preserve)
+ {
+ Offset(0xc4),
+ U3WE, 2 // USB Wake Enable
+ }
+
+ Name (_PRW, Package(){ 3, 4 }) // Power Resources for Wake
+
+ Method (_PSW, 1) // Power State Wake method
+ {
+ // USB Controller can wake OS from Sleep State
+ If (Arg0) {
+ Store (3, U3WE)
+ } Else {
+ Store (0, U3WE)
+ }
+ }
+
+ // Leave USB ports on for to allow Wake from USB
+
+ Method(_S3D,0) // Highest D State in S3 State
+ {
+ Return (2)
+ }
+
+ Method(_S4D,0) // Highest D State in S4 State
+ {
+ Return (2)
+ }
+
+}
+
+
+// USB Controller 0:1d.3
+
+Device (USB4)
+{
+ Name(_ADR, 0x001d0003)
+
+ OperationRegion(U04P, PCI_Config, 0, 256)
+ Field(U04P, DWordAcc, NoLock, Preserve)
+ {
+ Offset(0xc4),
+ U4WE, 2 // USB Wake Enable
+ }
+
+ Name (_PRW, Package(){ 3, 4 }) // Power Resources for Wake
+
+ Method (_PSW, 1) // Power State Wake method
+ {
+ // USB Controller can wake OS from Sleep State
+ If (Arg0) {
+ Store (3, U4WE)
+ } Else {
+ Store (0, U4WE)
+ }
+ }
+
+ // Leave USB ports on for to allow Wake from USB
+
+ Method(_S3D,0) // Highest D State in S3 State
+ {
+ Return (2)
+ }
+
+ Method(_S4D,0) // Highest D State in S4 State
+ {
+ Return (2)
+ }
+
+}
+
+
+// EHCI Controller 0:1d.7
+
+Device (EHC1)
+{
+ Name(_ADR, 0x001d0007)
+
+ Name (_PRW, Package(){ 13, 4 }) // Power Resources for Wake
+
+ // Leave USB ports on for to allow Wake from USB
+
+ Method(_S3D,0) // Highest D State in S3 State
+ {
+ Return (2)
+ }
+
+ Method(_S4D,0) // Highest D State in S4 State
+ {
+ Return (2)
+ }
+
+ Device (HUB7)
+ {
+ Name (_ADR, 0x00000000)
+
+ // How many are there?
+ Device (PRT1) { Name (_ADR, 1) } // USB Port 0
+ Device (PRT2) { Name (_ADR, 2) } // USB Port 1
+ Device (PRT3) { Name (_ADR, 3) } // USB Port 2
+ Device (PRT4) { Name (_ADR, 4) } // USB Port 3
+ Device (PRT5) { Name (_ADR, 5) } // USB Port 4
+ Device (PRT6) { Name (_ADR, 6) } // USB Port 5
+ }
+}
+
+
diff --git a/src/southbridge/intel/i82801hx/azalia.c b/src/southbridge/intel/i82801hx/azalia.c
new file mode 100644
index 0000000..4c02093
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/azalia.c
@@ -0,0 +1,342 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008 Advanced Micro Devices, Inc.
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <arch/io.h>
+#include <delay.h>
+#include "i82801hx.h"
+
+#define HDA_ICII_REG 0x68
+#define HDA_ICII_BUSY (1 << 0)
+#define HDA_ICII_VALID (1 << 1)
+
+typedef struct southbridge_intel_i82801hx_config config_t;
+
+static int set_bits(u32 port, u32 mask, u32 val)
+{
+ u32 reg32;
+ int count;
+
+ /* Write (val & mask) to port */
+ val &= mask;
+ reg32 = read32(port);
+ reg32 &= ~mask;
+ reg32 |= val;
+ write32(port, reg32);
+
+ /* Wait for readback of register to
+ * match what was just written to it
+ */
+ count = 50;
+ do {
+ /* Wait 1ms based on BKDG wait time */
+ mdelay(1);
+ reg32 = read32(port);
+ reg32 &= mask;
+ } while ((reg32 != val) && --count);
+
+ /* Timeout occurred */
+ if (!count)
+ return -1;
+ return 0;
+}
+
+static int codec_detect(u32 base)
+{
+ u32 reg32;
+
+ /* Set Bit0 to 0 to enter reset state (BAR + 0x8)[0] */
+ if (set_bits(base + 0x08, 1, 0) == -1)
+ goto no_codec;
+
+ /* Set Bit 0 to 1 to exit reset state (BAR + 0x8)[0] */
+ if (set_bits(base + 0x08, 1, 1) == -1)
+ goto no_codec;
+
+ /* Read in Codec location (BAR + 0xe)[2..0]*/
+ reg32 = read32(base + 0xe);
+ reg32 &= 0x0f;
+ if (!reg32)
+ goto no_codec;
+
+ return reg32;
+
+no_codec:
+ /* Codec Not found */
+ /* Put HDA back in reset (BAR + 0x8) [0] */
+ set_bits(base + 0x08, 1, 0);
+ printk(BIOS_DEBUG, "Azalia: No codec!\n");
+ return 0;
+}
+
+const u32 * cim_verb_data = NULL;
+u32 cim_verb_data_size = 0;
+
+static u32 find_verb(struct device *dev, u32 viddid, const u32 ** verb)
+{
+ int idx=0;
+
+ while (idx < (cim_verb_data_size / sizeof(u32))) {
+ u32 verb_size = 4 * cim_verb_data[idx+2]; // in u32
+ if (cim_verb_data[idx] != viddid) {
+ idx += verb_size + 3; // skip verb + header
+ continue;
+ }
+ *verb = &cim_verb_data[idx+3];
+ return verb_size;
+ }
+
+ /* Not all codecs need to load another verb */
+ return 0;
+}
+
+/**
+ * Wait 50usec for the codec to indicate it is ready
+ * no response would imply that the codec is non-operative
+ */
+
+static int wait_for_ready(u32 base)
+{
+ /* Use a 50 usec timeout - the Linux kernel uses the
+ * same duration */
+
+ int timeout = 50;
+
+ while(timeout--) {
+ u32 reg32 = read32(base + HDA_ICII_REG);
+ if (!(reg32 & HDA_ICII_BUSY))
+ return 0;
+ udelay(1);
+ }
+
+ return -1;
+}
+
+/**
+ * Wait 50usec for the codec to indicate that it accepted
+ * the previous command. No response would imply that the code
+ * is non-operative
+ */
+
+static int wait_for_valid(u32 base)
+{
+ u32 reg32;
+
+ /* Send the verb to the codec */
+ reg32 = read32(base + 0x68);
+ reg32 |= (1 << 0) | (1 << 1);
+ write32(base + 0x68, reg32);
+
+ /* Use a 50 usec timeout - the Linux kernel uses the
+ * same duration */
+
+ int timeout = 50;
+ while(timeout--) {
+ reg32 = read32(base + HDA_ICII_REG);
+ if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) ==
+ HDA_ICII_VALID)
+ return 0;
+ udelay(1);
+ }
+
+ return -1;
+}
+
+static void codec_init(struct device *dev, u32 base, int addr)
+{
+ u32 reg32;
+ const u32 *verb;
+ u32 verb_size;
+ int i;
+
+ printk(BIOS_DEBUG, "Azalia: Initializing codec #%d\n", addr);
+
+ /* 1 */
+ if (wait_for_ready(base) == -1)
+ return;
+
+ reg32 = (addr << 28) | 0x000f0000;
+ write32(base + 0x60, reg32);
+
+ if (wait_for_valid(base) == -1)
+ return;
+
+ reg32 = read32(base + 0x64);
+
+ /* 2 */
+ printk(BIOS_DEBUG, "Azalia: codec viddid: %08x\n", reg32);
+ verb_size = find_verb(dev, reg32, &verb);
+
+ if (!verb_size) {
+ printk(BIOS_DEBUG, "Azalia: No verb!\n");
+ return;
+ }
+ printk(BIOS_DEBUG, "Azalia: verb_size: %d\n", verb_size);
+
+ /* 3 */
+ for (i = 0; i < verb_size; i++) {
+ if (wait_for_ready(base) == -1)
+ return;
+
+ write32(base + 0x60, verb[i]);
+
+ if (wait_for_valid(base) == -1)
+ return;
+ }
+ printk(BIOS_DEBUG, "Azalia: verb loaded.\n");
+}
+
+static void codecs_init(struct device *dev, u32 base, u32 codec_mask)
+{
+ int i;
+ for (i = 2; i >= 0; i--) {
+ if (codec_mask & (1 << i))
+ codec_init(dev, base, i);
+ }
+}
+
+static void azalia_init(struct device *dev)
+{
+ u32 base;
+ struct resource *res;
+ u32 codec_mask;
+ u8 reg8;
+ u32 reg32;
+
+ // ESD
+ reg32 = pci_read_config32(dev, 0x134);
+ reg32 &= 0xff00ffff;
+ reg32 |= (2 << 16);
+ pci_write_config32(dev, 0x134, reg32);
+
+ // Link1 description
+ reg32 = pci_read_config32(dev, 0x140);
+ reg32 &= 0xff00ffff;
+ reg32 |= (2 << 16);
+ pci_write_config32(dev, 0x140, reg32);
+
+ // Port VC0 Resource Control Register
+ reg32 = pci_read_config32(dev, 0x114);
+ reg32 &= 0xffffff00;
+ reg32 |= 1;
+ pci_write_config32(dev, 0x114, reg32);
+
+ // VCi traffic class
+ reg8 = pci_read_config8(dev, 0x44);
+ reg8 |= (7 << 0); // TC7
+ pci_write_config8(dev, 0x44, reg8);
+
+ // VCi Resource Control
+ reg32 = pci_read_config32(dev, 0x120);
+ reg32 |= (1 << 31);
+ reg32 |= (1 << 24); // VCi ID
+ reg32 |= (0x80 << 0); // VCi map
+ pci_write_config32(dev, 0x120, reg32);
+
+ /* Set Bus Master */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER);
+
+ pci_write_config8(dev, 0x3c, 0x0a); // unused?
+
+ // TODO Actually check if we're AC97 or HDA instead of hardcoding this
+ // here, in devicetree.cb and/or romstage.c.
+ reg8 = pci_read_config8(dev, 0x40);
+ reg8 |= (1 << 3); // Clear Clock Detect Bit
+ pci_write_config8(dev, 0x40, reg8);
+ reg8 &= ~(1 << 3); // Keep CLKDETCLR from clearing the bit over and over
+ pci_write_config8(dev, 0x40, reg8);
+ reg8 |= (1 << 2); // Enable clock detection
+ pci_write_config8(dev, 0x40, reg8);
+ mdelay(1);
+ reg8 = pci_read_config8(dev, 0x40);
+ printk(BIOS_DEBUG, "Azalia: codec type: %s\n", (reg8 & (1 << 1))?"Azalia":"AC97");
+
+ //
+ reg8 = pci_read_config8(dev, 0x40); // Audio Control
+ reg8 |= 1; // Select Azalia mode. This needs to be controlled via devicetree.cb
+ pci_write_config8(dev, 0x40, reg8);
+
+ reg8 = pci_read_config8(dev, 0x4d); // Docking Status
+ reg8 &= ~(1 << 7); // Docking not supported
+ pci_write_config8(dev, 0x4d, reg8);
+#if 0
+ /* Set routing pin */
+ pci_write_config32(dev, 0xf8, 0x0);
+ pci_write_config8(dev, 0xfc, 0xAA);
+
+ /* Set INTA */
+ pci_write_config8(dev, 0x63, 0x0);
+
+ /* Enable azalia, disable ac97 */
+ // pm_iowrite(0x59, 0xB);
+#endif
+
+ res = find_resource(dev, 0x10);
+ if (!res)
+ return;
+
+ // NOTE this will break as soon as the Azalia get's a bar above
+ // 4G. Is there anything we can do about it?
+ base = (u32)res->base;
+ printk(BIOS_DEBUG, "Azalia: base = %08x\n", (u32)base);
+ codec_mask = codec_detect(base);
+
+ if (codec_mask) {
+ printk(BIOS_DEBUG, "Azalia: codec_mask = %02x\n", codec_mask);
+ codecs_init(dev, base, codec_mask);
+ }
+}
+
+static void azalia_set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ if (!vendor || !device) {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ pci_read_config32(dev, PCI_VENDOR_ID));
+ } else {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+}
+
+static struct pci_operations azalia_pci_ops = {
+ .set_subsystem = azalia_set_subsystem,
+};
+
+static struct device_operations azalia_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = azalia_init,
+ .scan_bus = 0,
+ .enable = i82801hx_enable,
+ .ops_pci = &azalia_pci_ops,
+};
+
+static const struct pci_driver i82801hx_azalia __pci_driver = {
+ .ops = &azalia_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x284b,
+};
+
diff --git a/src/southbridge/intel/i82801hx/bootblock.c b/src/southbridge/intel/i82801hx/bootblock.c
new file mode 100644
index 0000000..153a456
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/bootblock.c
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Sven Schnelle <svens at stackframe.org>
+ *
+ * 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/io.h>
+#include <cpu/x86/tsc.h>
+
+static void store_initial_timestamp(void)
+{
+ /* On i945/ICH7 we have two 32bit scratchpad registers available:
+ * D0:F0 0xdc (SKPAD)
+ * D31:F2 0xd0 (SATA SP)
+ */
+ tsc_t tsc = rdtsc();
+ pci_write_config32(PCI_DEV(0, 0x00, 0), 0xdc, tsc.lo);
+ pci_write_config32(PCI_DEV(0, 0x1f, 2), 0xd0, tsc.hi);
+}
+
+static void enable_spi_prefetch(void)
+{
+ u8 reg8;
+ device_t dev;
+
+ dev = PCI_DEV(0, 0x1f, 0);
+
+ reg8 = pci_read_config8(dev, 0xdc);
+ reg8 &= ~(3 << 2);
+ reg8 |= (2 << 2); /* Prefetching and Caching Enabled */
+ pci_write_config8(dev, 0xdc, reg8);
+}
+
+static void bootblock_southbridge_init(void)
+{
+#if CONFIG_COLLECT_TIMESTAMPS
+ store_initial_timestamp();
+#endif
+ enable_spi_prefetch();
+}
+
diff --git a/src/southbridge/intel/i82801hx/chip.h b/src/southbridge/intel/i82801hx/chip.h
new file mode 100644
index 0000000..0a92d48
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/chip.h
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 SOUTHBRIDGE_INTEL_I82801HX_CHIP_H
+#define SOUTHBRIDGE_INTEL_I82801HX_CHIP_H
+
+struct southbridge_intel_i82801hx_config {
+ /**
+ * Interrupt Routing configuration
+ * If bit7 is 1, the interrupt is disabled.
+ */
+ uint8_t pirqa_routing;
+ uint8_t pirqb_routing;
+ uint8_t pirqc_routing;
+ uint8_t pirqd_routing;
+ uint8_t pirqe_routing;
+ uint8_t pirqf_routing;
+ uint8_t pirqg_routing;
+ uint8_t pirqh_routing;
+
+ /**
+ * GPI Routing configuration
+ *
+ * Only the lower two bits have a meaning:
+ * 00: No effect
+ * 01: SMI# (if corresponding ALT_GPI_SMI_EN bit is also set)
+ * 10: SCI (if corresponding GPIO_EN bit is also set)
+ * 11: reserved
+ */
+ uint8_t gpi0_routing;
+ uint8_t gpi1_routing;
+ uint8_t gpi2_routing;
+ uint8_t gpi3_routing;
+ uint8_t gpi4_routing;
+ uint8_t gpi5_routing;
+ uint8_t gpi6_routing;
+ uint8_t gpi7_routing;
+ uint8_t gpi8_routing;
+ uint8_t gpi9_routing;
+ uint8_t gpi10_routing;
+ uint8_t gpi11_routing;
+ uint8_t gpi12_routing;
+ uint8_t gpi13_routing;
+ uint8_t gpi14_routing;
+ uint8_t gpi15_routing;
+
+ uint32_t gpe0_en;
+ uint16_t alt_gp_smi_en;
+
+ /* IDE configuration */
+ uint32_t ide_legacy_combined;
+ uint32_t ide_enable_primary;
+ uint32_t ide_enable_secondary;
+ uint32_t sata_ahci;
+ uint32_t sata_ports_implemented;
+
+ int c4onc3_enable:1;
+};
+
+#endif /* SOUTHBRIDGE_INTEL_I82801HX_CHIP_H */
diff --git a/src/southbridge/intel/i82801hx/early_lpc.c b/src/southbridge/intel/i82801hx/early_lpc.c
new file mode 100644
index 0000000..9f80d41
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/early_lpc.c
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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/io.h>
+#include <timestamp.h>
+
+#if CONFIG_COLLECT_TIMESTAMPS
+tsc_t get_initial_timestamp(void)
+{
+ tsc_t base_time = {
+ .lo = pci_read_config32(PCI_DEV(0, 0x00, 0), 0xdc),
+ .hi = pci_read_config32(PCI_DEV(0, 0x1f, 2), 0xd0)
+ };
+ return base_time;
+}
+#endif
diff --git a/src/southbridge/intel/i82801hx/early_smbus.c b/src/southbridge/intel/i82801hx/early_smbus.c
new file mode 100644
index 0000000..0edd1c2
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/early_smbus.c
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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/io.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+#include "i82801hx.h"
+#include "smbus.h"
+
+void enable_smbus(void)
+{
+ device_t dev;
+
+ /* Set the SMBus device statically. */
+ dev = PCI_DEV(0x0, 0x1f, 0x3);
+
+ /* Check to make sure we've got the right device. */
+ if (pci_read_config16(dev, 0x2) != 0x283e) {
+ die("SMBus controller not found!");
+ }
+
+ /* Set SMBus I/O base. */
+ pci_write_config32(dev, SMB_BASE,
+ SMBUS_IO_BASE | PCI_BASE_ADDRESS_SPACE_IO);
+
+ /* Set SMBus enable. */
+ pci_write_config8(dev, HOSTC, HST_EN);
+
+ /* Set SMBus I/O space enable. */
+ pci_write_config16(dev, PCI_COMMAND, PCI_COMMAND_IO);
+
+ /* Disable interrupt generation. */
+ outb(0, SMBUS_IO_BASE + SMBHSTCTL);
+
+ /* Clear any lingering errors, so transactions can run. */
+ outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);
+ print_debug("SMBus controller enabled.\n");
+}
+
+int smbus_read_byte(unsigned device, unsigned address)
+{
+ return do_smbus_read_byte(SMBUS_IO_BASE, device, address);
+}
+
diff --git a/src/southbridge/intel/i82801hx/i82801hx.c b/src/southbridge/intel/i82801hx/i82801hx.c
new file mode 100644
index 0000000..958ca43
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/i82801hx.c
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include "i82801hx.h"
+
+#if !CONFIG_MMCONF_SUPPORT_DEFAULT
+#error ICH8 requires CONFIG_MMCONF_SUPPORT_DEFAULT
+#endif
+
+void i82801hx_enable(device_t dev)
+{
+ u32 reg32;
+
+ /* Enable SERR */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_SERR;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+}
+
+struct chip_operations southbridge_intel_i82801hx_ops = {
+ CHIP_NAME("Intel ICH8/ICH8-M (82801Hx) Series Southbridge")
+ .enable_dev = i82801hx_enable,
+};
diff --git a/src/southbridge/intel/i82801hx/i82801hx.h b/src/southbridge/intel/i82801hx/i82801hx.h
new file mode 100644
index 0000000..3ef24be
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/i82801hx.h
@@ -0,0 +1,386 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 SOUTHBRIDGE_INTEL_I82801HX_I82801HX_H
+#define SOUTHBRIDGE_INTEL_I82801HX_I82801HX_H
+/*
+ * It does not matter where we put the SMBus I/O base, as long as we
+ * keep it consistent and don't interfere with other devices. Stage2
+ * will relocate this anyways.
+ * Our solution is to have SMB initialization move the I/O to SMBUS_IO_BASE
+ * again. But handling static BARs is a generic problem that should be
+ * solved in the device allocator.
+ */
+#define SMBUS_IO_BASE 0x0400
+/* TODO Make sure these don't get changed by stage2 */
+#define DEFAULT_GPIOBASE 0x0480
+#define DEFAULT_PMBASE 0x0500
+
+#define DEFAULT_RCBA 0xfed1c000
+
+#ifndef __ACPI__
+#define DEBUG_PERIODIC_SMIS 0
+
+#if !defined(__ASSEMBLER__)
+#if !defined(__PRE_RAM__)
+#include "chip.h"
+extern void i82801hx_enable(device_t dev);
+#else
+void enable_smbus(void);
+int smbus_read_byte(unsigned device, unsigned address);
+#endif
+#endif
+
+#define MAINBOARD_POWER_OFF 0
+#define MAINBOARD_POWER_ON 1
+#define MAINBOARD_POWER_KEEP 2
+
+#ifndef CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL
+#define CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL MAINBOARD_POWER_ON
+#endif
+
+/* PCI Configuration Space (D30:F0): PCI2PCI */
+#define PSTS 0x06
+#define SMLT 0x1b
+#define SECSTS 0x1e
+#define INTR 0x3c
+#define BCTRL 0x3e
+#define SBR (1 << 6)
+#define SEE (1 << 1)
+#define PERE (1 << 0)
+
+/* PCI Configuration Space (D31:F0): LPC */
+
+#define SERIRQ_CNTL 0x64
+
+#define GEN_PMCON_1 0xa0
+#define GEN_PMCON_2 0xa2
+#define GEN_PMCON_3 0xa4
+
+/* GEN_PMCON_3 bits */
+#define RTC_BATTERY_DEAD (1 << 2)
+#define RTC_POWER_FAILED (1 << 1)
+#define SLEEP_AFTER_POWER_FAIL (1 << 0)
+
+#define PMBASE 0x40
+#define ACPI_CNTL 0x44
+#define ACPI_EN (1 << 7)
+#define BIOS_CNTL 0xDC
+#define GPIO_BASE 0x48 /* LPC GPIO Base Address Register */
+#define GPIO_CNTL 0x4C /* LPC GPIO Control Register */
+
+#define PIRQA_ROUT 0x60
+#define PIRQB_ROUT 0x61
+#define PIRQC_ROUT 0x62
+#define PIRQD_ROUT 0x63
+#define PIRQE_ROUT 0x68
+#define PIRQF_ROUT 0x69
+#define PIRQG_ROUT 0x6A
+#define PIRQH_ROUT 0x6B
+
+#define LPC_IO_DEC 0x80 /* IO Decode Ranges Register */
+#define LPC_EN 0x82 /* LPC IF Enables Register */
+
+/* PCI Configuration Space (D31:F1): IDE */
+#define INTR_LN 0x3c
+#define IDE_TIM_PRI 0x40 /* IDE timings, primary */
+#define IDE_DECODE_ENABLE (1 << 15)
+#define IDE_SITRE (1 << 14)
+#define IDE_ISP_5_CLOCKS (0 << 12)
+#define IDE_ISP_4_CLOCKS (1 << 12)
+#define IDE_ISP_3_CLOCKS (2 << 12)
+#define IDE_RCT_4_CLOCKS (0 << 8)
+#define IDE_RCT_3_CLOCKS (1 << 8)
+#define IDE_RCT_2_CLOCKS (2 << 8)
+#define IDE_RCT_1_CLOCKS (3 << 8)
+#define IDE_DTE1 (1 << 7)
+#define IDE_PPE1 (1 << 6)
+#define IDE_IE1 (1 << 5)
+#define IDE_TIME1 (1 << 4)
+#define IDE_DTE0 (1 << 3)
+#define IDE_PPE0 (1 << 2)
+#define IDE_IE0 (1 << 1)
+#define IDE_TIME0 (1 << 0)
+#define IDE_TIM_SEC 0x42 /* IDE timings, secondary */
+
+#define IDE_SDMA_CNT 0x48 /* Synchronous DMA control */
+#define IDE_SSDE1 (1 << 3)
+#define IDE_SSDE0 (1 << 2)
+#define IDE_PSDE1 (1 << 1)
+#define IDE_PSDE0 (1 << 0)
+
+#define IDE_SDMA_TIM 0x4a
+
+#define IDE_CONFIG 0x54 /* IDE I/O Configuration Register */
+#define SIG_MODE_SEC_NORMAL (0 << 18)
+#define SIG_MODE_SEC_TRISTATE (1 << 18)
+#define SIG_MODE_SEC_DRIVELOW (2 << 18)
+#define SIG_MODE_PRI_NORMAL (0 << 16)
+#define SIG_MODE_PRI_TRISTATE (1 << 16)
+#define SIG_MODE_PRI_DRIVELOW (2 << 16)
+#define FAST_SCB1 (1 << 15)
+#define FAST_SCB0 (1 << 14)
+#define FAST_PCB1 (1 << 13)
+#define FAST_PCB0 (1 << 12)
+#define SCB1 (1 << 3)
+#define SCB0 (1 << 2)
+#define PCB1 (1 << 1)
+#define PCB0 (1 << 0)
+
+/* PCI Configuration Space (D31:F3): SMBus */
+#define SMB_BASE 0x20
+#define HOSTC 0x40
+
+/* HOSTC bits */
+#define I2C_EN (1 << 2)
+#define SMB_SMI_EN (1 << 1)
+#define HST_EN (1 << 0)
+
+/* SMBus I/O bits. */
+#define SMBHSTSTAT 0x0
+#define SMBHSTCTL 0x2
+#define SMBHSTCMD 0x3
+#define SMBXMITADD 0x4
+#define SMBHSTDAT0 0x5
+#define SMBHSTDAT1 0x6
+#define SMBBLKDAT 0x7
+#define SMBTRNSADD 0x9
+#define SMBSLVDATA 0xa
+#define SMLINK_PIN_CTL 0xe
+#define SMBUS_PIN_CTL 0xf
+
+#define SMBUS_TIMEOUT (10 * 1000 * 100)
+
+
+/* Southbridge IO BARs */
+
+#define GPIOBASE 0x48
+
+#define PMBASE 0x40
+
+/* Root Complex Register Block */
+#define RCBA 0xf0
+
+#define RCBA8(x) *((volatile u8 *)(DEFAULT_RCBA + x))
+#define RCBA16(x) *((volatile u16 *)(DEFAULT_RCBA + x))
+#define RCBA32(x) *((volatile u32 *)(DEFAULT_RCBA + x))
+
+#define VCH 0x0000 /* 32bit */
+#define VCAP1 0x0004 /* 32bit */
+#define VCAP2 0x0008 /* 32bit */
+#define PVC 0x000c /* 16bit */
+#define PVS 0x000e /* 16bit */
+
+#define V0CAP 0x0010 /* 32bit */
+#define V0CTL 0x0014 /* 32bit */
+#define V0STS 0x001a /* 16bit */
+
+#define V1CAP 0x001c /* 32bit */
+#define V1CTL 0x0020 /* 32bit */
+#define V1STS 0x0026 /* 16bit */
+
+#define PAT 0x0030
+#define CIR1 0x0088 /* 32bit */
+
+#define RCTCL 0x0100 /* 32bit */
+#define ESD 0x0104 /* 32bit */
+#define ULD 0x0110 /* 32bit */
+#define ULBA 0x0118 /* 64bit */
+
+#define RP1D 0x0120 /* 32bit */
+#define RP1BA 0x0128 /* 64bit */
+#define RP2D 0x0130 /* 32bit */
+#define RP2BA 0x0138 /* 64bit */
+#define RP3D 0x0140 /* 32bit */
+#define RP3BA 0x0148 /* 64bit */
+#define RP4D 0x0150 /* 32bit */
+#define RP4BA 0x0158 /* 64bit */
+#define HDD 0x0160 /* 32bit */
+#define HDBA 0x0168 /* 64bit */
+#define RP5D 0x0170 /* 32bit */
+#define RP5BA 0x0178 /* 64bit */
+#define RP6D 0x0180 /* 32bit */
+#define RP6BA 0x0188 /* 64bit */
+
+#define ILCL 0x01a0 /* 32bit */
+#define LCAP 0x01a4 /* 32bit */
+#define LCTL 0x01a8 /* 16bit */
+#define LSTS 0x01aa /* 16bit */
+
+#define CIR3 0x01fc /* 16bit */
+#define CIR4 0x0200 /* 16bit */
+#define BCR 0x0220 /* 32bit */
+#define RPC 0x0224 /* 32bit */
+#define DMIC 0x0234 /* 32bit */
+#define RPFN 0x0238 /* 32bit */
+
+#define CIR5 0x1d40 /* 64bit */
+
+#define TRSR 0x1e00 /* 8bit */
+#define TRCR 0x1e10 /* 64bit */
+#define TWDR 0x1e18 /* 64bit */
+
+#define IOTR0 0x1e80 /* 64bit */
+#define IOTR1 0x1e88 /* 64bit */
+#define IOTR2 0x1e90 /* 64bit */
+#define IOTR3 0x1e98 /* 64bit */
+
+#define DMC 0x2010 /* 32bit */
+#define CIR6 0x2024 /* 32bit */
+#define CIR7 0x2034 /* 32bit */
+
+#define TCTL 0x3000 /* 8bit */
+
+#define D31IP 0x3100 /* 32bit */
+#define D30IP 0x3104 /* 32bit */
+#define D29IP 0x3108 /* 32bit */
+#define D28IP 0x310c /* 32bit */
+#define D27IP 0x3110 /* 32bit */
+#define D31IR 0x3140 /* 16bit */
+#define D30IR 0x3142 /* 16bit */
+#define D29IR 0x3144 /* 16bit */
+#define D28IR 0x3146 /* 16bit */
+#define D27IR 0x3148 /* 16bit */
+#define OIC 0x31ff /* 8bit */
+
+#define RC 0x3400 /* 32bit */
+#define HPTC 0x3404 /* 32bit */
+#define GCS 0x3410 /* 32bit */
+#define BUC 0x3414 /* 32bit */
+#define FD 0x3418 /* 32bit */
+#define CG 0x341c /* 32bit */
+#define FDSW 0x3420 /* 8bit */
+#define CIR8 0x3430 /* 8bit */
+#define CIR9 0x350c /* 32bit */
+
+/* Function Disable (FD) register values.
+ * Setting a bit disables the corresponding
+ * feature.
+ * Not all features might be disabled on
+ * all chipsets. Esp. ICH-7U is picky.
+ */
+#define FD_PCIE6 (1 << 21)
+#define FD_PCIE5 (1 << 20)
+#define FD_PCIE4 (1 << 19)
+#define FD_PCIE3 (1 << 18)
+#define FD_PCIE2 (1 << 17)
+#define FD_PCIE1 (1 << 16)
+#define FD_EHCI (1 << 15)
+#define FD_LPCB (1 << 14)
+
+/* UHCI must be disabled from 4 downwards.
+ * If UHCI controllers get disabled, EHCI
+ * must know about it, too! */
+#define FD_UHCI4 (1 << 11)
+#define FD_UHCI34 (1 << 10) | FD_UHCI4
+#define FD_UHCI234 (1 << 9) | FD_UHCI3
+#define FD_UHCI1234 (1 << 8) | FD_UHCI2
+
+#define FD_INTLAN (1 << 7)
+#define FD_ACMOD (1 << 6)
+#define FD_ACAUD (1 << 5)
+#define FD_HDAUD (1 << 4)
+#define FD_SMBUS (1 << 3)
+#define FD_SATA (1 << 2)
+#define FD_PATA (1 << 1)
+
+/* ICH8 GPIOBASE */
+#define GPIO_USE_SEL 0x00
+#define GP_IO_SEL 0x04
+#define GP_LVL 0x0c
+#define GPO_BLINK 0x18
+#define GPI_INV 0x2c
+#define GPIO_USE_SEL2 0x30
+#define GP_IO_SEL2 0x34
+#define GP_LVL2 0x38
+
+/* ICH8 PMBASE */
+#define PM1_STS 0x00
+#define WAK_STS (1 << 15)
+#define PCIEXPWAK_STS (1 << 14)
+#define PRBTNOR_STS (1 << 11)
+#define RTC_STS (1 << 10)
+#define PWRBTN_STS (1 << 8)
+#define GBL_STS (1 << 5)
+#define BM_STS (1 << 4)
+#define TMROF_STS (1 << 0)
+#define PM1_EN 0x02
+#define PCIEXPWAK_DIS (1 << 14)
+#define RTC_EN (1 << 10)
+#define PWRBTN_EN (1 << 8)
+#define GBL_EN (1 << 5)
+#define TMROF_EN (1 << 0)
+#define PM1_CNT 0x04
+#define SLP_EN (1 << 13)
+#define SLP_TYP (7 << 10)
+#define GBL_RLS (1 << 2)
+#define BM_RLD (1 << 1)
+#define SCI_EN (1 << 0)
+#define PM1_TMR 0x08
+#define PROC_CNT 0x10
+#define LV2 0x14
+#define LV3 0x15
+#define LV4 0x16
+#define PM2_CNT 0x20 // mobile only
+#define GPE0_STS 0x28
+#define USB4_STS (1 << 14)
+#define PME_B0_STS (1 << 13)
+#define USB3_STS (1 << 12)
+#define PME_STS (1 << 11)
+#define BATLOW_STS (1 << 10)
+#define PCI_EXP_STS (1 << 9)
+#define RI_STS (1 << 8)
+#define SMB_WAK_STS (1 << 7)
+#define TCOSCI_STS (1 << 6)
+#define AC97_STS (1 << 5)
+#define USB2_STS (1 << 4)
+#define USB1_STS (1 << 3)
+#define SWGPE_STS (1 << 2)
+#define HOT_PLUG_STS (1 << 1)
+#define THRM_STS (1 << 0)
+#define GPE0_EN 0x2c
+#define PME_B0_EN (1 << 13)
+#define PME_EN (1 << 11)
+#define SMI_EN 0x30
+#define EL_SMI_EN (1 << 25) // Intel Quick Resume Technology
+#define INTEL_USB2_EN (1 << 18) // Intel-Specific USB2 SMI logic
+#define LEGACY_USB2_EN (1 << 17) // Legacy USB2 SMI logic
+#define PERIODIC_EN (1 << 14) // SMI on PERIODIC_STS in SMI_STS
+#define TCO_EN (1 << 13) // Enable TCO Logic (BIOSWE et al)
+#define MCSMI_EN (1 << 11) // Trap microcontroller range access
+#define BIOS_RLS (1 << 7) // asserts SCI on bit set
+#define SWSMI_TMR_EN (1 << 6) // start software smi timer on bit set
+#define APMC_EN (1 << 5) // Writes to APM_CNT cause SMI#
+#define SLP_SMI_EN (1 << 4) // Write to SLP_EN in PM1_CNT asserts SMI#
+#define LEGACY_USB_EN (1 << 3) // Legacy USB circuit SMI logic
+#define BIOS_EN (1 << 2) // Assert SMI# on setting GBL_RLS bit
+#define EOS (1 << 1) // End of SMI (deassert SMI#)
+#define GBL_SMI_EN (1 << 0) // SMI# generation at all?
+#define SMI_STS 0x34
+#define ALT_GP_SMI_EN 0x38
+#define ALT_GP_SMI_STS 0x3a
+#define GPE_CNTL 0x42
+#define DEVACT_STS 0x44
+#define SS_CNT 0x50
+#define C3_RES 0x54
+
+#define SKPAD_ACPI_S3_MAGIC 0xcafed00d
+#define SKPAD_NORMAL_BOOT_MAGIC 0xcafebabe
+#endif /* __ACPI__ */
+#endif /* SOUTHBRIDGE_INTEL_I82801HX_I82801HX_H */
diff --git a/src/southbridge/intel/i82801hx/ide.c b/src/southbridge/intel/i82801hx/ide.c
new file mode 100644
index 0000000..64ae2ae
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/ide.c
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include "i82801hx.h"
+
+typedef struct southbridge_intel_i82801hx_config config_t;
+
+static void ide_init(struct device *dev)
+{
+ u16 ideTimingConfig;
+ u32 reg32;
+ u32 enable_primary, enable_secondary;
+
+ /* Get the chip configuration */
+ config_t *config = dev->chip_info;
+
+ printk(BIOS_DEBUG, "i82801hx_ide: initializing... ");
+ if (config == NULL) {
+ printk(BIOS_ERR, "\ni82801hx_ide: Not mentioned in devicetree.cb!\n");
+ // Trying to set somewhat safe defaults instead of bailing out.
+ enable_primary = enable_secondary = 1;
+ } else {
+ enable_primary = config->ide_enable_primary;
+ enable_secondary = config->ide_enable_secondary;
+ }
+
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_IO | PCI_COMMAND_MASTER);
+
+ /* Native Capable, but not enabled. */
+ pci_write_config8(dev, 0x09, 0x8a);
+
+ ideTimingConfig = pci_read_config16(dev, IDE_TIM_PRI);
+ ideTimingConfig &= ~IDE_DECODE_ENABLE;
+ ideTimingConfig |= IDE_SITRE;
+ if (enable_primary) {
+ /* Enable primary IDE interface. */
+ ideTimingConfig |= IDE_DECODE_ENABLE;
+ ideTimingConfig |= (2 << 12); // ISP = 3 clocks
+ ideTimingConfig |= (3 << 8); // RCT = 1 clock
+ ideTimingConfig |= (1 << 1); // IE0
+ ideTimingConfig |= (1 << 0); // TIME0
+ printk(BIOS_DEBUG, "IDE0 ");
+ }
+ pci_write_config16(dev, IDE_TIM_PRI, ideTimingConfig);
+
+ ideTimingConfig = pci_read_config16(dev, IDE_TIM_SEC);
+ ideTimingConfig &= ~IDE_DECODE_ENABLE;
+ ideTimingConfig |= IDE_SITRE;
+ if (enable_secondary) {
+ /* Enable secondary IDE interface. */
+ ideTimingConfig |= IDE_DECODE_ENABLE;
+ ideTimingConfig |= (2 << 12); // ISP = 3 clocks
+ ideTimingConfig |= (3 << 8); // RCT = 1 clock
+ ideTimingConfig |= (1 << 1); // IE0
+ ideTimingConfig |= (1 << 0); // TIME0
+ printk(BIOS_DEBUG, "IDE1 ");
+ }
+ pci_write_config16(dev, IDE_TIM_SEC, ideTimingConfig);
+
+ /* Set IDE I/O Configuration */
+ reg32 = 0;
+ /* FIXME: only set FAST_* for ata/100, only ?CBx for ata/66 */
+ if (enable_primary)
+ reg32 |= SIG_MODE_PRI_NORMAL | FAST_PCB0 | PCB0 | FAST_PCB1 | PCB1;
+ if (enable_secondary)
+ reg32 |= SIG_MODE_SEC_NORMAL | FAST_SCB0 | SCB0 | FAST_SCB1 | SCB1;
+ pci_write_config32(dev, IDE_CONFIG, reg32);
+
+ /* Set Interrupt Line */
+ /* Interrupt Pin is set by D31IP.PIP */
+ pci_write_config32(dev, INTR_LN, 0xff); /* Int 15 */
+
+ printk(BIOS_DEBUG, "\n");
+}
+
+static void ide_set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ if (!vendor || !device) {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ pci_read_config32(dev, PCI_VENDOR_ID));
+ } else {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+}
+
+static struct pci_operations ide_pci_ops = {
+ .set_subsystem = ide_set_subsystem,
+};
+
+static struct device_operations ide_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = ide_init,
+ .scan_bus = 0,
+ .enable = i82801hx_enable,
+ .ops_pci = &ide_pci_ops,
+};
+
+static const struct pci_driver i82801hx_ide __pci_driver = {
+ .ops = &ide_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2850,
+};
diff --git a/src/southbridge/intel/i82801hx/lpc.c b/src/southbridge/intel/i82801hx/lpc.c
new file mode 100644
index 0000000..f11d9a9
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/lpc.c
@@ -0,0 +1,538 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <pc80/mc146818rtc.h>
+#include <pc80/isa-dma.h>
+#include <pc80/i8259.h>
+#include <arch/io.h>
+#include <arch/ioapic.h>
+#include <arch/acpi.h>
+#include <cpu/cpu.h>
+#include "i82801hx.h"
+#include <cpu/x86/smm.h>
+
+#define NMI_OFF 0
+
+#define ENABLE_ACPI_MODE_IN_COREBOOT 0
+#define TEST_SMM_FLASH_LOCKDOWN 0
+
+typedef struct southbridge_intel_i82801hx_config config_t;
+
+/**
+ * Set miscellanous static southbridge features.
+ *
+ * @param dev PCI device with I/O APIC control registers
+ */
+static void i82801hx_enable_ioapic(struct device *dev)
+{
+ /* Enable ACPI I/O range decode */
+ pci_write_config8(dev, ACPI_CNTL, ACPI_EN);
+
+ set_ioapic_id(IO_APIC_ADDR, 0x02);
+
+ /*
+ * Select Boot Configuration register (0x03) and
+ * use Processor System Bus (0x01) to deliver interrupts.
+ */
+ io_apic_write(IO_APIC_ADDR, 0x03, 0x01);
+}
+
+static void i82801hx_enable_serial_irqs(struct device *dev)
+{
+ /* Set packet length and toggle silent mode bit for one frame. */
+ pci_write_config8(dev, SERIRQ_CNTL,
+ (1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0));
+}
+
+/* PIRQ[n]_ROUT[3:0] - PIRQ Routing Control
+ * 0x00 - 0000 = Reserved
+ * 0x01 - 0001 = Reserved
+ * 0x02 - 0010 = Reserved
+ * 0x03 - 0011 = IRQ3
+ * 0x04 - 0100 = IRQ4
+ * 0x05 - 0101 = IRQ5
+ * 0x06 - 0110 = IRQ6
+ * 0x07 - 0111 = IRQ7
+ * 0x08 - 1000 = Reserved
+ * 0x09 - 1001 = IRQ9
+ * 0x0A - 1010 = IRQ10
+ * 0x0B - 1011 = IRQ11
+ * 0x0C - 1100 = IRQ12
+ * 0x0D - 1101 = Reserved
+ * 0x0E - 1110 = IRQ14
+ * 0x0F - 1111 = IRQ15
+ * PIRQ[n]_ROUT[7] - PIRQ Routing Control
+ * 0x80 - The PIRQ is not routed.
+ */
+
+static void i82801hx_pirq_init(device_t dev)
+{
+ device_t irq_dev;
+ /* Get the chip configuration */
+ config_t *config = dev->chip_info;
+
+ pci_write_config8(dev, PIRQA_ROUT, config->pirqa_routing);
+ pci_write_config8(dev, PIRQB_ROUT, config->pirqb_routing);
+ pci_write_config8(dev, PIRQC_ROUT, config->pirqc_routing);
+ pci_write_config8(dev, PIRQD_ROUT, config->pirqd_routing);
+
+ pci_write_config8(dev, PIRQE_ROUT, config->pirqe_routing);
+ pci_write_config8(dev, PIRQF_ROUT, config->pirqf_routing);
+ pci_write_config8(dev, PIRQG_ROUT, config->pirqg_routing);
+ pci_write_config8(dev, PIRQH_ROUT, config->pirqh_routing);
+
+ /* Eric Biederman once said we should let the OS do this.
+ * I am not so sure anymore he was right.
+ */
+
+ for(irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) {
+ u8 int_pin=0, int_line=0;
+
+ if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI)
+ continue;
+
+ int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN);
+
+ switch (int_pin) {
+ case 1: /* INTA# */ int_line = config->pirqa_routing; break;
+ case 2: /* INTB# */ int_line = config->pirqb_routing; break;
+ case 3: /* INTC# */ int_line = config->pirqc_routing; break;
+ case 4: /* INTD# */ int_line = config->pirqd_routing; break;
+ }
+
+ if (!int_line)
+ continue;
+
+ pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line);
+ }
+}
+
+static void i82801hx_gpi_routing(device_t dev)
+{
+ /* Get the chip configuration */
+ config_t *config = dev->chip_info;
+ u32 reg32 = 0;
+
+ /* An array would be much nicer here, or some
+ * other method of doing this.
+ */
+ reg32 |= (config->gpi0_routing & 0x03) << 0;
+ reg32 |= (config->gpi1_routing & 0x03) << 2;
+ reg32 |= (config->gpi2_routing & 0x03) << 4;
+ reg32 |= (config->gpi3_routing & 0x03) << 6;
+ reg32 |= (config->gpi4_routing & 0x03) << 8;
+ reg32 |= (config->gpi5_routing & 0x03) << 10;
+ reg32 |= (config->gpi6_routing & 0x03) << 12;
+ reg32 |= (config->gpi7_routing & 0x03) << 14;
+ reg32 |= (config->gpi8_routing & 0x03) << 16;
+ reg32 |= (config->gpi9_routing & 0x03) << 18;
+ reg32 |= (config->gpi10_routing & 0x03) << 20;
+ reg32 |= (config->gpi11_routing & 0x03) << 22;
+ reg32 |= (config->gpi12_routing & 0x03) << 24;
+ reg32 |= (config->gpi13_routing & 0x03) << 26;
+ reg32 |= (config->gpi14_routing & 0x03) << 28;
+ reg32 |= (config->gpi15_routing & 0x03) << 30;
+
+ pci_write_config32(dev, 0xb8, reg32);
+}
+
+static void i82801hx_power_options(device_t dev)
+{
+ u8 reg8;
+ u16 reg16, pmbase;
+ u32 reg32;
+ const char *state;
+ /* Get the chip configuration */
+ config_t *config = dev->chip_info;
+
+ int pwr_on=CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL;
+ int nmi_option;
+
+ /* Which state do we want to goto after g3 (power restored)?
+ * 0 == S0 Full On
+ * 1 == S5 Soft Off
+ *
+ * If the option is not existent (Laptops), use MAINBOARD_POWER_ON.
+ */
+ if (get_option(&pwr_on, "power_on_after_fail") != CB_SUCCESS)
+ pwr_on = MAINBOARD_POWER_ON;
+
+ reg8 = pci_read_config8(dev, GEN_PMCON_3);
+ reg8 &= 0xfe;
+ switch (pwr_on) {
+ case MAINBOARD_POWER_OFF:
+ reg8 |= 1;
+ state = "off";
+ break;
+ case MAINBOARD_POWER_ON:
+ reg8 &= ~1;
+ state = "on";
+ break;
+ case MAINBOARD_POWER_KEEP:
+ reg8 &= ~1;
+ state = "state keep";
+ break;
+ default:
+ state = "undefined";
+ }
+
+ reg8 |= (3 << 4); /* avoid #S4 assertions */
+ reg8 &= ~(1 << 3); /* minimum asssertion is 1 to 2 RTCCLK */
+
+ pci_write_config8(dev, GEN_PMCON_3, reg8);
+ printk(BIOS_INFO, "Set power %s after power failure.\n", state);
+
+ /* Set up NMI on errors. */
+ reg8 = inb(0x61);
+ reg8 &= 0x0f; /* Higher Nibble must be 0 */
+ reg8 &= ~(1 << 3); /* IOCHK# NMI Enable */
+ // reg8 &= ~(1 << 2); /* PCI SERR# Enable */
+ reg8 |= (1 << 2); /* PCI SERR# Disable for now */
+ outb(reg8, 0x61);
+
+ reg8 = inb(0x70);
+ nmi_option = NMI_OFF;
+ get_option(&nmi_option, "nmi");
+ if (nmi_option) {
+ printk(BIOS_INFO, "NMI sources enabled.\n");
+ reg8 &= ~(1 << 7); /* Set NMI. */
+ } else {
+ printk(BIOS_INFO, "NMI sources disabled.\n");
+ reg8 |= ( 1 << 7); /* Can't mask NMI from PCI-E and NMI_NOW */
+ }
+ outb(reg8, 0x70);
+
+ /* Enable CPU_SLP# and Intel Speedstep, set SMI# rate down */
+ reg16 = pci_read_config16(dev, GEN_PMCON_1);
+ reg16 &= ~(3 << 0); // SMI# rate 1 minute
+ reg16 |= (1 << 2); // CLKRUN_EN - Mobile/Ultra only
+ reg16 |= (1 << 3); // Speedstep Enable - Mobile/Ultra only
+ reg16 |= (1 << 5); // CPUSLP_EN Desktop only
+
+ if (config->c4onc3_enable)
+ reg16 |= (1 << 7);
+
+ // another laptop wants this?
+ // reg16 &= ~(1 << 10); // BIOS_PCI_EXP_EN - Desktop/Mobile only
+ reg16 |= (1 << 10); // BIOS_PCI_EXP_EN - Desktop/Mobile only
+#if DEBUG_PERIODIC_SMIS
+ /* Set DEBUG_PERIODIC_SMIS in i82801hx.h to debug using
+ * periodic SMIs.
+ */
+ reg16 |= (3 << 0); // Periodic SMI every 8s
+#endif
+ pci_write_config16(dev, GEN_PMCON_1, reg16);
+
+ // Set the board's GPI routing.
+ i82801hx_gpi_routing(dev);
+
+ pmbase = pci_read_config16(dev, 0x40) & 0xfffe;
+
+ outl(config->gpe0_en, pmbase + GPE0_EN);
+ outw(config->alt_gp_smi_en, pmbase + ALT_GP_SMI_EN);
+
+ /* Set up power management block and determine sleep mode */
+ reg32 = inl(pmbase + 0x04); // PM1_CNT
+
+ reg32 &= ~(7 << 10); // SLP_TYP
+ reg32 |= (1 << 1); // enable C3->C0 transition on bus master
+ reg32 |= (1 << 0); // SCI_EN
+ outl(reg32, pmbase + 0x04);
+}
+
+static void i82801hx_configure_cstates(device_t dev)
+{
+ u8 reg8;
+
+ reg8 = pci_read_config8(dev, 0xa9); // Cx state configuration
+ reg8 |= (1 << 4) | (1 << 3) | (1 << 2); // Enable Popup & Popdown
+ pci_write_config8(dev, 0xa9, reg8);
+
+ // Set Deeper Sleep configuration to recommended values
+ reg8 = pci_read_config8(dev, 0xaa);
+ reg8 &= 0xf0;
+ reg8 |= (2 << 2); // Deeper Sleep to Stop CPU: 34-40us
+ reg8 |= (2 << 0); // Deeper Sleep to Sleep: 15us
+ pci_write_config8(dev, 0xaa, reg8);
+}
+
+static void i82801hx_rtc_init(struct device *dev)
+{
+ u8 reg8;
+ int rtc_failed;
+
+ reg8 = pci_read_config8(dev, GEN_PMCON_3);
+ rtc_failed = reg8 & RTC_BATTERY_DEAD;
+ if (rtc_failed) {
+ reg8 &= ~RTC_BATTERY_DEAD;
+ pci_write_config8(dev, GEN_PMCON_3, reg8);
+ }
+ printk(BIOS_DEBUG, "rtc_failed = 0x%x\n", rtc_failed);
+
+ rtc_init(rtc_failed);
+}
+
+static void enable_hpet(void)
+{
+ u32 reg32;
+
+ /* Move HPET to default address 0xfed00000 and enable it */
+ reg32 = RCBA32(HPTC);
+ reg32 |= (1 << 7); // HPET Address Enable
+ reg32 &= ~(3 << 0);
+ RCBA32(HPTC) = reg32;
+}
+
+static void enable_clock_gating(void)
+{
+ u32 reg32;
+
+ /* Enable Clock Gating for most devices */
+ reg32 = RCBA32(CG);
+ reg32 |= (1 << 31); // LPC clock gating
+ reg32 |= (1 << 30); // PATA clock gating
+ // SATA clock gating
+ reg32 |= (1 << 27) | (1 << 26) | (1 << 25) | (1 << 24);
+ reg32 |= (1 << 23); // AC97 clock gating
+ reg32 |= (1 << 19); // USB EHCI clock gating
+ reg32 |= (1 << 3) | (1 << 1); // DMI clock gating
+ reg32 |= (1 << 2); // PCIe clock gating;
+ reg32 &= ~(1 << 20); // No static clock gating for USB
+ reg32 &= ~( (1 << 29) | (1 << 28) ); // Disable UHCI clock gating
+ RCBA32(CG) = reg32;
+}
+
+#if CONFIG_HAVE_SMI_HANDLER
+static void i82801hx_lock_smm(struct device *dev)
+{
+#if TEST_SMM_FLASH_LOCKDOWN
+ u8 reg8;
+#endif
+
+ if (acpi_slp_type != 3) {
+#if ENABLE_ACPI_MODE_IN_COREBOOT
+ printk(BIOS_DEBUG, "Enabling ACPI via APMC:\n");
+ outb(APM_CNT_ACPI_ENABLE, APM_CNT); // Enable ACPI mode
+ printk(BIOS_DEBUG, "done.\n");
+#else
+ printk(BIOS_DEBUG, "Disabling ACPI via APMC:\n");
+ outb(APM_CNT_ACPI_DISABLE, APM_CNT); // Disable ACPI mode
+ printk(BIOS_DEBUG, "done.\n");
+#endif
+ } else {
+ printk(BIOS_DEBUG, "S3 wakeup, enabling ACPI via APMC\n");
+ outb(APM_CNT_ACPI_ENABLE, APM_CNT);
+ }
+ /* Don't allow evil boot loaders, kernels, or
+ * userspace applications to deceive us:
+ */
+ smm_lock();
+
+#if TEST_SMM_FLASH_LOCKDOWN
+ /* Now try this: */
+ printk(BIOS_DEBUG, "Locking BIOS to RO... ");
+ reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
+ printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
+ (reg8&1)?"rw":"ro");
+ reg8 &= ~(1 << 0); /* clear BIOSWE */
+ pci_write_config8(dev, 0xdc, reg8);
+ reg8 |= (1 << 1); /* set BLE */
+ pci_write_config8(dev, 0xdc, reg8);
+ printk(BIOS_DEBUG, "ok.\n");
+ reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
+ printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
+ (reg8&1)?"rw":"ro");
+
+ printk(BIOS_DEBUG, "Writing:\n");
+ *(volatile u8 *)0xfff00000 = 0x00;
+ printk(BIOS_DEBUG, "Testing:\n");
+ reg8 |= (1 << 0); /* set BIOSWE */
+ pci_write_config8(dev, 0xdc, reg8);
+
+ reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
+ printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off",
+ (reg8&1)?"rw":"ro");
+ printk(BIOS_DEBUG, "Done.\n");
+#endif
+}
+#endif
+
+#define SPIBASE 0x3020
+static void i82801hx_spi_init(void)
+{
+ u16 spicontrol;
+
+ spicontrol = RCBA16(SPIBASE + 2);
+ spicontrol &= ~(1 << 0); // SPI Access Request
+ RCBA16(SPIBASE + 2) = spicontrol;
+}
+
+static void i82801hx_fixups(struct device *dev)
+{
+ /* This needs to happen after PCI enumeration */
+ RCBA32(0x1d40) |= 1;
+
+ /* USB Transient Disconnect Detect:
+ * Prevent a SE0 condition on the USB ports from being
+ * interpreted by the UHCI controller as a disconnect
+ */
+ pci_write_config8(dev, 0xad, 0x3);
+}
+
+static void lpc_init(struct device *dev)
+{
+ printk(BIOS_DEBUG, "i82801hx: lpc_init\n");
+
+ /* Set the value for PCI command register. */
+ pci_write_config16(dev, PCI_COMMAND, 0x000f);
+
+ /* IO APIC initialization. */
+ i82801hx_enable_ioapic(dev);
+
+ i82801hx_enable_serial_irqs(dev);
+
+ /* Setup the PIRQ. */
+ i82801hx_pirq_init(dev);
+
+ /* Setup power options. */
+ i82801hx_power_options(dev);
+
+ /* Configure Cx state registers */
+ i82801hx_configure_cstates(dev);
+
+ /* Set the state of the GPIO lines. */
+ //gpio_init(dev);
+
+ /* Initialize the real time clock. */
+ i82801hx_rtc_init(dev);
+
+ /* Initialize ISA DMA. */
+ isa_dma_init();
+
+ /* Initialize the High Precision Event Timers, if present. */
+ enable_hpet();
+
+ /* Initialize Clock Gating */
+ enable_clock_gating();
+
+ setup_i8259();
+
+ /* The OS should do this? */
+ /* Interrupt 9 should be level triggered (SCI) */
+ i8259_configure_irq_trigger(9, 1);
+
+#if CONFIG_HAVE_SMI_HANDLER
+ i82801hx_lock_smm(dev);
+#endif
+
+ i82801hx_spi_init();
+
+ i82801hx_fixups(dev);
+}
+
+static void i82801hx_lpc_read_resources(device_t dev)
+{
+ struct resource *res;
+
+ /* Get the normal PCI resources of this device. */
+ pci_dev_read_resources(dev);
+
+ /* Add an extra subtractive resource for both memory and I/O. */
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0));
+ res->base = 0;
+ res->size = 0x1000;
+ res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
+ IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
+ res->base = 0xff800000;
+ res->size = 0x00800000; /* 8 MB for flash */
+ res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE |
+ IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+
+ res = new_resource(dev, 3); /* IOAPIC */
+ res->base = IO_APIC_ADDR;
+ res->size = 0x00001000;
+ res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+}
+
+static void set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ if (!vendor || !device) {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ pci_read_config32(dev, PCI_VENDOR_ID));
+ } else {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+}
+
+static struct pci_operations pci_ops = {
+ .set_subsystem = set_subsystem,
+};
+
+static struct device_operations device_ops = {
+ .read_resources = i82801hx_lpc_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = lpc_init,
+ .scan_bus = scan_static_bus,
+ .enable = i82801hx_enable,
+ .ops_pci = &pci_ops,
+};
+
+/* 82801HH (ICH8 DH) */
+static const struct pci_driver ich8_dh_lpc __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2812,
+};
+
+/* 82801HB/HR (ICH8/ICH8R) */
+static const struct pci_driver ich8_ich8r_lpc __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2810,
+};
+
+/* 82801HO (ICH8 DO) */
+static const struct pci_driver ich8_do_lpc __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2814,
+};
+
+/* 82801HM (ICH8-M) */
+static const struct pci_driver ich8m_lpc __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2815,
+};
+
+/* 82801HEM (ICH8M-E) */
+static const struct pci_driver ich8m_e_lpc __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2811,
+};
diff --git a/src/southbridge/intel/i82801hx/nic.c b/src/southbridge/intel/i82801hx/nic.c
new file mode 100644
index 0000000..a071a9b
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/nic.c
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+/* This code should work for all ICH* southbridges with a NIC. */
+
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+
+static void nic_init(struct device *dev)
+{
+ /* Nothing yet */
+}
+
+static struct device_operations nic_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = nic_init,
+ .scan_bus = 0,
+};
+
+/* PCI ID loaded from EEPROM. If EEPROM is 0, 0x???? is used. */
+static const struct pci_driver i82801hx_nic __pci_driver = {
+ .ops = &nic_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x1049,
+};
diff --git a/src/southbridge/intel/i82801hx/nvs.h b/src/southbridge/intel/i82801hx/nvs.h
new file mode 100644
index 0000000..03f8de7
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/nvs.h
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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
+ */
+
+typedef struct {
+ /* Miscellaneous */
+ u16 osys; /* 0x00 - Operating System */
+ u8 smif; /* 0x02 - SMI function call ("TRAP") */
+ u8 prm0; /* 0x03 - SMI function call parameter */
+ u8 prm1; /* 0x04 - SMI function call parameter */
+ u8 scif; /* 0x05 - SCI function call (via _L00) */
+ u8 prm2; /* 0x06 - SCI function call parameter */
+ u8 prm3; /* 0x07 - SCI function call parameter */
+ u8 lckf; /* 0x08 - Global Lock function for EC */
+ u8 prm4; /* 0x09 - Lock function parameter */
+ u8 prm5; /* 0x0a - Lock function parameter */
+ u32 p80d; /* 0x0b - Debug port (IO 0x80) value */
+ u8 lids; /* 0x0f - LID state (open = 1) */
+ u8 pwrs; /* 0x10 - Power state (AC = 1) */
+ u8 dbgs; /* 0x11 - Debug state */
+ u8 linx; /* 0x12 - Linux OS */
+ u8 dckn; /* 0x13 - PCIe docking state */
+ /* Thermal policy */
+ u8 actt; /* 0x14 - active trip point */
+ u8 psvt; /* 0x15 - passive trip point */
+ u8 tc1v; /* 0x16 - passive trip point TC1 */
+ u8 tc2v; /* 0x17 - passive trip point TC2 */
+ u8 tspv; /* 0x18 - passive trip point TSP */
+ u8 crtt; /* 0x19 - critical trip point */
+ u8 dtse; /* 0x1a - Digital Thermal Sensor enable */
+ u8 dts1; /* 0x1b - DT sensor 1 */
+ u8 dts2; /* 0x1c - DT sensor 2 */
+ u8 rsvd2;
+ /* Battery Support */
+ u8 bnum; /* 0x1e - number of batteries */
+ u8 b0sc, b1sc, b2sc; /* 0x1f-0x21 - stored capacity */
+ u8 b0ss, b1ss, b2ss; /* 0x22-0x24 - stored status */
+ u8 rsvd3[3];
+ /* Processor Identification */
+ u8 apic; /* 0x28 - APIC enabled */
+ u8 mpen; /* 0x29 - MP capable/enabled */
+ u8 pcp0; /* 0x2a - PDC CPU/CORE 0 */
+ u8 pcp1; /* 0x2b - PDC CPU/CORE 1 */
+ u8 ppcm; /* 0x2c - Max. PPC state */
+ u8 rsvd4[5];
+ /* Super I/O & CMOS config */
+ u8 natp; /* 0x32 - SIO type */
+ u8 cmap; /* 0x33 - */
+ u8 cmbp; /* 0x34 - */
+ u8 lptp; /* 0x35 - LPT port */
+ u8 fdcp; /* 0x36 - Floppy Disk Controller */
+ u8 rfdv; /* 0x37 - */
+ u8 hotk; /* 0x38 - Hot Key */
+ u8 rtcf;
+ u8 util;
+ u8 acin;
+ /* Integrated Graphics Device */
+ u8 igds; /* 0x3c - IGD state */
+ u8 tlst; /* 0x3d - Display Toggle List Pointer */
+ u8 cadl; /* 0x3e - currently attached devices */
+ u8 padl; /* 0x3f - previously attached devices */
+ u16 cste; /* 0x40 - current display state */
+ u16 nste; /* 0x42 - next display state */
+ u16 sste; /* 0x44 - set display state */
+ u8 ndid; /* 0x46 - number of device ids */
+ u32 did[5]; /* 0x47 - 5b device id 1..5 */
+ u8 rsvd5[0x9];
+ /* Backlight Control */
+ u8 blcs; /* 0x64 - Backlight Control possible */
+ u8 brtl;
+ u8 odds;
+ u8 rsvd6[0x7];
+ /* Ambient Light Sensors*/
+ u8 alse; /* 0x6e - ALS enable */
+ u8 alaf;
+ u8 llow;
+ u8 lhih;
+ u8 rsvd7[0x6];
+ /* EMA */
+ u8 emae; /* 0x78 - EMA enable */
+ u16 emap;
+ u16 emal;
+ u8 rsvd8[0x5];
+ /* MEF */
+ u8 mefe; /* 0x82 - MEF enable */
+ u8 rsvd9[0x9];
+ /* TPM support */
+ u8 tpmp; /* 0x8c - TPM */
+ u8 tpme;
+ u8 rsvd10[8];
+ /* SATA */
+ u8 gtf0[7]; /* 0x96 - GTF task file buffer for port 0 */
+ u8 gtf1[7];
+ u8 gtf2[7];
+ u8 idem;
+ u8 idet;
+ u8 rsvd11[7];
+ /* IGD OpRegion (not implemented yet) */
+ u32 aslb; /* 0xb4 - IGD OpRegion Base Address */
+ u8 ibtt;
+ u8 ipat;
+ u8 itvf;
+ u8 itvm;
+ u8 ipsc;
+ u8 iblc;
+ u8 ibia;
+ u8 issc;
+ u8 i409;
+ u8 i509;
+ u8 i609;
+ u8 i709;
+ u8 idmm;
+ u8 idms;
+ u8 if1e;
+ u8 hvco;
+ u32 nxd[8];
+ u8 rsvd12[8];
+ /* Mainboard specific */
+ u8 dock; /* 0xf0 - Docking Status */
+ u8 bten;
+ u8 rsvd13[14];
+} __attribute__((packed)) global_nvs_t;
+
diff --git a/src/southbridge/intel/i82801hx/pci.c b/src/southbridge/intel/i82801hx/pci.c
new file mode 100644
index 0000000..73d890f
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/pci.c
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include "i82801hx.h"
+
+static void pci_init(struct device *dev)
+{
+ u16 reg16;
+ u8 reg8;
+
+ /* Enable Bus Master */
+ reg16 = pci_read_config16(dev, PCI_COMMAND);
+ reg16 |= PCI_COMMAND_MASTER;
+ pci_write_config16(dev, PCI_COMMAND, reg16);
+
+ /* This device has no interrupt */
+ pci_write_config8(dev, INTR, 0xff);
+
+ /* disable parity error response and SERR */
+ reg16 = pci_read_config16(dev, BCTRL);
+ reg16 &= ~(1 << 0);
+ reg16 &= ~(1 << 1);
+ pci_write_config16(dev, BCTRL, reg16);
+
+ /* Master Latency Count must be set to 0x04! */
+ reg8 = pci_read_config8(dev, SMLT);
+ reg8 &= 0x07;
+ reg8 |= (0x04 << 3);
+ pci_write_config8(dev, SMLT, reg8);
+
+ /* Will this improve throughput of bus masters? */
+ pci_write_config8(dev, PCI_MIN_GNT, 0x06);
+
+ /* Clear errors in status registers */
+ reg16 = pci_read_config16(dev, PSTS);
+ //reg16 |= 0xf900;
+ pci_write_config16(dev, PSTS, reg16);
+
+ reg16 = pci_read_config16(dev, SECSTS);
+ // reg16 |= 0xf900;
+ pci_write_config16(dev, SECSTS, reg16);
+}
+
+#undef PCI_BRIDGE_UPDATE_COMMAND
+static void ich_pci_dev_enable_resources(struct device *dev)
+{
+ const struct pci_operations *ops;
+ uint16_t command;
+
+ /* Set the subsystem vendor and device id for mainboard devices */
+ ops = ops_pci(dev);
+ if (dev->on_mainboard && ops && ops->set_subsystem) {
+ printk(BIOS_DEBUG, "%s subsystem <- %04x/%04x\n",
+ dev_path(dev), dev->subsystem_vendor,
+ dev->subsystem_device);
+ ops->set_subsystem(dev, dev->subsystem_vendor,
+ dev->subsystem_device);
+ }
+
+ command = pci_read_config16(dev, PCI_COMMAND);
+ command |= dev->command;
+#ifdef PCI_BRIDGE_UPDATE_COMMAND
+ /* If we write to PCI_COMMAND, on some systems
+ * this will cause the ROM and APICs not being visible
+ * anymore.
+ */
+ printk(BIOS_DEBUG, "%s cmd <- %02x\n", dev_path(dev), command);
+ pci_write_config16(dev, PCI_COMMAND, command);
+#else
+ printk(BIOS_DEBUG, "%s cmd <- %02x (NOT WRITTEN!)\n", dev_path(dev), command);
+#endif
+}
+
+static void ich_pci_bus_enable_resources(struct device *dev)
+{
+ uint16_t ctrl;
+ /* enable IO in command register if there is VGA card
+ * connected with (even it does not claim IO resource)
+ */
+ if (dev->link_list->bridge_ctrl & PCI_BRIDGE_CTL_VGA)
+ dev->command |= PCI_COMMAND_IO;
+ ctrl = pci_read_config16(dev, PCI_BRIDGE_CONTROL);
+ ctrl |= dev->link_list->bridge_ctrl;
+ ctrl |= (PCI_BRIDGE_CTL_PARITY + PCI_BRIDGE_CTL_SERR); /* error check */
+ printk(BIOS_DEBUG, "%s bridge ctrl <- %04x\n", dev_path(dev), ctrl);
+ pci_write_config16(dev, PCI_BRIDGE_CONTROL, ctrl);
+
+ /* This is the reason we need our own pci_bus_enable_resources */
+ ich_pci_dev_enable_resources(dev);
+}
+
+static void set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ /* NOTE: This is not the default position! */
+ if (!vendor || !device) {
+ pci_write_config32(dev, 0x54,
+ pci_read_config32(dev, PCI_VENDOR_ID));
+ } else {
+ pci_write_config32(dev, 0x54,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+}
+
+static struct pci_operations pci_ops = {
+ .set_subsystem = set_subsystem,
+};
+
+static struct device_operations device_ops = {
+ .read_resources = pci_bus_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = ich_pci_bus_enable_resources,
+ .init = pci_init,
+ .scan_bus = pci_scan_bridge,
+ .ops_pci = &pci_ops,
+};
+
+/* Desktop */
+/* 82801BA/CA/DB/EB/ER/FB/FR/FW/FRW/GB/GR/GDH/HB/IB/6300ESB/i3100 */
+static const struct pci_driver i82801h_pci __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x244e,
+};
+
+/* Mobile / Ultra Mobile */
+/* 82801BAM/CAM/DBL/DBM/FBM/GBM/GHM/GU/HBM/HEM */
+static const struct pci_driver i82801hmu_pci __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2448,
+};
diff --git a/src/southbridge/intel/i82801hx/pcie.c b/src/southbridge/intel/i82801hx/pcie.c
new file mode 100644
index 0000000..979f268
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/pcie.c
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+
+static void pci_init(struct device *dev)
+{
+ u16 reg16;
+ u32 reg32;
+
+ printk(BIOS_DEBUG, "Initializing ICH8 PCIe bridge.\n");
+
+ /* Enable Bus Master */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_MASTER;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* Set Cache Line Size to 0x10 */
+ // This has no effect but the OS might expect it
+ pci_write_config8(dev, 0x0c, 0x10);
+
+ reg16 = pci_read_config16(dev, 0x3e);
+ reg16 &= ~(1 << 0); /* disable parity error response */
+ // reg16 &= ~(1 << 1); /* disable SERR */
+ reg16 |= (1 << 2); /* ISA enable */
+ pci_write_config16(dev, 0x3e, reg16);
+
+ /* Enable IO xAPIC on this PCIe port */
+ reg32 = pci_read_config32(dev, 0xd8);
+ reg32 |= (1 << 7);
+ pci_write_config32(dev, 0xd8, reg32);
+
+ /* Enable Backbone Clock Gating */
+ reg32 = pci_read_config32(dev, 0xe1);
+ reg32 |= (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0);
+ pci_write_config32(dev, 0xe1, reg32);
+
+ /* Set VC0 transaction class */
+ reg32 = pci_read_config32(dev, 0x114);
+ reg32 &= 0xffffff00;
+ reg32 |= 1;
+ pci_write_config32(dev, 0x114, reg32);
+
+ /* Mask completion timeouts */
+ reg32 = pci_read_config32(dev, 0x148);
+ reg32 |= (1 << 14);
+ pci_write_config32(dev, 0x148, reg32);
+
+ /* Enable common clock configuration */
+ // Are there cases when we don't want that?
+ reg16 = pci_read_config16(dev, 0x50);
+ reg16 |= (1 << 6);
+ pci_write_config16(dev, 0x50, reg16);
+
+#ifdef EVEN_MORE_DEBUG
+ reg32 = pci_read_config32(dev, 0x20);
+ printk(BIOS_SPEW, " MBL = 0x%08x\n", reg32);
+ reg32 = pci_read_config32(dev, 0x24);
+ printk(BIOS_SPEW, " PMBL = 0x%08x\n", reg32);
+ reg32 = pci_read_config32(dev, 0x28);
+ printk(BIOS_SPEW, " PMBU32 = 0x%08x\n", reg32);
+ reg32 = pci_read_config32(dev, 0x2c);
+ printk(BIOS_SPEW, " PMLU32 = 0x%08x\n", reg32);
+#endif
+
+ /* Clear errors in status registers */
+ reg16 = pci_read_config16(dev, 0x06);
+ //reg16 |= 0xf900;
+ pci_write_config16(dev, 0x06, reg16);
+
+ reg16 = pci_read_config16(dev, 0x1e);
+ //reg16 |= 0xf900;
+ pci_write_config16(dev, 0x1e, reg16);
+}
+
+static void pcie_set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ /* NOTE: This is not the default position! */
+ if (!vendor || !device) {
+ pci_write_config32(dev, 0x94,
+ pci_read_config32(dev, 0));
+ } else {
+ pci_write_config32(dev, 0x94,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+}
+
+static struct pci_operations pci_ops = {
+ .set_subsystem = pcie_set_subsystem,
+};
+
+static struct device_operations device_ops = {
+ .read_resources = pci_bus_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_bus_enable_resources,
+ .init = pci_init,
+ .scan_bus = pci_scan_bridge,
+ .ops_pci = &pci_ops,
+};
+
+static const unsigned short i82801hx_pcie_ids[] = {
+ 0x283f, /* 82801HB/GR/GDH/GBM/GHM (ICH8/ICH8R/ICH8DH/ICH8-M/ICH8-M DH) */
+ 0x2841, /* 82801HB/GR/GDH/GBM/GHM (ICH8/ICH8R/ICH8DH/ICH8-M/ICH8-M DH) */
+ 0x2843, /* 82801HB/GR/GDH/GBM/GHM (ICH8/ICH8R/ICH8DH/ICH8-M/ICH8-M DH) */
+ 0x2845, /* 82801HB/GR/GDH/GBM/GHM (ICH8/ICH8R/ICH8DH/ICH8-M/ICH8-M DH) */
+ 0x2847, /* 82801HR/GDH/GHM (ICH8R/ICH8DH/ICH8-M DH) */
+ 0x2849, /* 82801HR/GDH/GHM (ICH8R/ICH8DH/ICH8-M DH) */
+ 0
+};
+
+static const struct pci_driver i82801hx_pcie __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices= i82801hx_pcie_ids,
+};
diff --git a/src/southbridge/intel/i82801hx/reset.c b/src/southbridge/intel/i82801hx/reset.c
new file mode 100644
index 0000000..0229f19
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/reset.c
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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/io.h>
+#include <reset.h>
+
+void soft_reset(void)
+{
+ outb(0x04, 0xcf9);
+}
+
+#if 0
+void hard_reset(void)
+{
+ /* Try rebooting through port 0xcf9. */
+ outb((1 << 2) | (1 << 1), 0xcf9);
+}
+#endif
+
+void hard_reset(void)
+{
+ outb(0x02, 0xcf9);
+ outb(0x06, 0xcf9);
+}
diff --git a/src/southbridge/intel/i82801hx/sata.c b/src/southbridge/intel/i82801hx/sata.c
new file mode 100644
index 0000000..e7314b4
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/sata.c
@@ -0,0 +1,230 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include "i82801hx.h"
+
+typedef struct southbridge_intel_i82801hx_config config_t;
+
+static void sata_init(struct device *dev)
+{
+ u32 reg32;
+ u16 reg16;
+ u32 *ahci_bar;
+
+ /* Get the chip configuration */
+ config_t *config = dev->chip_info;
+
+ printk(BIOS_DEBUG, "i82801hx_sata: initializing...\n");
+
+ if (config == NULL) {
+ printk(BIOS_ERR, "i82801hx_sata: error: device not in devicetree.cb!\n");
+ return;
+ }
+
+ /* SATA configuration */
+
+ /* Enable BARs */
+ pci_write_config16(dev, PCI_COMMAND, 0x0007);
+
+ if (config->ide_legacy_combined) {
+ printk(BIOS_DEBUG, "SATA controller in combined mode.\n");
+ /* No AHCI: clear AHCI base */
+ pci_write_config32(dev, 0x24, 0x00000000);
+ /* And without AHCI BAR no memory decoding */
+ reg16 = pci_read_config16(dev, PCI_COMMAND);
+ reg16 &= ~PCI_COMMAND_MEMORY;
+ pci_write_config16(dev, PCI_COMMAND, reg16);
+
+ pci_write_config8(dev, 0x09, 0x80);
+
+ /* Set timings */
+ pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
+ IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS);
+ pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
+ IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
+ IDE_PPE0 | IDE_IE0 | IDE_TIME0);
+
+ /* Sync DMA */
+ pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0);
+ pci_write_config16(dev, IDE_SDMA_TIM, 0x0200);
+
+ /* Set IDE I/O Configuration */
+ reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0;
+ pci_write_config32(dev, IDE_CONFIG, reg32);
+
+ /* Combine IDE - SATA configuration */
+ pci_write_config8(dev, 0x90, 0x02);
+
+ /* Port 0 & 1 enable */
+ pci_write_config8(dev, 0x92, 0x0f);
+
+ /* SATA Initialization register */
+ pci_write_config32(dev, 0x94, 0x5a000180);
+ } else if(config->sata_ahci) {
+ printk(BIOS_DEBUG, "SATA controller in AHCI mode.\n");
+ /* Allow both Legacy and Native mode */
+ pci_write_config8(dev, 0x09, 0x8f);
+
+ /* Set Interrupt Line */
+ /* Interrupt Pin is set by D31IP.PIP */
+ pci_write_config8(dev, INTR_LN, 0x0a);
+
+ /* Set timings */
+ pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
+ IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
+ IDE_PPE0 | IDE_IE0 | IDE_TIME0);
+ pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
+ IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS);
+
+ /* Sync DMA */
+ pci_write_config16(dev, IDE_SDMA_CNT, IDE_PSDE0);
+ pci_write_config16(dev, IDE_SDMA_TIM, 0x0001);
+
+ /* Set IDE I/O Configuration */
+ reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0;
+ pci_write_config32(dev, IDE_CONFIG, reg32);
+
+ /* Set Sata Controller Mode. */
+ pci_write_config8(dev, 0x90, 0x40); // 40=AHCI
+
+ /* In ACHI mode, bit[3:0] must always be set
+ * (Port status is controlled through AHCI BAR)
+ */
+ pci_write_config8(dev, 0x92, 0x0f);
+
+ ahci_bar = (u32 *)(pci_read_config32(dev, 0x27) & ~0x3ff);
+ ahci_bar[3] = config->sata_ports_implemented;
+
+ /* SATA Initialization register */
+ pci_write_config32(dev, 0x94, 0x1a000180);
+ } else {
+ printk(BIOS_DEBUG, "SATA controller in plain mode.\n");
+ /* Set Sata Controller Mode. No Mapping(?) */
+ pci_write_config8(dev, 0x90, 0x00);
+
+ /* No AHCI: clear AHCI base */
+ pci_write_config32(dev, 0x24, 0x00000000);
+
+ /* And without AHCI BAR no memory decoding */
+ reg16 = pci_read_config16(dev, PCI_COMMAND);
+ reg16 &= ~PCI_COMMAND_MEMORY;
+ pci_write_config16(dev, PCI_COMMAND, reg16);
+
+ /* Native mode capable on both primary and secondary (0xa)
+ * or'ed with enabled (0x50) = 0xf
+ */
+ pci_write_config8(dev, 0x09, 0x8f);
+
+ /* Set Interrupt Line */
+ /* Interrupt Pin is set by D31IP.PIP */
+ pci_write_config8(dev, INTR_LN, 0xff);
+
+ /* Set timings */
+ pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
+ IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
+ IDE_PPE0 | IDE_IE0 | IDE_TIME0);
+ pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
+ IDE_SITRE | IDE_ISP_3_CLOCKS |
+ IDE_RCT_1_CLOCKS | IDE_IE0 | IDE_TIME0);
+
+ /* Sync DMA */
+ pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0 | IDE_PSDE0);
+ pci_write_config16(dev, IDE_SDMA_TIM, 0x0201);
+
+ /* Set IDE I/O Configuration */
+ reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0;
+ pci_write_config32(dev, IDE_CONFIG, reg32);
+
+ /* Port 0 & 1 enable XXX */
+ pci_write_config8(dev, 0x92, 0x15);
+
+ /* SATA Initialization register */
+ pci_write_config32(dev, 0x94, 0x1a000180);
+ }
+
+ /* All configurations need this SATA initialization sequence */
+ pci_write_config8(dev, 0xa0, 0x40);
+ pci_write_config8(dev, 0xa6, 0x22);
+ pci_write_config8(dev, 0xa0, 0x78);
+ pci_write_config8(dev, 0xa6, 0x22);
+ pci_write_config8(dev, 0xa0, 0x88);
+ reg32 = pci_read_config32(dev, 0xa4);
+ reg32 &= 0xc0c0c0c0;
+ reg32 |= 0x1b1b1212;
+ pci_write_config32(dev, 0xa4, reg32);
+ pci_write_config8(dev, 0xa0, 0x8c);
+ reg32 = pci_read_config32(dev, 0xa4);
+ reg32 &= 0xc0c0ff00;
+ reg32 |= 0x121200aa;
+ pci_write_config32(dev, 0xa4, reg32);
+ pci_write_config8(dev, 0xa0, 0x00);
+
+ pci_write_config8(dev, PCI_INTERRUPT_LINE, 0);
+
+ /* Sata Initialization Register */
+ reg32 = pci_read_config32(dev, 0x94);
+ reg32 |= (1 << 30); // due to some bug
+ pci_write_config32(dev, 0x94, reg32);
+}
+
+static void sata_set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ if (!vendor || !device) {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ pci_read_config32(dev, PCI_VENDOR_ID));
+ } else {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+}
+
+static struct pci_operations sata_pci_ops = {
+ .set_subsystem = sata_set_subsystem,
+};
+
+static struct device_operations sata_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = sata_init,
+ .scan_bus = 0,
+ .enable = i82801hx_enable,
+ .ops_pci = &sata_pci_ops,
+};
+
+static const unsigned short sata_ids[] = {
+ 0x2820, /* 82801H (ICH8 Family) 4 port SATA Controller [IDE mode] */
+ 0x2821, /* 82801HR/HO/HH (ICH8R/DO/DH) 6 port SATA Controller [AHCI mode] */
+ 0x2824, /* 82801HB (ICH8) 4 port SATA Controller [AHCI mode] */
+ 0x2825, /* 82801HR/HO/HH (ICH8R/DO/DH) 2 port SATA Controller [IDE mode] */
+ 0x2828, /* 82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [IDE mode] */
+ 0x2829, /* 82801HM/HEM (ICH8M/ICH8M-E) SATA Controller [AHCI mode] */
+ 0
+};
+
+static const struct pci_driver i82801hx_sata_driver __pci_driver = {
+ .ops = &sata_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices= sata_ids,
+};
diff --git a/src/southbridge/intel/i82801hx/smbus.c b/src/southbridge/intel/i82801hx/smbus.c
new file mode 100644
index 0000000..46b24e9
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/smbus.c
@@ -0,0 +1,271 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/path.h>
+#include <device/smbus.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <arch/io.h>
+#include "i82801hx.h"
+#include "smbus.h"
+
+static int lsmbus_read_byte(device_t dev, u8 address)
+{
+ u16 device;
+ struct resource *res;
+ struct bus *pbus;
+
+ device = dev->path.i2c.device;
+ pbus = get_pbus_smbus(dev);
+ res = find_resource(pbus->dev, 0x20);
+
+ return do_smbus_read_byte(res->base, device, address);
+}
+
+static int do_smbus_write_byte(unsigned smbus_base, unsigned device, unsigned address, unsigned data)
+{
+ unsigned char global_status_register;
+
+ if (smbus_wait_until_ready(smbus_base) < 0)
+ return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
+
+ /* Setup transaction */
+ /* Disable interrupts */
+ outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
+ /* Set the device I'm talking too */
+ outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
+ /* Set the command/address... */
+ outb(address & 0xff, smbus_base + SMBHSTCMD);
+ /* Set up for a byte data read */
+ outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2),
+ (smbus_base + SMBHSTCTL));
+ /* Clear any lingering errors, so the transaction will run */
+ outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
+
+ /* Clear the data byte... */
+ outb(data, smbus_base + SMBHSTDAT0);
+
+ /* Start the command */
+ outb((inb(smbus_base + SMBHSTCTL) | 0x40),
+ smbus_base + SMBHSTCTL);
+
+ /* Poll for transaction completion */
+ if (smbus_wait_until_done(smbus_base) < 0)
+ return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
+
+ global_status_register = inb(smbus_base + SMBHSTSTAT);
+
+ /* Ignore the "In Use" status... */
+ global_status_register &= ~(3 << 5);
+
+ /* Read results of transaction */
+ if (global_status_register != (1 << 1))
+ return SMBUS_ERROR;
+ return 0;
+}
+
+static int lsmbus_write_byte(device_t dev, u8 address, u8 data)
+{
+ u16 device;
+ struct resource *res;
+ struct bus *pbus;
+
+ device = dev->path.i2c.device;
+ pbus = get_pbus_smbus(dev);
+ res = find_resource(pbus->dev, 0x20);
+ return do_smbus_write_byte(res->base, device, address, data);
+}
+
+static int do_smbus_block_write(unsigned smbus_base, unsigned device,
+ unsigned cmd, unsigned bytes, const u8 *buf)
+{
+ u8 status;
+
+ if (smbus_wait_until_ready(smbus_base) < 0)
+ return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
+
+ /* Setup transaction */
+ /* Disable interrupts */
+ outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
+ /* Set the device I'm talking too */
+ outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD);
+ /* Set the command/address... */
+ outb(cmd & 0xff, smbus_base + SMBHSTCMD);
+ /* Set up for a block data write */
+ outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x5 << 2),
+ (smbus_base + SMBHSTCTL));
+ /* Clear any lingering errors, so the transaction will run */
+ outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
+
+ /* set number of bytes to transfer */
+ outb(bytes, smbus_base + SMBHSTDAT0);
+
+ outb(*buf++, smbus_base + SMBBLKDAT);
+ bytes--;
+
+ /* Start the command */
+ outb((inb(smbus_base + SMBHSTCTL) | 0x40),
+ smbus_base + SMBHSTCTL);
+
+ while(!(inb(smbus_base + SMBHSTSTAT) & 1));
+ /* Poll for transaction completion */
+ do {
+ status = inb(smbus_base + SMBHSTSTAT);
+ if (status & ((1 << 4) | /* FAILED */
+ (1 << 3) | /* BUS ERR */
+ (1 << 2))) /* DEV ERR */
+ return SMBUS_ERROR;
+
+ if (status & 0x80) { /* Byte done */
+ outb(*buf++, smbus_base + SMBBLKDAT);
+ outb(status, smbus_base + SMBHSTSTAT);
+ }
+ } while(status & 0x01);
+
+ return 0;
+}
+
+
+
+static int lsmbus_block_write(device_t dev, u8 cmd, u8 bytes, const u8 *buf)
+{
+ u16 device;
+ struct resource *res;
+ struct bus *pbus;
+
+ device = dev->path.i2c.device;
+ pbus = get_pbus_smbus(dev);
+ res = find_resource(pbus->dev, 0x20);
+ return do_smbus_block_write(res->base, device, cmd, bytes, buf);
+}
+
+static int do_smbus_block_read(unsigned smbus_base, unsigned device,
+ unsigned cmd, unsigned bytes, u8 *buf)
+{
+ u8 status;
+ int bytes_read = 0;
+ if (smbus_wait_until_ready(smbus_base) < 0)
+ return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
+
+ /* Setup transaction */
+ /* Disable interrupts */
+ outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
+ /* Set the device I'm talking too */
+ outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD);
+ /* Set the command/address... */
+ outb(cmd & 0xff, smbus_base + SMBHSTCMD);
+ /* Set up for a block data read */
+ outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x5 << 2),
+ (smbus_base + SMBHSTCTL));
+ /* Clear any lingering errors, so the transaction will run */
+ outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
+
+ /* Start the command */
+ outb((inb(smbus_base + SMBHSTCTL) | 0x40),
+ smbus_base + SMBHSTCTL);
+
+ while(!(inb(smbus_base + SMBHSTSTAT) & 1));
+ /* Poll for transaction completion */
+ do {
+ status = inb(smbus_base + SMBHSTSTAT);
+ if (status & ((1 << 4) | /* FAILED */
+ (1 << 3) | /* BUS ERR */
+ (1 << 2))) /* DEV ERR */
+ return SMBUS_ERROR;
+
+ if (status & 0x80) { /* Byte done */
+ *buf = inb(smbus_base + SMBBLKDAT);
+ buf++;
+ bytes_read++;
+ outb(status, smbus_base + SMBHSTSTAT);
+ if (--bytes == 1) {
+ /* indicate that next byte is the last one */
+ outb(inb(smbus_base + SMBHSTCTL) | 0x20,
+ smbus_base + SMBHSTCTL);
+ }
+ }
+ } while(status & 0x01);
+
+ return bytes_read;
+}
+
+static int lsmbus_block_read(device_t dev, u8 cmd, u8 bytes, u8 *buf)
+{
+ u16 device;
+ struct resource *res;
+ struct bus *pbus;
+
+ device = dev->path.i2c.device;
+ pbus = get_pbus_smbus(dev);
+ res = find_resource(pbus->dev, 0x20);
+ return do_smbus_block_read(res->base, device, cmd, bytes, buf);
+}
+
+
+static struct smbus_bus_operations lops_smbus_bus = {
+ .read_byte = lsmbus_read_byte,
+ .write_byte = lsmbus_write_byte,
+ .block_read = lsmbus_block_read,
+ .block_write = lsmbus_block_write,
+};
+
+static void smbus_set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ if (!vendor || !device) {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ pci_read_config32(dev, PCI_VENDOR_ID));
+ } else {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+}
+
+static struct pci_operations smbus_pci_ops = {
+ .set_subsystem = smbus_set_subsystem,
+};
+
+static void smbus_read_resources(device_t dev)
+{
+ struct resource *res = new_resource(dev, PCI_BASE_ADDRESS_4);
+ res->base = SMBUS_IO_BASE;
+ res->size = 32;
+ res->limit = res->base + res->size - 1;
+ res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_RESERVE |
+ IORESOURCE_STORED | IORESOURCE_ASSIGNED;
+}
+
+static struct device_operations smbus_ops = {
+ .read_resources = smbus_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .scan_bus = scan_static_bus,
+ .enable = i82801hx_enable,
+ .ops_smbus_bus = &lops_smbus_bus,
+ .ops_pci = &smbus_pci_ops,
+};
+
+static const struct pci_driver i82801hx_smbus __pci_driver = {
+ .ops = &smbus_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x283e,
+};
diff --git a/src/southbridge/intel/i82801hx/smbus.h b/src/southbridge/intel/i82801hx/smbus.h
new file mode 100644
index 0000000..96426b4
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/smbus.h
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2005 Yinghai Lu <yinghailu at gmail.com>
+ * Copyright (C) 2009 coresystems GmbH
+ *
+ * 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 <device/smbus_def.h>
+#include "i82801hx.h"
+
+static void smbus_delay(void)
+{
+ inb(0x80);
+}
+
+static int smbus_wait_until_ready(u16 smbus_base)
+{
+ unsigned loops = SMBUS_TIMEOUT;
+ unsigned char byte;
+ do {
+ smbus_delay();
+ if (--loops == 0)
+ break;
+ byte = inb(smbus_base + SMBHSTSTAT);
+ } while (byte & 1);
+ return loops ? 0 : -1;
+}
+
+static int smbus_wait_until_done(u16 smbus_base)
+{
+ unsigned loops = SMBUS_TIMEOUT;
+ unsigned char byte;
+ do {
+ smbus_delay();
+ if (--loops == 0)
+ break;
+ byte = inb(smbus_base + SMBHSTSTAT);
+ } while ((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0);
+ return loops ? 0 : -1;
+}
+
+static int do_smbus_read_byte(unsigned smbus_base, unsigned device, unsigned address)
+{
+ unsigned char global_status_register;
+ unsigned char byte;
+
+ if (smbus_wait_until_ready(smbus_base) < 0) {
+ return SMBUS_WAIT_UNTIL_READY_TIMEOUT;
+ }
+ /* Setup transaction */
+ /* Disable interrupts */
+ outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL);
+ /* Set the device I'm talking too */
+ outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD);
+ /* Set the command/address... */
+ outb(address & 0xff, smbus_base + SMBHSTCMD);
+ /* Set up for a byte data read */
+ outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2),
+ (smbus_base + SMBHSTCTL));
+ /* Clear any lingering errors, so the transaction will run */
+ outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT);
+
+ /* Clear the data byte... */
+ outb(0, smbus_base + SMBHSTDAT0);
+
+ /* Start the command */
+ outb((inb(smbus_base + SMBHSTCTL) | 0x40),
+ smbus_base + SMBHSTCTL);
+
+ /* Poll for transaction completion */
+ if (smbus_wait_until_done(smbus_base) < 0) {
+ return SMBUS_WAIT_UNTIL_DONE_TIMEOUT;
+ }
+
+ global_status_register = inb(smbus_base + SMBHSTSTAT);
+
+ /* Ignore the "In Use" status... */
+ global_status_register &= ~(3 << 5);
+
+ /* Read results of transaction */
+ byte = inb(smbus_base + SMBHSTDAT0);
+ if (global_status_register != (1 << 1)) {
+ return SMBUS_ERROR;
+ }
+ return byte;
+}
+
diff --git a/src/southbridge/intel/i82801hx/smi.c b/src/southbridge/intel/i82801hx/smi.c
new file mode 100644
index 0000000..0ce44ad
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/smi.c
@@ -0,0 +1,393 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <console/console.h>
+#include <arch/io.h>
+#include <cpu/cpu.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/smm.h>
+#include <string.h>
+#include "i82801hx.h"
+
+extern unsigned char _binary_smm_start;
+extern unsigned char _binary_smm_size;
+
+/* I945 */
+#define SMRAM 0x9d
+#define D_OPEN (1 << 6)
+#define D_CLS (1 << 5)
+#define D_LCK (1 << 4)
+#define G_SMRAME (1 << 3)
+#define C_BASE_SEG ((0 << 2) | (1 << 1) | (0 << 0))
+
+/* While we read PMBASE dynamically in case it changed, let's
+ * initialize it with a sane value
+ */
+static u16 pmbase = DEFAULT_PMBASE;
+
+/**
+ * @brief read and clear PM1_STS
+ * @return PM1_STS register
+ */
+static u16 reset_pm1_status(void)
+{
+ u16 reg16;
+
+ reg16 = inw(pmbase + PM1_STS);
+ /* set status bits are cleared by writing 1 to them */
+ outw(reg16, pmbase + PM1_STS);
+
+ return reg16;
+}
+
+static void dump_pm1_status(u16 pm1_sts)
+{
+ printk(BIOS_DEBUG, "PM1_STS: ");
+ if (pm1_sts & (1 << 15)) printk(BIOS_DEBUG, "WAK ");
+ if (pm1_sts & (1 << 14)) printk(BIOS_DEBUG, "PCIEXPWAK ");
+ if (pm1_sts & (1 << 11)) printk(BIOS_DEBUG, "PRBTNOR ");
+ if (pm1_sts & (1 << 10)) printk(BIOS_DEBUG, "RTC ");
+ if (pm1_sts & (1 << 8)) printk(BIOS_DEBUG, "PWRBTN ");
+ if (pm1_sts & (1 << 5)) printk(BIOS_DEBUG, "GBL ");
+ if (pm1_sts & (1 << 4)) printk(BIOS_DEBUG, "BM ");
+ if (pm1_sts & (1 << 0)) printk(BIOS_DEBUG, "TMROF ");
+ printk(BIOS_DEBUG, "\n");
+}
+
+/**
+ * @brief read and clear SMI_STS
+ * @return SMI_STS register
+ */
+static u32 reset_smi_status(void)
+{
+ u32 reg32;
+
+ reg32 = inl(pmbase + SMI_STS);
+ /* set status bits are cleared by writing 1 to them */
+ outl(reg32, pmbase + SMI_STS);
+
+ return reg32;
+}
+
+static void dump_smi_status(u32 smi_sts)
+{
+ printk(BIOS_DEBUG, "SMI_STS: ");
+ if (smi_sts & (1 << 26)) printk(BIOS_DEBUG, "SPI ");
+ if (smi_sts & (1 << 25)) printk(BIOS_DEBUG, "EL_SMI ");
+ if (smi_sts & (1 << 21)) printk(BIOS_DEBUG, "MONITOR ");
+ if (smi_sts & (1 << 20)) printk(BIOS_DEBUG, "PCI_EXP_SMI ");
+ if (smi_sts & (1 << 18)) printk(BIOS_DEBUG, "INTEL_USB2 ");
+ if (smi_sts & (1 << 17)) printk(BIOS_DEBUG, "LEGACY_USB2 ");
+ if (smi_sts & (1 << 16)) printk(BIOS_DEBUG, "SMBUS_SMI ");
+ if (smi_sts & (1 << 15)) printk(BIOS_DEBUG, "SERIRQ_SMI ");
+ if (smi_sts & (1 << 14)) printk(BIOS_DEBUG, "PERIODIC ");
+ if (smi_sts & (1 << 13)) printk(BIOS_DEBUG, "TCO ");
+ if (smi_sts & (1 << 12)) printk(BIOS_DEBUG, "DEVMON ");
+ if (smi_sts & (1 << 11)) printk(BIOS_DEBUG, "MCSMI ");
+ if (smi_sts & (1 << 10)) printk(BIOS_DEBUG, "GPI ");
+ if (smi_sts & (1 << 9)) printk(BIOS_DEBUG, "GPE0 ");
+ if (smi_sts & (1 << 8)) printk(BIOS_DEBUG, "PM1 ");
+ if (smi_sts & (1 << 6)) printk(BIOS_DEBUG, "SWSMI_TMR ");
+ if (smi_sts & (1 << 5)) printk(BIOS_DEBUG, "APM ");
+ if (smi_sts & (1 << 4)) printk(BIOS_DEBUG, "SLP_SMI ");
+ if (smi_sts & (1 << 3)) printk(BIOS_DEBUG, "LEGACY_USB ");
+ if (smi_sts & (1 << 2)) printk(BIOS_DEBUG, "BIOS ");
+ printk(BIOS_DEBUG, "\n");
+}
+
+
+/**
+ * @brief read and clear GPE0_STS
+ * @return GPE0_STS register
+ */
+static u32 reset_gpe0_status(void)
+{
+ u32 reg32;
+
+ reg32 = inl(pmbase + GPE0_STS);
+ /* set status bits are cleared by writing 1 to them */
+ outl(reg32, pmbase + GPE0_STS);
+
+ return reg32;
+}
+
+static void dump_gpe0_status(u32 gpe0_sts)
+{
+ int i;
+ printk(BIOS_DEBUG, "GPE0_STS: ");
+ for (i=31; i>= 16; i--) {
+ if (gpe0_sts & (1 << i)) printk(BIOS_DEBUG, "GPIO%d ", (i-16));
+ }
+ if (gpe0_sts & (1 << 14)) printk(BIOS_DEBUG, "USB4 ");
+ if (gpe0_sts & (1 << 13)) printk(BIOS_DEBUG, "PME_B0 ");
+ if (gpe0_sts & (1 << 12)) printk(BIOS_DEBUG, "USB3 ");
+ if (gpe0_sts & (1 << 11)) printk(BIOS_DEBUG, "PME ");
+ if (gpe0_sts & (1 << 10)) printk(BIOS_DEBUG, "EL_SCI/BATLOW ");
+ if (gpe0_sts & (1 << 9)) printk(BIOS_DEBUG, "PCI_EXP ");
+ if (gpe0_sts & (1 << 8)) printk(BIOS_DEBUG, "RI ");
+ if (gpe0_sts & (1 << 7)) printk(BIOS_DEBUG, "SMB_WAK ");
+ if (gpe0_sts & (1 << 6)) printk(BIOS_DEBUG, "TCO_SCI ");
+ if (gpe0_sts & (1 << 5)) printk(BIOS_DEBUG, "AC97 ");
+ if (gpe0_sts & (1 << 4)) printk(BIOS_DEBUG, "USB2 ");
+ if (gpe0_sts & (1 << 3)) printk(BIOS_DEBUG, "USB1 ");
+ if (gpe0_sts & (1 << 2)) printk(BIOS_DEBUG, "HOT_PLUG ");
+ if (gpe0_sts & (1 << 0)) printk(BIOS_DEBUG, "THRM ");
+ printk(BIOS_DEBUG, "\n");
+}
+
+
+/**
+ * @brief read and clear ALT_GP_SMI_STS
+ * @return ALT_GP_SMI_STS register
+ */
+static u16 reset_alt_gp_smi_status(void)
+{
+ u16 reg16;
+
+ reg16 = inl(pmbase + ALT_GP_SMI_STS);
+ /* set status bits are cleared by writing 1 to them */
+ outl(reg16, pmbase + ALT_GP_SMI_STS);
+
+ return reg16;
+}
+
+static void dump_alt_gp_smi_status(u16 alt_gp_smi_sts)
+{
+ int i;
+ printk(BIOS_DEBUG, "ALT_GP_SMI_STS: ");
+ for (i=15; i>= 0; i--) {
+ if (alt_gp_smi_sts & (1 << i)) printk(BIOS_DEBUG, "GPI%d ", i);
+ }
+ printk(BIOS_DEBUG, "\n");
+}
+
+
+
+/**
+ * @brief read and clear TCOx_STS
+ * @return TCOx_STS registers
+ */
+static u32 reset_tco_status(void)
+{
+ u32 tcobase = pmbase + 0x60;
+ u32 reg32;
+
+ reg32 = inl(tcobase + 0x04);
+ /* set status bits are cleared by writing 1 to them */
+ outl(reg32 & ~(1<<18), tcobase + 0x04); // Don't clear BOOT_STS before SECOND_TO_STS
+ if (reg32 & (1 << 18))
+ outl(reg32 & (1<<18), tcobase + 0x04); // clear BOOT_STS
+
+ return reg32;
+}
+
+
+static void dump_tco_status(u32 tco_sts)
+{
+ printk(BIOS_DEBUG, "TCO_STS: ");
+ if (tco_sts & (1 << 20)) printk(BIOS_DEBUG, "SMLINK_SLV ");
+ if (tco_sts & (1 << 18)) printk(BIOS_DEBUG, "BOOT ");
+ if (tco_sts & (1 << 17)) printk(BIOS_DEBUG, "SECOND_TO ");
+ if (tco_sts & (1 << 16)) printk(BIOS_DEBUG, "INTRD_DET ");
+ if (tco_sts & (1 << 12)) printk(BIOS_DEBUG, "DMISERR ");
+ if (tco_sts & (1 << 10)) printk(BIOS_DEBUG, "DMISMI ");
+ if (tco_sts & (1 << 9)) printk(BIOS_DEBUG, "DMISCI ");
+ if (tco_sts & (1 << 8)) printk(BIOS_DEBUG, "BIOSWR ");
+ if (tco_sts & (1 << 7)) printk(BIOS_DEBUG, "NEWCENTURY ");
+ if (tco_sts & (1 << 3)) printk(BIOS_DEBUG, "TIMEOUT ");
+ if (tco_sts & (1 << 2)) printk(BIOS_DEBUG, "TCO_INT ");
+ if (tco_sts & (1 << 1)) printk(BIOS_DEBUG, "SW_TCO ");
+ if (tco_sts & (1 << 0)) printk(BIOS_DEBUG, "NMI2SMI ");
+ printk(BIOS_DEBUG, "\n");
+}
+
+
+
+/**
+ * @brief Set the EOS bit
+ */
+static void smi_set_eos(void)
+{
+ u8 reg8;
+
+ reg8 = inb(pmbase + SMI_EN);
+ reg8 |= EOS;
+ outb(reg8, pmbase + SMI_EN);
+}
+
+extern uint8_t smm_relocation_start, smm_relocation_end;
+
+static void smm_relocate(void)
+{
+ u32 smi_en;
+ u16 pm1_en;
+
+ printk(BIOS_DEBUG, "Initializing SMM handler...");
+
+ pmbase = pci_read_config16(dev_find_slot(0, PCI_DEVFN(0x1f, 0)), 0x40) & 0xfffc;
+ printk(BIOS_SPEW, " ... pmbase = 0x%04x\n", pmbase);
+
+ smi_en = inl(pmbase + SMI_EN);
+ if (smi_en & APMC_EN) {
+ printk(BIOS_INFO, "SMI# handler already enabled?\n");
+ return;
+ }
+
+ /* copy the SMM relocation code */
+ memcpy((void *)0x38000, &smm_relocation_start,
+ &smm_relocation_end - &smm_relocation_start);
+
+ printk(BIOS_DEBUG, "\n");
+ dump_smi_status(reset_smi_status());
+ dump_pm1_status(reset_pm1_status());
+ dump_gpe0_status(reset_gpe0_status());
+ dump_alt_gp_smi_status(reset_alt_gp_smi_status());
+ dump_tco_status(reset_tco_status());
+
+ /* Enable SMI generation:
+ * - on TCO events
+ * - on APMC writes (io 0xb2)
+ * - on writes to SLP_EN (sleep states)
+ * - on writes to GBL_RLS (bios commands)
+ * No SMIs:
+ * - on microcontroller writes (io 0x62/0x66)
+ */
+
+ smi_en = 0; /* reset SMI enables */
+
+#if 0
+ smi_en |= LEGACY_USB2_EN | LEGACY_USB_EN;
+#endif
+ smi_en |= TCO_EN;
+ smi_en |= APMC_EN;
+#if DEBUG_PERIODIC_SMIS
+ /* Set DEBUG_PERIODIC_SMIS in i82801hx.h to debug using
+ * periodic SMIs.
+ */
+ smi_en |= PERIODIC_EN;
+#endif
+ smi_en |= SLP_SMI_EN;
+ smi_en |= BIOS_EN;
+
+ /* The following need to be on for SMIs to happen */
+ smi_en |= EOS | GBL_SMI_EN;
+
+ outl(smi_en, pmbase + SMI_EN);
+
+ pm1_en = 0;
+ pm1_en |= PWRBTN_EN;
+ pm1_en |= GBL_EN;
+ outw(pm1_en, pmbase + PM1_EN);
+
+ /**
+ * There are several methods of raising a controlled SMI# via
+ * software, among them:
+ * - Writes to io 0xb2 (APMC)
+ * - Writes to the Local Apic ICR with Delivery mode SMI.
+ *
+ * Using the local apic is a bit more tricky. According to
+ * AMD Family 11 Processor BKDG no destination shorthand must be
+ * used.
+ * The whole SMM initialization is quite a bit hardware specific, so
+ * I'm not too worried about the better of the methods at the moment
+ */
+
+ /* raise an SMI interrupt */
+ printk(BIOS_SPEW, " ... raise SMI#\n");
+ outb(0x00, 0xb2);
+}
+
+static int smm_handler_copied = 0;
+
+static int is_wakeup(void)
+{
+ device_t dev0 = dev_find_slot(0, PCI_DEVFN(0,0));
+
+ if (!dev0)
+ return 0;
+
+ return pci_read_config32(dev0, 0xdc) == SKPAD_ACPI_S3_MAGIC;
+}
+
+static void smm_install(void)
+{
+ /* The first CPU running this gets to copy the SMM handler. But not all
+ * of them.
+ */
+ if (smm_handler_copied)
+ return;
+ smm_handler_copied = 1;
+
+
+ /* if we're resuming from S3, the SMM code is already in place,
+ * so don't copy it again to keep the current SMM state */
+
+ if (!is_wakeup()) {
+ /* enable the SMM memory window */
+ pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
+ D_OPEN | G_SMRAME | C_BASE_SEG);
+
+ /* copy the real SMM handler */
+ memcpy((void *)0xa0000, &_binary_smm_start, (size_t)&_binary_smm_size);
+ wbinvd();
+ }
+
+ /* close the SMM memory window and enable normal SMM */
+ pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
+ G_SMRAME | C_BASE_SEG);
+}
+
+void smm_init(void)
+{
+ /* Put SMM code to 0xa0000 */
+ smm_install();
+
+ /* Put relocation code to 0x38000 and relocate SMBASE */
+ smm_relocate();
+
+ /* We're done. Make sure SMIs can happen! */
+ smi_set_eos();
+}
+
+void smm_lock(void)
+{
+ /* LOCK the SMM memory window and enable normal SMM.
+ * After running this function, only a full reset can
+ * make the SMM registers writable again.
+ */
+ printk(BIOS_DEBUG, "Locking SMM.\n");
+ pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM,
+ D_LCK | G_SMRAME | C_BASE_SEG);
+}
+
+void smm_setup_structures(void *gnvs, void *tcg, void *smi1)
+{
+ /* The GDT or coreboot table is going to live here. But a long time
+ * after we relocated the GNVS, so this is not troublesome.
+ */
+ *(u32 *)0x500 = (u32)gnvs;
+ *(u32 *)0x504 = (u32)tcg;
+ *(u32 *)0x508 = (u32)smi1;
+ outb(0xea, 0xb2);
+}
diff --git a/src/southbridge/intel/i82801hx/smihandler.c b/src/southbridge/intel/i82801hx/smihandler.c
new file mode 100644
index 0000000..bac9964
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/smihandler.c
@@ -0,0 +1,666 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <types.h>
+#include <arch/hlt.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <cpu/x86/cache.h>
+#include <cpu/x86/smm.h>
+#include <device/pci_def.h>
+#include <pc80/mc146818rtc.h>
+#include "i82801hx.h"
+
+/* I965 */
+#define SMRAM 0x9d
+#define D_OPEN (1 << 6)
+#define D_CLS (1 << 5)
+#define D_LCK (1 << 4)
+#define G_SMRANE (1 << 3)
+#define C_BASE_SEG ((0 << 2) | (1 << 1) | (0 << 0))
+
+#include "nvs.h"
+
+/* While we read PMBASE dynamically in case it changed, let's
+ * initialize it with a sane value
+ */
+u16 pmbase = DEFAULT_PMBASE;
+u8 smm_initialized = 0;
+
+/* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
+ * by coreboot.
+ */
+global_nvs_t *gnvs = (global_nvs_t *)0x0;
+void *tcg = (void *)0x0;
+void *smi1 = (void *)0x0;
+
+/**
+ * @brief read and clear PM1_STS
+ * @return PM1_STS register
+ */
+static u16 reset_pm1_status(void)
+{
+ u16 reg16;
+
+ reg16 = inw(pmbase + PM1_STS);
+ /* set status bits are cleared by writing 1 to them */
+ outw(reg16, pmbase + PM1_STS);
+
+ return reg16;
+}
+
+static void dump_pm1_status(u16 pm1_sts)
+{
+ printk(BIOS_SPEW, "PM1_STS: ");
+ if (pm1_sts & (1 << 15)) printk(BIOS_SPEW, "WAK ");
+ if (pm1_sts & (1 << 14)) printk(BIOS_SPEW, "PCIEXPWAK ");
+ if (pm1_sts & (1 << 11)) printk(BIOS_SPEW, "PRBTNOR ");
+ if (pm1_sts & (1 << 10)) printk(BIOS_SPEW, "RTC ");
+ if (pm1_sts & (1 << 8)) printk(BIOS_SPEW, "PWRBTN ");
+ if (pm1_sts & (1 << 5)) printk(BIOS_SPEW, "GBL ");
+ if (pm1_sts & (1 << 4)) printk(BIOS_SPEW, "BM ");
+ if (pm1_sts & (1 << 0)) printk(BIOS_SPEW, "TMROF ");
+ printk(BIOS_SPEW, "\n");
+ int reg16 = inw(pmbase + PM1_EN);
+ printk(BIOS_SPEW, "PM1_EN: %x\n", reg16);
+}
+
+/**
+ * @brief read and clear SMI_STS
+ * @return SMI_STS register
+ */
+static u32 reset_smi_status(void)
+{
+ u32 reg32;
+
+ reg32 = inl(pmbase + SMI_STS);
+ /* set status bits are cleared by writing 1 to them */
+ outl(reg32, pmbase + SMI_STS);
+
+ return reg32;
+}
+
+static void dump_smi_status(u32 smi_sts)
+{
+ printk(BIOS_DEBUG, "SMI_STS: ");
+ if (smi_sts & (1 << 26)) printk(BIOS_DEBUG, "SPI ");
+ if (smi_sts & (1 << 25)) printk(BIOS_DEBUG, "EL_SMI ");
+ if (smi_sts & (1 << 21)) printk(BIOS_DEBUG, "MONITOR ");
+ if (smi_sts & (1 << 20)) printk(BIOS_DEBUG, "PCI_EXP_SMI ");
+ if (smi_sts & (1 << 18)) printk(BIOS_DEBUG, "INTEL_USB2 ");
+ if (smi_sts & (1 << 17)) printk(BIOS_DEBUG, "LEGACY_USB2 ");
+ if (smi_sts & (1 << 16)) printk(BIOS_DEBUG, "SMBUS_SMI ");
+ if (smi_sts & (1 << 15)) printk(BIOS_DEBUG, "SERIRQ_SMI ");
+ if (smi_sts & (1 << 14)) printk(BIOS_DEBUG, "PERIODIC ");
+ if (smi_sts & (1 << 13)) printk(BIOS_DEBUG, "TCO ");
+ if (smi_sts & (1 << 12)) printk(BIOS_DEBUG, "DEVMON ");
+ if (smi_sts & (1 << 11)) printk(BIOS_DEBUG, "MCSMI ");
+ if (smi_sts & (1 << 10)) printk(BIOS_DEBUG, "GPI ");
+ if (smi_sts & (1 << 9)) printk(BIOS_DEBUG, "GPE0 ");
+ if (smi_sts & (1 << 8)) printk(BIOS_DEBUG, "PM1 ");
+ if (smi_sts & (1 << 6)) printk(BIOS_DEBUG, "SWSMI_TMR ");
+ if (smi_sts & (1 << 5)) printk(BIOS_DEBUG, "APM ");
+ if (smi_sts & (1 << 4)) printk(BIOS_DEBUG, "SLP_SMI ");
+ if (smi_sts & (1 << 3)) printk(BIOS_DEBUG, "LEGACY_USB ");
+ if (smi_sts & (1 << 2)) printk(BIOS_DEBUG, "BIOS ");
+ printk(BIOS_DEBUG, "\n");
+}
+
+
+/**
+ * @brief read and clear GPE0_STS
+ * @return GPE0_STS register
+ */
+static u32 reset_gpe0_status(void)
+{
+ u32 reg32;
+
+ reg32 = inl(pmbase + GPE0_STS);
+ /* set status bits are cleared by writing 1 to them */
+ outl(reg32, pmbase + GPE0_STS);
+
+ return reg32;
+}
+
+static void dump_gpe0_status(u32 gpe0_sts)
+{
+ int i;
+ printk(BIOS_DEBUG, "GPE0_STS: ");
+ for (i=31; i>= 16; i--) {
+ if (gpe0_sts & (1 << i)) printk(BIOS_DEBUG, "GPIO%d ", (i-16));
+ }
+ if (gpe0_sts & (1 << 14)) printk(BIOS_DEBUG, "USB4 ");
+ if (gpe0_sts & (1 << 13)) printk(BIOS_DEBUG, "PME_B0 ");
+ if (gpe0_sts & (1 << 12)) printk(BIOS_DEBUG, "USB3 ");
+ if (gpe0_sts & (1 << 11)) printk(BIOS_DEBUG, "PME ");
+ if (gpe0_sts & (1 << 10)) printk(BIOS_DEBUG, "EL_SCI/BATLOW ");
+ if (gpe0_sts & (1 << 9)) printk(BIOS_DEBUG, "PCI_EXP ");
+ if (gpe0_sts & (1 << 8)) printk(BIOS_DEBUG, "RI ");
+ if (gpe0_sts & (1 << 7)) printk(BIOS_DEBUG, "SMB_WAK ");
+ if (gpe0_sts & (1 << 6)) printk(BIOS_DEBUG, "TCO_SCI ");
+ if (gpe0_sts & (1 << 5)) printk(BIOS_DEBUG, "AC97 ");
+ if (gpe0_sts & (1 << 4)) printk(BIOS_DEBUG, "USB2 ");
+ if (gpe0_sts & (1 << 3)) printk(BIOS_DEBUG, "USB1 ");
+ if (gpe0_sts & (1 << 2)) printk(BIOS_DEBUG, "HOT_PLUG ");
+ if (gpe0_sts & (1 << 0)) printk(BIOS_DEBUG, "THRM ");
+ printk(BIOS_DEBUG, "\n");
+}
+
+
+/**
+ * @brief read and clear TCOx_STS
+ * @return TCOx_STS registers
+ */
+static u32 reset_tco_status(void)
+{
+ u32 tcobase = pmbase + 0x60;
+ u32 reg32;
+
+ reg32 = inl(tcobase + 0x04);
+ /* set status bits are cleared by writing 1 to them */
+ outl(reg32 & ~(1<<18), tcobase + 0x04); // Don't clear BOOT_STS before SECOND_TO_STS
+ if (reg32 & (1 << 18))
+ outl(reg32 & (1<<18), tcobase + 0x04); // clear BOOT_STS
+
+ return reg32;
+}
+
+
+static void dump_tco_status(u32 tco_sts)
+{
+ printk(BIOS_DEBUG, "TCO_STS: ");
+ if (tco_sts & (1 << 20)) printk(BIOS_DEBUG, "SMLINK_SLV ");
+ if (tco_sts & (1 << 18)) printk(BIOS_DEBUG, "BOOT ");
+ if (tco_sts & (1 << 17)) printk(BIOS_DEBUG, "SECOND_TO ");
+ if (tco_sts & (1 << 16)) printk(BIOS_DEBUG, "INTRD_DET ");
+ if (tco_sts & (1 << 12)) printk(BIOS_DEBUG, "DMISERR ");
+ if (tco_sts & (1 << 10)) printk(BIOS_DEBUG, "DMISMI ");
+ if (tco_sts & (1 << 9)) printk(BIOS_DEBUG, "DMISCI ");
+ if (tco_sts & (1 << 8)) printk(BIOS_DEBUG, "BIOSWR ");
+ if (tco_sts & (1 << 7)) printk(BIOS_DEBUG, "NEWCENTURY ");
+ if (tco_sts & (1 << 3)) printk(BIOS_DEBUG, "TIMEOUT ");
+ if (tco_sts & (1 << 2)) printk(BIOS_DEBUG, "TCO_INT ");
+ if (tco_sts & (1 << 1)) printk(BIOS_DEBUG, "SW_TCO ");
+ if (tco_sts & (1 << 0)) printk(BIOS_DEBUG, "NMI2SMI ");
+ printk(BIOS_DEBUG, "\n");
+}
+
+/* We are using PCIe accesses for now
+ * 1. the chipset can do it
+ * 2. we don't need to worry about how we leave 0xcf8/0xcfc behind
+ */
+#include <arch/pci_mmio_cfg.h>
+
+int southbridge_io_trap_handler(int smif)
+{
+ switch (smif) {
+ case 0x32:
+ printk(BIOS_DEBUG, "OS Init\n");
+ /* gnvs->smif:
+ * On success, the IO Trap Handler returns 0
+ * On failure, the IO Trap Handler returns a value != 0
+ */
+ gnvs->smif = 0;
+ return 1; /* IO trap handled */
+ }
+
+ /* Not handled */
+ return 0;
+}
+
+/**
+ * @brief Set the EOS bit
+ */
+void southbridge_smi_set_eos(void)
+{
+ u8 reg8;
+
+ reg8 = inb(pmbase + SMI_EN);
+ reg8 |= EOS;
+ outb(reg8, pmbase + SMI_EN);
+}
+
+static void busmaster_disable_on_bus(int bus)
+{
+ int slot, func;
+ unsigned int val;
+ unsigned char hdr;
+
+ for (slot = 0; slot < 0x20; slot++) {
+ for (func = 0; func < 8; func++) {
+ u32 reg32;
+ device_t dev = PCI_DEV(bus, slot, func);
+
+ val = pci_read_config32(dev, PCI_VENDOR_ID);
+
+ if (val == 0xffffffff || val == 0x00000000 ||
+ val == 0x0000ffff || val == 0xffff0000)
+ continue;
+
+ /* Disable Bus Mastering for this one device */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 &= ~PCI_COMMAND_MASTER;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* If this is a bridge, then follow it. */
+ hdr = pci_read_config8(dev, PCI_HEADER_TYPE);
+ hdr &= 0x7f;
+ if (hdr == PCI_HEADER_TYPE_BRIDGE ||
+ hdr == PCI_HEADER_TYPE_CARDBUS) {
+ unsigned int buses;
+ buses = pci_read_config32(dev, PCI_PRIMARY_BUS);
+ busmaster_disable_on_bus((buses >> 8) & 0xff);
+ }
+ }
+ }
+}
+
+
+static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *state_save)
+{
+ u8 reg8;
+ u32 reg32;
+ u8 slp_typ;
+ u8 s5pwr = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL;
+
+ // save and recover RTC port values
+ u8 tmp70, tmp72;
+ tmp70 = inb(0x70);
+ tmp72 = inb(0x72);
+ get_option(&s5pwr, "power_on_after_fail");
+ outb(tmp70, 0x70);
+ outb(tmp72, 0x72);
+
+ /* First, disable further SMIs */
+ reg8 = inb(pmbase + SMI_EN);
+ reg8 &= ~SLP_SMI_EN;
+ outb(reg8, pmbase + SMI_EN);
+
+ /* Figure out SLP_TYP */
+ reg32 = inl(pmbase + PM1_CNT);
+ printk(BIOS_SPEW, "SMI#: SLP = 0x%08x\n", reg32);
+ slp_typ = (reg32 >> 10) & 7;
+
+ /* Next, do the deed.
+ */
+
+ switch (slp_typ) {
+ case 0: printk(BIOS_DEBUG, "SMI#: Entering S0 (On)\n"); break;
+ case 1: printk(BIOS_DEBUG, "SMI#: Entering S1 (Assert STPCLK#)\n"); break;
+ case 5:
+ printk(BIOS_DEBUG, "SMI#: Entering S3 (Suspend-To-RAM)\n");
+ /* Invalidate the cache before going to S3 */
+ wbinvd();
+ break;
+ case 6: printk(BIOS_DEBUG, "SMI#: Entering S4 (Suspend-To-Disk)\n"); break;
+ case 7:
+ printk(BIOS_DEBUG, "SMI#: Entering S5 (Soft Power off)\n");
+
+ outl(0, pmbase + GPE0_EN);
+
+ /* Always set the flag in case CMOS was changed on runtime. For
+ * "KEEP", switch to "OFF" - KEEP is software emulated
+ */
+ reg8 = pci_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3);
+ if (s5pwr == MAINBOARD_POWER_ON) {
+ reg8 &= ~1;
+ } else {
+ reg8 |= 1;
+ }
+ pci_write_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3, reg8);
+
+ /* also iterates over all bridges on bus 0 */
+ busmaster_disable_on_bus(0);
+ break;
+ default: printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); break;
+ }
+
+#if !CONFIG_SMM_TSEG
+ /* Unlock the SMI semaphore. We're currently in SMM, and the semaphore
+ * will never be unlocked because the next outl will switch off the CPU.
+ * This might open a small race between the smi_release_lock() and the outl()
+ * for other SMI handlers. Not sure if this could cause trouble. */
+ if (slp_typ == 5)
+ smi_release_lock();
+#endif
+
+ /* Write back to the SLP register to cause the originally intended
+ * event again. We need to set BIT13 (SLP_EN) though to make the
+ * sleep happen.
+ */
+ outl(reg32 | SLP_EN, pmbase + PM1_CNT);
+
+ /* Make sure to stop executing code here for S3/S4/S5 */
+ if (slp_typ > 1)
+ hlt();
+ /* In most sleep states, the code flow of this function ends at
+ * the line above. However, if we entered sleep state S1 and wake
+ * up again, we will continue to execute code in this function.
+ */
+ reg32 = inl(pmbase + PM1_CNT);
+ if (reg32 & SCI_EN) {
+ /* The OS is not an ACPI OS, so we set the state to S0 */
+ reg32 &= ~(SLP_EN | SLP_TYP);
+ outl(reg32, pmbase + PM1_CNT);
+ }
+}
+
+static void southbridge_smi_apmc(unsigned int node, smm_state_save_area_t *state_save)
+{
+ u32 pmctrl;
+ u8 reg8;
+
+ /* Emulate B2 register as the FADT / Linux expects it */
+
+ reg8 = inb(APM_CNT);
+ if (mainboard_smi_apmc && mainboard_smi_apmc(reg8))
+ return;
+
+ switch (reg8) {
+ case APM_CNT_CST_CONTROL:
+ /* Calling this function seems to cause
+ * some kind of race condition in Linux
+ * and causes a kernel oops
+ */
+ printk(BIOS_DEBUG, "C-state control\n");
+ break;
+ case APM_CNT_PST_CONTROL:
+ /* Calling this function seems to cause
+ * some kind of race condition in Linux
+ * and causes a kernel oops
+ */
+ printk(BIOS_DEBUG, "P-state control\n");
+ break;
+ case APM_CNT_ACPI_DISABLE:
+ pmctrl = inl(pmbase + PM1_CNT);
+ pmctrl &= ~SCI_EN;
+ outl(pmctrl, pmbase + PM1_CNT);
+ printk(BIOS_DEBUG, "SMI#: ACPI disabled.\n");
+ break;
+ case APM_CNT_ACPI_ENABLE:
+ pmctrl = inl(pmbase + PM1_CNT);
+ pmctrl |= SCI_EN;
+ outl(pmctrl, pmbase + PM1_CNT);
+ printk(BIOS_DEBUG, "SMI#: ACPI enabled.\n");
+ break;
+ case APM_CNT_GNVS_UPDATE:
+ if (smm_initialized) {
+ printk(BIOS_DEBUG, "SMI#: SMM structures already initialized!\n");
+ return;
+ }
+ gnvs = *(global_nvs_t **)0x500;
+ tcg = *(void **)0x504;
+ smi1 = *(void **)0x508;
+ smm_initialized = 1;
+ printk(BIOS_DEBUG, "SMI#: Setting up structures to %p, %p, %p\n", gnvs, tcg, smi1);
+ break;
+ default:
+ printk(BIOS_DEBUG, "SMI#: Unknown function APM_CNT=%02x\n", reg8);
+ }
+}
+
+static void southbridge_smi_pm1(unsigned int node, smm_state_save_area_t *state_save)
+{
+ u16 pm1_sts;
+ volatile u8 cmos_status;
+
+ pm1_sts = reset_pm1_status();
+ dump_pm1_status(pm1_sts);
+
+ /* While OSPM is not active, poweroff immediately
+ * on a power button event.
+ */
+ if (pm1_sts & PWRBTN_STS) {
+ // power button pressed
+ u32 reg32;
+ reg32 = (7 << 10) | (1 << 13);
+ outl(reg32, pmbase + PM1_CNT);
+ }
+
+ if (pm1_sts & RTC_STS) {
+ /* read RTC status register to disable the interrupt */
+ cmos_status = cmos_read(RTC_REG_C);
+ printk(BIOS_DEBUG, "RTC IRQ status: %02X\n", cmos_status);
+ }
+}
+
+static void southbridge_smi_gpe0(unsigned int node, smm_state_save_area_t *state_save)
+{
+ u32 gpe0_sts;
+
+ gpe0_sts = reset_gpe0_status();
+ dump_gpe0_status(gpe0_sts);
+}
+
+static void southbridge_smi_gpi(unsigned int node, smm_state_save_area_t *state_save)
+{
+ u16 reg16;
+ reg16 = inw(pmbase + ALT_GP_SMI_STS);
+ outw(reg16, pmbase + ALT_GP_SMI_STS);
+
+ reg16 &= inw(pmbase + ALT_GP_SMI_EN);
+
+ if (mainboard_smi_gpi) {
+ mainboard_smi_gpi(reg16);
+ } else {
+ if (reg16)
+ printk(BIOS_DEBUG, "GPI (mask %04x)\n",reg16);
+ }
+}
+
+static void southbridge_smi_mc(unsigned int node, smm_state_save_area_t *state_save)
+{
+ u32 reg32;
+
+ reg32 = inl(pmbase + SMI_EN);
+
+ /* Are periodic SMIs enabled? */
+ if ((reg32 & MCSMI_EN) == 0)
+ return;
+
+ printk(BIOS_DEBUG, "Microcontroller SMI.\n");
+}
+
+
+
+static void southbridge_smi_tco(unsigned int node, smm_state_save_area_t *state_save)
+{
+ u32 tco_sts;
+
+ tco_sts = reset_tco_status();
+
+ /* Any TCO event? */
+ if (!tco_sts)
+ return;
+
+ if (tco_sts & (1 << 8)) { // BIOSWR
+ u8 bios_cntl;
+
+ bios_cntl = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0xdc);
+
+ if (bios_cntl & 1) {
+ /* BWE is RW, so the SMI was caused by a
+ * write to BWE, not by a write to the BIOS
+ */
+
+ /* This is the place where we notice someone
+ * is trying to tinker with the BIOS. We are
+ * trying to be nice and just ignore it. A more
+ * resolute answer would be to power down the
+ * box.
+ */
+ printk(BIOS_DEBUG, "Switching back to RO\n");
+ pci_write_config32(PCI_DEV(0, 0x1f, 0), 0xdc, (bios_cntl & ~1));
+ } /* No else for now? */
+ } else if (tco_sts & (1 << 3)) { /* TIMEOUT */
+ /* Handle TCO timeout */
+ printk(BIOS_DEBUG, "TCO Timeout.\n");
+ } else if (!tco_sts) {
+ dump_tco_status(tco_sts);
+ }
+}
+
+static void southbridge_smi_periodic(unsigned int node, smm_state_save_area_t *state_save)
+{
+ u32 reg32;
+
+ reg32 = inl(pmbase + SMI_EN);
+
+ /* Are periodic SMIs enabled? */
+ if ((reg32 & PERIODIC_EN) == 0)
+ return;
+
+ printk(BIOS_DEBUG, "Periodic SMI.\n");
+}
+
+static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *state_save)
+{
+#define IOTRAP(x) (trap_sts & (1 << x))
+ u32 trap_sts, trap_cycle;
+ u32 data, mask = 0;
+ int i;
+
+ trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register
+ RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR
+
+ trap_cycle = RCBA32(0x1e10);
+ for (i=16; i<20; i++) {
+ if (trap_cycle & (1 << i))
+ mask |= (0xff << ((i - 16) << 2));
+ }
+
+
+ /* IOTRAP(3) SMI function call */
+ if (IOTRAP(3)) {
+ if (gnvs && gnvs->smif)
+ io_trap_handler(gnvs->smif); // call function smif
+ return;
+ }
+
+ /* IOTRAP(2) currently unused
+ * IOTRAP(1) currently unused */
+
+ /* IOTRAP(0) SMIC */
+ if (IOTRAP(0)) {
+ if (!(trap_cycle & (1 << 24))) { // It's a write
+ printk(BIOS_DEBUG, "SMI1 command\n");
+ data = RCBA32(0x1e18);
+ data &= mask;
+ // if (smi1)
+ // southbridge_smi_command(data);
+ // return;
+ }
+ // Fall through to debug
+ }
+
+ printk(BIOS_DEBUG, " trapped io address = 0x%x\n", trap_cycle & 0xfffc);
+ for (i=0; i < 4; i++) if(IOTRAP(i)) printk(BIOS_DEBUG, " TRAP = %d\n", i);
+ printk(BIOS_DEBUG, " AHBE = %x\n", (trap_cycle >> 16) & 0xf);
+ printk(BIOS_DEBUG, " MASK = 0x%08x\n", mask);
+ printk(BIOS_DEBUG, " read/write: %s\n", (trap_cycle & (1 << 24)) ? "read" : "write");
+
+ if (!(trap_cycle & (1 << 24))) {
+ /* Write Cycle */
+ data = RCBA32(0x1e18);
+ printk(BIOS_DEBUG, " iotrap written data = 0x%08x\n", data);
+ }
+#undef IOTRAP
+}
+
+typedef void (*smi_handler_t)(unsigned int node,
+ smm_state_save_area_t *state_save);
+
+smi_handler_t southbridge_smi[32] = {
+ NULL, // [0] reserved
+ NULL, // [1] reserved
+ NULL, // [2] BIOS_STS
+ NULL, // [3] LEGACY_USB_STS
+ southbridge_smi_sleep, // [4] SLP_SMI_STS
+ southbridge_smi_apmc, // [5] APM_STS
+ NULL, // [6] SWSMI_TMR_STS
+ NULL, // [7] reserved
+ southbridge_smi_pm1, // [8] PM1_STS
+ southbridge_smi_gpe0, // [9] GPE0_STS
+ southbridge_smi_gpi, // [10] GPI_STS
+ southbridge_smi_mc, // [11] MCSMI_STS
+ NULL, // [12] DEVMON_STS
+ southbridge_smi_tco, // [13] TCO_STS
+ southbridge_smi_periodic, // [14] PERIODIC_STS
+ NULL, // [15] SERIRQ_SMI_STS
+ NULL, // [16] SMBUS_SMI_STS
+ NULL, // [17] LEGACY_USB2_STS
+ NULL, // [18] INTEL_USB2_STS
+ NULL, // [19] reserved
+ NULL, // [20] PCI_EXP_SMI_STS
+ southbridge_smi_monitor, // [21] MONITOR_STS
+ NULL, // [22] reserved
+ NULL, // [23] reserved
+ NULL, // [24] reserved
+ NULL, // [25] EL_SMI_STS
+ NULL, // [26] SPI_STS
+ NULL, // [27] reserved
+ NULL, // [28] reserved
+ NULL, // [29] reserved
+ NULL, // [30] reserved
+ NULL // [31] reserved
+};
+
+/**
+ * @brief Interrupt handler for SMI#
+ *
+ * @param smm_revision revision of the smm state save map
+ */
+
+void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save)
+{
+ int i, dump = 0;
+ u32 smi_sts;
+
+ /* Update global variable pmbase */
+ pmbase = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0x40) & 0xfffc;
+
+ /* We need to clear the SMI status registers, or we won't see what's
+ * happening in the following calls.
+ */
+ smi_sts = reset_smi_status();
+
+ /* Filter all non-enabled SMI events */
+ // FIXME Double check, this clears MONITOR
+ // smi_sts &= inl(pmbase + SMI_EN);
+
+ /* Call SMI sub handler for each of the status bits */
+ for (i = 0; i < 31; i++) {
+ if (smi_sts & (1 << i)) {
+ if (southbridge_smi[i])
+ southbridge_smi[i](node, state_save);
+ else {
+ printk(BIOS_DEBUG, "SMI_STS[%d] occured, but no "
+ "handler available.\n", i);
+ dump = 1;
+ }
+ }
+ }
+
+ if(dump) {
+ dump_smi_status(smi_sts);
+ }
+
+}
diff --git a/src/southbridge/intel/i82801hx/usb.c b/src/southbridge/intel/i82801hx/usb.c
new file mode 100644
index 0000000..b9a1094
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/usb.c
@@ -0,0 +1,108 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include "i82801hx.h"
+
+static void usb_init(struct device *dev)
+{
+ u32 reg32;
+ u8 reg8;
+
+ /* USB Specification says the device must be Bus Master */
+ printk(BIOS_DEBUG, "UHCI: Setting up controller.. ");
+
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER);
+
+ // Erratum
+ pci_write_config8(dev, 0xca, 0x00);
+
+ // Yes. Another Erratum
+ reg8 = pci_read_config8(dev, 0xca);
+ reg8 |= (1 << 0);
+ pci_write_config8(dev, 0xca, reg8);
+
+ printk(BIOS_DEBUG, "done.\n");
+}
+
+static void usb_set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ if (!vendor || !device) {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ pci_read_config32(dev, PCI_VENDOR_ID));
+ } else {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+}
+
+static struct pci_operations usb_pci_ops = {
+ .set_subsystem = usb_set_subsystem,
+};
+
+static struct device_operations usb_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = usb_init,
+ .scan_bus = 0,
+ .enable = i82801hx_enable,
+ .ops_pci = &usb_pci_ops,
+};
+
+static const struct pci_driver i82801hb_usb1 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2830,
+};
+
+static const struct pci_driver i82801hb_usb2 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2831,
+};
+
+static const struct pci_driver i82801hb_usb3 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2832,
+};
+
+static const struct pci_driver i82801hb_usb4 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2833,
+};
+
+static const struct pci_driver i82801hb_usb5 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2834,
+};
+
+static const struct pci_driver i82801hb_usb6 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2835,
+};
diff --git a/src/southbridge/intel/i82801hx/usb_ehci.c b/src/southbridge/intel/i82801hx/usb_ehci.c
new file mode 100644
index 0000000..84664e8
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/usb_ehci.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include "i82801hx.h"
+#include <device/pci_ehci.h>
+#include <arch/io.h>
+
+static void usb_ehci_init(struct device *dev)
+{
+ struct resource *res;
+ u32 base;
+ u32 reg32;
+ u8 reg8;
+
+ printk(BIOS_DEBUG, "EHCI: Setting up controller.. ");
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_MASTER;
+ reg32 |= PCI_COMMAND_SERR;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ reg32 = pci_read_config32(dev, 0xdc);
+ reg32 |= (1 << 31) | (1 << 27);
+ pci_write_config32(dev, 0xdc, reg32);
+
+ reg32 = pci_read_config32(dev, 0xfc);
+ reg32 &= ~(3 << 2);
+ reg32 |= (2 << 2) | (1 << 29) | (1 << 17);
+ pci_write_config32(dev, 0xfc, reg32);
+
+ /* Clear any pending port changes */
+ res = find_resource(dev, 0x10);
+ base = res->base;
+ reg32 = read32(base + 0x24) | (1 << 2);
+ write32(base + 0x24, reg32);
+
+ /* workaround */
+ reg8 = pci_read_config8(dev, 0x84);
+ reg8 |= (1 << 4);
+ pci_write_config8(dev, 0x84, reg8);
+
+ printk(BIOS_DEBUG, "done.\n");
+}
+
+static void usb_ehci_set_subsystem(device_t dev, unsigned vendor, unsigned device)
+{
+ u8 access_cntl;
+
+ access_cntl = pci_read_config8(dev, 0x80);
+
+ /* Enable writes to protected registers. */
+ pci_write_config8(dev, 0x80, access_cntl | 1);
+
+ if (!vendor || !device) {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ pci_read_config32(dev, PCI_VENDOR_ID));
+ } else {
+ pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
+ ((device & 0xffff) << 16) | (vendor & 0xffff));
+ }
+
+ /* Restore protection. */
+ pci_write_config8(dev, 0x80, access_cntl);
+}
+
+static struct pci_operations lops_pci = {
+ .set_subsystem = &usb_ehci_set_subsystem,
+};
+
+static struct device_operations usb_ehci_ops = {
+ .read_resources = pci_ehci_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = usb_ehci_init,
+ .scan_bus = 0,
+ .enable = i82801hx_enable,
+ .ops_pci = &lops_pci,
+};
+
+static const struct pci_driver i82801hx_usb1_ehci __pci_driver = {
+ .ops = &usb_ehci_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x2836,
+};
+
+static const struct pci_driver i82801hx_usb2_ehci __pci_driver = {
+ .ops = &usb_ehci_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x283a,
+};
diff --git a/src/southbridge/intel/i82801hx/watchdog.c b/src/southbridge/intel/i82801hx/watchdog.c
new file mode 100644
index 0000000..1c402f7
--- /dev/null
+++ b/src/southbridge/intel/i82801hx/watchdog.c
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * 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 <arch/io.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <watchdog.h>
+
+void watchdog_off(void)
+{
+ device_t dev;
+ unsigned long value, base;
+
+ /* Turn off the ICH7 watchdog. */
+ dev = dev_find_slot(0, PCI_DEVFN(0x1f, 0));
+
+ /* Enable I/O space. */
+ value = pci_read_config16(dev, 0x04);
+ value |= (1 << 10);
+ pci_write_config16(dev, 0x04, value);
+
+ /* Get TCO base. */
+ base = (pci_read_config32(dev, 0x40) & 0x0fffe) + 0x60;
+
+ /* Disable the watchdog timer. */
+ value = inw(base + 0x08);
+ value |= 1 << 11;
+ outw(value, base + 0x08);
+
+ /* Clear TCO timeout status. */
+ outw(0x0008, base + 0x04);
+ outw(0x0002, base + 0x06);
+
+ printk(BIOS_DEBUG, "ICH8 watchdog disabled\n");
+}
More information about the coreboot-gerrit
mailing list