summaryrefslogtreecommitdiff
path: root/src/southbridge/intel
diff options
context:
space:
mode:
Diffstat (limited to 'src/southbridge/intel')
-rw-r--r--src/southbridge/intel/fsp_i89xx/Kconfig58
-rw-r--r--src/southbridge/intel/fsp_i89xx/Makefile.inc52
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/globalnvs.asl286
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/irqlinks.asl491
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/lpc.asl223
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/pch.asl265
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/pcie.asl169
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/pcie_port.asl29
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/platform.asl57
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/sata.asl81
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/sleepstates.asl30
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/smbus.asl240
-rw-r--r--src/southbridge/intel/fsp_i89xx/acpi/usb.asl54
-rw-r--r--src/southbridge/intel/fsp_i89xx/bootblock.c80
-rw-r--r--src/southbridge/intel/fsp_i89xx/chip.h95
-rw-r--r--src/southbridge/intel/fsp_i89xx/early_init.c77
-rw-r--r--src/southbridge/intel/fsp_i89xx/early_me.c199
-rw-r--r--src/southbridge/intel/fsp_i89xx/early_smbus.c63
-rw-r--r--src/southbridge/intel/fsp_i89xx/early_spi.c114
-rw-r--r--src/southbridge/intel/fsp_i89xx/early_usb.c47
-rw-r--r--src/southbridge/intel/fsp_i89xx/elog.c114
-rw-r--r--src/southbridge/intel/fsp_i89xx/finalize.c64
-rw-r--r--src/southbridge/intel/fsp_i89xx/gpio.c135
-rw-r--r--src/southbridge/intel/fsp_i89xx/gpio.h194
-rw-r--r--src/southbridge/intel/fsp_i89xx/lpc.c688
-rw-r--r--src/southbridge/intel/fsp_i89xx/me.c740
-rw-r--r--src/southbridge/intel/fsp_i89xx/me.h372
-rw-r--r--src/southbridge/intel/fsp_i89xx/me_8.x.c882
-rw-r--r--src/southbridge/intel/fsp_i89xx/me_status.c212
-rw-r--r--src/southbridge/intel/fsp_i89xx/nvs.h159
-rw-r--r--src/southbridge/intel/fsp_i89xx/pch.c344
-rw-r--r--src/southbridge/intel/fsp_i89xx/pch.h475
-rw-r--r--src/southbridge/intel/fsp_i89xx/reset.c33
-rw-r--r--src/southbridge/intel/fsp_i89xx/romstage.c228
-rw-r--r--src/southbridge/intel/fsp_i89xx/romstage.h36
-rw-r--r--src/southbridge/intel/fsp_i89xx/sata.c120
-rw-r--r--src/southbridge/intel/fsp_i89xx/smbus.h96
-rw-r--r--src/southbridge/intel/fsp_i89xx/smi.c351
-rw-r--r--src/southbridge/intel/fsp_i89xx/smihandler.c757
-rw-r--r--src/southbridge/intel/fsp_i89xx/southbridge_pci_devs.h97
-rw-r--r--src/southbridge/intel/fsp_i89xx/watchdog.c59
41 files changed, 8866 insertions, 0 deletions
diff --git a/src/southbridge/intel/fsp_i89xx/Kconfig b/src/southbridge/intel/fsp_i89xx/Kconfig
new file mode 100644
index 0000000000..d1426d670b
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/Kconfig
@@ -0,0 +1,58 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2011 Google Inc.
+## Copyright (C) 2013 Sage Electronic Engineering, LLC.
+##
+## 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.
+##
+
+config SOUTHBRIDGE_INTEL_FSP_I89XX
+ bool
+
+if SOUTHBRIDGE_INTEL_FSP_I89XX
+
+config SOUTH_BRIDGE_OPTIONS # dummy
+ def_bool y
+ select IOAPIC
+ select HAVE_HARD_RESET
+ select HAVE_SMI_HANDLER
+ select USE_WATCHDOG_ON_BOOT
+ select PCIEXP_ASPM
+ select PCIEXP_COMMON_CLOCK
+ select SPI_FLASH
+ select COMMON_FADT
+ select HAVE_INTEL_FIRMWARE
+
+config EHCI_BAR
+ hex
+ default 0xfe700000
+
+
+config BOOTBLOCK_SOUTHBRIDGE_INIT
+ string
+ default "southbridge/intel/fsp_i89xx/bootblock.c"
+
+config SERIRQ_CONTINUOUS_MODE
+ bool
+ default n
+ help
+ If you set this option to y, the serial IRQ machine will be
+ operated in continuous mode.
+
+config HPET_MIN_TICKS
+ hex
+ default 0x80
+
+endif
diff --git a/src/southbridge/intel/fsp_i89xx/Makefile.inc b/src/southbridge/intel/fsp_i89xx/Makefile.inc
new file mode 100644
index 0000000000..b93d941649
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/Makefile.inc
@@ -0,0 +1,52 @@
+##
+## This file is part of the coreboot project.
+##
+## Copyright (C) 2010 Google Inc.
+## Copyright (C) 2013-2015 Sage Electronic Engineering, LLC.
+##
+## 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.
+##
+
+ifeq ($(CONFIG_SOUTHBRIDGE_INTEL_FSP_I89XX),y)
+
+subdirs-y += ../common/firmware
+
+ramstage-y += pch.c
+ramstage-y += lpc.c
+ramstage-y += sata.c
+ramstage-y += me.c
+ramstage-y += me_8.x.c
+ramstage-y += me_status.c
+ramstage-y += reset.c
+ramstage-y += watchdog.c
+
+ramstage-$(CONFIG_ELOG) += elog.c
+ramstage-y += ../common/spi.c
+smm-$(CONFIG_SPI_FLASH_SMM) += ../common/spi.c
+
+ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c
+smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c me.c me_8.x.c finalize.c
+smm-$(CONFIG_HAVE_SMI_HANDLER) += ../../../console/post.c
+
+romstage-y += early_usb.c early_smbus.c early_me.c me_status.c gpio.c early_init.c
+romstage-$(CONFIG_USBDEBUG) += usb_debug.c
+ramstage-$(CONFIG_USBDEBUG) += usb_debug.c
+smm-$(CONFIG_USBDEBUG) += usb_debug.c
+romstage-y += reset.c
+romstage-y += early_spi.c
+romstage-y += romstage.c
+
+CPPFLAGS_common += -I$(src)/southbridge/intel/fsp_i89xx
+
+endif
diff --git a/src/southbridge/intel/fsp_i89xx/acpi/globalnvs.asl b/src/southbridge/intel/fsp_i89xx/acpi/globalnvs.asl
new file mode 100644
index 0000000000..f124eb0aee
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/globalnvs.asl
@@ -0,0 +1,286 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ * Copyright (C) 2012 The Chromium OS Authors
+ *
+ * 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.
+ */
+
+/* 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.
+ */
+
+External(NVSA)
+OperationRegion (GNVS, SystemMemory, NVSA, 0xf00)
+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)
+ /* Thermal policy */
+ Offset (0x11),
+ TLVL, 8, // 0x11 - Throttle Level Limit
+ FLVL, 8, // 0x12 - Current FAN Level
+ TCRT, 8, // 0x13 - Critical Threshold
+ TPSV, 8, // 0x14 - Passive Threshold
+ TMAX, 8, // 0x15 - CPU Tj_max
+ F0OF, 8, // 0x16 - FAN 0 OFF Threshold
+ F0ON, 8, // 0x17 - FAN 0 ON Threshold
+ F0PW, 8, // 0x18 - FAN 0 PWM value
+ F1OF, 8, // 0x19 - FAN 1 OFF Threshold
+ F1ON, 8, // 0x1a - FAN 1 ON Threshold
+ F1PW, 8, // 0x1b - FAN 1 PWM value
+ F2OF, 8, // 0x1c - FAN 2 OFF Threshold
+ F2ON, 8, // 0x1d - FAN 2 ON Threshold
+ F2PW, 8, // 0x1e - FAN 2 PWM value
+ F3OF, 8, // 0x1f - FAN 3 OFF Threshold
+ F3ON, 8, // 0x20 - FAN 3 ON Threshold
+ F3PW, 8, // 0x21 - FAN 3 PWM value
+ F4OF, 8, // 0x22 - FAN 4 OFF Threshold
+ F4ON, 8, // 0x23 - FAN 4 ON Threshold
+ F4PW, 8, // 0x24 - FAN 4 PWM value
+ TMPS, 8, // 0x25 - Temperature Sensor ID
+ /* 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
+ PCNT, 8, // 0x2d - Processor count
+ /* Super I/O & CMOS config */
+ Offset (0x32),
+ NATP, 8, // 0x32 -
+ S5U0, 8, // 0x33 - Enable USB0 in S5
+ S5U1, 8, // 0x34 - Enable USB1 in S5
+ S3U0, 8, // 0x35 - Enable USB0 in S3
+ S3U1, 8, // 0x36 - Enable USB1 in S3
+ S33G, 8, // 0x37 - Enable 3G in S3
+ CMEM, 32, // 0x38 - CBMEM TOC
+ /* 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 Power conservation feature
+ 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
+
+ ISCI, 8, // 0xe8 - IGD SMI/SCI mode (0: SCI)
+ PAVP, 8, // 0xe9 - IGD PAVP data
+ Offset (0xeb),
+ OSCC, 8, // 0xeb - PCIe OSC control
+ NPCE, 8, // 0xec - native pcie support
+ PLFL, 8, // 0xed - platform flavor
+ BREV, 8, // 0xee - board revision
+ DPBM, 8, // 0xef - digital port b mode
+ DPCM, 8, // 0xf0 - digital port c mode
+ DPDM, 8, // 0xf1 - digital port d mode
+ ALFP, 8, // 0xf2 - active lfp
+ IMON, 8, // 0xf3 - current graphics turbo imon value
+ MMIO, 8, // 0xf4 - 64bit mmio support
+
+ /* ChromeOS specific */
+ Offset (0x100),
+ #include <vendorcode/google/chromeos/acpi/gnvs.asl>
+}
+
+/* Set flag to enable USB charging in S3 */
+Method (S3UE)
+{
+ Store (One, \S3U0)
+ Store (One, \S3U1)
+}
+
+/* Set flag to disable USB charging in S3 */
+Method (S3UD)
+{
+ Store (Zero, \S3U0)
+ Store (Zero, \S3U1)
+}
+
+/* Set flag to enable USB charging in S5 */
+Method (S5UE)
+{
+ Store (One, \S5U0)
+ Store (One, \S5U1)
+}
+
+/* Set flag to disable USB charging in S5 */
+Method (S5UD)
+{
+ Store (Zero, \S5U0)
+ Store (Zero, \S5U1)
+}
+
+/* Set flag to enable 3G module in S3 */
+Method (S3GE)
+{
+ Store (One, \S33G)
+}
+
+/* Set flag to disable 3G module in S3 */
+Method (S3GD)
+{
+ Store (Zero, \S33G)
+}
+
+External (\_TZ.THRM)
+External (\_TZ.SKIN)
+
+Method (TZUP)
+{
+ /* Update Primary Thermal Zone */
+ If (CondRefOf (\_TZ.THRM, Local0)) {
+ Notify (\_TZ.THRM, 0x81)
+ }
+
+ /* Update Secondary Thermal Zone */
+ If (CondRefOf (\_TZ.SKIN, Local0)) {
+ Notify (\_TZ.SKIN, 0x81)
+ }
+}
+
+/* Update Fan 0 thresholds */
+Method (F0UT, 2)
+{
+ Store (Arg0, \F0OF)
+ Store (Arg1, \F0ON)
+ TZUP ()
+}
+
+/* Update Fan 1 thresholds */
+Method (F1UT, 2)
+{
+ Store (Arg0, \F1OF)
+ Store (Arg1, \F1ON)
+ TZUP ()
+}
+
+/* Update Fan 2 thresholds */
+Method (F2UT, 2)
+{
+ Store (Arg0, \F2OF)
+ Store (Arg1, \F2ON)
+ TZUP ()
+}
+
+/* Update Fan 3 thresholds */
+Method (F3UT, 2)
+{
+ Store (Arg0, \F3OF)
+ Store (Arg1, \F3ON)
+ TZUP ()
+}
+
+/* Update Fan 4 thresholds */
+Method (F4UT, 2)
+{
+ Store (Arg0, \F4OF)
+ Store (Arg1, \F4ON)
+ TZUP ()
+}
+
+/* Update Temperature Sensor ID */
+Method (TMPU, 1)
+{
+ Store (Arg0, \TMPS)
+ TZUP ()
+}
diff --git a/src/southbridge/intel/fsp_i89xx/acpi/irqlinks.asl b/src/southbridge/intel/fsp_i89xx/acpi/irqlinks.asl
new file mode 100644
index 0000000000..acc03f0ceb
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/irqlinks.asl
@@ -0,0 +1,491 @@
+/*
+ * 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.
+ */
+
+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)
+ { 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)
+ { 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)
+ { 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)
+ { 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)
+ { 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)
+ { 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)
+ { 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)
+ { 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/fsp_i89xx/acpi/lpc.asl b/src/southbridge/intel/fsp_i89xx/acpi/lpc.asl
new file mode 100644
index 0000000000..83c04d5045
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/lpc.asl
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+
+// 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 (0xb8), // GPIO Routing Control
+ GR00, 2,
+ GR01, 2,
+ GR02, 2,
+ GR03, 2,
+ GR04, 2,
+ GR05, 2,
+ GR06, 2,
+ GR07, 2,
+ GR08, 2,
+ GR09, 2,
+ GR10, 2,
+ GR11, 2,
+ GR12, 2,
+ GR13, 2,
+ GR14, 2,
+ GR15, 2,
+
+ 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) // ICH7-M ACPI
+ IO (Decode16, DEFAULT_GPIOBASE, DEFAULT_GPIOBASE, 0x1, 0x40) // ICH7-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"
+}
diff --git a/src/southbridge/intel/fsp_i89xx/acpi/pch.asl b/src/southbridge/intel/fsp_i89xx/acpi/pch.asl
new file mode 100644
index 0000000000..46df6e7360
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/pch.asl
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+
+/* Intel Cave Creek PCH support */
+
+Scope(\)
+{
+ // IO-Trap at 0x800. This is the ACPI->SMI communication interface.
+
+ OperationRegion(IO_T, SystemIO, 0x800, 0x10)
+ Field(IO_T, ByteAcc, NoLock, Preserve)
+ {
+ Offset(0x8),
+ TRP0, 8 // IO-Trap at 0x808
+ }
+
+ // PCH Power Management Registers, located at PMBASE (0x1f.0 0x40.l)
+ OperationRegion(PMIO, SystemIO, DEFAULT_PMBASE, 0x80)
+ Field(PMIO, ByteAcc, NoLock, Preserve)
+ {
+ Offset(0x20), // GPE0_STS
+ , 16,
+ GS00, 1, // GPIO00 SCI/Wake Status
+ GS01, 1, // GPIO01 SCI/Wake Status
+ GS02, 1, // GPIO02 SCI/Wake Status
+ GS03, 1, // GPIO03 SCI/Wake Status
+ GS04, 1, // GPIO04 SCI/Wake Status
+ GS05, 1, // GPIO05 SCI/Wake Status
+ GS06, 1, // GPIO06 SCI/Wake Status
+ GS07, 1, // GPIO07 SCI/Wake Status
+ GS08, 1, // GPIO08 SCI/Wake Status
+ GS09, 1, // GPIO09 SCI/Wake Status
+ GS10, 1, // GPIO10 SCI/Wake Status
+ GS11, 1, // GPIO11 SCI/Wake Status
+ GS12, 1, // GPIO12 SCI/Wake Status
+ GS13, 1, // GPIO13 SCI/Wake Status
+ GS14, 1, // GPIO14 SCI/Wake Status
+ GS15, 1, // GPIO15 SCI/Wake Status
+ Offset(0x28), // GPE0_EN
+ , 16,
+ GE00, 1, // GPIO00 SCI/Wake Enable
+ GE01, 1, // GPIO01 SCI/Wake Enable
+ GE02, 1, // GPIO02 SCI/Wake Enable
+ GE03, 1, // GPIO03 SCI/Wake Enable
+ GE04, 1, // GPIO04 SCI/Wake Enable
+ GE05, 1, // GPIO05 SCI/Wake Enable
+ GE06, 1, // GPIO06 SCI/Wake Enable
+ GE07, 1, // GPIO07 SCI/Wake Enable
+ GE08, 1, // GPIO08 SCI/Wake Enable
+ GE09, 1, // GPIO09 SCI/Wake Enable
+ GE10, 1, // GPIO10 SCI/Wake Enable
+ GE11, 1, // GPIO11 SCI/Wake Enable
+ GE12, 1, // GPIO12 SCI/Wake Enable
+ GE13, 1, // GPIO13 SCI/Wake Enable
+ GE14, 1, // GPIO14 SCI/Wake Enable
+ GE15, 1, // GPIO15 SCI/Wake Enable
+ Offset(0x42), // General Purpose Control
+ , 1, // skip 1 bit
+ GPEC, 1, // SWGPE_CTRL
+ }
+
+ // GPIO IO mapped registers (0x1f.0 reg 0x48.l)
+ OperationRegion(GPIO, SystemIO, DEFAULT_GPIOBASE, 0x6c)
+ Field(GPIO, ByteAcc, NoLock, Preserve)
+ {
+ Offset(0x00), // GPIO Use Select
+ GU00, 8,
+ GU01, 8,
+ GU02, 8,
+ GU03, 8,
+ Offset(0x04), // GPIO IO Select
+ GIO0, 8,
+ GIO1, 8,
+ GIO2, 8,
+ GIO3, 8,
+ Offset(0x0c), // GPIO Level
+ GL00, 1,
+ GP01, 1,
+ GP02, 1,
+ GP03, 1,
+ GP04, 1,
+ GP05, 1,
+ GP06, 1,
+ GP07, 1,
+ GP08, 1,
+ GP09, 1,
+ GP10, 1,
+ GP11, 1,
+ GP12, 1,
+ GP13, 1,
+ GP14, 1,
+ GP15, 1,
+ GP16, 1,
+ GP17, 1,
+ GP18, 1,
+ GP19, 1,
+ GP20, 1,
+ GP21, 1,
+ GP22, 1,
+ GP23, 1,
+ GP24, 1,
+ GP25, 1,
+ GP26, 1,
+ GP27, 1,
+ GP28, 1,
+ GP29, 1,
+ GP30, 1,
+ GP31, 1,
+ Offset(0x18), // GPIO Blink
+ GB00, 8,
+ GB01, 8,
+ GB02, 8,
+ GB03, 8,
+ Offset(0x2c), // GPIO Invert
+ GIV0, 8,
+ GIV1, 8,
+ GIV2, 8,
+ GIV3, 8,
+ Offset(0x30), // GPIO Use Select 2
+ GU04, 8,
+ GU05, 8,
+ GU06, 8,
+ GU07, 8,
+ Offset(0x34), // GPIO IO Select 2
+ GIO4, 8,
+ GIO5, 8,
+ GIO6, 8,
+ GIO7, 8,
+ Offset(0x38), // GPIO Level 2
+ GP32, 1,
+ GP33, 1,
+ GP34, 1,
+ GP35, 1,
+ GP36, 1,
+ GP37, 1,
+ GP38, 1,
+ GP39, 1,
+ GP40, 1,
+ GP41, 1,
+ GP42, 1,
+ GP43, 1,
+ GP44, 1,
+ GP45, 1,
+ GP46, 1,
+ GP47, 1,
+ GP48, 1,
+ GP49, 1,
+ GP50, 1,
+ GP51, 1,
+ GP52, 1,
+ GP53, 1,
+ GP54, 1,
+ GP55, 1,
+ GP56, 1,
+ GP57, 1,
+ GP58, 1,
+ GP59, 1,
+ GP60, 1,
+ GP61, 1,
+ GP62, 1,
+ GP63, 1,
+ Offset(0x40), // GPIO Use Select 3
+ GU08, 8,
+ GU09, 4,
+ Offset(0x44), // GPIO IO Select 3
+ GIO8, 8,
+ GIO9, 4,
+ Offset(0x48), // GPIO Level 3
+ GP64, 1,
+ GP65, 1,
+ GP66, 1,
+ GP67, 1,
+ GP68, 1,
+ GP69, 1,
+ GP70, 1,
+ GP71, 1,
+ GP72, 1,
+ GP73, 1,
+ GP74, 1,
+ GP75, 1,
+ }
+
+
+ // ICH7 Root Complex Register Block. Memory Mapped through RCBA)
+ OperationRegion(RCRB, SystemMemory, DEFAULT_RCBA, 0x4000)
+ Field(RCRB, DWordAcc, Lock, Preserve)
+ {
+ Offset(0x0000), // Backbone
+ Offset(0x1000), // Chipset
+ Offset(0x3000), // Legacy Configuration Registers
+ Offset(0x3404), // High Performance Timer Configuration
+ HPAS, 2, // Address Select
+ , 5,
+ HPTE, 1, // Address Enable
+ Offset(0x3418), // FD (Function Disable)
+ , 2, // Reserved
+ SA1D, 1, // SATA1 disable
+ SMBD, 1, // SMBUS disable
+ , 10, // Reserved
+ LPBD, 1, // LPC bridge disable
+ EH1D, 1, // EHCI #1 disable
+ RP1D, 1, // Root Port 1 disable
+ RP2D, 1, // Root Port 2 disable
+ RP3D, 1, // Root Port 3 disable
+ RP4D, 1, // Root Port 4 disable
+ , 4,
+ TTRD, 1, // Thermal sensor registers disable
+ SA2D, 1, // SATA2 disable
+ Offset(0x3428), // FD2 (Function Disable 2)
+ , 1, // Reserved
+ ME1D, 1, // ME Interface 1 disable
+ ME2D, 1, // ME Interface 2 disable
+ IDRD, 1, // IDE redirect disable
+ KTCT, 1, // Keyboard Text redirect disable
+ }
+}
+
+// PCI Express Ports 0:1c.x
+#include "pcie.asl"
+
+// USB 0:1d.0
+#include "usb.asl"
+
+// LPC Bridge 0:1f.0
+#include "lpc.asl"
+
+// SATA 0:1f.2, 0:1f.5
+#include "sata.asl"
+
+// SMBus 0:1f.3
+#include "smbus.asl"
+
+Method (_OSC, 4)
+{
+ /* Check for proper GUID */
+ If (LEqual (Arg0, ToUUID("33DB4D5B-1FF7-401C-9657-7441C03DD766")))
+ {
+ /* Let OS control everything */
+ Return (Arg3)
+ }
+ Else
+ {
+ /* Unrecognized UUID */
+ CreateDWordField (Arg3, 0, CDW1)
+ Or (CDW1, 4, CDW1)
+ Return (Arg3)
+ }
+}
diff --git a/src/southbridge/intel/fsp_i89xx/acpi/pcie.asl b/src/southbridge/intel/fsp_i89xx/acpi/pcie.asl
new file mode 100644
index 0000000000..3b6c3db3aa
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/pcie.asl
@@ -0,0 +1,169 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2009 coresystems GmbH
+ * Copyright (C) 2012 The Chromium OS Authors. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+/* Intel 6/7 Series PCH PCIe support */
+
+// PCI Express Ports
+
+Method (IRQM, 1, Serialized) {
+
+ /* Interrupt Map INTA->INTA, INTB->INTB, INTC->INTC, INTD->INTD */
+ Name (IQAA, Package() {
+ Package() { 0x0000ffff, 0, 0, 16 },
+ Package() { 0x0000ffff, 1, 0, 17 },
+ Package() { 0x0000ffff, 2, 0, 18 },
+ Package() { 0x0000ffff, 3, 0, 19 } })
+ Name (IQAP, 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 } })
+
+ /* Interrupt Map INTA->INTB, INTB->INTC, INTC->INTD, INTD->INTA */
+ Name (IQBA, Package() {
+ Package() { 0x0000ffff, 0, 0, 17 },
+ Package() { 0x0000ffff, 1, 0, 18 },
+ Package() { 0x0000ffff, 2, 0, 19 },
+ Package() { 0x0000ffff, 3, 0, 16 } })
+ Name (IQBP, 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 } })
+
+ /* Interrupt Map INTA->INTC, INTB->INTD, INTC->INTA, INTD->INTB */
+ Name (IQCA, Package() {
+ Package() { 0x0000ffff, 0, 0, 18 },
+ Package() { 0x0000ffff, 1, 0, 19 },
+ Package() { 0x0000ffff, 2, 0, 16 },
+ Package() { 0x0000ffff, 3, 0, 17 } })
+ Name (IQCP, 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 } })
+
+ /* Interrupt Map INTA->INTD, INTB->INTA, INTC->INTB, INTD->INTC */
+ Name (IQDA, Package() {
+ Package() { 0x0000ffff, 0, 0, 19 },
+ Package() { 0x0000ffff, 1, 0, 16 },
+ Package() { 0x0000ffff, 2, 0, 17 },
+ Package() { 0x0000ffff, 3, 0, 18 } })
+ Name (IQDP, 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 } })
+
+ Switch (ToInteger (Arg0)) {
+ /* PCIe Root Port 1 and 5 */
+ Case (Package() { 1, 5 }) {
+ If (PICM) {
+ Return (IQAA)
+ } Else {
+ Return (IQAP)
+ }
+ }
+
+ /* PCIe Root Port 2 and 6 */
+ Case (Package() { 2, 6 }) {
+ If (PICM) {
+ Return (IQBA)
+ } Else {
+ Return (IQBP)
+ }
+ }
+
+ /* PCIe Root Port 3 and 7 */
+ Case (Package() { 3, 7 }) {
+ If (PICM) {
+ Return (IQCA)
+ } Else {
+ Return (IQCP)
+ }
+ }
+
+ /* PCIe Root Port 4 and 8 */
+ Case (Package() { 4, 8 }) {
+ If (PICM) {
+ Return (IQDA)
+ } Else {
+ Return (IQDP)
+ }
+ }
+
+ Default {
+ If (PICM) {
+ Return (IQDA)
+ } Else {
+ Return (IQDP)
+ }
+ }
+ }
+}
+
+Device (RP01)
+{
+ Name (_ADR, 0x001c0000)
+
+ #include "pcie_port.asl"
+
+ Method (_PRT)
+ {
+ Return (IRQM (RPPN))
+ }
+}
+
+Device (RP02)
+{
+ Name (_ADR, 0x001c0001)
+
+ #include "pcie_port.asl"
+
+ Method (_PRT)
+ {
+ Return (IRQM (RPPN))
+ }
+}
+
+Device (RP03)
+{
+ Name (_ADR, 0x001c0002)
+
+ #include "pcie_port.asl"
+
+ Method (_PRT)
+ {
+ Return (IRQM (RPPN))
+ }
+}
+
+Device (RP04)
+{
+ Name (_ADR, 0x001c0003)
+
+ #include "pcie_port.asl"
+
+ Method (_PRT)
+ {
+ Return (IRQM (RPPN))
+ }
+}
diff --git a/src/southbridge/intel/fsp_i89xx/acpi/pcie_port.asl b/src/southbridge/intel/fsp_i89xx/acpi/pcie_port.asl
new file mode 100644
index 0000000000..276227bb50
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/pcie_port.asl
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 The Chromium OS Authors. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+/* Included in each PCIe Root Port device */
+
+OperationRegion (RPCS, PCI_Config, 0x00, 0xFF)
+Field (RPCS, AnyAcc, NoLock, Preserve)
+{
+ Offset (0x4c), // Link Capabilities
+ , 24,
+ RPPN, 8, // Root Port Number
+}
diff --git a/src/southbridge/intel/fsp_i89xx/acpi/platform.asl b/src/southbridge/intel/fsp_i89xx/acpi/platform.asl
new file mode 100644
index 0000000000..e2d6008bb6
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/platform.asl
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011-2012 The Chromium OS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+/* The APM port can be used for generating software SMIs */
+
+OperationRegion (APMP, SystemIO, 0xb2, 2)
+Field (APMP, ByteAcc, NoLock, Preserve)
+{
+ APMC, 8, // APM command
+ APMS, 8 // APM status
+}
+
+
+/* Port 80 POST */
+
+OperationRegion (POST, SystemIO, 0x80, 1)
+Field (POST, ByteAcc, Lock, Preserve)
+{
+ DBG0, 8
+}
+
+/* SMI I/O Trap */
+Method(TRAP, 1, Serialized)
+{
+ Store (Arg0, SMIF) // SMI Function
+ Store (0, TRP0) // Generate trap
+ Return (SMIF) // Return value of SMI handler
+}
+
+/* The _PIC method is called by the OS to choose between interrupt
+ * routing via the i8259 interrupt controller or the APIC.
+ *
+ * _PIC is called with a parameter of 0 for i8259 configuration and
+ * with a parameter of 1 for Local Apic/IOAPIC configuration.
+ */
+
+Method(_PIC, 1)
+{
+ // Remember the OS' IRQ routing choice.
+ Store(Arg0, PICM)
+}
diff --git a/src/southbridge/intel/fsp_i89xx/acpi/sata.asl b/src/southbridge/intel/fsp_i89xx/acpi/sata.asl
new file mode 100644
index 0000000000..fc9cbb0630
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/sata.asl
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+// 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, 0, Serialized)
+ {
+ 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/fsp_i89xx/acpi/sleepstates.asl b/src/southbridge/intel/fsp_i89xx/acpi/sleepstates.asl
new file mode 100644
index 0000000000..7e6b3c38ba
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/sleepstates.asl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+Name(\_S0, Package(){0x0,0x0,0x0,0x0})
+
+/*
+ * S1 and S3 are currently not supported by the FSP
+ * 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/fsp_i89xx/acpi/smbus.asl b/src/southbridge/intel/fsp_i89xx/acpi/smbus.asl
new file mode 100644
index 0000000000..efd323dea5
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/smbus.asl
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+// Intel SMBus Controller 0:1f.3
+
+Device (SBUS)
+{
+ Name (_ADR, 0x001f0003)
+
+#ifdef ENABLE_SMBUS_METHODS
+ OperationRegion (SMBP, PCI_Config, 0x00, 0x100)
+ Field(SMBP, DWordAcc, NoLock, Preserve)
+ {
+ Offset(0x40),
+ , 2,
+ I2CE, 1
+ }
+
+ OperationRegion (SMBI, SystemIO, SMBUS_IO_BASE, 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
+ }
+
+ // 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/fsp_i89xx/acpi/usb.asl b/src/southbridge/intel/fsp_i89xx/acpi/usb.asl
new file mode 100644
index 0000000000..f33cb803ea
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/acpi/usb.asl
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+/* Intel Cave Creek USB support */
+
+/* EHCI Controller 0:1d.0 */
+
+Device (EHC1)
+{
+ Name(_ADR, 0x001d0000)
+
+ 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)
+
+ 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/fsp_i89xx/bootblock.c b/src/southbridge/intel/fsp_i89xx/bootblock.c
new file mode 100644
index 0000000000..0e6465c0cd
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/bootblock.c
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <arch/io.h>
+#include <cpu/x86/tsc.h>
+#include "pch.h"
+
+/*
+ * Enable Prefetching and Caching.
+ */
+static void enable_spi_prefetch(void)
+{
+ u8 reg8;
+ pci_devfn_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 enable_port80_on_lpc(void)
+{
+ pci_devfn_t dev = PCI_DEV(0, 0x1f, 0);
+
+ /* Enable port 80 POST on LPC */
+ pci_write_config32(dev, RCBA, (uintptr_t)DEFAULT_RCBA | 1);
+ volatile u32 *gcs = (volatile u32 *)(DEFAULT_RCBA + GCS);
+ u32 reg32 = *gcs;
+ reg32 = reg32 & ~0x04;
+ *gcs = reg32;
+}
+
+static void set_spi_speed(void)
+{
+ u32 fdod;
+ u8 ssfc;
+
+ /* Observe SPI Descriptor Component Section 0 */
+ RCBA32(0x38b0) = 0x1000;
+
+ /* Extract the Write/Erase SPI Frequency from descriptor */
+ fdod = RCBA32(0x38b4);
+ fdod >>= 24;
+ fdod &= 7;
+
+ /* Set Software Sequence frequency to match */
+ ssfc = RCBA8(0x3893);
+ ssfc &= ~7;
+ ssfc |= fdod;
+ RCBA8(0x3893) = ssfc;
+}
+
+static void bootblock_southbridge_init(void)
+{
+ enable_spi_prefetch();
+ enable_port80_on_lpc();
+ set_spi_speed();
+
+ /* Enable upper 128bytes of CMOS */
+ RCBA32(RC) = (1 << 2);
+}
diff --git a/src/southbridge/intel/fsp_i89xx/chip.h b/src/southbridge/intel/fsp_i89xx/chip.h
new file mode 100644
index 0000000000..ae6e92f25a
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/chip.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#ifndef SOUTHBRIDGE_INTEL_I89XX_CHIP_H
+#define SOUTHBRIDGE_INTEL_I89XX_CHIP_H
+
+struct southbridge_intel_fsp_i89xx_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 sata_ahci;
+ uint8_t sata_port_map;
+ uint32_t sata_port0_gen3_tx;
+ uint32_t sata_port1_gen3_tx;
+
+ uint32_t gen1_dec;
+ uint32_t gen2_dec;
+ uint32_t gen3_dec;
+ uint32_t gen4_dec;
+
+ /* Enable linear PCIe Root Port function numbers starting at zero */
+ uint8_t pcie_port_coalesce;
+
+ /* Override PCIe ASPM */
+ uint8_t pcie_aspm_f0;
+ uint8_t pcie_aspm_f1;
+ uint8_t pcie_aspm_f2;
+ uint8_t pcie_aspm_f3;
+ uint8_t pcie_aspm_f4;
+ uint8_t pcie_aspm_f5;
+ uint8_t pcie_aspm_f6;
+ uint8_t pcie_aspm_f7;
+
+ int p_cnt_throttling_supported;
+ int c2_latency;
+};
+
+#endif /* SOUTHBRIDGE_INTEL_I89XX_CHIP_H */
diff --git a/src/southbridge/intel/fsp_i89xx/early_init.c b/src/southbridge/intel/fsp_i89xx/early_init.c
new file mode 100644
index 0000000000..96e6cd2785
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/early_init.c
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2010 coresystems GmbH
+ * Copyright (C) 2011 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <console/console.h>
+#include <arch/io.h>
+#include <device/pci_def.h>
+#include <elog.h>
+#include "pch.h"
+
+static void sandybridge_setup_bars(void)
+{
+ /* Setting up Southbridge. */
+ printk(BIOS_DEBUG, "Setting up static southbridge registers...");
+ pci_write_config32(PCI_DEV(0, 0x1f, 0), RCBA, (uintptr_t)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 */
+
+ 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");
+
+#if CONFIG_ELOG_BOOT_COUNT
+ /* Increment Boot Counter for non-S3 resume */
+ if ((inw(DEFAULT_PMBASE + PM1_STS) & WAK_STS) &&
+ ((inl(DEFAULT_PMBASE + PM1_CNT) >> 10) & 7) != SLP_TYP_S3)
+ boot_count_increment();
+#endif
+
+ printk(BIOS_DEBUG, " done.\n");
+
+#if CONFIG_ELOG_BOOT_COUNT
+ /* Increment Boot Counter except when resuming from S3 */
+ if ((inw(DEFAULT_PMBASE + PM1_STS) & WAK_STS) &&
+ ((inl(DEFAULT_PMBASE + PM1_CNT) >> 10) & 7) == SLP_TYP_S3)
+ return;
+ boot_count_increment();
+#endif
+}
+
+void sandybridge_sb_early_initialization(void)
+{
+ /* Setup all BARs required for early PCIe and raminit */
+ sandybridge_setup_bars();
+}
+
+void early_pch_init(void)
+{
+ u8 reg8;
+
+ // reset rtc power status
+ reg8 = pci_read_config8(PCH_LPC_DEV, 0xa4);
+ reg8 &= ~(1 << 2);
+ pci_write_config8(PCH_LPC_DEV, 0xa4, reg8);
+}
diff --git a/src/southbridge/intel/fsp_i89xx/early_me.c b/src/southbridge/intel/fsp_i89xx/early_me.c
new file mode 100644
index 0000000000..b7dbf3439f
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/early_me.c
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <delay.h>
+#include <device/pci_ids.h>
+#include <halt.h>
+#include <string.h>
+#include "me.h"
+#include "pch.h"
+
+static const char *me_ack_values[] = {
+ [ME_HFS_ACK_NO_DID] = "No DID Ack received",
+ [ME_HFS_ACK_RESET] = "Non-power cycle reset",
+ [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset",
+ [ME_HFS_ACK_S3] = "Go to S3",
+ [ME_HFS_ACK_S4] = "Go to S4",
+ [ME_HFS_ACK_S5] = "Go to S5",
+ [ME_HFS_ACK_GBL_RESET] = "Global Reset",
+ [ME_HFS_ACK_CONTINUE] = "Continue to boot"
+};
+
+static inline void pci_read_dword_ptr(void *ptr, int offset)
+{
+ u32 dword = pci_read_config32(PCH_ME_DEV, offset);
+ memcpy(ptr, &dword, sizeof(dword));
+}
+
+static inline void pci_write_dword_ptr(void *ptr, int offset)
+{
+ u32 dword = 0;
+ memcpy(&dword, ptr, sizeof(dword));
+ pci_write_config32(PCH_ME_DEV, offset, dword);
+}
+
+void intel_early_me_status(void)
+{
+ struct me_hfs hfs;
+ struct me_gmes gmes;
+
+ pci_read_dword_ptr(&hfs, PCI_ME_HFS);
+ pci_read_dword_ptr(&gmes, PCI_ME_GMES);
+
+ intel_me_status(&hfs, &gmes);
+}
+
+int intel_early_me_init(void)
+{
+ int count;
+ struct me_uma uma;
+ struct me_hfs hfs;
+
+ printk(BIOS_INFO, "Intel ME early init\n");
+
+ /* Wait for ME UMA SIZE VALID bit to be set */
+ for (count = ME_RETRY; count > 0; --count) {
+ pci_read_dword_ptr(&uma, PCI_ME_UMA);
+ if (uma.valid)
+ break;
+ udelay(ME_DELAY);
+ }
+ if (!count) {
+ printk(BIOS_ERR, "ERROR: ME is not ready!\n");
+ return -1;
+ }
+
+ /* Check for valid firmware */
+ pci_read_dword_ptr(&hfs, PCI_ME_HFS);
+ if (hfs.fpt_bad) {
+ printk(BIOS_WARNING, "WARNING: ME has bad firmware\n");
+ return -1;
+ }
+
+ printk(BIOS_INFO, "Intel ME firmware is ready\n");
+ return 0;
+}
+
+int intel_early_me_uma_size(void)
+{
+ struct me_uma uma;
+
+ pci_read_dword_ptr(&uma, PCI_ME_UMA);
+ if (uma.valid) {
+ printk(BIOS_DEBUG, "ME: Requested %uMB UMA\n", uma.size);
+ return uma.size;
+ }
+
+ printk(BIOS_DEBUG, "ME: Invalid UMA size\n");
+ return 0;
+}
+
+static inline void set_global_reset(int enable)
+{
+ u32 etr3 = pci_read_config32(PCH_LPC_DEV, ETR3);
+
+ /* Clear CF9 Without Resume Well Reset Enable */
+ etr3 &= ~ETR3_CWORWRE;
+
+ /* CF9GR indicates a Global Reset */
+ if (enable)
+ etr3 |= ETR3_CF9GR;
+ else
+ etr3 &= ~ETR3_CF9GR;
+
+ pci_write_config32(PCH_LPC_DEV, ETR3, etr3);
+}
+
+int intel_early_me_init_done(u8 status)
+{
+ u8 reset;
+ int count;
+ u32 mebase_l, mebase_h;
+ struct me_hfs hfs;
+ struct me_did did = {
+ .init_done = ME_INIT_DONE,
+ .status = status
+ };
+
+ /* MEBASE from MESEG_BASE[35:20] */
+ mebase_l = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_L);
+ mebase_h = pci_read_config32(PCI_CPU_DEVICE, PCI_CPU_MEBASE_H) & 0xf;
+ did.uma_base = (mebase_l >> 20) | (mebase_h << 12);
+
+ /* Send message to ME */
+ printk(BIOS_DEBUG, "ME: Sending Init Done with status: %d, "
+ "UMA base: 0x%04x\n", status, did.uma_base);
+
+ pci_write_dword_ptr(&did, PCI_ME_H_GS);
+
+ /* Must wait for ME acknowledgement */
+ for (count = ME_RETRY; count > 0; --count) {
+ pci_read_dword_ptr(&hfs, PCI_ME_HFS);
+ if (hfs.bios_msg_ack)
+ break;
+ udelay(ME_DELAY);
+ }
+ if (!count) {
+ printk(BIOS_ERR, "ERROR: ME failed to respond\n");
+ return -1;
+ }
+
+ /* Return the requested BIOS action */
+ printk(BIOS_NOTICE, "ME: Requested BIOS Action: %s\n",
+ me_ack_values[hfs.ack_data]);
+
+ /* Check status after acknowledgement */
+ intel_early_me_status();
+
+ reset = 0;
+ switch (hfs.ack_data) {
+ case ME_HFS_ACK_CONTINUE:
+ /* Continue to boot */
+ return 0;
+ case ME_HFS_ACK_RESET:
+ /* Non-power cycle reset */
+ set_global_reset(0);
+ reset = 0x06;
+ break;
+ case ME_HFS_ACK_PWR_CYCLE:
+ /* Power cycle reset */
+ set_global_reset(0);
+ reset = 0x0e;
+ break;
+ case ME_HFS_ACK_GBL_RESET:
+ /* Global reset */
+ set_global_reset(1);
+ reset = 0x0e;
+ break;
+ case ME_HFS_ACK_S3:
+ case ME_HFS_ACK_S4:
+ case ME_HFS_ACK_S5:
+ break;
+ }
+
+ /* Perform the requested reset */
+ if (reset) {
+ outb(reset, 0xcf9);
+ halt();
+ }
+ return -1;
+}
diff --git a/src/southbridge/intel/fsp_i89xx/early_smbus.c b/src/southbridge/intel/fsp_i89xx/early_smbus.c
new file mode 100644
index 0000000000..190535b6cc
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/early_smbus.c
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2012 Sage Electronic Engineering, LLC
+ *
+ * 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.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+#include "pch.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, 0x0) != 0x8086) {
+ 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);
+ printk(BIOS_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/fsp_i89xx/early_spi.c b/src/southbridge/intel/fsp_i89xx/early_spi.c
new file mode 100644
index 0000000000..1ebe044810
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/early_spi.c
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+#include <delay.h>
+#include "pch.h"
+
+#define SPI_DELAY 10 /* 10us */
+#define SPI_RETRY 200000 /* 2s */
+
+static int early_spi_read_block(u32 offset, u8 size, u8 *buffer)
+{
+ u32 *ptr32 = (u32*)buffer;
+ u32 i;
+
+ /* Clear status bits */
+ RCBA16(SPIBAR_HSFS) |= SPIBAR_HSFS_AEL | SPIBAR_HSFS_FCERR |
+ SPIBAR_HSFS_FDONE;
+
+ if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_SCIP) {
+ printk(BIOS_ERR, "SPI ERROR: transaction in progress\n");
+ return -1;
+ }
+
+ /* Set flash address */
+ RCBA32(SPIBAR_FADDR) = offset;
+
+ /* Setup read transaction */
+ RCBA16(SPIBAR_HSFC) = SPIBAR_HSFC_BYTE_COUNT(size) |
+ SPIBAR_HSFC_CYCLE_READ;
+
+ /* Start transactinon */
+ RCBA16(SPIBAR_HSFC) |= SPIBAR_HSFC_GO;
+
+ /* Wait for completion */
+ for (i = 0; i < SPI_RETRY; i++) {
+ if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_SCIP) {
+ /* Cycle in progress, wait 1ms */
+ udelay(SPI_DELAY);
+ continue;
+ }
+
+ if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_AEL) {
+ printk(BIOS_ERR, "SPI ERROR: Access Error\n");
+ return -1;
+
+ }
+
+ if (RCBA16(SPIBAR_HSFS) & SPIBAR_HSFS_FCERR) {
+ printk(BIOS_ERR, "SPI ERROR: Flash Cycle Error\n");
+ return -1;
+ }
+ break;
+ }
+
+ if (i >= SPI_RETRY) {
+ printk(BIOS_ERR, "SPI ERROR: Timeout\n");
+ return -1;
+ }
+
+ /* Read the data */
+ for (i = 0; i < size; i+=sizeof(u32)) {
+ if (size-i >= 4) {
+ /* reading >= dword */
+ *ptr32++ = RCBA32(SPIBAR_FDATA(i/sizeof(u32)));
+ } else {
+ /* reading < dword */
+ u8 j, *ptr8 = (u8*)ptr32;
+ u32 temp = RCBA32(SPIBAR_FDATA(i/sizeof(u32)));
+ for (j = 0; j < (size-i); j++) {
+ *ptr8++ = temp & 0xff;
+ temp >>= 8;
+ }
+ }
+ }
+
+ return size;
+}
+
+int early_spi_read(u32 offset, u32 size, u8 *buffer)
+{
+ u32 current = 0;
+
+ while (size > 0) {
+ u8 count = (size < 64) ? size : 64;
+ if (early_spi_read_block(offset + current, count,
+ buffer + current) < 0)
+ return -1;
+ size -= count;
+ current += count;
+ }
+
+ return 0;
+}
diff --git a/src/southbridge/intel/fsp_i89xx/early_usb.c b/src/southbridge/intel/fsp_i89xx/early_usb.c
new file mode 100644
index 0000000000..3d83d3be9b
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/early_usb.c
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+#include "pch.h"
+
+#define PCH_EHCI1_TEMP_BAR0 0xe8000000
+
+/*
+ * Setup USB controller MMIO BAR to prevent the
+ * reference code from resetting the controller.
+ *
+ * The BAR will be re-assigned during device
+ * enumeration so these are only temporary.
+ */
+void enable_usb_bar(void)
+{
+ device_t usb0 = PCH_EHCI1_DEV;
+ u32 cmd;
+
+ /* USB Controller 0 */
+ pci_write_config32(usb0, PCI_BASE_ADDRESS_0,
+ PCH_EHCI1_TEMP_BAR0);
+ cmd = pci_read_config32(usb0, PCI_COMMAND);
+ cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_write_config32(usb0, PCI_COMMAND, cmd);
+}
diff --git a/src/southbridge/intel/fsp_i89xx/elog.c b/src/southbridge/intel/fsp_i89xx/elog.c
new file mode 100644
index 0000000000..c93d3ec6c1
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/elog.c
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <arch/io.h>
+#include <arch/acpi.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ops.h>
+#include <stdint.h>
+#include <string.h>
+#include <elog.h>
+#include "pch.h"
+
+void pch_log_state(void)
+{
+ u16 pm1_sts, gen_pmcon_3, tco2_sts;
+ u32 gpe0_sts, gpe0_en;
+ u8 gen_pmcon_2;
+ int i;
+ struct device *lpc = dev_find_slot(0, PCI_DEVFN(0x1f, 0));
+ if (!lpc)
+ return;
+
+ pm1_sts = inw(DEFAULT_PMBASE + PM1_STS);
+ gpe0_sts = inl(DEFAULT_PMBASE + GPE0_STS);
+ gpe0_en = inl(DEFAULT_PMBASE + GPE0_EN);
+ tco2_sts = inw(DEFAULT_PMBASE + TCO2_STS);
+ gen_pmcon_2 = pci_read_config8(lpc, GEN_PMCON_2);
+ gen_pmcon_3 = pci_read_config16(lpc, GEN_PMCON_3);
+
+ /* PWR_FLR Power Failure */
+ if (gen_pmcon_2 & (1 << 0))
+ elog_add_event(ELOG_TYPE_POWER_FAIL);
+
+ /* SUS Well Power Failure */
+ if (gen_pmcon_3 & (1 << 14))
+ elog_add_event(ELOG_TYPE_SUS_POWER_FAIL);
+
+ /* SYS_PWROK Failure */
+ if (gen_pmcon_2 & (1 << 1))
+ elog_add_event(ELOG_TYPE_SYS_PWROK_FAIL);
+
+ /* PWROK Failure */
+ if (gen_pmcon_2 & (1 << 0))
+ elog_add_event(ELOG_TYPE_PWROK_FAIL);
+
+ /* Second TCO Timeout */
+ if (tco2_sts & (1 << 1))
+ elog_add_event(ELOG_TYPE_TCO_RESET);
+
+ /* Power Button Override */
+ if (pm1_sts & (1 << 11))
+ elog_add_event(ELOG_TYPE_POWER_BUTTON_OVERRIDE);
+
+ /* System Reset Status (reset button pushed) */
+ if (gen_pmcon_2 & (1 << 4))
+ elog_add_event(ELOG_TYPE_RESET_BUTTON);
+
+ /* General Reset Status */
+ if (gen_pmcon_3 & (1 << 9))
+ elog_add_event(ELOG_TYPE_SYSTEM_RESET);
+
+ /* ACPI Wake */
+ if (pm1_sts & (1 << 15))
+ elog_add_event_byte(ELOG_TYPE_ACPI_WAKE,
+ acpi_is_wakeup_s3() ? 3 : 5);
+
+ /*
+ * Wake sources
+ */
+
+ /* RTC */
+ if (pm1_sts & (1 << 10))
+ elog_add_event_wake(ELOG_WAKE_SOURCE_RTC, 0);
+
+ /* PCI Express (TODO: determine wake device) */
+ if (pm1_sts & (1 << 14))
+ elog_add_event_wake(ELOG_WAKE_SOURCE_PCIE, 0);
+
+ /* PME (TODO: determine wake device) */
+ if (gpe0_sts & (1 << 13))
+ elog_add_event_wake(ELOG_WAKE_SOURCE_PME, 0);
+
+ /* Internal PME (TODO: determine wake device) */
+ if (gpe0_sts & (1 << 13))
+ elog_add_event_wake(ELOG_WAKE_SOURCE_PME_INTERNAL, 0);
+
+ /* GPIO 0-15 */
+ for (i = 0; i < 16; i++) {
+ if ((gpe0_sts & (1 << (16+i))) && (gpe0_en & (1 << (16+i))))
+ elog_add_event_wake(ELOG_WAKE_SOURCE_GPIO, i);
+ }
+
+ /* SMBUS Wake */
+ if (gpe0_sts & (1 << 7))
+ elog_add_event_wake(ELOG_WAKE_SOURCE_SMBUS, 0);
+}
diff --git a/src/southbridge/intel/fsp_i89xx/finalize.c b/src/southbridge/intel/fsp_i89xx/finalize.c
new file mode 100644
index 0000000000..99fa4c23ac
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/finalize.c
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 The Chromium OS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <arch/io.h>
+#include <console/post_codes.h>
+#include "pch.h"
+#include <spi-generic.h>
+
+void intel_pch_finalize_smm(void)
+{
+ /* Set SPI opcode menu */
+ RCBA16(0x3894) = SPI_OPPREFIX;
+ RCBA16(0x3896) = SPI_OPTYPE;
+ RCBA32(0x3898) = SPI_OPMENU_LOWER;
+ RCBA32(0x389c) = SPI_OPMENU_UPPER;
+
+ /* Lock SPIBAR */
+ RCBA32_OR(0x3804, (1 << 15));
+
+#if CONFIG_SPI_FLASH_SMM
+ /* Re-init SPI driver to handle locked BAR */
+ spi_init();
+#endif
+
+ /* TCLOCKDN: TC Lockdown */
+ RCBA32_OR(0x0050, (1 << 31));
+
+ /* BIOS Interface Lockdown */
+ RCBA32_OR(0x3410, (1 << 0));
+
+ /* Function Disable SUS Well Lockdown */
+ RCBA_AND_OR(8, 0x3420, ~0U, (1 << 7));
+
+ /* Global SMI Lock */
+ pcie_or_config16(PCH_LPC_DEV, 0xa0, 1 << 4);
+
+ /* GEN_PMCON Lock */
+ pcie_or_config8(PCH_LPC_DEV, 0xa6, (1 << 1) | (1 << 2));
+
+ /* R/WO registers */
+ RCBA32(0x21a4) = RCBA32(0x21a4);
+ pcie_write_config32(PCI_DEV(0, 27, 0), 0x74,
+ pcie_read_config32(PCI_DEV(0, 27, 0), 0x74));
+
+ /* Indicate finalize step with post code */
+ outb(POST_OS_BOOT, 0x80);
+}
diff --git a/src/southbridge/intel/fsp_i89xx/gpio.c b/src/southbridge/intel/fsp_i89xx/gpio.c
new file mode 100644
index 0000000000..f725282ea8
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/gpio.c
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
+ * Copyright (C) 2013 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <arch/io.h>
+
+#include "pch.h"
+#include "gpio.h"
+
+#define MAX_GPIO_NUMBER 75 /* zero based */
+static const int gpio_reg_offsets[] = {GP_LVL, GP_LVL2, GP_LVL3};
+
+void setup_pch_gpios(const struct pch_gpio_map *gpio)
+{
+ u16 gpiobase = pci_read_config16(PCH_LPC_DEV, GPIO_BASE) & 0xfffc;
+
+ /* GPIO Set 1 */
+ if (gpio->set1.level)
+ outl(*((u32*)gpio->set1.level), gpiobase + GP_LVL);
+ if (gpio->set1.mode)
+ outl(*((u32*)gpio->set1.mode), gpiobase + GPIO_USE_SEL);
+ if (gpio->set1.direction)
+ outl(*((u32*)gpio->set1.direction), gpiobase + GP_IO_SEL);
+ if (gpio->set1.reset)
+ outl(*((u32*)gpio->set1.reset), gpiobase + GP_RST_SEL1);
+ if (gpio->set1.invert)
+ outl(*((u32*)gpio->set1.invert), gpiobase + GPI_INV);
+ if (gpio->set1.blink)
+ outl(*((u32*)gpio->set1.blink), gpiobase + GPO_BLINK);
+
+ /* GPIO Set 2 */
+ if (gpio->set2.level)
+ outl(*((u32*)gpio->set2.level), gpiobase + GP_LVL2);
+ if (gpio->set2.mode)
+ outl(*((u32*)gpio->set2.mode), gpiobase + GPIO_USE_SEL2);
+ if (gpio->set2.direction)
+ outl(*((u32*)gpio->set2.direction), gpiobase + GP_IO_SEL2);
+ if (gpio->set2.reset)
+ outl(*((u32*)gpio->set2.reset), gpiobase + GP_RST_SEL2);
+
+ /* GPIO Set 3 */
+ if (gpio->set3.level)
+ outl(*((u32*)gpio->set3.level), gpiobase + GP_LVL3);
+ if (gpio->set3.mode)
+ outl(*((u32*)gpio->set3.mode), gpiobase + GPIO_USE_SEL3);
+ if (gpio->set3.direction)
+ outl(*((u32*)gpio->set3.direction), gpiobase + GP_IO_SEL3);
+ if (gpio->set3.reset)
+ outl(*((u32*)gpio->set3.reset), gpiobase + GP_RST_SEL3);
+}
+
+int get_gpio(int gpio_num)
+{
+ u16 gpio_base = pci_read_config16(PCH_LPC_DEV, GPIO_BASE) & 0xfffc;
+ int index, bit;
+
+ if (gpio_num > MAX_GPIO_NUMBER)
+ return 0; /* Just ignore wrong gpio numbers. */
+
+ index = gpio_num / 32;
+ bit = gpio_num % 32;
+
+ return (inl(gpio_base + gpio_reg_offsets[index]) >> bit) & 1;
+}
+
+void set_gpio(int gpio_num)
+{
+ u16 gpio_base = pci_read_config16(PCH_LPC_DEV, GPIO_BASE) & 0xfffc;
+ u32 index, bit, level;
+
+ if (gpio_num <= MAX_GPIO_NUMBER){
+
+ index = gpio_num / 32;
+ bit = gpio_num % 32;
+ level = inl(gpio_base + gpio_reg_offsets[index]);
+
+ outl((gpio_base + gpio_reg_offsets[index]), level | (1UL << bit));
+
+ }
+
+ return;
+}
+void clear_gpio(int gpio_num)
+{
+ u16 gpio_base = pci_read_config16(PCH_LPC_DEV, GPIO_BASE) & 0xfffc;
+ u32 index, bit, level;
+
+ if (gpio_num <= MAX_GPIO_NUMBER){
+ index = gpio_num / 32;
+ bit = gpio_num % 32;
+ level = inl(gpio_base + gpio_reg_offsets[index]);
+
+ outl((gpio_base + gpio_reg_offsets[index]), level & (~(1UL << bit)));
+ }
+
+ return;
+}
+
+
+/*
+ * get a number comprised of multiple GPIO values. gpio_num_array points to
+ * the array of gpio pin numbers to scan, terminated by -1.
+ */
+unsigned get_gpios(const int *gpio_num_array)
+{
+ int gpio;
+ unsigned bitmask = 1;
+ unsigned vector = 0;
+
+ while (bitmask &&
+ ((gpio = *gpio_num_array++) != -1)) {
+ if (get_gpio(gpio))
+ vector |= bitmask;
+ bitmask <<= 1;
+ }
+ return vector;
+}
diff --git a/src/southbridge/intel/fsp_i89xx/gpio.h b/src/southbridge/intel/fsp_i89xx/gpio.h
new file mode 100644
index 0000000000..022c81f82a
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/gpio.h
@@ -0,0 +1,194 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
+ * Copyright (C) 2013 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#ifndef INTEL_I89XX_GPIO_H
+#define INTEL_I89XX_GPIO_H
+
+#define GPIO_MODE_NATIVE 0
+#define GPIO_MODE_GPIO 1
+#define GPIO_MODE_NONE 1
+
+#define GPIO_DIR_OUTPUT 0
+#define GPIO_DIR_INPUT 1
+
+#define GPIO_NO_INVERT 0
+#define GPIO_INVERT 1
+
+#define GPIO_LEVEL_LOW 0
+#define GPIO_LEVEL_HIGH 1
+
+#define GPIO_NO_BLINK 0
+#define GPIO_BLINK 1
+
+#define GPIO_RESET_PWROK 0
+#define GPIO_RESET_RSMRST 1
+
+struct pch_gpio_set1 {
+ u32 gpio0 : 1;
+ u32 gpio1 : 1;
+ u32 gpio2 : 1;
+ u32 gpio3 : 1;
+ u32 gpio4 : 1;
+ u32 gpio5 : 1;
+ u32 gpio6 : 1;
+ u32 gpio7 : 1;
+ u32 gpio8 : 1;
+ u32 gpio9 : 1;
+ u32 gpio10 : 1;
+ u32 gpio11 : 1;
+ u32 gpio12 : 1;
+ u32 gpio13 : 1;
+ u32 gpio14 : 1;
+ u32 gpio15 : 1;
+ u32 gpio16 : 1;
+ u32 gpio17 : 1;
+ u32 gpio18 : 1;
+ u32 gpio19 : 1;
+ u32 gpio20 : 1;
+ u32 gpio21 : 1;
+ u32 gpio22 : 1;
+ u32 gpio23 : 1;
+ u32 gpio24 : 1;
+ u32 gpio25 : 1;
+ u32 gpio26 : 1;
+ u32 gpio27 : 1;
+ u32 gpio28 : 1;
+ u32 gpio29 : 1;
+ u32 gpio30 : 1;
+ u32 gpio31 : 1;
+} __attribute__ ((packed));
+
+struct pch_gpio_set2 {
+ u32 gpio32 : 1;
+ u32 gpio33 : 1;
+ u32 gpio34 : 1;
+ u32 gpio35 : 1;
+ u32 gpio36 : 1;
+ u32 gpio37 : 1;
+ u32 gpio38 : 1;
+ u32 gpio39 : 1;
+ u32 gpio40 : 1;
+ u32 gpio41 : 1;
+ u32 gpio42 : 1;
+ u32 gpio43 : 1;
+ u32 gpio44 : 1;
+ u32 gpio45 : 1;
+ u32 gpio46 : 1;
+ u32 gpio47 : 1;
+ u32 gpio48 : 1;
+ u32 gpio49 : 1;
+ u32 gpio50 : 1;
+ u32 gpio51 : 1;
+ u32 gpio52 : 1;
+ u32 gpio53 : 1;
+ u32 gpio54 : 1;
+ u32 gpio55 : 1;
+ u32 gpio56 : 1;
+ u32 gpio57 : 1;
+ u32 gpio58 : 1;
+ u32 gpio59 : 1;
+ u32 gpio60 : 1;
+ u32 gpio61 : 1;
+ u32 gpio62 : 1;
+ u32 gpio63 : 1;
+} __attribute__ ((packed));
+
+struct pch_gpio_set3 {
+ u32 gpio64 : 1;
+ u32 gpio65 : 1;
+ u32 gpio66 : 1;
+ u32 gpio67 : 1;
+ u32 gpio68 : 1;
+ u32 gpio69 : 1;
+ u32 gpio70 : 1;
+ u32 gpio71 : 1;
+ u32 gpio72 : 1;
+ u32 gpio73 : 1;
+ u32 gpio74 : 1;
+ u32 gpio75 : 1;
+ u32 fill_bitfield : 20;
+} __attribute__ ((packed));
+
+struct pch_gpio_map {
+ union {
+ struct {
+ const struct pch_gpio_set1 *mode;
+ const struct pch_gpio_set1 *direction;
+ const struct pch_gpio_set1 *level;
+ const struct pch_gpio_set1 *reset;
+ const struct pch_gpio_set1 *invert;
+ const struct pch_gpio_set1 *blink;
+ } set1;
+ struct {
+ const u32 *mode;
+ const u32 *direction;
+ const u32 *level;
+ const u32 *reset;
+ const u32 *invert;
+ const u32 *blink;
+ } set1_vals;
+ };
+
+ union {
+ struct {
+ const struct pch_gpio_set2 *mode;
+ const struct pch_gpio_set2 *direction;
+ const struct pch_gpio_set2 *level;
+ const struct pch_gpio_set2 *reset;
+ } set2;
+ struct {
+ const u32 *mode;
+ const u32 *direction;
+ const u32 *level;
+ const u32 *reset;
+ } set2_vals;
+ };
+
+ union {
+ struct {
+ const struct pch_gpio_set3 *mode;
+ const struct pch_gpio_set3 *direction;
+ const struct pch_gpio_set3 *level;
+ const struct pch_gpio_set3 *reset;
+ } set3;
+ struct {
+ const u32 *mode;
+ const u32 *direction;
+ const u32 *level;
+ const u32 *reset;
+ } set3_vals;
+ };
+};
+
+/* Configure GPIOs with mainboard provided settings */
+void setup_pch_gpios(const struct pch_gpio_map *gpio);
+
+/* get GPIO and set pin values */
+int get_gpio(int gpio_num);
+void set_gpio(int gpio_num);
+void clear_gpio(int gpio_num);
+
+/*
+ * get a number comprised of multiple GPIO values. gpio_num_array points to
+ * the array of gpio pin numbers to scan, terminated by -1.
+ */
+unsigned get_gpios(const int *gpio_num_array);
+
+#endif
diff --git a/src/southbridge/intel/fsp_i89xx/lpc.c b/src/southbridge/intel/fsp_i89xx/lpc.c
new file mode 100644
index 0000000000..5477a54558
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/lpc.c
@@ -0,0 +1,688 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2013-2015 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#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 <elog.h>
+#include <arch/acpigen.h>
+#include <drivers/intel/gma/i915.h>
+#include <cpu/x86/smm.h>
+#include <cbmem.h>
+#include <string.h>
+#include "pch.h"
+#include "nvs.h"
+
+#define NMI_OFF 0
+
+#define ENABLE_ACPI_MODE_IN_COREBOOT 0
+
+typedef struct southbridge_intel_fsp_i89xx_config config_t;
+
+static void pch_enable_apic(struct device *dev)
+{
+ int i;
+ u32 reg32;
+ volatile u32 *ioapic_index = (volatile u32 *)(IO_APIC_ADDR);
+ volatile u32 *ioapic_data = (volatile u32 *)(IO_APIC_ADDR + 0x10);
+
+ /* Enable ACPI I/O and power management.
+ * Set SCI IRQ to IRQ9
+ */
+ pci_write_config8(dev, ACPI_CNTL, 0x80);
+
+ *ioapic_index = 0;
+ *ioapic_data = (1 << 25);
+
+ /* affirm full set of redirection table entries ("write once") */
+ *ioapic_index = 1;
+ reg32 = *ioapic_data;
+ *ioapic_index = 1;
+ *ioapic_data = reg32;
+
+ *ioapic_index = 0;
+ reg32 = *ioapic_data;
+ printk(BIOS_DEBUG, "Southbridge APIC ID = %x\n", (reg32 >> 24) & 0x0f);
+ if (reg32 != (1 << 25))
+ die("APIC Error\n");
+
+ printk(BIOS_SPEW, "Dumping IOAPIC registers\n");
+ for (i=0; i<3; i++) {
+ *ioapic_index = i;
+ printk(BIOS_SPEW, " reg 0x%04x:", i);
+ reg32 = *ioapic_data;
+ printk(BIOS_SPEW, " 0x%08x\n", reg32);
+ }
+
+ *ioapic_index = 3; /* Select Boot Configuration register. */
+ *ioapic_data = 1; /* Use Processor System Bus to deliver interrupts. */
+}
+
+static void pch_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));
+#if !CONFIG_SERIRQ_CONTINUOUS_MODE
+ pci_write_config8(dev, SERIRQ_CNTL,
+ (1 << 7) | (0 << 6) | ((21 - 17) << 2) | (0 << 0));
+#endif
+}
+
+/* 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 pch_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 pch_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, GPIO_ROUT, reg32);
+}
+
+static void pch_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 Kconfig setting.
+ */
+ get_option(&pwr_on, "power_on_after_fail");
+
+ reg16 = pci_read_config16(dev, GEN_PMCON_3);
+ reg16 &= 0xfffe;
+ switch (pwr_on) {
+ case MAINBOARD_POWER_OFF:
+ reg16 |= 1;
+ state = "off";
+ break;
+ case MAINBOARD_POWER_ON:
+ reg16 &= ~1;
+ state = "on";
+ break;
+ case MAINBOARD_POWER_KEEP:
+ reg16 &= ~1;
+ state = "state keep";
+ break;
+ default:
+ state = "undefined";
+ }
+
+ reg16 &= ~(3 << 4); /* SLP_S4# Assertion Stretch 4s */
+ reg16 |= (1 << 3); /* SLP_S4# Assertion Stretch Enable */
+
+ reg16 &= ~(1 << 10);
+ reg16 |= (1 << 11); /* SLP_S3# Min Assertion Width 50ms */
+
+ reg16 |= (1 << 12); /* Disable SLP stretch after SUS well */
+
+ pci_write_config16(dev, GEN_PMCON_3, reg16);
+ 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 << 10); // Disable BIOS_PCI_EXP_EN for native PME
+#if DEBUG_PERIODIC_SMIS
+ /* Set DEBUG_PERIODIC_SMIS in pch.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.
+ pch_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 << 0); // SCI_EN
+ outl(reg32, pmbase + 0x04);
+
+ /* Clear magic status bits to prevent unexpected wake */
+ reg32 = RCBA32(0x3310);
+ reg32 |= (1 << 4)|(1 << 5)|(1 << 0);
+ RCBA32(0x3310) = reg32;
+}
+
+static void pch_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);
+#if CONFIG_ELOG
+ elog_add_event(ELOG_TYPE_RTC_RESET);
+#endif
+ }
+ printk(BIOS_DEBUG, "rtc_failed = 0x%x\n", rtc_failed);
+
+ cmos_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 pch_set_acpi_mode(void)
+{
+ if (!acpi_is_wakeup_s3() && CONFIG_HAVE_SMI_HANDLER) {
+#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
+ }
+}
+
+static void pch_disable_smm_only_flashing(struct device *dev)
+{
+ u8 reg8;
+
+ printk(BIOS_SPEW, "Enabling BIOS updates outside of SMM... ");
+ reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
+ reg8 &= ~(1 << 5);
+ pci_write_config8(dev, 0xdc, reg8);
+}
+
+static void pch_fixups(struct device *dev)
+{
+ u8 gen_pmcon_2;
+
+ /* Indicate DRAM init done for MRC S3 to know it can resume */
+ gen_pmcon_2 = pci_read_config8(dev, GEN_PMCON_2);
+ gen_pmcon_2 |= (1 << 7);
+ pci_write_config8(dev, GEN_PMCON_2, gen_pmcon_2);
+
+}
+
+static void pch_decode_init(struct device *dev)
+{
+ config_t *config = dev->chip_info;
+
+ printk(BIOS_DEBUG, "pch_decode_init\n");
+
+ pci_write_config32(dev, LPC_GEN1_DEC, config->gen1_dec);
+ pci_write_config32(dev, LPC_GEN2_DEC, config->gen2_dec);
+ pci_write_config32(dev, LPC_GEN3_DEC, config->gen3_dec);
+ pci_write_config32(dev, LPC_GEN4_DEC, config->gen4_dec);
+}
+
+static void lpc_init(struct device *dev)
+{
+ printk(BIOS_DEBUG, "pch: lpc_init\n");
+
+ /* Set the value for PCI command register. */
+ pci_write_config16(dev, PCI_COMMAND, 0x000f);
+
+ /* IO APIC initialization. */
+ pch_enable_apic(dev);
+
+ pch_enable_serial_irqs(dev);
+
+ /* Setup the PIRQ. */
+ pch_pirq_init(dev);
+
+ /* Setup power options. */
+ pch_power_options(dev);
+
+ /* Initialize power management */
+ switch (pch_silicon_type()) {
+ case PCH_TYPE_CC: /* CaveCreek */
+ break;
+ default:
+ printk(BIOS_ERR, "Unknown Chipset: 0x%04x\n", dev->device);
+ }
+
+ /* Set the state of the GPIO lines. */
+ //gpio_init(dev);
+
+ /* Initialize the real time clock. */
+ pch_rtc_init(dev);
+
+ /* Initialize ISA DMA. */
+ isa_dma_init();
+
+ /* Initialize the High Precision Event Timers, if present. */
+ enable_hpet();
+
+ setup_i8259();
+
+ /* The OS should do this? */
+ /* Interrupt 9 should be level triggered (SCI) */
+ i8259_configure_irq_trigger(9, 1);
+
+ pch_disable_smm_only_flashing(dev);
+
+ pch_set_acpi_mode();
+
+ pch_fixups(dev);
+}
+
+static void pch_lpc_read_resources(device_t dev)
+{
+ struct resource *res;
+ config_t *config = dev->chip_info;
+ u8 io_index = 0;
+
+ /* 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(io_index++, 0));
+ res->base = 0;
+ res->size = 0x1000;
+ res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
+ IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 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;
+
+ /* Set PCH IO decode ranges if required.*/
+ if ((config->gen1_dec & 0xFFFC) > 0x1000) {
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
+ res->base = config->gen1_dec & 0xFFFC;
+ res->size = (config->gen1_dec >> 16) & 0xFC;
+ res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
+ IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+ }
+
+ if ((config->gen2_dec & 0xFFFC) > 0x1000) {
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
+ res->base = config->gen2_dec & 0xFFFC;
+ res->size = (config->gen2_dec >> 16) & 0xFC;
+ res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
+ IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+ }
+
+ if ((config->gen3_dec & 0xFFFC) > 0x1000) {
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
+ res->base = config->gen3_dec & 0xFFFC;
+ res->size = (config->gen3_dec >> 16) & 0xFC;
+ res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
+ IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+ }
+
+ if ((config->gen4_dec & 0xFFFC) > 0x1000) {
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
+ res->base = config->gen4_dec & 0xFFFC;
+ res->size = (config->gen4_dec >> 16) & 0xFC;
+ res->flags = IORESOURCE_IO| IORESOURCE_SUBTRACTIVE |
+ IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
+ }
+}
+
+static void pch_lpc_enable_resources(device_t dev)
+{
+ pch_decode_init(dev);
+ return pci_dev_enable_resources(dev);
+}
+
+static void pch_lpc_enable(device_t dev)
+{
+ pch_enable(dev);
+}
+
+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 void southbridge_inject_dsdt(device_t dev)
+{
+ global_nvs_t *gnvs = cbmem_add (CBMEM_ID_ACPI_GNVS, sizeof (*gnvs));
+ void *opregion;
+
+ /* Calling northbridge code as gnvs contains opregion address. */
+ opregion = igd_make_opregion();
+
+ if (gnvs) {
+ const struct i915_gpu_controller_info *gfx = intel_gma_get_controller_info();
+
+ memset(gnvs, 0, sizeof (*gnvs));
+
+ acpi_create_gnvs(gnvs);
+ /* IGD OpRegion Base Address */
+ gnvs->aslb = (u32)opregion;
+
+ gnvs->ndid = gfx->ndid;
+ memcpy(gnvs->did, gfx->did, sizeof(gnvs->did));
+
+ /* And tell SMI about it */
+ smm_setup_structures(gnvs, NULL, NULL);
+
+ /* Add it to DSDT. */
+ acpigen_write_scope("\\");
+ acpigen_write_name_dword("NVSA", (u32) gnvs);
+ acpigen_pop_len();
+ }
+}
+
+void acpi_fill_fadt(acpi_fadt_t *fadt)
+{
+ device_t dev = dev_find_slot(0, PCI_DEVFN(0x1f,0));
+ config_t *chip = dev->chip_info;
+ u16 pmbase = pci_read_config16(dev, 0x40) & 0xfffe;
+ int c2_latency;
+
+ fadt->model = 1;
+
+ fadt->sci_int = 0x9;
+ fadt->smi_cmd = APM_CNT;
+ fadt->acpi_enable = APM_CNT_ACPI_ENABLE;
+ fadt->acpi_disable = APM_CNT_ACPI_DISABLE;
+ fadt->s4bios_req = 0x0;
+ fadt->pstate_cnt = 0;
+
+ fadt->pm1a_evt_blk = pmbase;
+ fadt->pm1b_evt_blk = 0x0;
+ fadt->pm1a_cnt_blk = pmbase + 0x4;
+ fadt->pm1b_cnt_blk = 0x0;
+ fadt->pm2_cnt_blk = pmbase + 0x50;
+ fadt->pm_tmr_blk = pmbase + 0x8;
+ fadt->gpe0_blk = pmbase + 0x20;
+ fadt->gpe1_blk = 0;
+
+ fadt->pm1_evt_len = 4;
+ fadt->pm1_cnt_len = 2;
+ fadt->pm2_cnt_len = 1;
+ fadt->pm_tmr_len = 4;
+ fadt->gpe0_blk_len = 16;
+ fadt->gpe1_blk_len = 0;
+ fadt->gpe1_base = 0;
+ fadt->cst_cnt = 0;
+ c2_latency = chip->c2_latency;
+ if (!c2_latency) {
+ c2_latency = 101; /* c2 unsupported */
+ }
+ fadt->p_lvl2_lat = c2_latency;
+ fadt->p_lvl3_lat = 87;
+ fadt->flush_size = 1024;
+ fadt->flush_stride = 16;
+ fadt->duty_offset = 1;
+ if (chip->p_cnt_throttling_supported) {
+ fadt->duty_width = 3;
+ } else {
+ fadt->duty_width = 0;
+ }
+ fadt->day_alrm = 0xd;
+ fadt->mon_alrm = 0x00;
+ fadt->century = 0x00;
+ fadt->iapc_boot_arch = ACPI_FADT_LEGACY_DEVICES | ACPI_FADT_8042;
+
+ fadt->flags = ACPI_FADT_WBINVD |
+ ACPI_FADT_C1_SUPPORTED |
+ ACPI_FADT_SLEEP_BUTTON |
+ ACPI_FADT_RESET_REGISTER |
+ ACPI_FADT_SEALED_CASE |
+ ACPI_FADT_S4_RTC_WAKE |
+ ACPI_FADT_PLATFORM_CLOCK;
+ if (c2_latency < 100) {
+ fadt->flags |= ACPI_FADT_C2_MP_SUPPORTED;
+ }
+
+ fadt->reset_reg.space_id = 1;
+ fadt->reset_reg.bit_width = 8;
+ fadt->reset_reg.bit_offset = 0;
+ fadt->reset_reg.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
+ fadt->reset_reg.addrl = 0xcf9;
+ fadt->reset_reg.addrh = 0;
+
+ fadt->reset_value = 6;
+
+ fadt->x_pm1a_evt_blk.space_id = 1;
+ fadt->x_pm1a_evt_blk.bit_width = 32;
+ fadt->x_pm1a_evt_blk.bit_offset = 0;
+ fadt->x_pm1a_evt_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+ fadt->x_pm1a_evt_blk.addrl = pmbase;
+ fadt->x_pm1a_evt_blk.addrh = 0x0;
+
+ fadt->x_pm1b_evt_blk.space_id = 1;
+ fadt->x_pm1b_evt_blk.bit_width = 0;
+ fadt->x_pm1b_evt_blk.bit_offset = 0;
+ fadt->x_pm1b_evt_blk.access_size = 0;
+ fadt->x_pm1b_evt_blk.addrl = 0x0;
+ fadt->x_pm1b_evt_blk.addrh = 0x0;
+
+ fadt->x_pm1a_cnt_blk.space_id = 1;
+ fadt->x_pm1a_cnt_blk.bit_width = 16;
+ fadt->x_pm1a_cnt_blk.bit_offset = 0;
+ fadt->x_pm1a_cnt_blk.access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
+ fadt->x_pm1a_cnt_blk.addrl = pmbase + 0x4;
+ fadt->x_pm1a_cnt_blk.addrh = 0x0;
+
+ fadt->x_pm1b_cnt_blk.space_id = 1;
+ fadt->x_pm1b_cnt_blk.bit_width = 0;
+ fadt->x_pm1b_cnt_blk.bit_offset = 0;
+ fadt->x_pm1b_cnt_blk.access_size = 0;
+ fadt->x_pm1b_cnt_blk.addrl = 0x0;
+ fadt->x_pm1b_cnt_blk.addrh = 0x0;
+
+ fadt->x_pm2_cnt_blk.space_id = 1;
+ fadt->x_pm2_cnt_blk.bit_width = 8;
+ fadt->x_pm2_cnt_blk.bit_offset = 0;
+ fadt->x_pm2_cnt_blk.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
+ fadt->x_pm2_cnt_blk.addrl = pmbase + 0x50;
+ fadt->x_pm2_cnt_blk.addrh = 0x0;
+
+ fadt->x_pm_tmr_blk.space_id = 1;
+ fadt->x_pm_tmr_blk.bit_width = 32;
+ fadt->x_pm_tmr_blk.bit_offset = 0;
+ fadt->x_pm_tmr_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+ fadt->x_pm_tmr_blk.addrl = pmbase + 0x8;
+ fadt->x_pm_tmr_blk.addrh = 0x0;
+
+ fadt->x_gpe0_blk.space_id = 1;
+ fadt->x_gpe0_blk.bit_width = 128;
+ fadt->x_gpe0_blk.bit_offset = 0;
+ fadt->x_gpe0_blk.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+ fadt->x_gpe0_blk.addrl = pmbase + 0x20;
+ fadt->x_gpe0_blk.addrh = 0x0;
+
+ fadt->x_gpe1_blk.space_id = 1;
+ fadt->x_gpe1_blk.bit_width = 0;
+ fadt->x_gpe1_blk.bit_offset = 0;
+ fadt->x_gpe1_blk.access_size = 0;
+ fadt->x_gpe1_blk.addrl = 0x0;
+ fadt->x_gpe1_blk.addrh = 0x0;
+}
+
+static struct pci_operations pci_ops = {
+ .set_subsystem = set_subsystem,
+};
+
+static struct device_operations device_ops = {
+ .read_resources = pch_lpc_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pch_lpc_enable_resources,
+ .write_acpi_tables = acpi_write_hpet,
+ .acpi_inject_dsdt_generator = southbridge_inject_dsdt,
+ .init = lpc_init,
+ .enable = pch_lpc_enable,
+ .scan_bus = scan_lpc_bus,
+ .ops_pci = &pci_ops,
+};
+
+
+/* IDs for LPC device of Intel 89xx Series Chipset */
+static const unsigned short pci_device_ids[] = { 0x2310, 0 };
+
+static const struct pci_driver pch_lpc __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices = pci_device_ids,
+};
diff --git a/src/southbridge/intel/fsp_i89xx/me.c b/src/southbridge/intel/fsp_i89xx/me.c
new file mode 100644
index 0000000000..6f8e5b6f86
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/me.c
@@ -0,0 +1,740 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
+ * Copyright (C) 2013 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+/*
+ * This is a ramstage driver for the Intel Management Engine found in the
+ * 6-series chipset. It handles the required boot-time messages over the
+ * MMIO-based Management Engine Interface to tell the ME that the BIOS is
+ * finished with POST. Additional messages are defined for debug but are
+ * not used unless the console loglevel is high enough.
+ */
+
+#include <arch/acpi.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+#include <string.h>
+#include <delay.h>
+#include <elog.h>
+#include <halt.h>
+
+#ifndef __SMM__
+# include <device/device.h>
+# include <device/pci.h>
+#endif
+
+#include "me.h"
+#include "pch.h"
+
+#if CONFIG_CHROMEOS
+#include <vendorcode/google/chromeos/gnvs.h>
+#endif
+
+#ifndef __SMM__
+/* Path that the BIOS should take based on ME state */
+static const char *me_bios_path_values[] = {
+ [ME_NORMAL_BIOS_PATH] = "Normal",
+ [ME_S3WAKE_BIOS_PATH] = "S3 Wake",
+ [ME_ERROR_BIOS_PATH] = "Error",
+ [ME_RECOVERY_BIOS_PATH] = "Recovery",
+ [ME_DISABLE_BIOS_PATH] = "Disable",
+ [ME_FIRMWARE_UPDATE_BIOS_PATH] = "Firmware Update",
+};
+#endif
+
+/* MMIO base address for MEI interface */
+static u32 *mei_base_address;
+
+#if CONFIG_DEBUG_INTEL_ME
+static void mei_dump(void *ptr, int dword, int offset, const char *type)
+{
+ struct mei_csr *csr;
+
+ printk(BIOS_SPEW, "%-9s[%02x] : ", type, offset);
+
+ switch (offset) {
+ case MEI_H_CSR:
+ case MEI_ME_CSR_HA:
+ csr = ptr;
+ if (!csr) {
+ printk(BIOS_SPEW, "ERROR: 0x%08x\n", dword);
+ break;
+ }
+ printk(BIOS_SPEW, "cbd=%u cbrp=%02u cbwp=%02u ready=%u "
+ "reset=%u ig=%u is=%u ie=%u\n", csr->buffer_depth,
+ csr->buffer_read_ptr, csr->buffer_write_ptr,
+ csr->ready, csr->reset, csr->interrupt_generate,
+ csr->interrupt_status, csr->interrupt_enable);
+ break;
+ case MEI_ME_CB_RW:
+ case MEI_H_CB_WW:
+ printk(BIOS_SPEW, "CB: 0x%08x\n", dword);
+ break;
+ default:
+ printk(BIOS_SPEW, "0x%08x\n", offset);
+ break;
+ }
+}
+#else
+# define mei_dump(ptr,dword,offset,type) do {} while (0)
+#endif
+
+/*
+ * ME/MEI access helpers using memcpy to avoid aliasing.
+ */
+
+static inline void mei_read_dword_ptr(void *ptr, int offset)
+{
+ u32 dword = read32(mei_base_address + (offset/sizeof(u32)));
+ memcpy(ptr, &dword, sizeof(dword));
+ mei_dump(ptr, dword, offset, "READ");
+}
+
+static inline void mei_write_dword_ptr(void *ptr, int offset)
+{
+ u32 dword = 0;
+ memcpy(&dword, ptr, sizeof(dword));
+ write32(mei_base_address + (offset/sizeof(u32)), dword);
+ mei_dump(ptr, dword, offset, "WRITE");
+}
+
+#ifndef __SMM__
+static inline void pci_read_dword_ptr(device_t dev, void *ptr, int offset)
+{
+ u32 dword = pci_read_config32(dev, offset);
+ memcpy(ptr, &dword, sizeof(dword));
+ mei_dump(ptr, dword, offset, "PCI READ");
+}
+#endif
+
+static inline void read_host_csr(struct mei_csr *csr)
+{
+ mei_read_dword_ptr(csr, MEI_H_CSR);
+}
+
+static inline void write_host_csr(struct mei_csr *csr)
+{
+ mei_write_dword_ptr(csr, MEI_H_CSR);
+}
+
+static inline void read_me_csr(struct mei_csr *csr)
+{
+ mei_read_dword_ptr(csr, MEI_ME_CSR_HA);
+}
+
+static inline void write_cb(u32 dword)
+{
+ write32(mei_base_address + (MEI_H_CB_WW/sizeof(u32)), dword);
+ mei_dump(NULL, dword, MEI_H_CB_WW, "WRITE");
+}
+
+static inline u32 read_cb(void)
+{
+ u32 dword = read32(mei_base_address + (MEI_ME_CB_RW/sizeof(u32)));
+ mei_dump(NULL, dword, MEI_ME_CB_RW, "READ");
+ return dword;
+}
+
+/* Wait for ME ready bit to be asserted */
+static int mei_wait_for_me_ready(void)
+{
+ struct mei_csr me;
+ unsigned try = ME_RETRY;
+
+ while (try--) {
+ read_me_csr(&me);
+ if (me.ready)
+ return 0;
+ udelay(ME_DELAY);
+ }
+
+ printk(BIOS_ERR, "ME: failed to become ready\n");
+ return -1;
+}
+
+static void mei_reset(void)
+{
+ struct mei_csr host;
+
+ if (mei_wait_for_me_ready() < 0)
+ return;
+
+ /* Reset host and ME circular buffers for next message */
+ read_host_csr(&host);
+ host.reset = 1;
+ host.interrupt_generate = 1;
+ write_host_csr(&host);
+
+ if (mei_wait_for_me_ready() < 0)
+ return;
+
+ /* Re-init and indicate host is ready */
+ read_host_csr(&host);
+ host.interrupt_generate = 1;
+ host.ready = 1;
+ host.reset = 0;
+ write_host_csr(&host);
+}
+
+static int mei_send_msg(struct mei_header *mei, struct mkhi_header *mkhi,
+ void *req_data)
+{
+ struct mei_csr host;
+ unsigned ndata, n;
+ u32 *data;
+
+ /* Number of dwords to write, ignoring MKHI */
+ ndata = mei->length >> 2;
+
+ /* Pad non-dword aligned request message length */
+ if (mei->length & 3)
+ ndata++;
+ if (!ndata) {
+ printk(BIOS_DEBUG, "ME: request does not include MKHI\n");
+ return -1;
+ }
+ ndata++; /* Add MEI header */
+
+ /*
+ * Make sure there is still room left in the circular buffer.
+ * Reset the buffer pointers if the requested message will not fit.
+ */
+ read_host_csr(&host);
+ if ((host.buffer_depth - host.buffer_write_ptr) < ndata) {
+ printk(BIOS_ERR, "ME: circular buffer full, resetting...\n");
+ mei_reset();
+ read_host_csr(&host);
+ }
+
+ /*
+ * This implementation does not handle splitting large messages
+ * across multiple transactions. Ensure the requested length
+ * will fit in the available circular buffer depth.
+ */
+ if ((host.buffer_depth - host.buffer_write_ptr) < ndata) {
+ printk(BIOS_ERR, "ME: message (%u) too large for buffer (%u)\n",
+ ndata + 2, host.buffer_depth);
+ return -1;
+ }
+
+ /* Write MEI header */
+ mei_write_dword_ptr(mei, MEI_H_CB_WW);
+ ndata--;
+
+ /* Write MKHI header */
+ mei_write_dword_ptr(mkhi, MEI_H_CB_WW);
+ ndata--;
+
+ /* Write message data */
+ data = req_data;
+ for (n = 0; n < ndata; ++n)
+ write_cb(*data++);
+
+ /* Generate interrupt to the ME */
+ read_host_csr(&host);
+ host.interrupt_generate = 1;
+ write_host_csr(&host);
+
+ /* Make sure ME is ready after sending request data */
+ return mei_wait_for_me_ready();
+}
+
+static int mei_recv_msg(struct mei_header *mei, struct mkhi_header *mkhi,
+ void *rsp_data, int rsp_bytes)
+{
+ struct mei_header mei_rsp;
+ struct mkhi_header mkhi_rsp;
+ struct mei_csr me, host;
+ unsigned ndata, n;
+ unsigned expected;
+ u32 *data;
+
+ /* Total number of dwords to read from circular buffer */
+ expected = (rsp_bytes + sizeof(mei_rsp) + sizeof(mkhi_rsp)) >> 2;
+ if (rsp_bytes & 3)
+ expected++;
+
+ /*
+ * The interrupt status bit does not appear to indicate that the
+ * message has actually been received. Instead we wait until the
+ * expected number of dwords are present in the circular buffer.
+ */
+ for (n = ME_RETRY; n; --n) {
+ read_me_csr(&me);
+ if ((me.buffer_write_ptr - me.buffer_read_ptr) >= expected)
+ break;
+ udelay(ME_DELAY);
+ }
+ if (!n) {
+ printk(BIOS_ERR, "ME: timeout waiting for data: expected "
+ "%u, available %u\n", expected,
+ me.buffer_write_ptr - me.buffer_read_ptr);
+ return -1;
+ }
+
+ /* Read and verify MEI response header from the ME */
+ mei_read_dword_ptr(&mei_rsp, MEI_ME_CB_RW);
+ if (!mei_rsp.is_complete) {
+ printk(BIOS_ERR, "ME: response is not complete\n");
+ return -1;
+ }
+
+ /* Handle non-dword responses and expect at least MKHI header */
+ ndata = mei_rsp.length >> 2;
+ if (mei_rsp.length & 3)
+ ndata++;
+ if (ndata != (expected - 1)) {
+ printk(BIOS_ERR, "ME: response is missing data\n");
+ return -1;
+ }
+
+ /* Read and verify MKHI response header from the ME */
+ mei_read_dword_ptr(&mkhi_rsp, MEI_ME_CB_RW);
+ if (!mkhi_rsp.is_response ||
+ mkhi->group_id != mkhi_rsp.group_id ||
+ mkhi->command != mkhi_rsp.command) {
+ printk(BIOS_ERR, "ME: invalid response, group %u ?= %u, "
+ "command %u ?= %u, is_response %u\n", mkhi->group_id,
+ mkhi_rsp.group_id, mkhi->command, mkhi_rsp.command,
+ mkhi_rsp.is_response);
+ return -1;
+ }
+ ndata--; /* MKHI header has been read */
+
+ /* Make sure caller passed a buffer with enough space */
+ if (ndata != (rsp_bytes >> 2)) {
+ printk(BIOS_ERR, "ME: not enough room in response buffer: "
+ "%u != %u\n", ndata, rsp_bytes >> 2);
+ return -1;
+ }
+
+ /* Read response data from the circular buffer */
+ data = rsp_data;
+ for (n = 0; n < ndata; ++n)
+ *data++ = read_cb();
+
+ /* Tell the ME that we have consumed the response */
+ read_host_csr(&host);
+ host.interrupt_status = 1;
+ host.interrupt_generate = 1;
+ write_host_csr(&host);
+
+ return mei_wait_for_me_ready();
+}
+
+static inline int mei_sendrecv(struct mei_header *mei, struct mkhi_header *mkhi,
+ void *req_data, void *rsp_data, int rsp_bytes)
+{
+ if (mei_send_msg(mei, mkhi, req_data) < 0)
+ return -1;
+ if (mei_recv_msg(mei, mkhi, rsp_data, rsp_bytes) < 0)
+ return -1;
+ return 0;
+}
+
+#ifdef __SMM__
+/* Send END OF POST message to the ME */
+static int mkhi_end_of_post(void)
+{
+ struct mkhi_header mkhi = {
+ .group_id = MKHI_GROUP_ID_GEN,
+ .command = MKHI_END_OF_POST,
+ };
+ struct mei_header mei = {
+ .is_complete = 1,
+ .host_address = MEI_HOST_ADDRESS,
+ .client_address = MEI_ADDRESS_MKHI,
+ .length = sizeof(mkhi),
+ };
+
+ /* Send request and wait for response */
+ if (mei_sendrecv(&mei, &mkhi, NULL, NULL, 0) < 0) {
+ printk(BIOS_ERR, "ME: END OF POST message failed\n");
+ return -1;
+ }
+
+ printk(BIOS_INFO, "ME: END OF POST message successful\n");
+ return 0;
+}
+#endif
+
+#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && !defined(__SMM__)
+/* Get ME firmware version */
+static int mkhi_get_fw_version(void)
+{
+ struct me_fw_version version;
+ struct mkhi_header mkhi = {
+ .group_id = MKHI_GROUP_ID_GEN,
+ .command = MKHI_GET_FW_VERSION,
+ };
+ struct mei_header mei = {
+ .is_complete = 1,
+ .host_address = MEI_HOST_ADDRESS,
+ .client_address = MEI_ADDRESS_MKHI,
+ .length = sizeof(mkhi),
+ };
+
+ /* Send request and wait for response */
+ if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) {
+ printk(BIOS_ERR, "ME: GET FW VERSION message failed\n");
+ return -1;
+ }
+
+ printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) "
+ "%u.%u.%u.%u (recovery)\n",
+ version.code_major, version.code_minor,
+ version.code_build_number, version.code_hot_fix,
+ version.recovery_major, version.recovery_minor,
+ version.recovery_build_number, version.recovery_hot_fix);
+
+ return 0;
+}
+
+static inline void print_cap(const char *name, int state)
+{
+ printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n",
+ name, state ? "en" : "dis");
+}
+
+/* Get ME Firmware Capabilities */
+static int mkhi_get_fwcaps(void)
+{
+ u32 rule_id = 0;
+ struct me_fwcaps cap;
+ struct mkhi_header mkhi = {
+ .group_id = MKHI_GROUP_ID_FWCAPS,
+ .command = MKHI_FWCAPS_GET_RULE,
+ };
+ struct mei_header mei = {
+ .is_complete = 1,
+ .host_address = MEI_HOST_ADDRESS,
+ .client_address = MEI_ADDRESS_MKHI,
+ .length = sizeof(mkhi) + sizeof(rule_id),
+ };
+
+ /* Send request and wait for response */
+ if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) {
+ printk(BIOS_ERR, "ME: GET FWCAPS message failed\n");
+ return -1;
+ }
+
+ print_cap("Full Network manageability", cap.caps_sku.full_net);
+ print_cap("Regular Network manageability", cap.caps_sku.std_net);
+ print_cap("Manageability", cap.caps_sku.manageability);
+ print_cap("Small business technology", cap.caps_sku.small_business);
+ print_cap("Level III manageability", cap.caps_sku.l3manageability);
+ print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at);
+ print_cap("IntelR Capability Licensing Service (CLS)",
+ cap.caps_sku.intel_cls);
+ print_cap("IntelR Power Sharing Technology (MPC)",
+ cap.caps_sku.intel_mpc);
+ print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking);
+ print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp);
+ print_cap("IPV6", cap.caps_sku.ipv6);
+ print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm);
+ print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och);
+ print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan);
+ print_cap("TLS", cap.caps_sku.tls);
+ print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan);
+
+ return 0;
+}
+#endif
+
+#ifdef __SMM__
+static void intel_me7_finalize_smm(void)
+{
+ struct me_hfs hfs;
+ u32 reg32;
+
+ mei_base_address = (u32 *)
+ (pcie_read_config32(PCH_ME_DEV, PCI_BASE_ADDRESS_0) & ~0xf);
+
+ /* S3 path will have hidden this device already */
+ if (!mei_base_address || mei_base_address == (u32 *)0xfffffff0)
+ return;
+
+ /* Make sure ME is in a mode that expects EOP */
+ reg32 = pcie_read_config32(PCH_ME_DEV, PCI_ME_HFS);
+ memcpy(&hfs, &reg32, sizeof(u32));
+
+ /* Abort and leave device alone if not normal mode */
+ if (hfs.fpt_bad ||
+ hfs.working_state != ME_HFS_CWS_NORMAL ||
+ hfs.operation_mode != ME_HFS_MODE_NORMAL)
+ return;
+
+ /* Try to send EOP command so ME stops accepting other commands */
+ mkhi_end_of_post();
+
+ /* Make sure IO is disabled */
+ reg32 = pcie_read_config32(PCH_ME_DEV, PCI_COMMAND);
+ reg32 &= ~(PCI_COMMAND_MASTER |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+ pcie_write_config32(PCH_ME_DEV, PCI_COMMAND, reg32);
+
+ /* Hide the PCI device */
+ RCBA32_OR(FD2, PCH_DISABLE_MEI1);
+}
+
+void intel_me_finalize_smm(void)
+{
+ u32 did = pcie_read_config32(PCH_ME_DEV, PCI_VENDOR_ID);
+ switch (did) {
+ case 0x1c3a8086:
+ intel_me7_finalize_smm();
+ break;
+ case 0x1e3a8086:
+ intel_me8_finalize_smm();
+ break;
+ default:
+ printk(BIOS_ERR, "No finalize handler for ME %08x.\n", did);
+ }
+}
+#else /* !__SMM__ */
+
+/* Determine the path that we should take based on ME status */
+static me_bios_path intel_me_path(device_t dev)
+{
+ me_bios_path path = ME_DISABLE_BIOS_PATH;
+ struct me_hfs hfs;
+ struct me_gmes gmes;
+
+ /* S3 wake skips all MKHI messages */
+ if (acpi_is_wakeup_s3())
+ return ME_S3WAKE_BIOS_PATH;
+
+ pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS);
+ pci_read_dword_ptr(dev, &gmes, PCI_ME_GMES);
+
+ /* Check and dump status */
+ intel_me_status(&hfs, &gmes);
+
+ /* Check Current Working State */
+ switch (hfs.working_state) {
+ case ME_HFS_CWS_NORMAL:
+ path = ME_NORMAL_BIOS_PATH;
+ break;
+ case ME_HFS_CWS_REC:
+ path = ME_RECOVERY_BIOS_PATH;
+ break;
+ default:
+ path = ME_DISABLE_BIOS_PATH;
+ break;
+ }
+
+ /* Check Current Operation Mode */
+ switch (hfs.operation_mode) {
+ case ME_HFS_MODE_NORMAL:
+ break;
+ case ME_HFS_MODE_DEBUG:
+ case ME_HFS_MODE_DIS:
+ case ME_HFS_MODE_OVER_JMPR:
+ case ME_HFS_MODE_OVER_MEI:
+ default:
+ path = ME_DISABLE_BIOS_PATH;
+ break;
+ }
+
+ /* Check for any error code and valid firmware */
+ if (hfs.error_code || hfs.fpt_bad)
+ path = ME_ERROR_BIOS_PATH;
+
+#if CONFIG_ELOG
+ if (path != ME_NORMAL_BIOS_PATH) {
+ struct elog_event_data_me_extended data = {
+ .current_working_state = hfs.working_state,
+ .operation_state = hfs.operation_state,
+ .operation_mode = hfs.operation_mode,
+ .error_code = hfs.error_code,
+ .progress_code = gmes.progress_code,
+ .current_pmevent = gmes.current_pmevent,
+ .current_state = gmes.current_state,
+ };
+ elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path);
+ elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT,
+ &data, sizeof(data));
+ }
+#endif
+
+ return path;
+}
+
+/* Prepare ME for MEI messages */
+static int intel_mei_setup(device_t dev)
+{
+ struct resource *res;
+ struct mei_csr host;
+ u32 reg32;
+
+ /* Find the MMIO base for the ME interface */
+ res = find_resource(dev, PCI_BASE_ADDRESS_0);
+ if (!res || res->base == 0 || res->size == 0) {
+ printk(BIOS_DEBUG, "ME: MEI resource not present!\n");
+ return -1;
+ }
+ mei_base_address = res2mmio(res, 0, 0);
+
+ /* Ensure Memory and Bus Master bits are set */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* Clean up status for next message */
+ read_host_csr(&host);
+ host.interrupt_generate = 1;
+ host.ready = 1;
+ host.reset = 0;
+ write_host_csr(&host);
+
+ return 0;
+}
+
+/* Read the Extend register hash of ME firmware */
+static int intel_me_extend_valid(device_t dev)
+{
+ struct me_heres status;
+ u32 extend[8] = {0};
+ int i, count = 0;
+
+ pci_read_dword_ptr(dev, &status, PCI_ME_HERES);
+ if (!status.extend_feature_present) {
+ printk(BIOS_ERR, "ME: Extend Feature not present\n");
+ return -1;
+ }
+
+ if (!status.extend_reg_valid) {
+ printk(BIOS_ERR, "ME: Extend Register not valid\n");
+ return -1;
+ }
+
+ switch (status.extend_reg_algorithm) {
+ case PCI_ME_EXT_SHA1:
+ count = 5;
+ printk(BIOS_DEBUG, "ME: Extend SHA-1: ");
+ break;
+ case PCI_ME_EXT_SHA256:
+ count = 8;
+ printk(BIOS_DEBUG, "ME: Extend SHA-256: ");
+ break;
+ default:
+ printk(BIOS_ERR, "ME: Extend Algorithm %d unknown\n",
+ status.extend_reg_algorithm);
+ return -1;
+ }
+
+ for (i = 0; i < count; ++i) {
+ extend[i] = pci_read_config32(dev, PCI_ME_HER(i));
+ printk(BIOS_DEBUG, "%08x", extend[i]);
+ }
+ printk(BIOS_DEBUG, "\n");
+
+#if CONFIG_CHROMEOS
+ /* Save hash in NVS for the OS to verify */
+ chromeos_set_me_hash(extend, count);
+#endif
+
+ return 0;
+}
+
+/* Hide the ME virtual PCI devices */
+static void intel_me_hide(device_t dev)
+{
+ dev->enabled = 0;
+ pch_enable(dev);
+}
+
+/* Check whether ME is present and do basic init */
+static void intel_me_init(device_t dev)
+{
+ me_bios_path path = intel_me_path(dev);
+
+ /* Do initial setup and determine the BIOS path */
+ printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_bios_path_values[path]);
+
+ switch (path) {
+ case ME_S3WAKE_BIOS_PATH:
+ intel_me_hide(dev);
+ break;
+
+ case ME_NORMAL_BIOS_PATH:
+ /* Validate the extend register */
+ if (intel_me_extend_valid(dev) < 0)
+ break; /* TODO: force recovery mode */
+
+ /* Prepare MEI MMIO interface */
+ if (intel_mei_setup(dev) < 0)
+ break;
+
+#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG)
+ /* Print ME firmware version */
+ mkhi_get_fw_version();
+ /* Print ME firmware capabilities */
+ mkhi_get_fwcaps();
+#endif
+
+ /*
+ * Leave the ME unlocked in this path.
+ * It will be locked via SMI command later.
+ */
+ break;
+
+ case ME_ERROR_BIOS_PATH:
+ case ME_RECOVERY_BIOS_PATH:
+ case ME_DISABLE_BIOS_PATH:
+ case ME_FIRMWARE_UPDATE_BIOS_PATH:
+ break;
+ }
+}
+
+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 = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = intel_me_init,
+ .ops_pci = &pci_ops,
+};
+
+static const unsigned short pci_device_ids[] = { 0x1c3a, 0x2304, 0 };
+
+static const struct pci_driver intel_me __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices = pci_device_ids,
+};
+
+#endif /* !__SMM__ */
diff --git a/src/southbridge/intel/fsp_i89xx/me.h b/src/southbridge/intel/fsp_i89xx/me.h
new file mode 100644
index 0000000000..967d46bb67
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/me.h
@@ -0,0 +1,372 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#ifndef _INTEL_ME_H
+#define _INTEL_ME_H
+
+#define ME_RETRY 100000 /* 1 second */
+#define ME_DELAY 10 /* 10 us */
+
+/*
+ * Management Engine PCI registers
+ */
+
+#define PCI_CPU_DEVICE PCI_DEV(0,0,0)
+#define PCI_CPU_MEBASE_L 0x70 /* Set by MRC */
+#define PCI_CPU_MEBASE_H 0x74 /* Set by MRC */
+
+#define PCI_ME_HFS 0x40
+#define ME_HFS_CWS_RESET 0
+#define ME_HFS_CWS_INIT 1
+#define ME_HFS_CWS_REC 2
+#define ME_HFS_CWS_NORMAL 5
+#define ME_HFS_CWS_WAIT 6
+#define ME_HFS_CWS_TRANS 7
+#define ME_HFS_CWS_INVALID 8
+#define ME_HFS_STATE_PREBOOT 0
+#define ME_HFS_STATE_M0_UMA 1
+#define ME_HFS_STATE_M3 4
+#define ME_HFS_STATE_M0 5
+#define ME_HFS_STATE_BRINGUP 6
+#define ME_HFS_STATE_ERROR 7
+#define ME_HFS_ERROR_NONE 0
+#define ME_HFS_ERROR_UNCAT 1
+#define ME_HFS_ERROR_IMAGE 3
+#define ME_HFS_ERROR_DEBUG 4
+#define ME_HFS_MODE_NORMAL 0
+#define ME_HFS_MODE_DEBUG 2
+#define ME_HFS_MODE_DIS 3
+#define ME_HFS_MODE_OVER_JMPR 4
+#define ME_HFS_MODE_OVER_MEI 5
+#define ME_HFS_BIOS_DRAM_ACK 1
+#define ME_HFS_ACK_NO_DID 0
+#define ME_HFS_ACK_RESET 1
+#define ME_HFS_ACK_PWR_CYCLE 2
+#define ME_HFS_ACK_S3 3
+#define ME_HFS_ACK_S4 4
+#define ME_HFS_ACK_S5 5
+#define ME_HFS_ACK_GBL_RESET 6
+#define ME_HFS_ACK_CONTINUE 7
+
+struct me_hfs {
+ u32 working_state: 4;
+ u32 mfg_mode: 1;
+ u32 fpt_bad: 1;
+ u32 operation_state: 3;
+ u32 fw_init_complete: 1;
+ u32 ft_bup_ld_flr: 1;
+ u32 update_in_progress: 1;
+ u32 error_code: 4;
+ u32 operation_mode: 4;
+ u32 reserved: 4;
+ u32 boot_options_present: 1;
+ u32 ack_data: 3;
+ u32 bios_msg_ack: 4;
+} __attribute__ ((packed));
+
+#define PCI_ME_UMA 0x44
+
+struct me_uma {
+ u32 size: 6;
+ u32 reserved_1: 10;
+ u32 valid: 1;
+ u32 reserved_0: 14;
+ u32 set_to_one: 1;
+} __attribute__ ((packed));
+
+#define PCI_ME_H_GS 0x4c
+#define ME_INIT_DONE 1
+#define ME_INIT_STATUS_SUCCESS 0
+#define ME_INIT_STATUS_NOMEM 1
+#define ME_INIT_STATUS_ERROR 2
+
+struct me_did {
+ u32 uma_base: 16;
+ u32 reserved: 8;
+ u32 status: 4;
+ u32 init_done: 4;
+} __attribute__ ((packed));
+
+#define PCI_ME_GMES 0x48
+#define ME_GMES_PHASE_ROM 0
+#define ME_GMES_PHASE_BUP 1
+#define ME_GMES_PHASE_UKERNEL 2
+#define ME_GMES_PHASE_POLICY 3
+#define ME_GMES_PHASE_MODULE 4
+#define ME_GMES_PHASE_UNKNOWN 5
+#define ME_GMES_PHASE_HOST 6
+
+struct me_gmes {
+ u32 bist_in_prog : 1;
+ u32 icc_prog_sts : 2;
+ u32 invoke_mebx : 1;
+ u32 cpu_replaced_sts : 1;
+ u32 mbp_rdy : 1;
+ u32 mfs_failure : 1;
+ u32 warm_rst_req_for_df : 1;
+ u32 cpu_replaced_valid : 1;
+ u32 reserved_1 : 2;
+ u32 fw_upd_ipu : 1;
+ u32 reserved_2 : 4;
+ u32 current_state: 8;
+ u32 current_pmevent: 4;
+ u32 progress_code: 4;
+} __attribute__ ((packed));
+
+#define PCI_ME_HERES 0xbc
+#define PCI_ME_EXT_SHA1 0x00
+#define PCI_ME_EXT_SHA256 0x02
+#define PCI_ME_HER(x) (0xc0+(4*(x)))
+
+struct me_heres {
+ u32 extend_reg_algorithm: 4;
+ u32 reserved: 26;
+ u32 extend_feature_present: 1;
+ u32 extend_reg_valid: 1;
+} __attribute__ ((packed));
+
+/*
+ * Management Engine MEI registers
+ */
+
+#define MEI_H_CB_WW 0x00
+#define MEI_H_CSR 0x04
+#define MEI_ME_CB_RW 0x08
+#define MEI_ME_CSR_HA 0x0c
+
+struct mei_csr {
+ u32 interrupt_enable: 1;
+ u32 interrupt_status: 1;
+ u32 interrupt_generate: 1;
+ u32 ready: 1;
+ u32 reset: 1;
+ u32 reserved: 3;
+ u32 buffer_read_ptr: 8;
+ u32 buffer_write_ptr: 8;
+ u32 buffer_depth: 8;
+} __attribute__ ((packed));
+
+#define MEI_ADDRESS_CORE 0x01
+#define MEI_ADDRESS_AMT 0x02
+#define MEI_ADDRESS_RESERVED 0x03
+#define MEI_ADDRESS_WDT 0x04
+#define MEI_ADDRESS_MKHI 0x07
+#define MEI_ADDRESS_ICC 0x08
+#define MEI_ADDRESS_THERMAL 0x09
+
+#define MEI_HOST_ADDRESS 0
+
+struct mei_header {
+ u32 client_address: 8;
+ u32 host_address: 8;
+ u32 length: 9;
+ u32 reserved: 6;
+ u32 is_complete: 1;
+} __attribute__ ((packed));
+
+#define MKHI_GROUP_ID_CBM 0x00
+#define MKHI_GROUP_ID_FWCAPS 0x03
+#define MKHI_GROUP_ID_MDES 0x08
+#define MKHI_GROUP_ID_GEN 0xff
+
+#define MKHI_GLOBAL_RESET 0x0b
+
+#define MKHI_FWCAPS_GET_RULE 0x02
+
+#define MKHI_MDES_ENABLE 0x09
+
+#define MKHI_GET_FW_VERSION 0x02
+#define MKHI_END_OF_POST 0x0c
+#define MKHI_FEATURE_OVERRIDE 0x14
+
+struct mkhi_header {
+ u32 group_id: 8;
+ u32 command: 7;
+ u32 is_response: 1;
+ u32 reserved: 8;
+ u32 result: 8;
+} __attribute__ ((packed));
+
+struct me_fw_version {
+ u16 code_minor;
+ u16 code_major;
+ u16 code_build_number;
+ u16 code_hot_fix;
+ u16 recovery_minor;
+ u16 recovery_major;
+ u16 recovery_build_number;
+ u16 recovery_hot_fix;
+} __attribute__ ((packed));
+
+
+#define HECI_EOP_STATUS_SUCCESS 0x0
+#define HECI_EOP_PERFORM_GLOBAL_RESET 0x1
+
+#define CBM_RR_GLOBAL_RESET 0x01
+
+#define GLOBAL_RESET_BIOS_MRC 0x01
+#define GLOBAL_RESET_BIOS_POST 0x02
+#define GLOBAL_RESET_MEBX 0x03
+
+struct me_global_reset {
+ u8 request_origin;
+ u8 reset_type;
+} __attribute__ ((packed));
+
+typedef enum {
+ ME_NORMAL_BIOS_PATH,
+ ME_S3WAKE_BIOS_PATH,
+ ME_ERROR_BIOS_PATH,
+ ME_RECOVERY_BIOS_PATH,
+ ME_DISABLE_BIOS_PATH,
+ ME_FIRMWARE_UPDATE_BIOS_PATH,
+} me_bios_path;
+
+/* Defined in me_status.c for both romstage and ramstage */
+void intel_me_status(struct me_hfs *hfs, struct me_gmes *gmes);
+
+#ifdef __PRE_RAM__
+void intel_early_me_status(void);
+int intel_early_me_init(void);
+int intel_early_me_uma_size(void);
+int intel_early_me_init_done(u8 status);
+#endif
+
+#ifdef __SMM__
+void intel_me_finalize_smm(void);
+void intel_me8_finalize_smm(void);
+#endif
+typedef struct {
+ u32 major_version : 16;
+ u32 minor_version : 16;
+ u32 hotfix_version : 16;
+ u32 build_version : 16;
+} __attribute__ ((packed)) mbp_fw_version_name;
+
+typedef struct {
+ u8 num_icc_profiles;
+ u8 icc_profile_soft_strap;
+ u8 icc_profile_index;
+ u8 reserved;
+ u32 register_lock_mask[3];
+} __attribute__ ((packed)) mbp_icc_profile;
+
+typedef struct {
+ u32 full_net : 1;
+ u32 std_net : 1;
+ u32 manageability : 1;
+ u32 small_business : 1;
+ u32 l3manageability : 1;
+ u32 intel_at : 1;
+ u32 intel_cls : 1;
+ u32 reserved : 3;
+ u32 intel_mpc : 1;
+ u32 icc_over_clocking : 1;
+ u32 pavp : 1;
+ u32 reserved_1 : 4;
+ u32 ipv6 : 1;
+ u32 kvm : 1;
+ u32 och : 1;
+ u32 vlan : 1;
+ u32 tls : 1;
+ u32 reserved_4 : 1;
+ u32 wlan : 1;
+ u32 reserved_5 : 8;
+} __attribute__ ((packed)) mefwcaps_sku;
+
+typedef struct {
+ u16 lock_state : 1;
+ u16 authenticate_module : 1;
+ u16 s3authentication : 1;
+ u16 flash_wear_out : 1;
+ u16 flash_variable_security : 1;
+ u16 wwan3gpresent : 1;
+ u16 wwan3goob : 1;
+ u16 reserved : 9;
+} __attribute__ ((packed)) tdt_state_flag;
+
+typedef struct {
+ u8 state;
+ u8 last_theft_trigger;
+ tdt_state_flag flags;
+} __attribute__ ((packed)) tdt_state_info;
+
+typedef struct {
+ u32 platform_target_usage_type : 4;
+ u32 platform_target_market_type : 2;
+ u32 super_sku : 1;
+ u32 reserved : 1;
+ u32 intel_me_fw_image_type : 4;
+ u32 platform_brand : 4;
+ u32 reserved_1 : 16;
+} __attribute__ ((packed)) platform_type_rule_data;
+
+typedef struct {
+ mefwcaps_sku fw_capabilities;
+ u8 available;
+} mbp_fw_caps;
+
+typedef struct {
+ u16 device_id;
+ u16 fuse_test_flags;
+ u32 umchid[4];
+} __attribute__ ((packed)) mbp_rom_bist_data;
+
+typedef struct {
+ u32 key[8];
+} mbp_platform_key;
+
+typedef struct {
+ platform_type_rule_data rule_data;
+ u8 available;
+} mbp_plat_type;
+
+typedef struct {
+ mbp_fw_version_name fw_version_name;
+ mbp_fw_caps fw_caps_sku;
+ mbp_rom_bist_data rom_bist_data;
+ mbp_platform_key platform_key;
+ mbp_plat_type fw_plat_type;
+ mbp_icc_profile icc_profile;
+ tdt_state_info at_state;
+ u32 mfsintegrity;
+} me_bios_payload;
+
+typedef struct {
+ u32 mbp_size : 8;
+ u32 num_entries : 8;
+ u32 rsvd : 16;
+} __attribute__ ((packed)) mbp_header;
+
+typedef struct {
+ u32 app_id : 8;
+ u32 item_id : 8;
+ u32 length : 8;
+ u32 rsvd : 8;
+} __attribute__ ((packed)) mbp_item_header;
+
+struct me_fwcaps {
+ u32 id;
+ u8 length;
+ mefwcaps_sku caps_sku;
+ u8 reserved[3];
+} __attribute__ ((packed));
+
+#endif /* _INTEL_ME_H */
diff --git a/src/southbridge/intel/fsp_i89xx/me_8.x.c b/src/southbridge/intel/fsp_i89xx/me_8.x.c
new file mode 100644
index 0000000000..ac37dae849
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/me_8.x.c
@@ -0,0 +1,882 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+/*
+ * This is a ramstage driver for the Intel Management Engine found in the
+ * 6-series chipset. It handles the required boot-time messages over the
+ * MMIO-based Management Engine Interface to tell the ME that the BIOS is
+ * finished with POST. Additional messages are defined for debug but are
+ * not used unless the console loglevel is high enough.
+ */
+
+#include <arch/acpi.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/pci_ids.h>
+#include <device/pci_def.h>
+#include <string.h>
+#include <delay.h>
+#include <elog.h>
+#include <halt.h>
+
+#ifndef __SMM__
+# include <device/device.h>
+# include <device/pci.h>
+#endif
+
+#include "me.h"
+#include "pch.h"
+
+#if CONFIG_CHROMEOS
+#include <vendorcode/google/chromeos/chromeos.h>
+#include <vendorcode/google/chromeos/gnvs.h>
+#endif
+
+#ifndef __SMM__
+/* Path that the BIOS should take based on ME state */
+static const char *me_bios_path_values[] = {
+ [ME_NORMAL_BIOS_PATH] = "Normal",
+ [ME_S3WAKE_BIOS_PATH] = "S3 Wake",
+ [ME_ERROR_BIOS_PATH] = "Error",
+ [ME_RECOVERY_BIOS_PATH] = "Recovery",
+ [ME_DISABLE_BIOS_PATH] = "Disable",
+ [ME_FIRMWARE_UPDATE_BIOS_PATH] = "Firmware Update",
+};
+static int intel_me_read_mbp(me_bios_payload *mbp_data);
+#endif
+
+/* MMIO base address for MEI interface */
+static u32 *mei_base_address;
+
+#if CONFIG_DEBUG_INTEL_ME
+static void mei_dump(void *ptr, int dword, int offset, const char *type)
+{
+ struct mei_csr *csr;
+
+ printk(BIOS_SPEW, "%-9s[%02x] : ", type, offset);
+
+ switch (offset) {
+ case MEI_H_CSR:
+ case MEI_ME_CSR_HA:
+ csr = ptr;
+ if (!csr) {
+ printk(BIOS_SPEW, "ERROR: 0x%08x\n", dword);
+ break;
+ }
+ printk(BIOS_SPEW, "cbd=%u cbrp=%02u cbwp=%02u ready=%u "
+ "reset=%u ig=%u is=%u ie=%u\n", csr->buffer_depth,
+ csr->buffer_read_ptr, csr->buffer_write_ptr,
+ csr->ready, csr->reset, csr->interrupt_generate,
+ csr->interrupt_status, csr->interrupt_enable);
+ break;
+ case MEI_ME_CB_RW:
+ case MEI_H_CB_WW:
+ printk(BIOS_SPEW, "CB: 0x%08x\n", dword);
+ break;
+ default:
+ printk(BIOS_SPEW, "0x%08x\n", offset);
+ break;
+ }
+}
+#else
+# define mei_dump(ptr,dword,offset,type) do {} while (0)
+#endif
+
+/*
+ * ME/MEI access helpers using memcpy to avoid aliasing.
+ */
+
+static inline void mei_read_dword_ptr(void *ptr, int offset)
+{
+ u32 dword = read32(mei_base_address + (offset/sizeof(u32)));
+ memcpy(ptr, &dword, sizeof(dword));
+ mei_dump(ptr, dword, offset, "READ");
+}
+
+static inline void mei_write_dword_ptr(void *ptr, int offset)
+{
+ u32 dword = 0;
+ memcpy(&dword, ptr, sizeof(dword));
+ write32(mei_base_address + (offset/sizeof(u32)), dword);
+ mei_dump(ptr, dword, offset, "WRITE");
+}
+
+#ifndef __SMM__
+static inline void pci_read_dword_ptr(device_t dev, void *ptr, int offset)
+{
+ u32 dword = pci_read_config32(dev, offset);
+ memcpy(ptr, &dword, sizeof(dword));
+ mei_dump(ptr, dword, offset, "PCI READ");
+}
+#endif
+
+static inline void read_host_csr(struct mei_csr *csr)
+{
+ mei_read_dword_ptr(csr, MEI_H_CSR);
+}
+
+static inline void write_host_csr(struct mei_csr *csr)
+{
+ mei_write_dword_ptr(csr, MEI_H_CSR);
+}
+
+static inline void read_me_csr(struct mei_csr *csr)
+{
+ mei_read_dword_ptr(csr, MEI_ME_CSR_HA);
+}
+
+static inline void write_cb(u32 dword)
+{
+ write32(mei_base_address + (MEI_H_CB_WW/sizeof(u32)), dword);
+ mei_dump(NULL, dword, MEI_H_CB_WW, "WRITE");
+}
+
+static inline u32 read_cb(void)
+{
+ u32 dword = read32(mei_base_address + (MEI_ME_CB_RW/sizeof(u32)));
+ mei_dump(NULL, dword, MEI_ME_CB_RW, "READ");
+ return dword;
+}
+
+/* Wait for ME ready bit to be asserted */
+static int mei_wait_for_me_ready(void)
+{
+ struct mei_csr me;
+ unsigned try = ME_RETRY;
+
+ while (try--) {
+ read_me_csr(&me);
+ if (me.ready)
+ return 0;
+ udelay(ME_DELAY);
+ }
+
+ printk(BIOS_ERR, "ME: failed to become ready\n");
+ return -1;
+}
+
+static void mei_reset(void)
+{
+ struct mei_csr host;
+
+ if (mei_wait_for_me_ready() < 0)
+ return;
+
+ /* Reset host and ME circular buffers for next message */
+ read_host_csr(&host);
+ host.reset = 1;
+ host.interrupt_generate = 1;
+ write_host_csr(&host);
+
+ if (mei_wait_for_me_ready() < 0)
+ return;
+
+ /* Re-init and indicate host is ready */
+ read_host_csr(&host);
+ host.interrupt_generate = 1;
+ host.ready = 1;
+ host.reset = 0;
+ write_host_csr(&host);
+}
+
+static int mei_send_msg(struct mei_header *mei, struct mkhi_header *mkhi,
+ void *req_data)
+{
+ struct mei_csr host;
+ unsigned ndata, n;
+ u32 *data;
+
+ /* Number of dwords to write, ignoring MKHI */
+ ndata = mei->length >> 2;
+
+ /* Pad non-dword aligned request message length */
+ if (mei->length & 3)
+ ndata++;
+ if (!ndata) {
+ printk(BIOS_DEBUG, "ME: request does not include MKHI\n");
+ return -1;
+ }
+ ndata++; /* Add MEI header */
+
+ /*
+ * Make sure there is still room left in the circular buffer.
+ * Reset the buffer pointers if the requested message will not fit.
+ */
+ read_host_csr(&host);
+ if ((host.buffer_depth - host.buffer_write_ptr) < ndata) {
+ printk(BIOS_ERR, "ME: circular buffer full, resetting...\n");
+ mei_reset();
+ read_host_csr(&host);
+ }
+
+ /*
+ * This implementation does not handle splitting large messages
+ * across multiple transactions. Ensure the requested length
+ * will fit in the available circular buffer depth.
+ */
+ if ((host.buffer_depth - host.buffer_write_ptr) < ndata) {
+ printk(BIOS_ERR, "ME: message (%u) too large for buffer (%u)\n",
+ ndata + 2, host.buffer_depth);
+ return -1;
+ }
+
+ /* Write MEI header */
+ mei_write_dword_ptr(mei, MEI_H_CB_WW);
+ ndata--;
+
+ /* Write MKHI header */
+ mei_write_dword_ptr(mkhi, MEI_H_CB_WW);
+ ndata--;
+
+ /* Write message data */
+ data = req_data;
+ for (n = 0; n < ndata; ++n)
+ write_cb(*data++);
+
+ /* Generate interrupt to the ME */
+ read_host_csr(&host);
+ host.interrupt_generate = 1;
+ write_host_csr(&host);
+
+ /* Make sure ME is ready after sending request data */
+ return mei_wait_for_me_ready();
+}
+
+static int mei_recv_msg(struct mkhi_header *mkhi,
+ void *rsp_data, int rsp_bytes)
+{
+ struct mei_header mei_rsp;
+ struct mkhi_header mkhi_rsp;
+ struct mei_csr me, host;
+ unsigned ndata, n/*, me_data_len*/;
+ unsigned expected;
+ u32 *data;
+
+ /* Total number of dwords to read from circular buffer */
+ expected = (rsp_bytes + sizeof(mei_rsp) + sizeof(mkhi_rsp)) >> 2;
+ if (rsp_bytes & 3)
+ expected++;
+
+ /*
+ * The interrupt status bit does not appear to indicate that the
+ * message has actually been received. Instead we wait until the
+ * expected number of dwords are present in the circular buffer.
+ */
+ for (n = ME_RETRY; n; --n) {
+ read_me_csr(&me);
+ if ((me.buffer_write_ptr - me.buffer_read_ptr) >= expected)
+ break;
+ udelay(ME_DELAY);
+ }
+ if (!n) {
+ printk(BIOS_ERR, "ME: timeout waiting for data: expected "
+ "%u, available %u\n", expected,
+ me.buffer_write_ptr - me.buffer_read_ptr);
+ return -1;
+ }
+
+ /* Read and verify MEI response header from the ME */
+ mei_read_dword_ptr(&mei_rsp, MEI_ME_CB_RW);
+ if (!mei_rsp.is_complete) {
+ printk(BIOS_ERR, "ME: response is not complete\n");
+ return -1;
+ }
+
+ /* Handle non-dword responses and expect at least MKHI header */
+ ndata = mei_rsp.length >> 2;
+ if (mei_rsp.length & 3)
+ ndata++;
+ if (ndata != (expected - 1)) {
+ printk(BIOS_ERR, "ME: response is missing data %d != %d\n",
+ ndata, (expected - 1));
+ return -1;
+ }
+
+ /* Read and verify MKHI response header from the ME */
+ mei_read_dword_ptr(&mkhi_rsp, MEI_ME_CB_RW);
+ if (!mkhi_rsp.is_response ||
+ mkhi->group_id != mkhi_rsp.group_id ||
+ mkhi->command != mkhi_rsp.command) {
+ printk(BIOS_ERR, "ME: invalid response, group %u ?= %u,"
+ "command %u ?= %u, is_response %u\n", mkhi->group_id,
+ mkhi_rsp.group_id, mkhi->command, mkhi_rsp.command,
+ mkhi_rsp.is_response);
+ return -1;
+ }
+ ndata--; /* MKHI header has been read */
+
+ /* Make sure caller passed a buffer with enough space */
+ if (ndata != (rsp_bytes >> 2)) {
+ printk(BIOS_ERR, "ME: not enough room in response buffer: "
+ "%u != %u\n", ndata, rsp_bytes >> 2);
+ return -1;
+ }
+
+ /* Read response data from the circular buffer */
+ data = rsp_data;
+ for (n = 0; n < ndata; ++n)
+ *data++ = read_cb();
+
+ /* Tell the ME that we have consumed the response */
+ read_host_csr(&host);
+ host.interrupt_status = 1;
+ host.interrupt_generate = 1;
+ write_host_csr(&host);
+
+ return mei_wait_for_me_ready();
+}
+
+static inline int mei_sendrecv(struct mei_header *mei, struct mkhi_header *mkhi,
+ void *req_data, void *rsp_data, int rsp_bytes)
+{
+ if (mei_send_msg(mei, mkhi, req_data) < 0)
+ return -1;
+ if (mei_recv_msg(mkhi, rsp_data, rsp_bytes) < 0)
+ return -1;
+ return 0;
+}
+
+#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) && !defined(__SMM__)
+static inline void print_cap(const char *name, int state)
+{
+ printk(BIOS_DEBUG, "ME Capability: %-41s : %sabled\n",
+ name, state ? " en" : "dis");
+}
+
+static void me_print_fw_version(mbp_fw_version_name *vers_name)
+{
+ if (!vers_name->major_version) {
+ printk(BIOS_ERR, "ME: mbp missing version report\n");
+ return;
+ }
+
+ printk(BIOS_DEBUG, "ME: found version %d.%d.%d.%d\n",
+ vers_name->major_version, vers_name->minor_version,
+ vers_name->hotfix_version, vers_name->build_version);
+}
+
+/* Get ME Firmware Capabilities */
+static int mkhi_get_fwcaps(mefwcaps_sku *cap)
+{
+ u32 rule_id = 0;
+ struct me_fwcaps cap_msg;
+ struct mkhi_header mkhi = {
+ .group_id = MKHI_GROUP_ID_FWCAPS,
+ .command = MKHI_FWCAPS_GET_RULE,
+ };
+ struct mei_header mei = {
+ .is_complete = 1,
+ .host_address = MEI_HOST_ADDRESS,
+ .client_address = MEI_ADDRESS_MKHI,
+ .length = sizeof(mkhi) + sizeof(rule_id),
+ };
+
+ /* Send request and wait for response */
+ if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap_msg, sizeof(cap_msg))
+ < 0) {
+ printk(BIOS_ERR, "ME: GET FWCAPS message failed\n");
+ return -1;
+ }
+ *cap = cap_msg.caps_sku;
+ return 0;
+}
+
+/* Get ME Firmware Capabilities */
+static void me_print_fwcaps(mbp_fw_caps *caps_section)
+{
+ mefwcaps_sku *cap = &caps_section->fw_capabilities;
+ if (!caps_section->available) {
+ printk(BIOS_ERR, "ME: mbp missing fwcaps report\n");
+ if (mkhi_get_fwcaps(cap))
+ return;
+ }
+
+ print_cap("Full Network manageability", cap->full_net);
+ print_cap("Regular Network manageability", cap->std_net);
+ print_cap("Manageability", cap->manageability);
+ print_cap("Small business technology", cap->small_business);
+ print_cap("Level III manageability", cap->l3manageability);
+ print_cap("IntelR Anti-Theft (AT)", cap->intel_at);
+ print_cap("IntelR Capability Licensing Service (CLS)", cap->intel_cls);
+ print_cap("IntelR Power Sharing Technology (MPC)", cap->intel_mpc);
+ print_cap("ICC Over Clocking", cap->icc_over_clocking);
+ print_cap("Protected Audio Video Path (PAVP)", cap->pavp);
+ print_cap("IPV6", cap->ipv6);
+ print_cap("KVM Remote Control (KVM)", cap->kvm);
+ print_cap("Outbreak Containment Heuristic (OCH)", cap->och);
+ print_cap("Virtual LAN (VLAN)", cap->vlan);
+ print_cap("TLS", cap->tls);
+ print_cap("Wireless LAN (WLAN)", cap->wlan);
+}
+#endif
+
+#ifdef __SMM__
+
+/* Send END OF POST message to the ME */
+static int mkhi_end_of_post(void)
+{
+ struct mkhi_header mkhi = {
+ .group_id = MKHI_GROUP_ID_GEN,
+ .command = MKHI_END_OF_POST,
+ };
+ struct mei_header mei = {
+ .is_complete = 1,
+ .host_address = MEI_HOST_ADDRESS,
+ .client_address = MEI_ADDRESS_MKHI,
+ .length = sizeof(mkhi),
+ };
+
+ u32 eop_ack;
+
+ /* Send request and wait for response */
+ printk(BIOS_NOTICE, "ME: %s\n", __FUNCTION__);
+ if (mei_sendrecv(&mei, &mkhi, NULL, &eop_ack, sizeof(eop_ack)) < 0) {
+ printk(BIOS_ERR, "ME: END OF POST message failed\n");
+ return -1;
+ }
+
+ printk(BIOS_INFO, "ME: END OF POST message successful (%d)\n", eop_ack);
+ return 0;
+}
+
+void intel_me8_finalize_smm(void)
+{
+ struct me_hfs hfs;
+ u32 reg32;
+
+ mei_base_address = (u32 *)
+ (pcie_read_config32(PCH_ME_DEV, PCI_BASE_ADDRESS_0) & ~0xf);
+
+ /* S3 path will have hidden this device already */
+ if (!mei_base_address || mei_base_address == (u32 *)0xfffffff0)
+ return;
+
+ /* Make sure ME is in a mode that expects EOP */
+ reg32 = pcie_read_config32(PCH_ME_DEV, PCI_ME_HFS);
+ memcpy(&hfs, &reg32, sizeof(u32));
+
+ /* Abort and leave device alone if not normal mode */
+ if (hfs.fpt_bad ||
+ hfs.working_state != ME_HFS_CWS_NORMAL ||
+ hfs.operation_mode != ME_HFS_MODE_NORMAL)
+ return;
+
+ /* Try to send EOP command so ME stops accepting other commands */
+ mkhi_end_of_post();
+
+ /* Make sure IO is disabled */
+ reg32 = pcie_read_config32(PCH_ME_DEV, PCI_COMMAND);
+ reg32 &= ~(PCI_COMMAND_MASTER |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+ pcie_write_config32(PCH_ME_DEV, PCI_COMMAND, reg32);
+
+ /* Hide the PCI device */
+ RCBA32_OR(FD2, PCH_DISABLE_MEI1);
+}
+
+#else /* !__SMM__ */
+
+/* Determine the path that we should take based on ME status */
+static me_bios_path intel_me_path(device_t dev)
+{
+ me_bios_path path = ME_DISABLE_BIOS_PATH;
+ struct me_hfs hfs;
+ struct me_gmes gmes;
+
+ /* S3 wake skips all MKHI messages */
+ if (acpi_is_wakeup_s3())
+ return ME_S3WAKE_BIOS_PATH;
+
+ pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS);
+ pci_read_dword_ptr(dev, &gmes, PCI_ME_GMES);
+
+ /* Check and dump status */
+ intel_me_status(&hfs, &gmes);
+
+ /* Check Current Working State */
+ switch (hfs.working_state) {
+ case ME_HFS_CWS_NORMAL:
+ path = ME_NORMAL_BIOS_PATH;
+ break;
+ case ME_HFS_CWS_REC:
+ path = ME_RECOVERY_BIOS_PATH;
+ break;
+ default:
+ path = ME_DISABLE_BIOS_PATH;
+ break;
+ }
+
+ /* Check Current Operation Mode */
+ switch (hfs.operation_mode) {
+ case ME_HFS_MODE_NORMAL:
+ break;
+ case ME_HFS_MODE_DEBUG:
+ case ME_HFS_MODE_DIS:
+ case ME_HFS_MODE_OVER_JMPR:
+ case ME_HFS_MODE_OVER_MEI:
+ default:
+ path = ME_DISABLE_BIOS_PATH;
+ break;
+ }
+
+ /* Check for any error code and valid firmware and MBP */
+ if (hfs.error_code || hfs.fpt_bad)
+ path = ME_ERROR_BIOS_PATH;
+
+ /* Check if the MBP is ready */
+ if (!gmes.mbp_rdy) {
+ printk(BIOS_CRIT, "%s: mbp is not ready!\n",
+ __FUNCTION__);
+ path = ME_ERROR_BIOS_PATH;
+ }
+
+#if CONFIG_ELOG
+ if (path != ME_NORMAL_BIOS_PATH) {
+ struct elog_event_data_me_extended data = {
+ .current_working_state = hfs.working_state,
+ .operation_state = hfs.operation_state,
+ .operation_mode = hfs.operation_mode,
+ .error_code = hfs.error_code,
+ .progress_code = gmes.progress_code,
+ .current_pmevent = gmes.current_pmevent,
+ .current_state = gmes.current_state,
+ };
+ elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path);
+ elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT,
+ &data, sizeof(data));
+ }
+#endif
+
+ return path;
+}
+
+/* Prepare ME for MEI messages */
+static int intel_mei_setup(device_t dev)
+{
+ struct resource *res;
+ struct mei_csr host;
+ u32 reg32;
+
+ /* Find the MMIO base for the ME interface */
+ res = find_resource(dev, PCI_BASE_ADDRESS_0);
+ if (!res || res->base == 0 || res->size == 0) {
+ printk(BIOS_DEBUG, "ME: MEI resource not present!\n");
+ return -1;
+ }
+ mei_base_address = (u32 *)(uintptr_t)res->base;
+
+ /* Ensure Memory and Bus Master bits are set */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* Clean up status for next message */
+ read_host_csr(&host);
+ host.interrupt_generate = 1;
+ host.ready = 1;
+ host.reset = 0;
+ write_host_csr(&host);
+
+ return 0;
+}
+
+/* Read the Extend register hash of ME firmware */
+static int intel_me_extend_valid(device_t dev)
+{
+ struct me_heres status;
+ u32 extend[8] = {0};
+ int i, count = 0;
+
+ pci_read_dword_ptr(dev, &status, PCI_ME_HERES);
+ if (!status.extend_feature_present) {
+ printk(BIOS_ERR, "ME: Extend Feature not present\n");
+ return -1;
+ }
+
+ if (!status.extend_reg_valid) {
+ printk(BIOS_ERR, "ME: Extend Register not valid\n");
+ return -1;
+ }
+
+ switch (status.extend_reg_algorithm) {
+ case PCI_ME_EXT_SHA1:
+ count = 5;
+ printk(BIOS_DEBUG, "ME: Extend SHA-1: ");
+ break;
+ case PCI_ME_EXT_SHA256:
+ count = 8;
+ printk(BIOS_DEBUG, "ME: Extend SHA-256: ");
+ break;
+ default:
+ printk(BIOS_ERR, "ME: Extend Algorithm %d unknown\n",
+ status.extend_reg_algorithm);
+ return -1;
+ }
+
+ for (i = 0; i < count; ++i) {
+ extend[i] = pci_read_config32(dev, PCI_ME_HER(i));
+ printk(BIOS_DEBUG, "%08x", extend[i]);
+ }
+ printk(BIOS_DEBUG, "\n");
+
+#if CONFIG_CHROMEOS
+ /* Save hash in NVS for the OS to verify */
+ chromeos_set_me_hash(extend, count);
+#endif
+
+ return 0;
+}
+
+/* Hide the ME virtual PCI devices */
+static void intel_me_hide(device_t dev)
+{
+ dev->enabled = 0;
+ pch_enable(dev);
+}
+
+/* Check whether ME is present and do basic init */
+static void intel_me_init(device_t dev)
+{
+ me_bios_path path = intel_me_path(dev);
+ me_bios_payload mbp_data;
+
+ /* Do initial setup and determine the BIOS path */
+ printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_bios_path_values[path]);
+
+ switch (path) {
+ case ME_S3WAKE_BIOS_PATH:
+ intel_me_hide(dev);
+ break;
+
+ case ME_NORMAL_BIOS_PATH:
+ /* Validate the extend register */
+ if (intel_me_extend_valid(dev) < 0)
+ break; /* TODO: force recovery mode */
+
+ /* Prepare MEI MMIO interface */
+ if (intel_mei_setup(dev) < 0)
+ break;
+
+ if(intel_me_read_mbp(&mbp_data))
+ break;
+
+#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG)
+ me_print_fw_version(&mbp_data.fw_version_name);
+ me_print_fwcaps(&mbp_data.fw_caps_sku);
+#endif
+
+ /*
+ * Leave the ME unlocked in this path.
+ * It will be locked via SMI command later.
+ */
+ break;
+
+ case ME_ERROR_BIOS_PATH:
+ case ME_RECOVERY_BIOS_PATH:
+ case ME_DISABLE_BIOS_PATH:
+ case ME_FIRMWARE_UPDATE_BIOS_PATH:
+ break;
+ }
+}
+
+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 = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = intel_me_init,
+ .ops_pci = &pci_ops,
+};
+
+static const struct pci_driver intel_me __pci_driver = {
+ .ops = &device_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = 0x1e3a,
+};
+
+/******************************************************************************
+ * */
+static u32 me_to_host_words_pending(void)
+{
+ struct mei_csr me;
+ read_me_csr(&me);
+ if (!me.ready)
+ return 0;
+ return (me.buffer_write_ptr - me.buffer_read_ptr) &
+ (me.buffer_depth - 1);
+}
+
+#if 0
+/* This function is not yet being used, keep it in for the future. */
+static u32 host_to_me_words_room(void)
+{
+ struct mei_csr csr;
+
+ read_me_csr(&csr);
+ if (!csr.ready)
+ return 0;
+
+ read_host_csr(&csr);
+ return (csr.buffer_read_ptr - csr.buffer_write_ptr - 1) &
+ (csr.buffer_depth - 1);
+}
+#endif
+/*
+ * mbp seems to be following its own flow, let's retrieve it in a dedicated
+ * function.
+ */
+static int intel_me_read_mbp(me_bios_payload *mbp_data)
+{
+ mbp_header mbp_hdr;
+ mbp_item_header mbp_item_hdr;
+ u32 me2host_pending;
+ u32 mbp_item_id;
+ struct mei_csr host;
+
+ me2host_pending = me_to_host_words_pending();
+ if (!me2host_pending) {
+ printk(BIOS_ERR, "ME: no mbp data!\n");
+ return -1;
+ }
+
+ /* we know for sure that at least the header is there */
+ mei_read_dword_ptr(&mbp_hdr, MEI_ME_CB_RW);
+
+ if ((mbp_hdr.num_entries > (mbp_hdr.mbp_size / 2)) ||
+ (me2host_pending < mbp_hdr.mbp_size)) {
+ printk(BIOS_ERR, "ME: mbp of %d entries, total size %d words"
+ " buffer contains %d words\n",
+ mbp_hdr.num_entries, mbp_hdr.mbp_size,
+ me2host_pending);
+ return -1;
+ }
+
+ me2host_pending--;
+ memset(mbp_data, 0, sizeof(*mbp_data));
+
+ while (mbp_hdr.num_entries--) {
+ u32* copy_addr;
+ u32 copy_size, buffer_room;
+ void *p;
+
+ if (!me2host_pending) {
+ printk(BIOS_ERR, "ME: no mbp data %d entries to go!\n",
+ mbp_hdr.num_entries + 1);
+ return -1;
+ }
+
+ mei_read_dword_ptr(&mbp_item_hdr, MEI_ME_CB_RW);
+
+ if (mbp_item_hdr.length > me2host_pending) {
+ printk(BIOS_ERR, "ME: insufficient mbp data %d "
+ "entries to go!\n",
+ mbp_hdr.num_entries + 1);
+ return -1;
+ }
+
+ me2host_pending -= mbp_item_hdr.length;
+
+ mbp_item_id = (((u32)mbp_item_hdr.item_id) << 8) +
+ mbp_item_hdr.app_id;
+
+ copy_size = mbp_item_hdr.length - 1;
+
+#define SET_UP_COPY(field) { copy_addr = (u32 *)&mbp_data->field; \
+ buffer_room = sizeof(mbp_data->field) / sizeof(u32); \
+ break; \
+ }
+
+ p = &mbp_item_hdr;
+ printk(BIOS_INFO, "ME: MBP item header %8.8x\n", *((u32*)p));
+
+ switch(mbp_item_id) {
+ case 0x101:
+ SET_UP_COPY(fw_version_name);
+
+ case 0x102:
+ SET_UP_COPY(icc_profile);
+
+ case 0x103:
+ SET_UP_COPY(at_state);
+
+ case 0x201:
+ mbp_data->fw_caps_sku.available = 1;
+ SET_UP_COPY(fw_caps_sku.fw_capabilities);
+
+ case 0x301:
+ SET_UP_COPY(rom_bist_data);
+
+ case 0x401:
+ SET_UP_COPY(platform_key);
+
+ case 0x501:
+ mbp_data->fw_plat_type.available = 1;
+ SET_UP_COPY(fw_plat_type.rule_data);
+
+ case 0x601:
+ SET_UP_COPY(mfsintegrity);
+
+ default:
+ printk(BIOS_ERR, "ME: unknown mbp item id 0x%x!!!\n",
+ mbp_item_id);
+ return -1;
+ }
+
+ if (buffer_room != copy_size) {
+ printk(BIOS_ERR, "ME: buffer room %d != %d copy size"
+ " for item 0x%x!!!\n",
+ buffer_room, copy_size, mbp_item_id);
+ return -1;
+ }
+ while(copy_size--)
+ *copy_addr++ = read_cb();
+ }
+
+ read_host_csr(&host);
+ host.interrupt_generate = 1;
+ write_host_csr(&host);
+
+ {
+ int cntr = 0;
+ while(host.interrupt_generate) {
+ read_host_csr(&host);
+ cntr++;
+ }
+ printk(BIOS_SPEW, "ME: mbp read OK after %d cycles\n", cntr);
+ }
+
+ return 0;
+}
+
+#endif /* !__SMM__ */
diff --git a/src/southbridge/intel/fsp_i89xx/me_status.c b/src/southbridge/intel/fsp_i89xx/me_status.c
new file mode 100644
index 0000000000..37fcf57677
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/me_status.c
@@ -0,0 +1,212 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <stdlib.h>
+#include <console/console.h>
+#include "me.h"
+
+#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG)
+/* HFS1[3:0] Current Working State Values */
+static const char *me_cws_values[] = {
+ [ME_HFS_CWS_RESET] = "Reset",
+ [ME_HFS_CWS_INIT] = "Initializing",
+ [ME_HFS_CWS_REC] = "Recovery",
+ [ME_HFS_CWS_NORMAL] = "Normal",
+ [ME_HFS_CWS_WAIT] = "Platform Disable Wait",
+ [ME_HFS_CWS_TRANS] = "OP State Transition",
+ [ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In"
+};
+
+/* HFS1[8:6] Current Operation State Values */
+static const char *me_opstate_values[] = {
+ [ME_HFS_STATE_PREBOOT] = "Preboot",
+ [ME_HFS_STATE_M0_UMA] = "M0 with UMA",
+ [ME_HFS_STATE_M3] = "M3 without UMA",
+ [ME_HFS_STATE_M0] = "M0 without UMA",
+ [ME_HFS_STATE_BRINGUP] = "Bring up",
+ [ME_HFS_STATE_ERROR] = "M0 without UMA but with error"
+};
+
+/* HFS[19:16] Current Operation Mode Values */
+static const char *me_opmode_values[] = {
+ [ME_HFS_MODE_NORMAL] = "Normal",
+ [ME_HFS_MODE_DEBUG] = "Debug",
+ [ME_HFS_MODE_DIS] = "Soft Temporary Disable",
+ [ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper",
+ [ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message"
+};
+
+/* HFS[15:12] Error Code Values */
+static const char *me_error_values[] = {
+ [ME_HFS_ERROR_NONE] = "No Error",
+ [ME_HFS_ERROR_UNCAT] = "Uncategorized Failure",
+ [ME_HFS_ERROR_IMAGE] = "Image Failure",
+ [ME_HFS_ERROR_DEBUG] = "Debug Failure"
+};
+
+/* GMES[31:28] ME Progress Code */
+static const char *me_progress_values[] = {
+ [ME_GMES_PHASE_ROM] = "ROM Phase",
+ [ME_GMES_PHASE_BUP] = "BUP Phase",
+ [ME_GMES_PHASE_UKERNEL] = "uKernel Phase",
+ [ME_GMES_PHASE_POLICY] = "Policy Module",
+ [ME_GMES_PHASE_MODULE] = "Module Loading",
+ [ME_GMES_PHASE_UNKNOWN] = "Unknown",
+ [ME_GMES_PHASE_HOST] = "Host Communication"
+};
+
+/* GMES[27:24] Power Management Event */
+static const char *me_pmevent_values[] = {
+ [0x00] = "Clean Moff->Mx wake",
+ [0x01] = "Moff->Mx wake after an error",
+ [0x02] = "Clean global reset",
+ [0x03] = "Global reset after an error",
+ [0x04] = "Clean Intel ME reset",
+ [0x05] = "Intel ME reset due to exception",
+ [0x06] = "Pseudo-global reset",
+ [0x07] = "S0/M0->Sx/M3",
+ [0x08] = "Sx/M3->S0/M0",
+ [0x09] = "Non-power cycle reset",
+ [0x0a] = "Power cycle reset through M3",
+ [0x0b] = "Power cycle reset through Moff",
+ [0x0c] = "Sx/Mx->Sx/Moff"
+};
+
+/* Progress Code 0 states */
+static const char *me_progress_rom_values[] = {
+ [0x00] = "BEGIN",
+ [0x06] = "DISABLE"
+};
+
+/* Progress Code 1 states */
+static const char *me_progress_bup_values[] = {
+ [0x00] = "Initialization starts",
+ [0x01] = "Disable the host wake event",
+ [0x04] = "Flow determination start process",
+ [0x08] = "Error reading/matching the VSCC table in the descriptor",
+ [0x0a] = "Check to see if straps say ME DISABLED",
+ [0x0b] = "Timeout waiting for PWROK",
+ [0x0d] = "Possibly handle BUP manufacturing override strap",
+ [0x11] = "Bringup in M3",
+ [0x12] = "Bringup in M0",
+ [0x13] = "Flow detection error",
+ [0x15] = "M3 clock switching error",
+ [0x18] = "M3 kernel load",
+ [0x1c] = "T34 missing - cannot program ICC",
+ [0x1f] = "Waiting for DID BIOS message",
+ [0x20] = "Waiting for DID BIOS message failure",
+ [0x21] = "DID reported an error",
+ [0x22] = "Enabling UMA",
+ [0x23] = "Enabling UMA error",
+ [0x24] = "Sending DID Ack to BIOS",
+ [0x25] = "Sending DID Ack to BIOS error",
+ [0x26] = "Switching clocks in M0",
+ [0x27] = "Switching clocks in M0 error",
+ [0x28] = "ME in temp disable",
+ [0x32] = "M0 kernel load",
+};
+
+/* Progress Code 3 states */
+static const char *me_progress_policy_values[] = {
+ [0x00] = "Entery into Policy Module",
+ [0x03] = "Received S3 entry",
+ [0x04] = "Received S4 entry",
+ [0x05] = "Received S5 entry",
+ [0x06] = "Received UPD entry",
+ [0x07] = "Received PCR entry",
+ [0x08] = "Received NPCR entry",
+ [0x09] = "Received host wake",
+ [0x0a] = "Received AC<>DC switch",
+ [0x0b] = "Received DRAM Init Done",
+ [0x0c] = "VSCC Data not found for flash device",
+ [0x0d] = "VSCC Table is not valid",
+ [0x0e] = "Flash Partition Boundary is outside address space",
+ [0x0f] = "ME cannot access the chipset descriptor region",
+ [0x10] = "Required VSCC values for flash parts do not match",
+};
+#endif
+
+void intel_me_status(struct me_hfs *hfs, struct me_gmes *gmes)
+{
+#if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG)
+ /* Check Current States */
+ printk(BIOS_DEBUG, "ME: FW Partition Table : %s\n",
+ hfs->fpt_bad ? "BAD" : "OK");
+ printk(BIOS_DEBUG, "ME: Bringup Loader Failure : %s\n",
+ hfs->ft_bup_ld_flr ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Firmware Init Complete : %s\n",
+ hfs->fw_init_complete ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Manufacturing Mode : %s\n",
+ hfs->mfg_mode ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Boot Options Present : %s\n",
+ hfs->boot_options_present ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Update In Progress : %s\n",
+ hfs->update_in_progress ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Current Working State : %s\n",
+ me_cws_values[hfs->working_state]);
+ printk(BIOS_DEBUG, "ME: Current Operation State : %s\n",
+ me_opstate_values[hfs->operation_state]);
+ printk(BIOS_DEBUG, "ME: Current Operation Mode : %s\n",
+ me_opmode_values[hfs->operation_mode]);
+ printk(BIOS_DEBUG, "ME: Error Code : %s\n",
+ me_error_values[hfs->error_code]);
+ printk(BIOS_DEBUG, "ME: Progress Phase : %s\n",
+ me_progress_values[gmes->progress_code]);
+ printk(BIOS_DEBUG, "ME: Power Management Event : %s\n",
+ me_pmevent_values[gmes->current_pmevent]);
+
+ printk(BIOS_DEBUG, "ME: Progress Phase State : ");
+ switch (gmes->progress_code) {
+ case ME_GMES_PHASE_ROM: /* ROM Phase */
+ printk(BIOS_DEBUG, "%s",
+ me_progress_rom_values[gmes->current_state]);
+ break;
+
+ case ME_GMES_PHASE_BUP: /* Bringup Phase */
+ if (gmes->current_state < ARRAY_SIZE(me_progress_bup_values)
+ && me_progress_bup_values[gmes->current_state])
+ printk(BIOS_DEBUG, "%s",
+ me_progress_bup_values[gmes->current_state]);
+ else
+ printk(BIOS_DEBUG, "0x%02x", gmes->current_state);
+ break;
+
+ case ME_GMES_PHASE_POLICY: /* Policy Module Phase */
+ if (gmes->current_state < ARRAY_SIZE(me_progress_policy_values)
+ && me_progress_policy_values[gmes->current_state])
+ printk(BIOS_DEBUG, "%s",
+ me_progress_policy_values[gmes->current_state]);
+ else
+ printk(BIOS_DEBUG, "0x%02x", gmes->current_state);
+ break;
+
+ case ME_GMES_PHASE_HOST: /* Host Communication Phase */
+ if (!gmes->current_state)
+ printk(BIOS_DEBUG, "Host communication established");
+ else
+ printk(BIOS_DEBUG, "0x%02x", gmes->current_state);
+ break;
+
+ default:
+ printk(BIOS_DEBUG, "Unknown 0x%02x", gmes->current_state);
+ }
+ printk(BIOS_DEBUG, "\n");
+#endif
+}
diff --git a/src/southbridge/intel/fsp_i89xx/nvs.h b/src/southbridge/intel/fsp_i89xx/nvs.h
new file mode 100644
index 0000000000..7eb487a0af
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/nvs.h
@@ -0,0 +1,159 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2011 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include "vendorcode/google/chromeos/gnvs.h"
+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) */
+ /* Thermal policy */
+ u8 tlvl; /* 0x11 - Throttle Level Limit */
+ u8 flvl; /* 0x12 - Current FAN Level */
+ u8 tcrt; /* 0x13 - Critical Threshold */
+ u8 tpsv; /* 0x14 - Passive Threshold */
+ u8 tmax; /* 0x15 - CPU Tj_max */
+ u8 f0of; /* 0x16 - FAN 0 OFF Threshold */
+ u8 f0on; /* 0x17 - FAN 0 ON Threshold */
+ u8 f0pw; /* 0x18 - FAN 0 PWM value */
+ u8 f1of; /* 0x19 - FAN 1 OFF Threshold */
+ u8 f1on; /* 0x1a - FAN 1 ON Threshold */
+ u8 f1pw; /* 0x1b - FAN 1 PWM value */
+ u8 f2of; /* 0x1c - FAN 2 OFF Threshold */
+ u8 f2on; /* 0x1d - FAN 2 ON Threshold */
+ u8 f2pw; /* 0x1e - FAN 2 PWM value */
+ u8 f3of; /* 0x1f - FAN 3 OFF Threshold */
+ u8 f3on; /* 0x20 - FAN 3 ON Threshold */
+ u8 f3pw; /* 0x21 - FAN 3 PWM value */
+ u8 f4of; /* 0x22 - FAN 4 OFF Threshold */
+ u8 f4on; /* 0x23 - FAN 4 ON Threshold */
+ u8 f4pw; /* 0x24 - FAN 4 PWM value */
+ u8 tmps; /* 0x25 - Temperature Sensor ID */
+ u8 rsvd3[2];
+ /* 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 pcnt; /* 0x2d - Processor Count */
+ u8 rsvd4[4];
+ /* Super I/O & CMOS config */
+ u8 natp; /* 0x32 - SIO type */
+ u8 s5u0; /* 0x33 - Enable USB0 in S5 */
+ u8 s5u1; /* 0x34 - Enable USB1 in S5 */
+ u8 s3u0; /* 0x35 - Enable USB0 in S3 */
+ u8 s3u1; /* 0x36 - Enable USB1 in S3 */
+ u8 s33g; /* 0x37 - Enable S3 in 3G */
+ u32 obsolete_cmem; /* 0x38 - CBMEM TOC */
+ /* 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];
+ /* Extended Mobile Access */
+ u8 emae; /* 0x78 - EMA enable */
+ u16 emap; /* 0x79 - EMA pointer */
+ u16 emal; /* 0x7a - EMA Length */
+ 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; /* 0xb8 - IGD boot type */
+ u8 ipat; /* 0xb9 - IGD panel type */
+ u8 itvf; /* 0xba - IGD TV format */
+ u8 itvm; /* 0xbb - IGD TV minor format */
+ u8 ipsc; /* 0xbc - IGD Panel Scaling */
+ u8 iblc; /* 0xbd - IGD BLC configuration */
+ u8 ibia; /* 0xbe - IGD BIA configuration */
+ u8 issc; /* 0xbf - IGD SSC configuration */
+ u8 i409; /* 0xc0 - IGD 0409 modified settings */
+ u8 i509; /* 0xc1 - IGD 0509 modified settings */
+ u8 i609; /* 0xc2 - IGD 0609 modified settings */
+ u8 i709; /* 0xc3 - IGD 0709 modified settings */
+ u8 idmm; /* 0xc4 - IGD Power Conservation */
+ u8 idms; /* 0xc5 - IGD DVMT memory size */
+ u8 if1e; /* 0xc6 - IGD Function 1 Enable */
+ u8 hvco; /* 0xc7 - IGD HPLL VCO */
+ u32 nxd[8]; /* 0xc8 - IGD next state DIDx for _DGS */
+ u8 isci; /* 0xe8 - IGD SMI/SCI mode (0: SCI) */
+ u8 pavp; /* 0xe9 - IGD PAVP data */
+ u8 rsvd12; /* 0xea - rsvd */
+ u8 oscc; /* 0xeb - PCIe OSC control */
+ u8 npce; /* 0xec - native pcie support */
+ u8 plfl; /* 0xed - platform flavor */
+ u8 brev; /* 0xee - board revision */
+ u8 dpbm; /* 0xef - digital port b mode */
+ u8 dpcm; /* 0xf0 - digital port c mode */
+ u8 dpdm; /* 0xf1 - digital port c mode */
+ u8 alfp; /* 0xf2 - active lfp */
+ u8 imon; /* 0xf3 - current graphics turbo imon value */
+ u8 mmio; /* 0xf4 - 64bit mmio support */
+ u8 rsvd13[11]; /* 0xf5 - rsvd */
+
+ /* ChromeOS specific (starts at 0x100)*/
+ chromeos_acpi_t chromeos;
+} __attribute__((packed)) global_nvs_t;
+
+#ifdef __SMM__
+/* Used in SMM to find the ACPI GNVS address */
+global_nvs_t *smm_get_gnvs(void);
+#endif
+void acpi_create_gnvs(global_nvs_t *gnvs);
diff --git a/src/southbridge/intel/fsp_i89xx/pch.c b/src/southbridge/intel/fsp_i89xx/pch.c
new file mode 100644
index 0000000000..cf3d061bfa
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/pch.c
@@ -0,0 +1,344 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2012 The Chromium OS Authors. All rights reserved.
+ * Copyright (C) 2013 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#include <console/console.h>
+#include <delay.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include "pch.h"
+
+static int pch_revision_id = -1;
+static int pch_type = -1;
+
+int pch_silicon_revision(void)
+{
+ if (pch_revision_id < 0)
+ pch_revision_id = pci_read_config8(
+ dev_find_slot(0, PCI_DEVFN(0x1f, 0)),
+ PCI_REVISION_ID);
+ return pch_revision_id;
+}
+
+int pch_silicon_type(void)
+{
+ if (pch_type < 0)
+ pch_type = pci_read_config8(
+ dev_find_slot(0, PCI_DEVFN(0x1f, 0)),
+ PCI_DEVICE_ID + 1);
+ return pch_type;
+}
+
+int pch_silicon_supported(int type, int rev)
+{
+ int cur_type = pch_silicon_type();
+ int cur_rev = pch_silicon_revision();
+
+ switch (type) {
+ case PCH_TYPE_CC:
+ if (cur_type == PCH_TYPE_CC && cur_rev >= rev)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Set bit in Function Disble register to hide this device */
+static void pch_hide_devfn(unsigned devfn)
+{
+ switch (devfn) {
+ case PCI_DEVFN(22, 0): /* MEI #1 */
+ RCBA32_OR(FD2, PCH_DISABLE_MEI1);
+ break;
+ case PCI_DEVFN(22, 1): /* MEI #2 */
+ RCBA32_OR(FD2, PCH_DISABLE_MEI2);
+ break;
+ case PCI_DEVFN(22, 2): /* IDE-R */
+ RCBA32_OR(FD2, PCH_DISABLE_IDER);
+ break;
+ case PCI_DEVFN(22, 3): /* KT */
+ RCBA32_OR(FD2, PCH_DISABLE_KT);
+ break;
+ case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */
+ case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */
+ case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */
+ case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */
+ RCBA32_OR(FD, PCH_DISABLE_PCIE(PCI_FUNC(devfn)));
+ break;
+ case PCI_DEVFN(29, 0): /* EHCI #1 */
+ RCBA32_OR(FD, PCH_DISABLE_EHCI1);
+ break;
+ case PCI_DEVFN(31, 0): /* LPC */
+ RCBA32_OR(FD, PCH_DISABLE_LPC);
+ break;
+ case PCI_DEVFN(31, 2): /* SATA #1 */
+ RCBA32_OR(FD, PCH_DISABLE_SATA1);
+ break;
+ case PCI_DEVFN(31, 3): /* SMBUS */
+ RCBA32_OR(FD, PCH_DISABLE_SMBUS);
+ break;
+ case PCI_DEVFN(31, 5): /* SATA #2 */
+ RCBA32_OR(FD, PCH_DISABLE_SATA2);
+ break;
+ case PCI_DEVFN(31, 6): /* Thermal Subsystem */
+ RCBA32_OR(FD, PCH_DISABLE_THERMAL);
+ break;
+ case PCI_DEVFN(31, 7): /* Watch Dog*/
+ /* No disable defined in datasheet */
+ break;
+ }
+}
+
+#define IOBP_RETRY 1000
+static inline int iobp_poll(void)
+{
+ unsigned try = IOBP_RETRY;
+ u32 data;
+
+ while (try--) {
+ data = RCBA32(IOBPS);
+ if ((data & 1) == 0)
+ return 1;
+ udelay(10);
+ }
+
+ printk(BIOS_ERR, "IOBP timeout\n");
+ return 0;
+}
+
+
+/* Check if any port in set X to X+3 is enabled */
+static int pch_pcie_check_set_enabled(device_t dev)
+{
+ device_t port;
+ int port_func;
+ int dev_func = PCI_FUNC(dev->path.pci.devfn);
+
+ printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev));
+
+ /* Go through static device tree list of devices
+ * because enumeration is still in progress */
+ for (port = all_devices; port; port = port->next) {
+ /* Only care about PCIe root ports */
+ if (PCI_SLOT(port->path.pci.devfn) !=
+ PCI_SLOT(dev->path.pci.devfn))
+ continue;
+
+ /* Check if port is in range and enabled */
+ port_func = PCI_FUNC(port->path.pci.devfn);
+ if (port_func >= dev_func &&
+ port_func < (dev_func + 4) &&
+ port->enabled)
+ return 1;
+ }
+
+ /* None of the ports in this set are enabled */
+ return 0;
+}
+
+/* RPFN is a write-once register so keep a copy until it is written */
+static u32 new_rpfn;
+
+/* Swap function numbers assigned to two PCIe Root Ports */
+static void pch_pcie_function_swap(u8 old_fn, u8 new_fn)
+{
+ u32 old_rpfn = new_rpfn;
+
+ printk(BIOS_DEBUG, "PCH: Remap PCIe function %d to %d\n",
+ old_fn, new_fn);
+
+ new_rpfn &= ~(RPFN_FNMASK(old_fn) | RPFN_FNMASK(new_fn));
+
+ /* Old function set to new function and disabled */
+ new_rpfn |= RPFN_FNSET(old_fn, RPFN_FNGET(old_rpfn, new_fn));
+ new_rpfn |= RPFN_FNSET(new_fn, RPFN_FNGET(old_rpfn, old_fn));
+}
+
+/* Update devicetree with new Root Port function number assignment */
+static void pch_pcie_devicetree_update(void)
+{
+ device_t dev;
+
+ /* Update the function numbers in the static devicetree */
+ for (dev = all_devices; dev; dev = dev->next) {
+ u8 new_devfn;
+
+ /* Only care about PCH PCIe root ports */
+ if (PCI_SLOT(dev->path.pci.devfn) !=
+ PCH_PCIE_DEV_SLOT)
+ continue;
+
+ /* Determine the new devfn for this port */
+ new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT,
+ RPFN_FNGET(new_rpfn,
+ PCI_FUNC(dev->path.pci.devfn)));
+
+ if (dev->path.pci.devfn != new_devfn) {
+ printk(BIOS_DEBUG,
+ "PCH: PCIe map %02x.%1x -> %02x.%1x\n",
+ PCI_SLOT(dev->path.pci.devfn),
+ PCI_FUNC(dev->path.pci.devfn),
+ PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
+
+ dev->path.pci.devfn = new_devfn;
+ }
+ }
+}
+
+/* Special handling for PCIe Root Port devices */
+static void pch_pcie_enable(device_t dev)
+{
+ struct southbridge_intel_fsp_i89xx_config *config = dev->chip_info;
+ u32 reg32;
+
+ /*
+ * Save a copy of the Root Port Function Number map when
+ * starting to walk the list of PCIe Root Ports so it can
+ * be updated locally and written out when the last port
+ * has been processed.
+ */
+ if (PCI_FUNC(dev->path.pci.devfn) == 0) {
+ new_rpfn = RCBA32(RPFN);
+
+ /*
+ * Enable Root Port coalescing if the first port is disabled
+ * or the other devices will not be enumerated by the OS.
+ */
+ if (!dev->enabled)
+ config->pcie_port_coalesce = 1;
+
+ if (config->pcie_port_coalesce)
+ printk(BIOS_INFO,
+ "PCH: PCIe Root Port coalescing is enabled\n");
+ }
+
+ if (!dev->enabled) {
+ printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
+
+ /*
+ * PCIE Power Savings
+ *
+ * If PCIe 0-3 disabled set Function 0 0xE2[0] = 1
+ * If PCIe 4-7 disabled set Function 4 0xE2[0] = 1
+ *
+ * This check is done here instead of pcie driver
+ * because the pcie driver enable() handler is not
+ * called unless the device is enabled.
+ */
+ if ((PCI_FUNC(dev->path.pci.devfn) == 0 ||
+ PCI_FUNC(dev->path.pci.devfn) == 4)) {
+ /* Handle workaround for PPT and CPT/B1+ */
+ if (pch_silicon_supported(PCH_TYPE_CC, PCH_STEP_A0) &&
+ !pch_pcie_check_set_enabled(dev)) {
+ u8 reg8 = pci_read_config8(dev, 0xe2);
+ reg8 |= 1;
+ pci_write_config8(dev, 0xe2, reg8);
+ }
+
+ /*
+ * Enable Clock Gating for shared PCIe resources
+ * before disabling this particular port.
+ */
+ pci_write_config8(dev, 0xe1, 0x3c);
+ }
+
+ /* Ensure memory, io, and bus master are all disabled */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 &= ~(PCI_COMMAND_MASTER |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* Do not claim downstream transactions for PCIe ports */
+ new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
+
+ /* Hide this device if possible */
+ pch_hide_devfn(dev->path.pci.devfn);
+ } else {
+ int fn;
+
+ /*
+ * Check if there is a lower disabled port to swap with this
+ * port in order to maintain linear order starting at zero.
+ */
+ if (config->pcie_port_coalesce) {
+ for (fn=0; fn < PCI_FUNC(dev->path.pci.devfn); fn++) {
+ if (!(new_rpfn & RPFN_HIDE(fn)))
+ continue;
+
+ /* Swap places with this function */
+ pch_pcie_function_swap(
+ PCI_FUNC(dev->path.pci.devfn), fn);
+ break;
+ }
+ }
+
+ /* Enable SERR */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_SERR;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+ }
+
+ /*
+ * When processing the last PCIe root port we can now
+ * update the Root Port Function Number and Hide register.
+ */
+ if (PCI_FUNC(dev->path.pci.devfn) == 7) {
+ printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
+ RCBA32(RPFN), new_rpfn);
+ RCBA32(RPFN) = new_rpfn;
+
+ /* Update static devictree with new function numbers */
+ if (config->pcie_port_coalesce)
+ pch_pcie_devicetree_update();
+ }
+}
+
+void pch_enable(device_t dev)
+{
+ u32 reg32;
+
+ /* PCH PCIe Root Ports get special handling */
+ if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT)
+ return pch_pcie_enable(dev);
+
+ if (!dev->enabled) {
+ printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
+
+ /* Ensure memory, io, and bus master are all disabled */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 &= ~(PCI_COMMAND_MASTER |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* Hide this device if possible */
+ pch_hide_devfn(dev->path.pci.devfn);
+ } else {
+ /* 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_fsp_i89xx_ops = {
+ CHIP_NAME("Intel Series 89xx (CaveCreek) Southbridge")
+ .enable_dev = pch_enable,
+};
diff --git a/src/southbridge/intel/fsp_i89xx/pch.h b/src/southbridge/intel/fsp_i89xx/pch.h
new file mode 100644
index 0000000000..4ef404436e
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/pch.h
@@ -0,0 +1,475 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2012 The Chromium OS Authors. All rights reserved.
+ * Copyright (C) 2013-2015 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#ifndef SOUTHBRIDGE_INTEL_I89XX_PCH_H
+#define SOUTHBRIDGE_INTEL_I89XX_PCH_H
+
+/* PCH types */
+#define PCH_TYPE_CC 0x23 /* CaveCreek */
+
+/* PCH stepping values for LPC device */
+#define PCH_STEP_A0 0
+#define PCH_STEP_A1 1
+#define PCH_STEP_B0 2
+#define PCH_STEP_B1 3
+#define PCH_STEP_B2 4
+#define PCH_STEP_B3 5
+
+/*
+ * 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 0x0500
+#define SMBUS_SLAVE_ADDR 0x24
+/* TODO Make sure these don't get changed by stage2 */
+#define DEFAULT_GPIOBASE 0x0480
+#define DEFAULT_PMBASE 0x0400
+
+#ifndef __ACPI__
+#define DEFAULT_RCBA ((u8 *)0xfed1c000)
+#else
+#define DEFAULT_RCBA 0xfed1c000
+#endif
+
+#ifndef __ACPI__
+#define DEBUG_PERIODIC_SMIS 0
+
+#if defined (__SMM__) && !defined(__ASSEMBLER__)
+void intel_pch_finalize_smm(void);
+#endif
+
+#if !defined(__ASSEMBLER__) && !defined(__ROMCC__)
+#if !defined(__PRE_RAM__) && !defined(__SMM__)
+#include "chip.h"
+int pch_silicon_revision(void);
+int pch_silicon_type(void);
+int pch_silicon_supported(int type, int rev);
+void pch_enable(device_t dev);
+#if CONFIG_ELOG
+void pch_log_state(void);
+#endif
+#else
+/* writes an address and one byte of data */
+s16 smbus_write_single_byte(u8 device, u8 address, u8 data);
+
+/* Sends an address and reads one byte of data */
+int smbus_read_byte(unsigned device, unsigned address);
+
+/* writes a single byte of data to smbus without an address byte */
+s16 smbus_quick_write(u8 device, u8 data);
+
+/* gets one byte of data from smbus without writing an address. */
+s16 smbus_quick_read(u8 device);
+
+void enable_smbus(void);
+void enable_usb_bar(void);
+int early_spi_read(u32 offset, u32 size, u8 *buffer);
+void sandybridge_sb_early_initialization(void);
+void early_pch_init(void);
+#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
+
+#define PCH_EHCI1_DEV PCI_DEV(0, 0x1d, 0)
+#define PCH_ME_DEV PCI_DEV(0, 0x16, 0)
+#define PCH_PCIE_DEV_SLOT 28
+
+/* PCI Configuration Space (D31:F0): LPC */
+#define PCH_LPC_DEV PCI_DEV(0, 0x1f, 0)
+#define SERIRQ_CNTL 0x64
+
+#define GEN_PMCON_1 0xa0
+#define GEN_PMCON_2 0xa2
+#define GEN_PMCON_3 0xa4
+#define ETR3 0xac
+#define ETR3_CWORWRE (1 << 18)
+#define ETR3_CF9GR (1 << 20)
+
+/* 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 BIOS_CNTL 0xDC
+#define GPIO_BASE 0x48 /* LPC GPIO Base Address Register */
+#define GPIO_CNTL 0x4C /* LPC GPIO Control Register */
+#define GPIO_ROUT 0xb8
+
+/* PIRQ[A-H] Routing registers */
+#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 */
+#define CNF2_LPC_EN (1 << 13) /* 0x4e/0x4f */
+#define CNF1_LPC_EN (1 << 12) /* 0x2e/0x2f */
+#define MC_LPC_EN (1 << 11) /* 0x62/0x66 */
+#define KBC_LPC_EN (1 << 10) /* 0x60/0x64 */
+#define GAMEH_LPC_EN (1 << 9) /* 0x208/0x20f */
+#define GAMEL_LPC_EN (1 << 8) /* 0x200/0x207 */
+#define FDD_LPC_EN (1 << 3) /* LPC_IO_DEC[12] */
+#define LPT_LPC_EN (1 << 2) /* LPC_IO_DEC[9:8] */
+#define COMB_LPC_EN (1 << 1) /* LPC_IO_DEC[6:4] */
+#define COMA_LPC_EN (1 << 0) /* LPC_IO_DEC[3:2] */
+#define LPC_GEN1_DEC 0x84 /* LPC IF Generic Decode Range 1 */
+#define LPC_GEN2_DEC 0x88 /* LPC IF Generic Decode Range 2 */
+#define LPC_GEN3_DEC 0x8c /* LPC IF Generic Decode Range 3 */
+#define LPC_GEN4_DEC 0x90 /* LPC IF Generic Decode Range 4 */
+
+/* PCI Configuration Space (D31:F2/5) */
+#define PCH_SATA_DEV PCI_DEV(0, 0x1f, 2)
+#define PCH_SATA2_DEV PCI_DEV(0, 0x1f, 5)
+#define INTR_LN 0x3c
+#define SATA_SIRI 0xa0 /* SATA Indexed Register Index */
+#define SATA_SIRD 0xa4 /* SATA Indexed Register Data */
+#define SATA_SP 0xd0 /* Scratchpad */
+
+/* SATA IOBP Registers */
+#define SATA_IOBP_SP0G3IR 0xea000151
+#define SATA_IOBP_SP1G3IR 0xea000051
+
+/* PCI Configuration Space (D31:F3): SMBus */
+#define PCH_SMBUS_DEV PCI_DEV(0, 0x1f, 3)
+#define SMB_BASE 0x20
+#define HOSTC 0x40
+#define SMB_RCV_SLVA 0x09
+
+/* 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 RCBA_AND_OR(bits, x, and, or) \
+ RCBA##bits(x) = ((RCBA##bits(x) & (and)) | (or))
+#define RCBA8_AND_OR(x, and, or) RCBA_AND_OR(8, x, and, or)
+#define RCBA16_AND_OR(x, and, or) RCBA_AND_OR(16, x, and, or)
+#define RCBA32_AND_OR(x, and, or) RCBA_AND_OR(32, x, and, or)
+#define RCBA32_OR(x, or) RCBA_AND_OR(32, x, ~0UL, or)
+
+#define V0CTL 0x0014 /* 32bit */
+#define V0STS 0x001a /* 16bit */
+
+#define V1CAP 0x001c /* 32bit */
+#define V1CTL 0x0020 /* 32bit */
+#define V1STS 0x0026 /* 16bit */
+
+#define RPC 0x0224 /* 32bit */
+#define RPFN 0x0238 /* 32bit */
+
+/* Root Port configuratinon space hide */
+#define RPFN_HIDE(port) (1 << (((port) * 4) + 3))
+/* Get the function number assigned to a Root Port */
+#define RPFN_FNGET(reg,port) (((reg) >> ((port) * 4)) & 7)
+/* Set the function number for a Root Port */
+#define RPFN_FNSET(port,func) (((func) & 7) << ((port) * 4))
+/* Root Port function number mask */
+#define RPFN_FNMASK(port) (7 << ((port) * 4))
+
+#define TRSR 0x1e00 /* 8bit */
+#define TRCR 0x1e10 /* 64bit */
+#define TWDR 0x1e18 /* 64bit */
+
+#define TCTL 0x3000 /* 8bit */
+
+/* IO Buffer Programming */
+#define IOBPIRI 0x2330
+#define IOBPD 0x2334
+#define IOBPS 0x2338
+#define IOBPS_RW_BX ((1 << 9)|(1 << 10))
+#define IOBPS_WRITE_AX ((1 << 9)|(1 << 10))
+#define IOBPS_READ_AX ((1 << 8)|(1 << 9)|(1 << 10))
+
+/*
+ * Interrupt Pin (IP) and Interrupt Route (IR)
+ * registers
+ */
+#define D31IP 0x3100 /* 32bit */
+#define D31IP_TTIP 24 /* Thermal Throttle Pin */
+#define D31IP_SIP2 20 /* SATA Pin 2 */
+#define D31IP_SMIP 12 /* SMBUS Pin */
+#define D31IP_SIP 8 /* SATA Pin */
+#define D29IP 0x3108 /* 32bit */
+#define D29IP_E1P 0 /* EHCI #1 Pin */
+#define D28IP 0x310c /* 32bit */
+#define D28IP_P4IP 12 /* PCI Express Port 4 */
+#define D28IP_P3IP 8 /* PCI Express Port 3 */
+#define D28IP_P2IP 4 /* PCI Express Port 2 */
+#define D28IP_P1IP 0 /* PCI Express Port 1 */
+#define D22IP 0x3124 /* 32bit */
+#define D22IP_KTIP 12 /* KT Pin */
+#define D22IP_IDERIP 8 /* IDE-R Pin */
+#define D22IP_MEI2IP 4 /* MEI #2 Pin */
+#define D22IP_MEI1IP 0 /* MEI #1 Pin */
+#define D31IR 0x3140 /* 16bit */
+#define D29IR 0x3144 /* 16bit */
+#define D28IR 0x3146 /* 16bit */
+#define D22IR 0x315c /* 16bit */
+#define OIC 0x31fe /* 16bit */
+#define SOFT_RESET_CTRL 0x38f4
+#define SOFT_RESET_DATA 0x38f8
+
+#if !defined(__ASSEMBLER__)
+#define NUM_IR_DEVS 32
+
+/* Certain device numbers have Interrupt registers in the RCBA */
+#define PCH_ALLOWED_IRQ_DEVNUM 0xB0400000 /* 31, 29, 28, 22 */
+
+/*
+ * Some devices have specific IRQ registers in
+ * the LPC for interrupt control
+ */
+struct dev_irq_regs {
+ uint32_t ip_reg; /* Interrupt Pin register */
+ uint32_t ir_reg; /* Interrupt Route register */
+};
+
+/*
+ * Certain PCH device numbers have specific IRQ registers
+ * associated with them (D(x)IP / D(x)IR) for Interrupt Pin
+ * and Interrupt Routing information.
+ */
+static const struct dev_irq_regs irq_regs[NUM_IR_DEVS] = {
+ [31] = {D31IP, D31IR},
+ [29] = {D29IP, D29IR},
+ [28] = {D28IP, D28IR},
+ [22] = {D22IP, D22IR},
+};
+#endif
+
+#define RC 0x3400 /* 32bit */
+#define HPTC 0x3404 /* 32bit */
+#define GCS 0x3410 /* 32bit */
+#define BUC 0x3414 /* 32bit */
+#define PCH_DISABLE_GBE (1 << 5)
+#define FD 0x3418 /* 32bit */
+#define DISPBDF 0x3424 /* 16bit */
+#define FD2 0x3428 /* 32bit */
+#define CG 0x341c /* 32bit */
+
+/* Function Disable 1 RCBA 0x3418 */
+#define PCH_DISABLE_ALWAYS 0
+#define PCH_DISABLE_SATA1 (1 << 2)
+#define PCH_DISABLE_SMBUS (1 << 3)
+#define PCH_DISABLE_LPC (1 << 14)
+#define PCH_DISABLE_EHCI1 (1 << 15)
+#define PCH_DISABLE_PCIE(x) (1 << (16 + x))
+#define PCH_DISABLE_THERMAL (1 << 24)
+#define PCH_DISABLE_SATA2 (1 << 25)
+
+/* Function Disable 2 RCBA 0x3428 */
+#define PCH_DISABLE_KT (1 << 4)
+#define PCH_DISABLE_IDER (1 << 3)
+#define PCH_DISABLE_MEI2 (1 << 2)
+#define PCH_DISABLE_MEI1 (1 << 1)
+
+/* ICH7 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
+#define GPIO_USE_SEL3 0x40
+#define GP_IO_SEL3 0x44
+#define GP_LVL3 0x48
+#define GP_RST_SEL1 0x60
+#define GP_RST_SEL2 0x64
+#define GP_RST_SEL3 0x68
+
+/* ICH7 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 SLP_TYP_S0 0
+#define SLP_TYP_S1 1
+#define SLP_TYP_S3 5
+#define SLP_TYP_S4 6
+#define SLP_TYP_S5 7
+#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 0x50 // mobile only
+#define GPE0_STS 0x20
+#define PME_B0_STS (1 << 13)
+#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 SWGPE_STS (1 << 2)
+#define HOT_PLUG_STS (1 << 1)
+#define GPE0_EN 0x28
+#define PME_B0_EN (1 << 13)
+#define PME_EN (1 << 11)
+#define TCOSCI_EN (1 << 6)
+#define SMI_EN 0x30
+#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 TCO1_STS 0x64
+#define DMISCI_STS (1 << 9)
+#define TCO2_STS 0x66
+
+/*
+ * SPI Opcode Menu setup for SPIBAR lockdown
+ * should support most common flash chips.
+ */
+
+#define SPI_OPMENU_0 0x01 /* WRSR: Write Status Register */
+#define SPI_OPTYPE_0 0x01 /* Write, no address */
+
+#define SPI_OPMENU_1 0x02 /* BYPR: Byte Program */
+#define SPI_OPTYPE_1 0x03 /* Write, address required */
+
+#define SPI_OPMENU_2 0x03 /* READ: Read Data */
+#define SPI_OPTYPE_2 0x02 /* Read, address required */
+
+#define SPI_OPMENU_3 0x05 /* RDSR: Read Status Register */
+#define SPI_OPTYPE_3 0x00 /* Read, no address */
+
+#define SPI_OPMENU_4 0x20 /* SE20: Sector Erase 0x20 */
+#define SPI_OPTYPE_4 0x03 /* Write, address required */
+
+#define SPI_OPMENU_5 0x9f /* RDID: Read ID */
+#define SPI_OPTYPE_5 0x00 /* Read, no address */
+
+#define SPI_OPMENU_6 0xd8 /* BED8: Block Erase 0xd8 */
+#define SPI_OPTYPE_6 0x03 /* Write, address required */
+
+#define SPI_OPMENU_7 0x0b /* FAST: Fast Read */
+#define SPI_OPTYPE_7 0x02 /* Read, address required */
+
+#define SPI_OPMENU_UPPER ((SPI_OPMENU_7 << 24) | (SPI_OPMENU_6 << 16) | \
+ (SPI_OPMENU_5 << 8) | SPI_OPMENU_4)
+#define SPI_OPMENU_LOWER ((SPI_OPMENU_3 << 24) | (SPI_OPMENU_2 << 16) | \
+ (SPI_OPMENU_1 << 8) | SPI_OPMENU_0)
+
+#define SPI_OPTYPE ((SPI_OPTYPE_7 << 14) | (SPI_OPTYPE_6 << 12) | \
+ (SPI_OPTYPE_5 << 10) | (SPI_OPTYPE_4 << 8) | \
+ (SPI_OPTYPE_3 << 6) | (SPI_OPTYPE_2 << 4) | \
+ (SPI_OPTYPE_1 << 2) | (SPI_OPTYPE_0))
+
+#define SPI_OPPREFIX ((0x50 << 8) | 0x06) /* EWSR and WREN */
+
+#define SPIBAR_HSFS 0x3804 /* SPI hardware sequence status */
+#define SPIBAR_HSFS_SCIP (1 << 5) /* SPI Cycle In Progress */
+#define SPIBAR_HSFS_AEL (1 << 2) /* SPI Access Error Log */
+#define SPIBAR_HSFS_FCERR (1 << 1) /* SPI Flash Cycle Error */
+#define SPIBAR_HSFS_FDONE (1 << 0) /* SPI Flash Cycle Done */
+#define SPIBAR_HSFC 0x3806 /* SPI hardware sequence control */
+#define SPIBAR_HSFC_BYTE_COUNT(c) (((c - 1) & 0x3f) << 8)
+#define SPIBAR_HSFC_CYCLE_READ (0 << 1) /* Read cycle */
+#define SPIBAR_HSFC_CYCLE_WRITE (2 << 1) /* Write cycle */
+#define SPIBAR_HSFC_CYCLE_ERASE (3 << 1) /* Erase cycle */
+#define SPIBAR_HSFC_GO (1 << 0) /* GO: start SPI transaction */
+#define SPIBAR_FADDR 0x3808 /* SPI flash address */
+#define SPIBAR_FDATA(n) (0x3810 + (4 * n)) /* SPI flash data */
+
+#endif /* __ACPI__ */
+#endif /* SOUTHBRIDGE_INTEL_I89XX_PCH_H */
diff --git a/src/southbridge/intel/fsp_i89xx/reset.c b/src/southbridge/intel/fsp_i89xx/reset.c
new file mode 100644
index 0000000000..8fe36f8297
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/reset.c
@@ -0,0 +1,33 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2013 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#include <arch/io.h>
+#include <reset.h>
+
+void soft_reset(void)
+{
+ outb(0x04, 0xcf9);
+}
+
+void hard_reset(void)
+{
+ outb(0x06, 0xcf9);
+}
diff --git a/src/southbridge/intel/fsp_i89xx/romstage.c b/src/southbridge/intel/fsp_i89xx/romstage.c
new file mode 100644
index 0000000000..3eb6670bc7
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/romstage.c
@@ -0,0 +1,228 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2007-2010 coresystems GmbH
+ * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved.
+ * Copyright (C) 2013-2014 Sage Electronic Engineering, LLC.
+ *
+ * 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
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <lib.h>
+#include <timestamp.h>
+#include <arch/cpu.h>
+#include <arch/io.h>
+#include <arch/stages.h>
+#include <device/pci_def.h>
+#include <device/pnp_def.h>
+#include <cpu/x86/lapic.h>
+#include <cpu/x86/msr.h>
+#include <pc80/mc146818rtc.h>
+#include <cbmem.h>
+#include <console/console.h>
+#include <console/usb.h>
+#include <halt.h>
+#include <reset.h>
+#include <drivers/intel/fsp1_0/fsp_util.h>
+#include <northbridge/intel/fsp_sandybridge/northbridge.h>
+#include <northbridge/intel/fsp_sandybridge/raminit.h>
+#include <superio/winbond/wpcd376i/wpcd376i.h>
+#include <superio/intel/i8900/i8900.h>
+#include "gpio.h"
+#include "me.h"
+#include "pch.h"
+#include "romstage.h"
+
+static inline void reset_system(void)
+{
+ hard_reset();
+ halt();
+}
+
+static void pch_enable_lpc(void)
+{
+ device_t dev = PCH_LPC_DEV;
+
+ /* Set COM1/COM2 decode range */
+ pci_write_config16(dev, LPC_IO_DEC, 0x0010);
+
+ /* Enable Board specific settings */
+ uint16_t lpc_config = get_lpc_setting();
+ pci_write_config16(dev, LPC_EN, lpc_config);
+}
+
+static void rcba_config(void)
+{
+ uint32_t reg32;
+ uint32_t fd_mask = 0, fd2_mask = 0;
+
+ /* Enable IOAPIC (generic) */
+ RCBA16(OIC) = 0x0100;
+ /* PCH BWG says to read back the IOAPIC enable register */
+ (void) RCBA16(OIC);
+
+ /* Board specific settings */
+ get_func_disables(&fd_mask, &fd2_mask);
+ reg32 = RCBA32(FD);
+ printk(BIOS_DEBUG, "FD original value: 0x%08lx\n",(unsigned long)reg32);
+
+ reg32 |= fd_mask;
+
+ printk(BIOS_DEBUG, "FD updated value: 0x%08lx\n",(unsigned long)reg32);
+ RCBA32(FD) = reg32;
+
+ reg32 = RCBA32(FD2);
+ printk(BIOS_DEBUG, "FD2 original value: 0x%08lx\n",(unsigned long)reg32);
+
+ reg32 |= fd2_mask;
+
+ printk(BIOS_DEBUG, "FD2 updated value: 0x%08lx\n",(unsigned long)reg32);
+ RCBA32(FD2) = reg32;
+}
+
+void main(FSP_INFO_HEADER *fsp_info_header)
+{
+#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
+ int32_t boot_mode = 0;
+#endif
+ uint32_t pm1_cnt;
+ uint16_t pm1_sts;
+
+ post_code(0x40);
+
+#if IS_ENABLED(CONFIG_COLLECT_TIMESTAMPS)
+ save_timestamp_to_cmos(CMOS_MAIN_START_ADDR, rdtsc());
+#endif
+ pch_enable_lpc();
+
+ /* Enable GPIOs */
+ pci_write_config32(PCH_LPC_DEV, GPIO_BASE, DEFAULT_GPIOBASE | 1);
+ pci_write_config8(PCH_LPC_DEV, GPIO_CNTL, 0x10);
+
+ /* Call into mainboard. */
+ early_mainboard_romstage_entry();
+
+ console_init();
+
+ post_code(0x41);
+
+ post_code(0x42);
+ sandybridge_sb_early_initialization();
+
+ post_code(0x43);
+ sandybridge_early_initialization(SANDYBRIDGE_MOBILE);
+ printk(BIOS_DEBUG, "Back from sandybridge_early_initialization()\n");
+
+ post_code(0x44);
+ /* Wait for ME to be ready */
+ intel_early_me_status();
+ intel_early_me_init();
+#if IS_ENABLED(CONFIG_GFXUMA)
+ intel_early_me_uma_size();
+#endif
+ post_code(0x45);
+ /* Check PM1_STS[15] to see if we are waking from Sx */
+ pm1_sts = inw(DEFAULT_PMBASE + PM1_STS);
+
+ /* Read PM1_CNT[12:10] to determine which Sx state */
+ pm1_cnt = inl(DEFAULT_PMBASE + PM1_CNT);
+ post_code(0x46);
+ if ((pm1_sts & WAK_STS) && ((pm1_cnt >> 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(pm1_cnt & ~(7 << 10), DEFAULT_PMBASE + PM1_CNT);
+#else
+ printk(BIOS_DEBUG, "Resume from S3 detected, but disabled.\n");
+#endif
+ }
+
+ post_code(0x48);
+
+#if IS_ENABLED(CONFIG_COLLECT_TIMESTAMPS)
+ save_timestamp_to_cmos(CMOS_PRE_INITRAM_ADDR, rdtsc());
+#endif
+
+ /*
+ * Call early init to initialize memory and chipset. This function returns
+ * to the romstage_main_continue function with a pointer to the HOB
+ * structure.
+ */
+ printk(BIOS_DEBUG, "Starting the Intel FSP (early_init)\n");
+ fsp_early_init(fsp_info_header);
+ die("Uh Oh! fsp_early_init should not return here.\n");
+}
+
+/*******************************************************************************
+ * The FSP early_init function returns to this function.
+ * Memory is setup and the stack is set by the FSP.
+ ******************************************************************************/
+void romstage_main_continue(EFI_STATUS status, VOID *HobListPtr) {
+ int32_t cbmem_was_initted;
+ void *cbmem_hob_ptr;
+
+ timestamp_add_now(TS_AFTER_INITRAM);
+
+ post_code(0x49);
+
+ /* For reference print FSP version */
+ uint32_t version = MCHBAR32(0x5034);
+ printk(BIOS_DEBUG, "FSP Version %d.%d.%d Build %d\n",
+ version >> 24 , (version >> 16) & 0xff,
+ (version >> 8) & 0xff, version & 0xff);
+ printk(BIOS_DEBUG, "FSP Status: 0x%0x\n", (uint32_t)status);
+
+ intel_early_me_init_done(ME_INIT_STATUS_SUCCESS);
+
+ report_memory_config();
+
+ post_code(0x4b);
+
+ late_mainboard_romstage_entry();
+
+ early_pch_init();
+ post_code(0x4c);
+
+ rcba_config();
+ post_code(0x4d);
+
+ quick_ram_check();
+ post_code(0x4e);
+
+ cbmem_was_initted = !cbmem_recovery(0);
+
+ if(cbmem_was_initted) {
+ reset_system();
+ }
+
+ /* Save the HOB pointer in CBMEM to be used in ramstage. */
+ cbmem_hob_ptr = cbmem_add (CBMEM_ID_HOB_POINTER, sizeof(HobListPtr));
+ *(uint32_t*)cbmem_hob_ptr = (uint32_t)HobListPtr;
+ post_code(0x4f);
+
+ timestamp_add_now(TS_END_ROMSTAGE);
+
+ copy_and_run();
+}
+
+uint64_t get_initial_timestamp(void)
+{
+ return (uint64_t) pci_read_config32(PCI_DEV(0, 0x1f, 2), 0xd0) << 4;
+}
+
diff --git a/src/southbridge/intel/fsp_i89xx/romstage.h b/src/southbridge/intel/fsp_i89xx/romstage.h
new file mode 100644
index 0000000000..4296fa7ec6
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/romstage.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#ifndef _FSP_I89XX_ROMSTAGE_H_
+#define _FSP_I89XX_ROMSTAGE_H_
+
+#if !defined(__PRE_RAM__)
+#error "Don't include romstage.h from a ramstage compilation unit!"
+#endif
+
+#include <stdint.h>
+#include <arch/cpu.h>
+
+void early_mainboard_romstage_entry(void);
+void late_mainboard_romstage_entry(void);
+
+void get_func_disables(uint32_t *fd_mask, uint32_t *fd2_mask);
+uint16_t get_lpc_setting(void);
+
+#endif /* _FSP_I89XX_ROMSTAGE_H_ */
diff --git a/src/southbridge/intel/fsp_i89xx/sata.c b/src/southbridge/intel/fsp_i89xx/sata.c
new file mode 100644
index 0000000000..955ac4583e
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/sata.c
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2013 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include "pch.h"
+
+typedef struct southbridge_intel_fsp_i89xx_config config_t;
+
+static void sata_init(struct device *dev)
+{
+ u32 reg32;
+ u16 reg16;
+ /* Get the chip configuration */
+ config_t *config = dev->chip_info;
+
+ printk(BIOS_DEBUG, "SATA: Initializing...\n");
+
+ if (config == NULL) {
+ printk(BIOS_ERR, "SATA: ERROR: Device not in devicetree.cb!\n");
+ return;
+ }
+
+ /* SATA configuration is handled by the FSP */
+
+ /* 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);
+ } else if(config->sata_ahci) {
+ u32 *abar;
+
+ printk(BIOS_DEBUG, "SATA: Controller in AHCI mode.\n");
+
+ /* Set Interrupt Line */
+ /* Interrupt Pin is set by D31IP.PIP */
+ pci_write_config8(dev, INTR_LN, 0x0a);
+
+ /* Initialize AHCI memory-mapped space */
+ abar = (u32 *)pci_read_config32(dev, PCI_BASE_ADDRESS_5);
+ printk(BIOS_DEBUG, "ABAR: %p\n", abar);
+ /* Enable AHCI Mode */
+ reg32 = read32(abar + 0x04);
+ reg32 |= (1 << 31);
+ write32(abar + 0x04, reg32);
+ } else {
+ printk(BIOS_DEBUG, "SATA: Controller in plain mode.\n");
+
+ /* Set Interrupt Line */
+ /* Interrupt Pin is set by D31IP.PIP */
+ pci_write_config8(dev, INTR_LN, 0xff);
+ }
+
+}
+
+static void sata_enable(device_t dev)
+{
+}
+
+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,
+ .enable = sata_enable,
+ .scan_bus = 0,
+ .ops_pci = &sata_pci_ops,
+};
+
+static const unsigned short pci_device_ids[] = { 0x2321, 0x2323, 0x2326, 0 };
+
+static const struct pci_driver pch_sata __pci_driver = {
+ .ops = &sata_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .devices = pci_device_ids,
+};
diff --git a/src/southbridge/intel/fsp_i89xx/smbus.h b/src/southbridge/intel/fsp_i89xx/smbus.h
new file mode 100644
index 0000000000..d16053d20e
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/smbus.h
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2005 Yinghai Lu <yinghailu@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.
+ */
+
+#include <device/smbus_def.h>
+#include "pch.h"
+#include <delay.h>
+
+static int smbus_wait_until_ready(u16 smbus_base)
+{
+ unsigned loops = SMBUS_TIMEOUT;
+ unsigned char byte;
+ do {
+ udelay(1);
+ 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 {
+ udelay(1);
+ 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/fsp_i89xx/smi.c b/src/southbridge/intel/fsp_i89xx/smi.c
new file mode 100644
index 0000000000..429bffb54b
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/smi.c
@@ -0,0 +1,351 @@
+/*
+ * 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.
+ */
+
+
+#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 <cpu/intel/smm/gen1/smi.h>
+#include "pch.h"
+
+/* 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);
+}
+
+void southbridge_smm_init(void)
+{
+ u32 smi_en;
+ u16 pm1_en;
+ u32 gpe0_en;
+
+#if CONFIG_ELOG
+ /* Log events from chipset before clearing */
+ pch_log_state();
+#endif
+
+ printk(BIOS_DEBUG, "Initializing southbridge SMI...");
+
+ pmbase = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x1f, 0)),
+ PMBASE) & 0xff80;
+
+ 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;
+ }
+
+ 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());
+
+ /* Disable GPE0 PME_B0 */
+ gpe0_en = inl(pmbase + GPE0_EN);
+ gpe0_en &= ~PME_B0_EN;
+ outl(gpe0_en, pmbase + GPE0_EN);
+
+ pm1_en = 0;
+ pm1_en |= PWRBTN_EN;
+ pm1_en |= GBL_EN;
+ outw(pm1_en, pmbase + PM1_EN);
+
+ /* 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 pch.h to debug using
+ * periodic SMIs.
+ */
+ smi_en |= PERIODIC_EN;
+#endif
+ smi_en |= SLP_SMI_EN;
+#if 0
+ smi_en |= BIOS_EN;
+#endif
+
+ /* The following need to be on for SMIs to happen */
+ smi_en |= EOS | GBL_SMI_EN;
+
+ outl(smi_en, pmbase + SMI_EN);
+}
+
+void southbridge_trigger_smi(void)
+{
+ /**
+ * 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);
+}
+
+void southbridge_clear_smi_status(void)
+{
+ /* Clear SMI status */
+ reset_smi_status();
+
+ /* Clear PM1 status */
+ reset_pm1_status();
+
+ /* Set EOS bit so other SMIs can occur. */
+ smi_set_eos();
+}
+
+void smm_setup_structures(void *gnvs, void *tcg, void *smi1)
+{
+ /*
+ * Issue SMI to set the gnvs pointer in SMM.
+ * tcg and smi1 are unused.
+ *
+ * EAX = APM_CNT_GNVS_UPDATE
+ * EBX = gnvs pointer
+ * EDX = APM_CNT
+ */
+ asm volatile (
+ "outb %%al, %%dx\n\t"
+ : /* ignore result */
+ : "a" (APM_CNT_GNVS_UPDATE),
+ "b" ((u32)gnvs),
+ "d" (APM_CNT)
+ );
+}
diff --git a/src/southbridge/intel/fsp_i89xx/smihandler.c b/src/southbridge/intel/fsp_i89xx/smihandler.c
new file mode 100644
index 0000000000..b2cd07bc52
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/smihandler.c
@@ -0,0 +1,757 @@
+/*
+ * 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.
+ */
+
+#include <types.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <cpu/x86/cache.h>
+#include <device/pci_def.h>
+#include <cpu/x86/smm.h>
+#include <elog.h>
+#include <halt.h>
+#include <pc80/mc146818rtc.h>
+#include "pch.h"
+
+#include "nvs.h"
+
+/* 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 <northbridge/intel/fsp_sandybridge/northbridge.h>
+
+/* While we read PMBASE dynamically in case it changed, let's
+ * initialize it with a sane value
+ */
+static u16 pmbase = DEFAULT_PMBASE;
+u16 smm_get_pmbase(void)
+{
+ return pmbase;
+}
+
+static u8 smm_initialized = 0;
+
+/* GNVS needs to be updated by an 0xEA PM Trap (B2) after it has been located
+ * by coreboot.
+ */
+static global_nvs_t *gnvs;
+global_nvs_t *smm_get_gnvs(void)
+{
+ return gnvs;
+}
+
+/**
+ * @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 << 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, "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, "SWGPE ");
+ if (gpe0_sts & (1 << 1)) printk(BIOS_DEBUG, "HOTPLUG ");
+ 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");
+}
+
+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);
+ }
+ }
+ }
+}
+
+/*
+ * Drive GPIO 60 low to gate memory reset in S3.
+ *
+ * Intel reference designs all use GPIO 60 but it is
+ * not a requirement and boards could use a different pin.
+ */
+static void southbridge_gate_memory_reset(void)
+{
+ u32 reg32;
+ u16 gpiobase;
+
+ gpiobase = pcie_read_config16(PCI_DEV(0, 0x1f, 0), GPIOBASE) & 0xfffc;
+ if (!gpiobase)
+ return;
+
+ /* Make sure it is set as GPIO */
+ reg32 = inl(gpiobase + GPIO_USE_SEL2);
+ if (!(reg32 & (1 << 28))) {
+ reg32 |= (1 << 28);
+ outl(reg32, gpiobase + GPIO_USE_SEL2);
+ }
+
+ /* Make sure it is set as output */
+ reg32 = inl(gpiobase + GP_IO_SEL2);
+ if (reg32 & (1 << 28)) {
+ reg32 &= ~(1 << 28);
+ outl(reg32, gpiobase + GP_IO_SEL2);
+ }
+
+ /* Drive the output low */
+ reg32 = inl(gpiobase + GP_LVL2);
+ reg32 &= ~(1 << 28);
+ outl(reg32, gpiobase + GP_LVL2);
+}
+
+static void southbridge_smi_sleep(void)
+{
+ 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;
+
+ /* Do any mainboard sleep handling */
+ mainboard_smi_sleep(slp_typ-2);
+
+#if CONFIG_ELOG_GSMI
+ /* Log S3, S4, and S5 entry */
+ if (slp_typ >= 5)
+ elog_add_event_byte(ELOG_TYPE_ACPI_ENTER, slp_typ-2);
+#endif
+
+ /* 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");
+
+ /* Gate memory reset */
+ southbridge_gate_memory_reset();
+
+ /* 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 = pcie_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3);
+ if (s5pwr == MAINBOARD_POWER_ON) {
+ reg8 &= ~1;
+ } else {
+ reg8 |= 1;
+ }
+ pcie_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;
+ }
+
+ /* 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)
+ halt();
+
+ /* 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);
+ }
+}
+
+/*
+ * Look for Synchronous IO SMI and use save state from that
+ * core in case we are not running on the same core that
+ * initiated the IO transaction.
+ */
+static em64t101_smm_state_save_area_t *smi_apmc_find_state_save(u8 cmd)
+{
+ em64t101_smm_state_save_area_t *state;
+ int node;
+
+ /* Check all nodes looking for the one that issued the IO */
+ for (node = 0; node < CONFIG_MAX_CPUS; node++) {
+ state = smm_get_save_state(node);
+
+ /* Check for Synchronous IO (bit0==1) */
+ if (!(state->io_misc_info & (1 << 0)))
+ continue;
+
+ /* Make sure it was a write (bit4==0) */
+ if (state->io_misc_info & (1 << 4))
+ continue;
+
+ /* Check for APMC IO port */
+ if (((state->io_misc_info >> 16) & 0xff) != APM_CNT)
+ continue;
+
+ /* Check AX against the requested command */
+ if ((state->rax & 0xff) != cmd)
+ continue;
+
+ return state;
+ }
+
+ return NULL;
+}
+
+#if CONFIG_ELOG_GSMI
+static void southbridge_smi_gsmi(void)
+{
+ u32 *ret, *param;
+ u8 sub_command;
+ em64t101_smm_state_save_area_t *io_smi =
+ smi_apmc_find_state_save(ELOG_GSMI_APM_CNT);
+
+ if (!io_smi)
+ return;
+
+ /* Command and return value in EAX */
+ ret = (u32*)&io_smi->rax;
+ sub_command = (u8)(*ret >> 8);
+
+ /* Parameter buffer in EBX */
+ param = (u32*)&io_smi->rbx;
+
+ /* drivers/elog/gsmi.c */
+ *ret = gsmi_exec(sub_command, param);
+}
+#endif
+
+static void southbridge_smi_apmc(void)
+{
+ u32 pmctrl;
+ u8 reg8;
+ em64t101_smm_state_save_area_t *state;
+
+ /* Emulate B2 register as the FADT / Linux expects it */
+
+ reg8 = inb(APM_CNT);
+ 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;
+ }
+ state = smi_apmc_find_state_save(reg8);
+ if (state) {
+ /* EBX in the state save contains the GNVS pointer */
+ gnvs = (global_nvs_t *)((u32)state->rbx);
+ smm_initialized = 1;
+ printk(BIOS_DEBUG, "SMI#: Setting GNVS to %p\n", gnvs);
+ }
+ break;
+#if CONFIG_ELOG_GSMI
+ case ELOG_GSMI_APM_CNT:
+ southbridge_smi_gsmi();
+ break;
+#endif
+ }
+
+ mainboard_smi_apmc(reg8);
+}
+
+static void southbridge_smi_pm1(void)
+{
+ u16 pm1_sts;
+
+ 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);
+#if CONFIG_ELOG_GSMI
+ elog_add_event(ELOG_TYPE_POWER_BUTTON);
+#endif
+ outl(reg32, pmbase + PM1_CNT);
+ }
+}
+
+static void southbridge_smi_gpe0(void)
+{
+ u32 gpe0_sts;
+
+ gpe0_sts = reset_gpe0_status();
+ dump_gpe0_status(gpe0_sts);
+}
+
+static void southbridge_smi_gpi(void)
+{
+ u16 reg16;
+ reg16 = inw(pmbase + ALT_GP_SMI_STS);
+ outw(reg16, pmbase + ALT_GP_SMI_STS);
+
+ reg16 &= inw(pmbase + ALT_GP_SMI_EN);
+
+ mainboard_smi_gpi(reg16);
+
+ if (reg16)
+ printk(BIOS_DEBUG, "GPI (mask %04x)\n",reg16);
+
+ outw(reg16, pmbase + ALT_GP_SMI_STS);
+}
+
+static void southbridge_smi_mc(void)
+{
+ 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(void)
+{
+ 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 = pcie_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");
+ pcie_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(void)
+{
+ 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(void)
+{
+#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)(void);
+
+static 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#
+ */
+
+void southbridge_smi_handler(void)
+{
+ int i, dump = 0;
+ u32 smi_sts;
+
+ /* Update global variable pmbase */
+ pmbase = pcie_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();
+
+ /* 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]();
+ } 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/fsp_i89xx/southbridge_pci_devs.h b/src/southbridge/intel/fsp_i89xx/southbridge_pci_devs.h
new file mode 100644
index 0000000000..557532a940
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/southbridge_pci_devs.h
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google Inc.
+ * Copyright (C) 2014 Sage Electronic Engineering, LLC.
+ *
+ * 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.
+ */
+
+#ifndef _INTEL_FSP_BD82X6X_PCI_DEVS_H_
+#define _INTEL_FSP_BD82X6X_PCI_DEVS_H_
+
+#include <device/pci_def.h>
+
+#define BUS0 0
+
+/* Management Engine Interface */
+#define MEI_DEV 0x16
+#define MEI1_FUNC 0x0
+# define MEI1_DEVFN PCI_DEVFN(MEI_DEV, MEI1_FUNC)
+
+#define MEI2_FUNC 0x1
+# define MEI2_DEVFN PCI_DEVFN(MEI_DEV, MEI2_FUNC)
+
+/* MEI - IDE Redirection */
+#define IDE_DEV 0x16
+#define IDE_FUNC 2
+# define IDE_DEVFN PCI_DEVFN(IDE_DEV, IDE_FUNC)
+
+/* MEI - Keyboard / Text Redirection */
+#define KT_DEV 0x16
+#define KT_FUNC 0x3
+#define KT_DEVFN PCI_DEVFN(KT_DEV, KT_FUNC)
+
+/* PCIe Ports */
+#define SB_PCIE_DEV 0x1C
+#define SB_PCIE_PORT1_FUNC 0
+# define SB_PCIE_PORT1_DEVFN PCI_DEVFN(SB_PCIE_DEV, SB_PCIE_PORT1_FUNC)
+
+#define SB_PCIE_PORT2_FUNC 1
+# define SB_PCIE_PORT2_DEVFN PCI_DEVFN(SB_PCIE_DEV, SB_PCIE_PORT2_FUNC)
+
+#define SB_PCIE_PORT3_FUNC 2
+# define SB_PCIE_PORT3_DEVFN PCI_DEVFN(SB_PCIE_DEV, SB_PCIE_PORT3_FUNC)
+
+#define SB_PCIE_PORT4_FUNC 3
+# define SB_PCIE_PORT4_DEVFN PCI_DEVFN(SB_PCIE_DEV, SB_PCIE_PORT4_FUNC)
+
+/* EHCI */
+#define EHCI1_DEV 0x1D
+#define EHCI1_FUNC 0
+#define EHCI1_DEVID 0x2334
+# define EHCI1_DEVFN PCI_DEVFN(EHCI1_DEV, EHCI1_FUNC)
+
+/* Platform Controller Hub */
+#define PCH_DEV 0x1F
+
+/* LPC */
+#define LPC_DEV PCH_DEV
+#define LPC_FUNC 0
+# define LPC_DEVFN PCI_DEVFN(LPC_DEV, LPC_FUNC)
+
+/* SATA */
+#define SATA_DEV PCH_DEV
+#define SATA1_FUNC 2
+# define SATA1_DEVFN PCI_DEVFN(SATA_DEV, SATA1_FUNC)
+
+/* SMBUS */
+#define SMBUS_DEV PCH_DEV
+#define SMBUS_FUNC 3
+# define SMBUS_DEVFN PCI_DEVFN(SMBUS_DEV, SMBUS_FUNC)
+
+#define SATA2_FUNC 5
+# define SATA2_DEVFN PCI_DEVFN(SATA_DEV, SATA2_FUNC)
+
+/* Thermal Sensor */
+#define TS_DEV PCH_DEV
+#define TS_FUNC 6
+# define TS_DEVFN PCI_DEVFN(TS_DEV, TS_FUNC)
+
+/* Watchdog Timer */
+#define WDT_DEV PCH_DEV
+#define WDT TS_FUNC 7
+# define WDT_DEVFN PCI_DEVFN(WDT_DEV, WDT_FUNC)
+
+#endif /* _INTEL_FSP_BD82X6X_PCI_DEVS_H_ */
diff --git a/src/southbridge/intel/fsp_i89xx/watchdog.c b/src/southbridge/intel/fsp_i89xx/watchdog.c
new file mode 100644
index 0000000000..91bc615003
--- /dev/null
+++ b/src/southbridge/intel/fsp_i89xx/watchdog.c
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2011 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc.
+ */
+
+#include <console/console.h>
+#include <arch/io.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <watchdog.h>
+
+ //
+ // Disable PCH Watchdog timer at SB_RCBA+0x3410
+ //
+ // Mmio32((MmPci32(0, 0, 0x1F, 0, 0xF0) & ~BIT0), 0x3410) |= 0x20;
+ //
+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, "PCH watchdog disabled\n");
+}