diff options
42 files changed, 8867 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, ®32, 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, ®32, 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"); +} diff --git a/src/vendorcode/intel/Kconfig b/src/vendorcode/intel/Kconfig index a000d99c14..1f9c930718 100644 --- a/src/vendorcode/intel/Kconfig +++ b/src/vendorcode/intel/Kconfig @@ -16,6 +16,7 @@ config FSP_VENDORCODE_HEADER_PATH string default "fsp1_0/ivybridge_bd82x6x" if CPU_INTEL_FSP_MODEL_306AX && SOUTHBRIDGE_INTEL_FSP_BD82X6X + default "fsp1_0/ivybridge_i89xx/" if CPU_INTEL_FSP_MODEL_306AX && SOUTHBRIDGE_INTEL_FSP_I89XX default "fsp1_0/baytrail" if SOC_INTEL_FSP_BAYTRAIL default "fsp1_0/rangeley" if CPU_INTEL_FSP_MODEL_406DX |