diff options
author | Vladimir Serbinenko <phcoder@gmail.com> | 2013-11-13 17:53:38 +0100 |
---|---|---|
committer | Vladimir Serbinenko <phcoder@gmail.com> | 2013-11-25 19:32:36 +0100 |
commit | 888d559b0373cb956314b8087439378c14a16918 (patch) | |
tree | 1887a30b1674c05276dc21164a4926441b812de7 /src/southbridge/intel | |
parent | cae09e0bbe61417667685c93cccc761c7d727daf (diff) |
Support for Ibexpeak southbridge
Part of X201 port.
Change-Id: If17d707004aba9f08459dbd8f3a146fa3c076aa9
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
Reviewed-on: http://review.coreboot.org/4052
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/southbridge/intel')
20 files changed, 6208 insertions, 0 deletions
diff --git a/src/southbridge/intel/Kconfig b/src/southbridge/intel/Kconfig index 788d3e190e..343d5a3d84 100644 --- a/src/southbridge/intel/Kconfig +++ b/src/southbridge/intel/Kconfig @@ -13,4 +13,5 @@ source src/southbridge/intel/i82870/Kconfig source src/southbridge/intel/pxhd/Kconfig source src/southbridge/intel/sch/Kconfig source src/southbridge/intel/bd82x6x/Kconfig +source src/southbridge/intel/ibexpeak/Kconfig source src/southbridge/intel/lynxpoint/Kconfig diff --git a/src/southbridge/intel/Makefile.inc b/src/southbridge/intel/Makefile.inc index 8f48caafc4..b3890585a0 100644 --- a/src/southbridge/intel/Makefile.inc +++ b/src/southbridge/intel/Makefile.inc @@ -14,4 +14,5 @@ subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_PXHD) += pxhd subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_SCH) += sch subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_BD82X6X) += bd82x6x subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_C216) += bd82x6x +subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_IBEXPEAK) += ibexpeak subdirs-$(CONFIG_SOUTHBRIDGE_INTEL_LYNXPOINT) += lynxpoint diff --git a/src/southbridge/intel/ibexpeak/Kconfig b/src/southbridge/intel/ibexpeak/Kconfig new file mode 100644 index 0000000000..c408dca972 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/Kconfig @@ -0,0 +1,123 @@ +## +## 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +config SOUTHBRIDGE_INTEL_IBEXPEAK + bool + +if SOUTHBRIDGE_INTEL_IBEXPEAK + +config SOUTH_BRIDGE_OPTIONS # dummy + def_bool y + select IOAPIC + select HAVE_HARD_RESET + select HAVE_USBDEBUG + select HAVE_SMI_HANDLER + select USE_WATCHDOG_ON_BOOT + select PCIEXP_ASPM + select PCIEXP_COMMON_CLOCK + select SPI_FLASH + select SOUTHBRIDGE_INTEL_COMMON + +config EHCI_BAR + hex + default 0xfef00000 + +config EHCI_DEBUG_OFFSET + hex + default 0xa0 + +config BOOTBLOCK_SOUTHBRIDGE_INIT + string + default "southbridge/intel/bd82x6x/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 BUILD_WITH_FAKE_IFD + bool "Build with a fake IFD" + default y if !HAVE_IFD_BIN + help + If you don't have an Intel Firmware Descriptor (ifd.bin) for your + board, you can select this option and coreboot will build without it. + Though, the resulting coreboot.rom will not contain all parts required + to get coreboot running on your board. You can however write only the + BIOS section to your board's flash ROM and keep the other sections + untouched. Unfortunately the current version of flashrom doesn't + support this yet. But there is a patch pending [1]. + + WARNING: Never write a complete coreboot.rom to your flash ROM if it + was built with a fake IFD. It just won't work. + + [1] http://www.flashrom.org/pipermail/flashrom/2013-June/011083.html + + +config IFD_BIOS_SECTION + depends on BUILD_WITH_FAKE_IFD + string + default "" + +config IFD_ME_SECTION + depends on BUILD_WITH_FAKE_IFD + string + default "" + +config IFD_BIN_PATH + string "Path to intel firmware descriptor" + depends on !BUILD_WITH_FAKE_IFD + default "3rdparty/mainboard/$(MAINBOARDDIR)/descriptor.bin" + + +config HAVE_ME_BIN + bool "Add Intel Management Engine firmware" + default n + help + The Intel processor in the selected system requires a special firmware + for an integrated controller called Management Engine (ME). The ME + firmware might be provided in coreboot's 3rdparty repository. If + not and if you don't have the firmware elsewhere, you can still + build coreboot without it. In this case however, you'll have to make + sure that you don't overwrite your ME firmware on your flash ROM. + +config ME_BIN_PATH + string "Path to management engine firmware" + depends on HAVE_ME_BIN + default "3rdparty/mainboard/$(MAINBOARDDIR)/me.bin" + +config HPET_MIN_TICKS + hex + default 0x80 + +config LOCK_MANAGEMENT_ENGINE + bool "Lock Management Engine section" + default n + help + The Intel Management Engine supports preventing write accesses + from the host to the Management Engine section in the firmware + descriptor. If the ME section is locked, it can only be overwritten + with an external SPI flash programmer. You will want this if you + want to increase security of your ROM image once you are sure + that the ME firmware is no longer going to change. + + If unsure, say N. + +endif diff --git a/src/southbridge/intel/ibexpeak/Makefile.inc b/src/southbridge/intel/ibexpeak/Makefile.inc new file mode 100644 index 0000000000..a29129d55c --- /dev/null +++ b/src/southbridge/intel/ibexpeak/Makefile.inc @@ -0,0 +1,97 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2010 Google Inc. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; version 2 of the License. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## + +# Run an intermediate step when producing coreboot.rom +# that adds additional components to the final firmware +# image outside of CBFS +INTERMEDIATE+=bd82x6x_add_me + +ramstage-y += ../bd82x6x/pch.c +ramstage-y += azalia.c +ramstage-y += lpc.c +ramstage-y += ../bd82x6x/pci.c +ramstage-y += ../bd82x6x/pcie.c +ramstage-y += sata.c +ramstage-y += usb_ehci.c +ramstage-y += me.c +ramstage-y += ../bd82x6x/me_8.x.c +ramstage-y += smbus.c +ramstage-y += thermal.c + +ramstage-y += ../bd82x6x/me_status.c +ramstage-y += ../bd82x6x/reset.c +ramstage-y += ../bd82x6x/watchdog.c + +ramstage-$(CONFIG_ELOG) += ../bd82x6x/elog.c +ramstage-y += spi.c +smm-$(CONFIG_SPI_FLASH_SMM) += spi.c + +ramstage-$(CONFIG_HAVE_SMI_HANDLER) += smi.c +smm-$(CONFIG_HAVE_SMI_HANDLER) += smihandler.c me.c ../bd82x6x/me_8.x.c ../bd82x6x/finalize.c ../bd82x6x/pch.c + +romstage-y += ../bd82x6x/early_usb.c early_smbus.c ../bd82x6x/early_me.c ../bd82x6x/me_status.c ../bd82x6x/gpio.c +romstage-y += ../bd82x6x/reset.c +romstage-$(CONFIG_SOUTHBRIDGE_INTEL_BD82X6X) += ../bd82x6x/early_spi.c +romstage-$(CONFIG_SOUTHBRIDGE_INTEL_C216) += ../bd82x6x/early_spi.c + +ifeq ($(CONFIG_BUILD_WITH_FAKE_IFD),y) +IFD_BIN_PATH := $(objgenerated)/ifdfake.bin +IFD_SECTIONS := $(addprefix -b ,$(CONFIG_IFD_BIOS_SECTION:"%"=%)) \ + $(addprefix -m ,$(CONFIG_IFD_ME_SECTION:"%"=%)) \ + $(addprefix -g ,$(CONFIG_IFD_GBE_SECTION:"%"=%)) \ + $(addprefix -p ,$(CONFIG_IFD_PLATFORM_SECTION:"%"=%)) +else +IFD_BIN_PATH := $(CONFIG_IFD_BIN_PATH) +endif + +bd82x6x_add_me: $(obj)/coreboot.pre $(IFDTOOL) $(IFDFAKE) +ifeq ($(CONFIG_BUILD_WITH_FAKE_IFD),y) + printf "\n** WARNING **\n" + printf "Coreboot will be built with a fake Intel Firmware Descriptor (IFD).\n" + printf "Never write a complete coreboot.rom with a fake IFD to your board's\n" + printf "flash ROM! Make sure that you only write valid flash regions.\n\n" + printf " IFDFAKE Building a fake Intel Firmware Descriptor\n" + $(IFDFAKE) $(IFD_SECTIONS) $(IFD_BIN_PATH) +endif + printf " DD Adding Intel Firmware Descriptor\n" + dd if=$(IFD_BIN_PATH) \ + of=$(obj)/coreboot.pre conv=notrunc >/dev/null 2>&1 +ifeq ($(CONFIG_HAVE_ME_BIN),y) + printf " IFDTOOL me.bin -> coreboot.pre\n" + $(objutil)/ifdtool/ifdtool \ + -i ME:$(CONFIG_ME_BIN_PATH) \ + $(obj)/coreboot.pre + mv $(obj)/coreboot.pre.new $(obj)/coreboot.pre +else + printf "\n** WARNING **\n" + printf "Coreboot will be built without Management Engine firmware.\n" + printf "Never write a complete coreboot.rom without ME to your board's\n" + printf "flash ROM! Make sure that you only write valid flash regions.\n\n" +endif +ifeq ($(CONFIG_LOCK_MANAGEMENT_ENGINE),y) + printf " IFDTOOL Locking Management Engine\n" + $(objutil)/ifdtool/ifdtool -l $(obj)/coreboot.pre + mv $(obj)/coreboot.pre.new $(obj)/coreboot.pre +else ifneq ($(CONFIG_BUILD_WITH_FAKE_IFD),y) + printf " IFDTOOL Unlocking Management Engine\n" + $(objutil)/ifdtool/ifdtool -u $(obj)/coreboot.pre + mv $(obj)/coreboot.pre.new $(obj)/coreboot.pre +endif + +PHONY += bd82x6x_add_me diff --git a/src/southbridge/intel/ibexpeak/azalia.c b/src/southbridge/intel/ibexpeak/azalia.c new file mode 100644 index 0000000000..8a1116a2d6 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/azalia.c @@ -0,0 +1,372 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008 Advanced Micro Devices, Inc. + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2011 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <arch/io.h> +#include <delay.h> +#include "pch.h" + +#define HDA_ICII_REG 0x68 +#define HDA_ICII_BUSY (1 << 0) +#define HDA_ICII_VALID (1 << 1) + +typedef struct southbridge_intel_bd82x6x_config config_t; + +static int set_bits(u32 port, u32 mask, u32 val) +{ + u32 reg32; + int count; + + /* Write (val & mask) to port */ + val &= mask; + reg32 = read32(port); + reg32 &= ~mask; + reg32 |= val; + write32(port, reg32); + + /* Wait for readback of register to + * match what was just written to it + */ + count = 50; + do { + /* Wait 1ms based on BKDG wait time */ + mdelay(1); + reg32 = read32(port); + reg32 &= mask; + } while ((reg32 != val) && --count); + + /* Timeout occurred */ + if (!count) + return -1; + return 0; +} + +static int codec_detect(u32 base) +{ + u8 reg8; + + /* Set Bit 0 to 1 to exit reset state (BAR + 0x8)[0] */ + if (set_bits(base + 0x08, 1, 1) == -1) + goto no_codec; + + /* Write back the value once reset bit is set. */ + write16(base + 0x0, read16(base + 0x0)); + + /* Read in Codec location (BAR + 0xe)[2..0]*/ + reg8 = read8(base + 0xe); + reg8 &= 0x0f; + if (!reg8) + goto no_codec; + + return reg8; + +no_codec: + /* Codec Not found */ + /* Put HDA back in reset (BAR + 0x8) [0] */ + set_bits(base + 0x08, 1, 0); + printk(BIOS_DEBUG, "Azalia: No codec!\n"); + return 0; +} + +const u32 * cim_verb_data = NULL; +u32 cim_verb_data_size = 0; +const u32 * pc_beep_verbs = NULL; +u32 pc_beep_verbs_size = 0; + +static u32 find_verb(struct device *dev, u32 viddid, const u32 ** verb) +{ + int idx=0; + + while (idx < (cim_verb_data_size / sizeof(u32))) { + u32 verb_size = 4 * cim_verb_data[idx+2]; // in u32 + if (cim_verb_data[idx] != viddid) { + idx += verb_size + 3; // skip verb + header + continue; + } + *verb = &cim_verb_data[idx+3]; + return verb_size; + } + + /* Not all codecs need to load another verb */ + return 0; +} + +/** + * Wait 50usec for the codec to indicate it is ready + * no response would imply that the codec is non-operative + */ + +static int wait_for_ready(u32 base) +{ + /* Use a 1msec timeout */ + + int timeout = 1000; + + while(timeout--) { + u32 reg32 = read32(base + HDA_ICII_REG); + if (!(reg32 & HDA_ICII_BUSY)) + return 0; + udelay(1); + } + + return -1; +} + +/** + * Wait 50usec for the codec to indicate that it accepted + * the previous command. No response would imply that the code + * is non-operative + */ + +static int wait_for_valid(u32 base) +{ + u32 reg32; + + /* Send the verb to the codec */ + reg32 = read32(base + HDA_ICII_REG); + reg32 |= HDA_ICII_BUSY | HDA_ICII_VALID; + write32(base + HDA_ICII_REG, reg32); + + /* Use a 1msec timeout */ + + int timeout = 1000; + while(timeout--) { + reg32 = read32(base + HDA_ICII_REG); + if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) == + HDA_ICII_VALID) + return 0; + udelay(1); + } + + return -1; +} + +static void codec_init(struct device *dev, u32 base, int addr) +{ + u32 reg32; + const u32 *verb; + u32 verb_size; + int i; + + printk(BIOS_DEBUG, "Azalia: Initializing codec #%d\n", addr); + + /* 1 */ + if (wait_for_ready(base) == -1) { + printk(BIOS_DEBUG, " codec not ready.\n"); + return; + } + + reg32 = (addr << 28) | 0x000f0000; + write32(base + 0x60, reg32); + + if (wait_for_valid(base) == -1) { + printk(BIOS_DEBUG, " codec not valid.\n"); + return; + } + + reg32 = read32(base + 0x64); + + /* 2 */ + printk(BIOS_DEBUG, "Azalia: codec viddid: %08x\n", reg32); + verb_size = find_verb(dev, reg32, &verb); + + if (!verb_size) { + printk(BIOS_DEBUG, "Azalia: No verb!\n"); + return; + } + printk(BIOS_DEBUG, "Azalia: verb_size: %d\n", verb_size); + + /* 3 */ + for (i = 0; i < verb_size; i++) { + if (wait_for_ready(base) == -1) + return; + + write32(base + 0x60, verb[i]); + + if (wait_for_valid(base) == -1) + return; + } + printk(BIOS_DEBUG, "Azalia: verb loaded.\n"); +} + +static void codecs_init(struct device *dev, u32 base, u32 codec_mask) +{ + int i; + for (i = 3; i >= 0; i--) { + if (codec_mask & (1 << i)) + codec_init(dev, base, i); + } + + for (i = 0; i < pc_beep_verbs_size; i++) { + if (wait_for_ready(base) == -1) + return; + + write32(base + 0x60, pc_beep_verbs[i]); + + if (wait_for_valid(base) == -1) + return; + } +} + +static void azalia_init(struct device *dev) +{ + u32 base; + struct resource *res; + u32 codec_mask; + u8 reg8; + u16 reg16; + u32 reg32; + + /* Find base address */ + res = find_resource(dev, PCI_BASE_ADDRESS_0); + if (!res) + return; + + // NOTE this will break as soon as the Azalia get's a bar above + // 4G. Is there anything we can do about it? + base = (u32)res->base; + printk(BIOS_DEBUG, "Azalia: base = %08x\n", (u32)base); + + if (RCBA32(0x2030) & (1 << 31)) { + reg32 = pci_read_config32(dev, 0x120); + reg32 &= 0xf8ffff01; + reg32 |= (1 << 24); // 2 << 24 for server + reg32 |= RCBA32(0x2030) & 0xfe; + pci_write_config32(dev, 0x120, reg32); + + reg16 = pci_read_config16(dev, 0x78); + reg16 |= (1 << 11); + pci_write_config16(dev, 0x78, reg16); + } else + printk(BIOS_DEBUG, "Azalia: V1CTL disabled.\n"); + + reg32 = pci_read_config32(dev, 0x114); + reg32 &= ~0xfe; + pci_write_config32(dev, 0x114, reg32); + + // Set VCi enable bit + reg32 = pci_read_config32(dev, 0x120); + reg32 |= (1 << 31); + pci_write_config32(dev, 0x120, reg32); + + // Enable HDMI codec: + reg32 = pci_read_config32(dev, 0xc4); + reg32 |= (1 << 1); + pci_write_config32(dev, 0xc4, reg32); + + reg8 = pci_read_config8(dev, 0x43); + reg8 |= (1 << 6); + pci_write_config8(dev, 0x43, reg8); + + /* Additional programming steps */ + reg32 = pci_read_config32(dev, 0xc4); + reg32 |= (1 << 13); + pci_write_config32(dev, 0xc4, reg32); + + reg32 = pci_read_config32(dev, 0xc4); + reg32 |= (1 << 10); + pci_write_config32(dev, 0xc4, reg32); + + reg32 = pci_read_config32(dev, 0xd0); + reg32 &= ~(1 << 31); + pci_write_config32(dev, 0xd0, reg32); + + if (dev->device == 0x1e20) { + /* Additional step on Panther Point */ + reg32 = pci_read_config32(dev, 0xc4); + reg32 |= (1 << 17); + pci_write_config32(dev, 0xc4, reg32); + } + + /* Set Bus Master */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER); + + pci_write_config8(dev, 0x3c, 0x0a); // unused? + + /* Codec Initialization Programming Sequence */ + + /* Take controller out of reset */ + reg32 = read32(base + 0x08); + reg32 |= (1 << 0); + write32(base + 0x08, reg32); + /* Wait 1ms */ + udelay(1000); + + // + reg8 = pci_read_config8(dev, 0x40); // Audio Control + reg8 |= 1; // Select Azalia mode. This needs to be controlled via devicetree.cb + pci_write_config8(dev, 0x40, reg8); + + reg8 = pci_read_config8(dev, 0x4d); // Docking Status + reg8 &= ~(1 << 7); // Docking not supported + pci_write_config8(dev, 0x4d, reg8); + + codec_mask = codec_detect(base); + + if (codec_mask) { + printk(BIOS_DEBUG, "Azalia: codec_mask = %02x\n", codec_mask); + codecs_init(dev, base, codec_mask); + } + + /* Enable dynamic clock gating */ + reg8 = pci_read_config8(dev, 0x43); + reg8 &= ~0x7; + reg8 |= (1 << 2) | (1 << 0); + pci_write_config8(dev, 0x43, reg8); +} + +static void azalia_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations azalia_pci_ops = { + .set_subsystem = azalia_set_subsystem, +}; + +static struct device_operations azalia_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = azalia_init, + .scan_bus = 0, + .ops_pci = &azalia_pci_ops, +}; + +static const unsigned short pci_device_ids[] = { 0x1c20, 0x1e20, 0x3b56, 0 }; + +static const struct pci_driver pch_azalia __pci_driver = { + .ops = &azalia_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; + diff --git a/src/southbridge/intel/ibexpeak/chip.h b/src/southbridge/intel/ibexpeak/chip.h new file mode 100644 index 0000000000..828466c0c4 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/chip.h @@ -0,0 +1,103 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SOUTHBRIDGE_INTEL_BD82X6X_CHIP_H +#define SOUTHBRIDGE_INTEL_BD82X6X_CHIP_H + +struct southbridge_intel_bd82x6x_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; + + /** + * SATA Interface Speed Support Configuration + * + * Only the lower two bits have a meaning: + * 00 - No effect (leave as chip default) + * 01 - 1.5 Gb/s maximum speed + * 10 - 3.0 Gb/s maximum speed + * 11 - 6.0 Gb/s maximum speed + */ + uint8_t sata_interface_speed_support; + + 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; +}; + +#endif /* SOUTHBRIDGE_INTEL_BD82X6X_CHIP_H */ diff --git a/src/southbridge/intel/ibexpeak/early_smbus.c b/src/southbridge/intel/ibexpeak/early_smbus.c new file mode 100644 index 0000000000..d8aff926cc --- /dev/null +++ b/src/southbridge/intel/ibexpeak/early_smbus.c @@ -0,0 +1,77 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <console/console.h> +#include <device/pci_ids.h> +#include <device/pci_def.h> +#include "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); + print_debug("SMBus controller enabled.\n"); +} + +int smbus_read_byte(unsigned device, unsigned address) +{ + return do_smbus_read_byte(SMBUS_IO_BASE, device, address); +} + +int smbus_write_byte(unsigned device, unsigned address, u8 data) +{ + return do_smbus_write_byte(SMBUS_IO_BASE, device, address, data); +} + +int smbus_block_read(unsigned device, unsigned cmd, u8 bytes, u8 *buf) +{ + return do_smbus_block_read(SMBUS_IO_BASE, device, cmd, bytes, buf); +} + +int smbus_block_write(unsigned device, unsigned cmd, u8 bytes, const u8 *buf) +{ + return do_smbus_block_write(SMBUS_IO_BASE, device, cmd, bytes, buf); +} + diff --git a/src/southbridge/intel/ibexpeak/lpc.c b/src/southbridge/intel/ibexpeak/lpc.c new file mode 100644 index 0000000000..8417a90cdf --- /dev/null +++ b/src/southbridge/intel/ibexpeak/lpc.c @@ -0,0 +1,695 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2013 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <pc80/mc146818rtc.h> +#include <pc80/isa-dma.h> +#include <pc80/i8259.h> +#include <arch/io.h> +#include <arch/ioapic.h> +#include <arch/acpi.h> +#include <cpu/cpu.h> +#include <elog.h> +#include "pch.h" + +#define NMI_OFF 0 + +#define ENABLE_ACPI_MODE_IN_COREBOOT 0 +#define TEST_SMM_FLASH_LOCKDOWN 0 + +typedef struct southbridge_intel_bd82x6x_config config_t; + +/** + * Set miscellanous static southbridge features. + * + * @param dev PCI device with I/O APIC control registers + */ +static void pch_enable_ioapic(struct device *dev) +{ + u32 reg32; + + /* Enable ACPI I/O range decode */ + pci_write_config8(dev, ACPI_CNTL, ACPI_EN); + + set_ioapic_id(IO_APIC_ADDR, 0x01); + /* affirm full set of redirection table entries ("write once") */ + reg32 = io_apic_read(IO_APIC_ADDR, 0x01); + io_apic_write(IO_APIC_ADDR, 0x01, reg32); + + /* + * Select Boot Configuration register (0x03) and + * use Processor System Bus (0x01) to deliver interrupts. + */ + io_apic_write(IO_APIC_ADDR, 0x03, 0x01); +} + +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, 0xb8, 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; + + reg32 = RCBA32(0x3f02); + reg32 &= ~0xf; + RCBA32(0x3f02) = 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); + + rtc_init(rtc_failed); +} + +static void mobile5_pm_init(struct device *dev) +{ + int i; + + printk(BIOS_DEBUG, "Mobile 5 PM init\n"); + pci_write_config8(dev, 0xa9, 0x47); + + RCBA32 (0x1d44) = 0x00000000; + (void) RCBA32 (0x1d44); + RCBA32 (0x1d48) = 0x00030000; + (void) RCBA32 (0x1d48); + RCBA32 (0x1e80) = 0x000c0801; + (void) RCBA32 (0x1e80); + RCBA32 (0x1e84) = 0x000200f0; + (void) RCBA32 (0x1e84); + + const u32 rcba2010[] = + { + /* 2010: */ 0x00188200, 0x14000016, 0xbc4abcb5, 0x00000000, + /* 2020: */ 0xf0c9605b, 0x13683040, 0x04c8f16e, 0x09e90170 + }; + for (i = 0; i < sizeof (rcba2010) / sizeof (rcba2010[0]); i++) + { + RCBA32 (0x2010 + 4 * i) = rcba2010[i]; + RCBA32 (0x2010 + 4 * i); + } + + RCBA32 (0x2100) = 0x00000000; + (void) RCBA32 (0x2100); + RCBA32 (0x2104) = 0x00000757; + (void) RCBA32 (0x2104); + RCBA32 (0x2108) = 0x00170001; + (void) RCBA32 (0x2108); + + RCBA32 (0x211c) = 0x00000000; + (void) RCBA32 (0x211c); + RCBA32 (0x2120) = 0x00010000; + (void) RCBA32 (0x2120); + + RCBA32 (0x21fc) = 0x00000000; + (void) RCBA32 (0x21fc); + RCBA32 (0x2200) = 0x20000044; + (void) RCBA32 (0x2200); + RCBA32 (0x2204) = 0x00000001; + (void) RCBA32 (0x2204); + RCBA32 (0x2208) = 0x00003457; + (void) RCBA32 (0x2208); + + const u32 rcba2210[] = + { + /* 2210 */ 0x00000000, 0x00000001, 0xa0fff210, 0x0000df00, + /* 2220 */ 0x00e30880, 0x00000070, 0x00004000, 0x00000000, + /* 2230 */ 0x00e30880, 0x00000070, 0x00004000, 0x00000000, + /* 2240 */ 0x00002301, 0x36000000, 0x00010107, 0x00160000, + /* 2250 */ 0x00001b01, 0x36000000, 0x00010107, 0x00160000, + /* 2260 */ 0x00000601, 0x16000000, 0x00010107, 0x00160000, + /* 2270 */ 0x00001c01, 0x16000000, 0x00010107, 0x00160000 + }; + + for (i = 0; i < sizeof (rcba2210) / sizeof (rcba2210[0]); i++) + { + RCBA32 (0x2210 + 4 * i) = rcba2210[i]; + RCBA32 (0x2210 + 4 * i); + } + + const u32 rcba2300[] = + { + /* 2300: */ 0x00000000, 0x40000000, 0x4646827b, 0x6e803131, + /* 2310: */ 0x32c77887, 0x00077733, 0x00007447, 0x00000040, + /* 2320: */ 0xcccc0cfc, 0x0fbb0fff + }; + + for (i = 0; i < sizeof (rcba2300) / sizeof (rcba2300[0]); i++) + { + RCBA32 (0x2300 + 4 * i) = rcba2300[i]; + RCBA32 (0x2300 + 4 * i); + } + + RCBA32 (0x37fc) = 0x00000000; + (void) RCBA32 (0x37fc); + RCBA32 (0x3dfc) = 0x00000000; + (void) RCBA32 (0x3dfc); + RCBA32 (0x3e7c) = 0xffffffff; + (void) RCBA32 (0x3e7c); + RCBA32 (0x3efc) = 0x00000000; + (void) RCBA32 (0x3efc); + RCBA32 (0x3f00) = 0x0000010b; + (void) RCBA32 (0x3f00); +} + +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; + + write32(0xfed00010, read32(0xfed00010) | 1); +} + +static void enable_clock_gating(device_t dev) +{ + u32 reg32; + u16 reg16; + + RCBA32_AND_OR(0x2234, ~0UL, 0xf); + + reg16 = pci_read_config16(dev, GEN_PMCON_1); + reg16 |= (1 << 2) | (1 << 11); + pci_write_config16(dev, GEN_PMCON_1, reg16); + + pch_iobp_update(0xEB007F07, ~0UL, (1 << 31)); + pch_iobp_update(0xEB004000, ~0UL, (1 << 7)); + pch_iobp_update(0xEC007F07, ~0UL, (1 << 31)); + pch_iobp_update(0xEC004000, ~0UL, (1 << 7)); + + reg32 = RCBA32(CG); + reg32 |= (1 << 31); + reg32 |= (1 << 29) | (1 << 28); + reg32 |= (1 << 27) | (1 << 26) | (1 << 25) | (1 << 24); + reg32 |= (1 << 16); + reg32 |= (1 << 17); + reg32 |= (1 << 18); + reg32 |= (1 << 22); + reg32 |= (1 << 23); + reg32 &= ~(1 << 20); + reg32 |= (1 << 19); + reg32 |= (1 << 0); + reg32 |= (0xf << 1); + RCBA32(CG) = reg32; + + RCBA32_OR(0x38c0, 0x7); + RCBA32_OR(0x36d4, 0x6680c004); + RCBA32_OR(0x3564, 0x3); +} + +#if CONFIG_HAVE_SMI_HANDLER +static void pch_lock_smm(struct device *dev) +{ +#if TEST_SMM_FLASH_LOCKDOWN + u8 reg8; +#endif + + if (acpi_slp_type != 3) { +#if ENABLE_ACPI_MODE_IN_COREBOOT + printk(BIOS_DEBUG, "Enabling ACPI via APMC:\n"); + outb(0xe1, 0xb2); // Enable ACPI mode + printk(BIOS_DEBUG, "done.\n"); +#else + printk(BIOS_DEBUG, "Disabling ACPI via APMC:\n"); + outb(0x1e, 0xb2); // Disable ACPI mode + printk(BIOS_DEBUG, "done.\n"); +#endif + } + + /* Don't allow evil boot loaders, kernels, or + * userspace applications to deceive us: + */ + smm_lock(); + +#if TEST_SMM_FLASH_LOCKDOWN + /* Now try this: */ + printk(BIOS_DEBUG, "Locking BIOS to RO... "); + reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */ + printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off", + (reg8&1)?"rw":"ro"); + reg8 &= ~(1 << 0); /* clear BIOSWE */ + pci_write_config8(dev, 0xdc, reg8); + reg8 |= (1 << 1); /* set BLE */ + pci_write_config8(dev, 0xdc, reg8); + printk(BIOS_DEBUG, "ok.\n"); + reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */ + printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off", + (reg8&1)?"rw":"ro"); + + printk(BIOS_DEBUG, "Writing:\n"); + *(volatile u8 *)0xfff00000 = 0x00; + printk(BIOS_DEBUG, "Testing:\n"); + reg8 |= (1 << 0); /* set BIOSWE */ + pci_write_config8(dev, 0xdc, reg8); + + reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */ + printk(BIOS_DEBUG, " BLE: %s; BWE: %s\n", (reg8&2)?"on":"off", + (reg8&1)?"rw":"ro"); + printk(BIOS_DEBUG, "Done.\n"); +#endif +} +#endif + +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) +{ + /* + * Enable DMI ASPM in the PCH + */ + RCBA32_AND_OR(0x2304, ~(1 << 10), 0); + RCBA32_OR(0x21a4, (1 << 11)|(1 << 10)); + RCBA32_OR(0x21a8, 0x3); +} + +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_ioapic(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_MOBILE5: + mobile5_pm_init (dev); + 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(); + + /* Initialize Clock Gating */ + enable_clock_gating(dev); + + 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); + +#if CONFIG_HAVE_SMI_HANDLER + pch_lock_smm(dev); +#endif + + 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) +{ + /* Enable PCH Display Port */ + RCBA16(DISPBDF) = 0x0010; + RCBA32_OR(FD2, PCH_ENABLE_DBDF); + + 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 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, + .init = lpc_init, + .enable = pch_lpc_enable, + .scan_bus = scan_static_bus, + .ops_pci = &pci_ops, +}; + + +/* IDs for LPC device of Intel 6 Series Chipset, Intel 7 Series Chipset, and + * Intel C200 Series Chipset + */ + +static const unsigned short pci_device_ids[] = { 0x1c46, 0x1c47, 0x1c49, 0x1c4a, + 0x1c4b, 0x1c4c, 0x1c4d, 0x1c4e, + 0x1c4f, 0x1c50, 0x1c52, 0x1c54, + 0x1e55, 0x1c56, 0x1e57, 0x1c5c, + 0x1e5d, 0x1e5e, 0x1e5f, 0x3b07, + 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/ibexpeak/me.c b/src/southbridge/intel/ibexpeak/me.c new file mode 100644 index 0000000000..bc56012b12 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/me.c @@ -0,0 +1,781 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* + * 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/hlt.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> + +#ifdef __SMM__ +#include <arch/pci_mmio_cfg.h> +#else +# 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); + 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, 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, 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); + 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__) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) +/* Get ME firmware version */ +static int mkhi_get_fw_version(void) +{ + struct me_fw_version version; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_GEN, + .command = MKHI_GET_FW_VERSION, + }; + struct mei_header mei = { + .is_complete = 1, + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + .length = sizeof(mkhi), + }; + + /* Send request and wait for response */ + if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) { + printk(BIOS_ERR, "ME: GET FW VERSION message failed\n"); + return -1; + } + + printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) " + "%u.%u.%u.%u (recovery)\n", + version.code_major, version.code_minor, + version.code_build_number, version.code_hot_fix, + version.recovery_major, version.recovery_minor, + version.recovery_build_number, version.recovery_hot_fix); + + return 0; +} + +static inline void print_cap(const char *name, int state) +{ + printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n", + name, state ? "en" : "dis"); +} + +/* Get ME Firmware Capabilities */ +static int mkhi_get_fwcaps(void) +{ + u32 rule_id = 0; + struct me_fwcaps cap; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_FWCAPS, + .command = MKHI_FWCAPS_GET_RULE, + }; + struct mei_header mei = { + .is_complete = 1, + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + .length = sizeof(mkhi) + sizeof(rule_id), + }; + + /* Send request and wait for response */ + if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) { + printk(BIOS_ERR, "ME: GET FWCAPS message failed\n"); + return -1; + } + + print_cap("Full Network manageability", cap.caps_sku.full_net); + print_cap("Regular Network manageability", cap.caps_sku.std_net); + print_cap("Manageability", cap.caps_sku.manageability); + print_cap("Small business technology", cap.caps_sku.small_business); + print_cap("Level III manageability", cap.caps_sku.l3manageability); + print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at); + print_cap("IntelR Capability Licensing Service (CLS)", + cap.caps_sku.intel_cls); + print_cap("IntelR Power Sharing Technology (MPC)", + cap.caps_sku.intel_mpc); + print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking); + print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp); + print_cap("IPV6", cap.caps_sku.ipv6); + print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm); + print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och); + print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan); + print_cap("TLS", cap.caps_sku.tls); + print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan); + + return 0; +} +#endif + +#if CONFIG_CHROMEOS && 0 /* DISABLED */ +/* Tell ME to issue a global reset */ +int mkhi_global_reset(void) +{ + struct me_global_reset reset = { + .request_origin = GLOBAL_RESET_BIOS_POST, + .reset_type = CBM_RR_GLOBAL_RESET, + }; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_CBM, + .command = MKHI_GLOBAL_RESET, + }; + struct mei_header mei = { + .is_complete = 1, + .length = sizeof(mkhi) + sizeof(reset), + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + }; + + printk(BIOS_NOTICE, "ME: Requesting global reset\n"); + + /* Send request and wait for response */ + if (mei_sendrecv(&mei, &mkhi, &reset, NULL, 0) < 0) { + /* No response means reset will happen shortly... */ + hlt(); + } + + /* If the ME responded it rejected the reset request */ + printk(BIOS_ERR, "ME: Global Reset failed\n"); + return -1; +} +#endif + +#ifdef __SMM__ +static void intel_me7_finalize_smm(void) +{ + struct me_hfs hfs; + u32 reg32; + + mei_base_address = + pci_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 == 0xfffffff0) + return; + + /* Make sure ME is in a mode that expects EOP */ + reg32 = pci_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 = pci_read_config32(PCH_ME_DEV, PCI_COMMAND); + reg32 &= ~(PCI_COMMAND_MASTER | + PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + pci_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 = pci_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; + +#if CONFIG_HAVE_ACPI_RESUME + /* S3 wake skips all MKHI messages */ + if (acpi_slp_type == 3) { + return ME_S3WAKE_BIOS_PATH; + } +#endif + + 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 = 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); + + /* 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) && (CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE) + /* Print ME firmware version */ + mkhi_get_fw_version(); + /* Print ME firmware capabilities */ + mkhi_get_fwcaps(); +#endif + + /* + * Leave the ME unlocked in this path. + * It will be locked via SMI command later. + */ + 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, + .scan_bus = scan_static_bus, + .ops_pci = &pci_ops, +}; + +static const unsigned short pci_device_ids[] = { 0x1c3a, 0x3b64, + 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/ibexpeak/me.h b/src/southbridge/intel/ibexpeak/me.h new file mode 100644 index 0000000000..6e2062de32 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/me.h @@ -0,0 +1,374 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#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_SET_UMA 0x08 +#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/ibexpeak/nvs.h b/src/southbridge/intel/ibexpeak/nvs.h new file mode 100644 index 0000000000..7b8b6c9e9c --- /dev/null +++ b/src/southbridge/intel/ibexpeak/nvs.h @@ -0,0 +1,160 @@ +/* + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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 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[6]; + /* XHCI */ + u8 xhci; + /* 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 diff --git a/src/southbridge/intel/ibexpeak/pch.h b/src/southbridge/intel/ibexpeak/pch.h new file mode 100644 index 0000000000..356dd8ae28 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/pch.h @@ -0,0 +1,571 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SOUTHBRIDGE_INTEL_BD82X6X_PCH_H +#define SOUTHBRIDGE_INTEL_BD82X6X_PCH_H + +/* PCH types */ +#define PCH_TYPE_CPT 0x1c /* CougarPoint */ +#define PCH_TYPE_PPT 0x1e /* IvyBridge */ +#define PCH_TYPE_MOBILE5 0x3b + +/* 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 0x0400 +#define SMBUS_SLAVE_ADDR 0x24 +/* TODO Make sure these don't get changed by stage2 */ +#define DEFAULT_GPIOBASE 0x0480 +#define DEFAULT_PMBASE 0x0500 + +#define DEFAULT_RCBA 0xfed1c000 + +#ifndef __ACPI__ +#define DEBUG_PERIODIC_SMIS 0 + +#if defined (__SMM__) && !defined(__ASSEMBLER__) +void intel_pch_finalize_smm(void); +#endif + +#if !defined(__ASSEMBLER__) +#if !defined(__PRE_RAM__) +#if !defined(__SMM__) +#include "chip.h" +void pch_enable(device_t dev); +#endif +int pch_silicon_revision(void); +int pch_silicon_type(void); +int pch_silicon_supported(int type, int rev); +void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue); +#if CONFIG_ELOG +void pch_log_state(void); +#endif +#else /* __PRE_RAM__ */ +void enable_smbus(void); +void enable_usb_bar(void); +int smbus_read_byte(unsigned device, unsigned address); +int smbus_write_byte(unsigned device, unsigned address, u8 data); +int smbus_block_read(unsigned device, unsigned cmd, u8 bytes, u8 *buf); +int smbus_block_write(unsigned device, unsigned cmd, u8 bytes, const u8 *buf); +int early_spi_read(u32 offset, u32 size, u8 *buffer); +#endif +#endif + +#define MAINBOARD_POWER_OFF 0 +#define MAINBOARD_POWER_ON 1 +#define MAINBOARD_POWER_KEEP 2 + +#ifndef CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL +#define CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL MAINBOARD_POWER_ON +#endif + +/* PCI Configuration Space (D30:F0): PCI2PCI */ +#define PSTS 0x06 +#define SMLT 0x1b +#define SECSTS 0x1e +#define INTR 0x3c +#define BCTRL 0x3e +#define SBR (1 << 6) +#define SEE (1 << 1) +#define PERE (1 << 0) + +#define PCH_EHCI1_DEV PCI_DEV(0, 0x1d, 0) +#define PCH_EHCI2_DEV PCI_DEV(0, 0x1a, 0) +#define PCH_XHCI_DEV PCI_DEV(0, 0x14, 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 ACPI_EN (1 << 7) +#define BIOS_CNTL 0xDC +#define GPIO_BASE 0x48 /* LPC GPIO Base Address Register */ +#define GPIO_CNTL 0x4C /* LPC GPIO Control Register */ +#define GPIO_ROUT 0xb8 + +#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:F1): IDE */ +#define PCH_IDE_DEV PCI_DEV(0, 0x1f, 1) +#define PCH_SATA_DEV PCI_DEV(0, 0x1f, 2) +#define PCH_SATA2_DEV PCI_DEV(0, 0x1f, 5) +#define INTR_LN 0x3c +#define IDE_TIM_PRI 0x40 /* IDE timings, primary */ +#define IDE_DECODE_ENABLE (1 << 15) +#define IDE_SITRE (1 << 14) +#define IDE_ISP_5_CLOCKS (0 << 12) +#define IDE_ISP_4_CLOCKS (1 << 12) +#define IDE_ISP_3_CLOCKS (2 << 12) +#define IDE_RCT_4_CLOCKS (0 << 8) +#define IDE_RCT_3_CLOCKS (1 << 8) +#define IDE_RCT_2_CLOCKS (2 << 8) +#define IDE_RCT_1_CLOCKS (3 << 8) +#define IDE_DTE1 (1 << 7) +#define IDE_PPE1 (1 << 6) +#define IDE_IE1 (1 << 5) +#define IDE_TIME1 (1 << 4) +#define IDE_DTE0 (1 << 3) +#define IDE_PPE0 (1 << 2) +#define IDE_IE0 (1 << 1) +#define IDE_TIME0 (1 << 0) +#define IDE_TIM_SEC 0x42 /* IDE timings, secondary */ + +#define IDE_SDMA_CNT 0x48 /* Synchronous DMA control */ +#define IDE_SSDE1 (1 << 3) +#define IDE_SSDE0 (1 << 2) +#define IDE_PSDE1 (1 << 1) +#define IDE_PSDE0 (1 << 0) + +#define IDE_SDMA_TIM 0x4a + +#define IDE_CONFIG 0x54 /* IDE I/O Configuration Register */ +#define SIG_MODE_SEC_NORMAL (0 << 18) +#define SIG_MODE_SEC_TRISTATE (1 << 18) +#define SIG_MODE_SEC_DRIVELOW (2 << 18) +#define SIG_MODE_PRI_NORMAL (0 << 16) +#define SIG_MODE_PRI_TRISTATE (1 << 16) +#define SIG_MODE_PRI_DRIVELOW (2 << 16) +#define FAST_SCB1 (1 << 15) +#define FAST_SCB0 (1 << 14) +#define FAST_PCB1 (1 << 13) +#define FAST_PCB0 (1 << 12) +#define SCB1 (1 << 3) +#define SCB0 (1 << 2) +#define PCB1 (1 << 1) +#define PCB0 (1 << 0) + +#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 VCH 0x0000 /* 32bit */ +#define VCAP1 0x0004 /* 32bit */ +#define VCAP2 0x0008 /* 32bit */ +#define PVC 0x000c /* 16bit */ +#define PVS 0x000e /* 16bit */ + +#define V0CAP 0x0010 /* 32bit */ +#define V0CTL 0x0014 /* 32bit */ +#define V0STS 0x001a /* 16bit */ + +#define V1CAP 0x001c /* 32bit */ +#define V1CTL 0x0020 /* 32bit */ +#define V1STS 0x0026 /* 16bit */ + +#define RCTCL 0x0100 /* 32bit */ +#define ESD 0x0104 /* 32bit */ +#define ULD 0x0110 /* 32bit */ +#define ULBA 0x0118 /* 64bit */ + +#define RP1D 0x0120 /* 32bit */ +#define RP1BA 0x0128 /* 64bit */ +#define RP2D 0x0130 /* 32bit */ +#define RP2BA 0x0138 /* 64bit */ +#define RP3D 0x0140 /* 32bit */ +#define RP3BA 0x0148 /* 64bit */ +#define RP4D 0x0150 /* 32bit */ +#define RP4BA 0x0158 /* 64bit */ +#define HDD 0x0160 /* 32bit */ +#define HDBA 0x0168 /* 64bit */ +#define RP5D 0x0170 /* 32bit */ +#define RP5BA 0x0178 /* 64bit */ +#define RP6D 0x0180 /* 32bit */ +#define RP6BA 0x0188 /* 64bit */ + +#define RPC 0x0400 /* 32bit */ +#define RPFN 0x0404 /* 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 IOTR0 0x1e80 /* 64bit */ +#define IOTR1 0x1e88 /* 64bit */ +#define IOTR2 0x1e90 /* 64bit */ +#define IOTR3 0x1e98 /* 64bit */ + +#define TCTL 0x3000 /* 8bit */ + +#define NOINT 0 +#define INTA 1 +#define INTB 2 +#define INTC 3 +#define INTD 4 + +#define DIR_IDR 12 /* Interrupt D Pin Offset */ +#define DIR_ICR 8 /* Interrupt C Pin Offset */ +#define DIR_IBR 4 /* Interrupt B Pin Offset */ +#define DIR_IAR 0 /* Interrupt A Pin Offset */ + +#define PIRQA 0 +#define PIRQB 1 +#define PIRQC 2 +#define PIRQD 3 +#define PIRQE 4 +#define PIRQF 5 +#define PIRQG 6 +#define PIRQH 7 + +/* 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)) + +#define D31IP 0x3100 /* 32bit */ +#define D31IP_TTIP 24 /* Thermal Throttle Pin */ +#define D31IP_SIP2 20 /* SATA Pin 2 */ +#define D31IP_UNKIP 16 +#define D31IP_SMIP 12 /* SMBUS Pin */ +#define D31IP_SIP 8 /* SATA Pin */ +#define D30IP 0x3104 /* 32bit */ +#define D30IP_PIP 0 /* PCI Bridge Pin */ +#define D29IP 0x3108 /* 32bit */ +#define D29IP_E1P 0 /* EHCI #1 Pin */ +#define D28IP 0x310c /* 32bit */ +#define D28IP_P8IP 28 /* PCI Express Port 8 */ +#define D28IP_P7IP 24 /* PCI Express Port 7 */ +#define D28IP_P6IP 20 /* PCI Express Port 6 */ +#define D28IP_P5IP 16 /* PCI Express Port 5 */ +#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 D27IP 0x3110 /* 32bit */ +#define D27IP_ZIP 0 /* HD Audio Pin */ +#define D26IP 0x3114 /* 32bit */ +#define D26IP_E2P 0 /* EHCI #2 Pin */ +#define D25IP 0x3118 /* 32bit */ +#define D25IP_LIP 0 /* GbE LAN Pin */ +#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 D20IP 0x3128 /* 32bit */ +#define D20IP_XHCIIP 0 +#define D31IR 0x3140 /* 16bit */ +#define D30IR 0x3142 /* 16bit */ +#define D29IR 0x3144 /* 16bit */ +#define D28IR 0x3146 /* 16bit */ +#define D27IR 0x3148 /* 16bit */ +#define D26IR 0x314c /* 16bit */ +#define D25IR 0x3150 /* 16bit */ +#define D22IR 0x315c /* 16bit */ +#define D20IR 0x3160 /* 16bit */ +#define OIC 0x31fe /* 16bit */ +#define SOFT_RESET_CTRL 0x38f4 +#define SOFT_RESET_DATA 0x38f8 + +#define DIR_ROUTE(x,a,b,c,d) \ + RCBA32(x) = (((d) << DIR_IDR) | ((c) << DIR_ICR) | \ + ((b) << DIR_IBR) | ((a) << DIR_IAR)) + +#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 ((1 << 0)|(1 << 26)) +#define PCH_DISABLE_P2P (1 << 1) +#define PCH_DISABLE_SATA1 (1 << 2) +#define PCH_DISABLE_SMBUS (1 << 3) +#define PCH_DISABLE_HD_AUDIO (1 << 4) +#define PCH_DISABLE_EHCI2 (1 << 13) +#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) +#define PCH_DISABLE_XHCI (1 << 27) + +/* 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) +#define PCH_ENABLE_DBDF (1 << 0) + +/* 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_BD82X6X_PCH_H */ diff --git a/src/southbridge/intel/ibexpeak/sata.c b/src/southbridge/intel/ibexpeak/sata.c new file mode 100644 index 0000000000..943d0854a9 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/sata.c @@ -0,0 +1,301 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2013 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include "pch.h" + +typedef struct southbridge_intel_bd82x6x_config config_t; + +static inline u32 sir_read(struct device *dev, int idx) +{ + pci_write_config32(dev, SATA_SIRI, idx); + return pci_read_config32(dev, SATA_SIRD); +} + +static inline void sir_write(struct device *dev, int idx, u32 value) +{ + pci_write_config32(dev, SATA_SIRI, idx); + pci_write_config32(dev, SATA_SIRD, value); +} + +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 */ + + /* Enable BARs */ + pci_write_config16(dev, PCI_COMMAND, 0x0007); + + if (config->ide_legacy_combined) { + printk(BIOS_DEBUG, "SATA: Controller in combined mode.\n"); + + /* No AHCI: clear AHCI base */ + pci_write_config32(dev, 0x24, 0x00000000); + /* And without AHCI BAR no memory decoding */ + reg16 = pci_read_config16(dev, PCI_COMMAND); + reg16 &= ~PCI_COMMAND_MEMORY; + pci_write_config16(dev, PCI_COMMAND, reg16); + + pci_write_config8(dev, 0x09, 0x80); + + /* Set timings */ + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE | + IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS); + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE | + IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS | + IDE_PPE0 | IDE_IE0 | IDE_TIME0); + + /* Sync DMA */ + pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0); + pci_write_config16(dev, IDE_SDMA_TIM, 0x0200); + + /* Set IDE I/O Configuration */ + reg32 = + SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0; + pci_write_config32(dev, IDE_CONFIG, reg32); + + /* Port enable */ + reg16 = pci_read_config16(dev, 0x92); + reg16 &= ~0x3f; + reg16 |= config->sata_port_map; + pci_write_config16(dev, 0x92, reg16); + + /* SATA Initialization register */ + pci_write_config32(dev, 0x94, + ((config-> + sata_port_map ^ 0x3f) << 24) | 0x183); + } 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, 0x0b); + + /* Set timings */ + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE | + IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS); + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE | + IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS); + + /* Sync DMA */ + pci_write_config16(dev, IDE_SDMA_CNT, 0); + pci_write_config16(dev, IDE_SDMA_TIM, 0); + + /* Set IDE I/O Configuration */ + reg32 = SIG_MODE_PRI_NORMAL; // | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0; + pci_write_config32(dev, IDE_CONFIG, reg32); + + /* for AHCI, Port Enable is managed in memory mapped space */ + reg16 = pci_read_config16(dev, 0x92); + reg16 &= ~0x3f; /* 6 ports SKU + ORM */ + reg16 |= 0x8100 | config->sata_port_map; + pci_write_config16(dev, 0x92, reg16); + + /* SATA Initialization register */ + pci_write_config32(dev, 0x94, + ((config-> + sata_port_map ^ 0x3f) << 24) | 0x183 | + 0x40000000); + pci_write_config32(dev, 0x98, 0x00590200); + + /* Initialize AHCI memory-mapped space */ + abar = pci_read_config32(dev, PCI_BASE_ADDRESS_5); + printk(BIOS_DEBUG, "ABAR: %08X\n", abar); + /* CAP (HBA Capabilities) : enable power management */ + reg32 = read32(abar + 0x00); + reg32 |= 0x0c006000; // set PSC+SSC+SALP+SSS + reg32 &= ~0x00020060; // clear SXS+EMS+PMS + /* Set ISS, if available */ + if (config->sata_interface_speed_support) { + reg32 &= ~0x00f00000; + reg32 |= (config->sata_interface_speed_support & 0x03) + << 20; + } + write32(abar + 0x00, reg32); + /* PI (Ports implemented) */ + write32(abar + 0x0c, config->sata_port_map); + (void)read32(abar + 0x0c); /* Read back 1 */ + (void)read32(abar + 0x0c); /* Read back 2 */ + /* CAP2 (HBA Capabilities Extended) */ + reg32 = read32(abar + 0x24); + reg32 &= ~0x00000002; + write32(abar + 0x24, reg32); + /* VSP (Vendor Specific Register */ + reg32 = read32(abar + 0xa0); + reg32 &= ~0x00000005; + write32(abar + 0xa0, reg32); + } else { + printk(BIOS_DEBUG, "SATA: Controller in plain 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); + + /* Native mode capable on both primary and secondary (0xa) + * or'ed with enabled (0x50) = 0xf + */ + pci_write_config8(dev, 0x09, 0x8f); + + /* Set Interrupt Line */ + /* Interrupt Pin is set by D31IP.PIP */ + pci_write_config8(dev, INTR_LN, 0xff); + + /* Set timings */ + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE | + IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS | + IDE_PPE0 | IDE_IE0 | IDE_TIME0); + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE | + IDE_SITRE | IDE_ISP_3_CLOCKS | + IDE_RCT_1_CLOCKS | IDE_IE0 | IDE_TIME0); + + /* Sync DMA */ + pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0 | IDE_PSDE0); + pci_write_config16(dev, IDE_SDMA_TIM, 0x0201); + + /* Set IDE I/O Configuration */ + reg32 = + SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 | PCB0; + pci_write_config32(dev, IDE_CONFIG, reg32); + + /* Port enable */ + reg16 = pci_read_config16(dev, 0x92); + reg16 &= ~0x3f; + reg16 |= config->sata_port_map; + pci_write_config16(dev, 0x92, reg16); + + /* SATA Initialization register */ + pci_write_config32(dev, 0x94, + ((config-> + sata_port_map ^ 0x3f) << 24) | 0x183); + } + + /* Set Gen3 Transmitter settings if needed */ + if (config->sata_port0_gen3_tx) + pch_iobp_update(SATA_IOBP_SP0G3IR, 0, + config->sata_port0_gen3_tx); + + if (config->sata_port1_gen3_tx) + pch_iobp_update(SATA_IOBP_SP1G3IR, 0, + config->sata_port1_gen3_tx); + + /* Additional Programming Requirements */ + sir_write(dev, 0x04, 0x00000000); + sir_write(dev, 0x28, 0x0a000033); + reg32 = sir_read(dev, 0x54); + reg32 &= 0xff000000; + reg32 |= 0x555555; + sir_write(dev, 0x54, reg32); + sir_write(dev, 0x64, 0xcccccccc); + reg32 = sir_read(dev, 0x68); + reg32 &= 0xffff0000; + reg32 |= 0xcccc; + sir_write(dev, 0x68, reg32); + reg32 = sir_read(dev, 0x78); + reg32 &= 0x0000ffff; + reg32 |= 0x88880000; + sir_write(dev, 0x78, reg32); + sir_write(dev, 0x84, 0x001c7000); + sir_write(dev, 0x88, 0x88888888); + sir_write(dev, 0xa0, 0x001c7000); + // a4 + sir_write(dev, 0xc4, 0x0c0c0c0c); + sir_write(dev, 0xc8, 0x0c0c0c0c); + sir_write(dev, 0xd4, 0x10000000); + + pch_iobp_update(0xea004001, 0x3fffffff, 0xc0000000); + pch_iobp_update(0xea00408a, 0xfffffcff, 0x00000100); +} + +static void sata_enable(device_t dev) +{ + /* Get the chip configuration */ + config_t *config = dev->chip_info; + u16 map = 0; + + if (!config) + return; + + /* + * Set SATA controller mode early so the resource allocator can + * properly assign IO/Memory resources for the controller. + */ + if (config->sata_ahci) + map = 0x0060; + + map |= (config->sata_port_map ^ 0x3f) << 8; + + pci_write_config16(dev, 0x90, map); +} + +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[] = { 0x3b2e, 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/ibexpeak/smbus.c b/src/southbridge/intel/ibexpeak/smbus.c new file mode 100644 index 0000000000..ad7c8f9215 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/smbus.c @@ -0,0 +1,109 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <device/device.h> +#include <device/path.h> +#include <device/smbus.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <arch/io.h> +#include "pch.h" +#include "smbus.h" + +static void pch_smbus_init(device_t dev) +{ + struct resource *res; + u16 reg16; + + /* Enable clock gating */ + reg16 = pci_read_config32(dev, 0x80); + reg16 &= ~((1 << 8)|(1 << 10)|(1 << 12)|(1 << 14)); + pci_write_config32(dev, 0x80, reg16); + + /* Set Receive Slave Address */ + res = find_resource(dev, PCI_BASE_ADDRESS_4); + if (res) + outb(SMBUS_SLAVE_ADDR, res->base + SMB_RCV_SLVA); +} + +static int lsmbus_read_byte(device_t dev, u8 address) +{ + u16 device; + struct resource *res; + struct bus *pbus; + + device = dev->path.i2c.device; + pbus = get_pbus_smbus(dev); + res = find_resource(pbus->dev, 0x20); + + return do_smbus_read_byte(res->base, device, address); +} + +static struct smbus_bus_operations lops_smbus_bus = { + .read_byte = lsmbus_read_byte, +}; + +static void smbus_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations smbus_pci_ops = { + .set_subsystem = smbus_set_subsystem, +}; + +static void smbus_read_resources(device_t dev) +{ + struct resource *res = new_resource(dev, PCI_BASE_ADDRESS_4); + res->base = SMBUS_IO_BASE; + res->size = 32; + res->limit = res->base + res->size - 1; + res->flags = IORESOURCE_IO | IORESOURCE_FIXED | IORESOURCE_RESERVE | + IORESOURCE_STORED | IORESOURCE_ASSIGNED; + + /* Also add MMIO resource */ + res = pci_get_resource(dev, PCI_BASE_ADDRESS_0); +} + +static struct device_operations smbus_ops = { + .read_resources = smbus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .scan_bus = scan_static_bus, + .init = pch_smbus_init, + .ops_smbus_bus = &lops_smbus_bus, + .ops_pci = &smbus_pci_ops, +}; + +static const unsigned short pci_device_ids[] = { 0x1c22, 0x1e22, 0x3b30, 0 }; + +static const struct pci_driver pch_smbus __pci_driver = { + .ops = &smbus_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/southbridge/intel/ibexpeak/smbus.h b/src/southbridge/intel/ibexpeak/smbus.h new file mode 100644 index 0000000000..6858e7d176 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/smbus.h @@ -0,0 +1,245 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2005 Yinghai Lu <yinghailu@gmail.com> + * Copyright (C) 2009 coresystems GmbH + * Copyright (C) 2013 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <device/smbus_def.h> +#include "pch.h" + +static void smbus_delay(void) +{ + inb(0x80); +} + +static int smbus_wait_until_ready(u16 smbus_base) +{ + unsigned loops = SMBUS_TIMEOUT; + unsigned char byte; + do { + smbus_delay(); + if (--loops == 0) + break; + byte = inb(smbus_base + SMBHSTSTAT); + } while (byte & 1); + return loops ? 0 : -1; +} + +static int smbus_wait_until_done(u16 smbus_base) +{ + unsigned loops = SMBUS_TIMEOUT; + unsigned char byte; + do { + smbus_delay(); + if (--loops == 0) + break; + byte = inb(smbus_base + SMBHSTSTAT); + } while ((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0); + return loops ? 0 : -1; +} + +static int do_smbus_read_byte(unsigned smbus_base, unsigned device, unsigned address) +{ + unsigned char global_status_register; + unsigned char byte; + + if (smbus_wait_until_ready(smbus_base) < 0) { + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + } + /* Setup transaction */ + /* Disable interrupts */ + outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL); + /* Set the device I'm talking too */ + outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD); + /* Set the command/address... */ + outb(address & 0xff, smbus_base + SMBHSTCMD); + /* Set up for a byte data read */ + outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2), + (smbus_base + SMBHSTCTL)); + /* Clear any lingering errors, so the transaction will run */ + outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT); + + /* Clear the data byte... */ + outb(0, smbus_base + SMBHSTDAT0); + + /* Start the command */ + outb((inb(smbus_base + SMBHSTCTL) | 0x40), + smbus_base + SMBHSTCTL); + + /* Poll for transaction completion */ + if (smbus_wait_until_done(smbus_base) < 0) { + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + } + + global_status_register = inb(smbus_base + SMBHSTSTAT); + + /* Ignore the "In Use" status... */ + global_status_register &= ~(3 << 5); + + /* Read results of transaction */ + byte = inb(smbus_base + SMBHSTDAT0); + if (global_status_register != (1 << 1)) { + return SMBUS_ERROR; + } + return byte; +} + +#ifdef __PRE_RAM__ + +static int do_smbus_write_byte(unsigned smbus_base, unsigned device, unsigned address, unsigned data) +{ + unsigned char global_status_register; + + if (smbus_wait_until_ready(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + + /* Setup transaction */ + /* Disable interrupts */ + outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL); + /* Set the device I'm talking too */ + outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD); + /* Set the command/address... */ + outb(address & 0xff, smbus_base + SMBHSTCMD); + /* Set up for a byte data read */ + outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x2 << 2), + (smbus_base + SMBHSTCTL)); + /* Clear any lingering errors, so the transaction will run */ + outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT); + + /* Clear the data byte... */ + outb(data, smbus_base + SMBHSTDAT0); + + /* Start the command */ + outb((inb(smbus_base + SMBHSTCTL) | 0x40), + smbus_base + SMBHSTCTL); + + /* Poll for transaction completion */ + if (smbus_wait_until_done(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + + global_status_register = inb(smbus_base + SMBHSTSTAT); + + /* Ignore the "In Use" status... */ + global_status_register &= ~(3 << 5); + + /* Read results of transaction */ + if (global_status_register != (1 << 1)) + return SMBUS_ERROR; + + return 0; +} + +static int do_smbus_block_write(unsigned smbus_base, unsigned device, + unsigned cmd, unsigned bytes, const u8 *buf) +{ + u8 status; + + if (smbus_wait_until_ready(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + + /* Setup transaction */ + /* Disable interrupts */ + outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL); + /* Set the device I'm talking too */ + outb(((device & 0x7f) << 1) & ~0x01, smbus_base + SMBXMITADD); + /* Set the command/address... */ + outb(cmd & 0xff, smbus_base + SMBHSTCMD); + /* Set up for a block data write */ + outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x5 << 2), + (smbus_base + SMBHSTCTL)); + /* Clear any lingering errors, so the transaction will run */ + outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT); + + /* set number of bytes to transfer */ + outb(bytes, smbus_base + SMBHSTDAT0); + + outb(*buf++, smbus_base + SMBBLKDAT); + bytes--; + + /* Start the command */ + outb((inb(smbus_base + SMBHSTCTL) | 0x40), + smbus_base + SMBHSTCTL); + + while(!(inb(smbus_base + SMBHSTSTAT) & 1)); + /* Poll for transaction completion */ + do { + status = inb(smbus_base + SMBHSTSTAT); + if (status & ((1 << 4) | /* FAILED */ + (1 << 3) | /* BUS ERR */ + (1 << 2))) /* DEV ERR */ + return SMBUS_ERROR; + + if (status & 0x80) { /* Byte done */ + outb(*buf++, smbus_base + SMBBLKDAT); + outb(status, smbus_base + SMBHSTSTAT); + } + } while(status & 0x01); + + return 0; +} + +static int do_smbus_block_read(unsigned smbus_base, unsigned device, + unsigned cmd, unsigned bytes, u8 *buf) +{ + u8 status; + int bytes_read = 0; + if (smbus_wait_until_ready(smbus_base) < 0) + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + + /* Setup transaction */ + /* Disable interrupts */ + outb(inb(smbus_base + SMBHSTCTL) & (~1), smbus_base + SMBHSTCTL); + /* Set the device I'm talking too */ + outb(((device & 0x7f) << 1) | 1, smbus_base + SMBXMITADD); + /* Set the command/address... */ + outb(cmd & 0xff, smbus_base + SMBHSTCMD); + /* Set up for a block data read */ + outb((inb(smbus_base + SMBHSTCTL) & 0xe3) | (0x5 << 2), + (smbus_base + SMBHSTCTL)); + /* Clear any lingering errors, so the transaction will run */ + outb(inb(smbus_base + SMBHSTSTAT), smbus_base + SMBHSTSTAT); + + /* Start the command */ + outb((inb(smbus_base + SMBHSTCTL) | 0x40), + smbus_base + SMBHSTCTL); + + while(!(inb(smbus_base + SMBHSTSTAT) & 1)); + /* Poll for transaction completion */ + do { + status = inb(smbus_base + SMBHSTSTAT); + if (status & ((1 << 4) | /* FAILED */ + (1 << 3) | /* BUS ERR */ + (1 << 2))) /* DEV ERR */ + return SMBUS_ERROR; + + if (status & 0x80) { /* Byte done */ + *buf = inb(smbus_base + SMBBLKDAT); + buf++; + bytes_read++; + outb(status, smbus_base + SMBHSTSTAT); + if (--bytes == 1) { + /* indicate that next byte is the last one */ + outb(inb(smbus_base + SMBHSTCTL) | 0x20, + smbus_base + SMBHSTCTL); + } + } + } while(status & 0x01); + + return bytes_read; +} +#endif diff --git a/src/southbridge/intel/ibexpeak/smi.c b/src/southbridge/intel/ibexpeak/smi.c new file mode 100644 index 0000000000..2b85e2b842 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/smi.c @@ -0,0 +1,421 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + + +#include <device/device.h> +#include <device/pci.h> +#include <console/console.h> +#include <arch/io.h> +#include <cpu/cpu.h> +#include <cpu/x86/cache.h> +#include <cpu/x86/smm.h> +#include <string.h> +#include "pch.h" + +#if CONFIG_NORTHBRIDGE_INTEL_SANDYBRIDGE || CONFIG_NORTHBRIDGE_INTEL_IVYBRIDGE +#include "northbridge/intel/sandybridge/sandybridge.h" +#endif + +#if CONFIG_NORTHBRIDGE_INTEL_NEHALEM +#include "northbridge/intel/nehalem/nehalem.h" +#endif + +extern unsigned char _binary_smm_start; +extern unsigned char _binary_smm_end; + +/* While we read PMBASE dynamically in case it changed, let's + * initialize it with a sane value + */ +static u16 pmbase = DEFAULT_PMBASE; + +/** + * @brief read and clear PM1_STS + * @return PM1_STS register + */ +static u16 reset_pm1_status(void) +{ + u16 reg16; + + reg16 = inw(pmbase + PM1_STS); + /* set status bits are cleared by writing 1 to them */ + outw(reg16, pmbase + PM1_STS); + + return reg16; +} + +static void dump_pm1_status(u16 pm1_sts) +{ + printk(BIOS_DEBUG, "PM1_STS: "); + if (pm1_sts & (1 << 15)) printk(BIOS_DEBUG, "WAK "); + if (pm1_sts & (1 << 14)) printk(BIOS_DEBUG, "PCIEXPWAK "); + if (pm1_sts & (1 << 11)) printk(BIOS_DEBUG, "PRBTNOR "); + if (pm1_sts & (1 << 10)) printk(BIOS_DEBUG, "RTC "); + if (pm1_sts & (1 << 8)) printk(BIOS_DEBUG, "PWRBTN "); + if (pm1_sts & (1 << 5)) printk(BIOS_DEBUG, "GBL "); + if (pm1_sts & (1 << 4)) printk(BIOS_DEBUG, "BM "); + if (pm1_sts & (1 << 0)) printk(BIOS_DEBUG, "TMROF "); + printk(BIOS_DEBUG, "\n"); +} + +/** + * @brief read and clear SMI_STS + * @return SMI_STS register + */ +static u32 reset_smi_status(void) +{ + u32 reg32; + + reg32 = inl(pmbase + SMI_STS); + /* set status bits are cleared by writing 1 to them */ + outl(reg32, pmbase + SMI_STS); + + return reg32; +} + +static void dump_smi_status(u32 smi_sts) +{ + printk(BIOS_DEBUG, "SMI_STS: "); + if (smi_sts & (1 << 26)) printk(BIOS_DEBUG, "SPI "); + if (smi_sts & (1 << 25)) printk(BIOS_DEBUG, "EL_SMI "); + if (smi_sts & (1 << 21)) printk(BIOS_DEBUG, "MONITOR "); + if (smi_sts & (1 << 20)) printk(BIOS_DEBUG, "PCI_EXP_SMI "); + if (smi_sts & (1 << 18)) printk(BIOS_DEBUG, "INTEL_USB2 "); + if (smi_sts & (1 << 17)) printk(BIOS_DEBUG, "LEGACY_USB2 "); + if (smi_sts & (1 << 16)) printk(BIOS_DEBUG, "SMBUS_SMI "); + if (smi_sts & (1 << 15)) printk(BIOS_DEBUG, "SERIRQ_SMI "); + if (smi_sts & (1 << 14)) printk(BIOS_DEBUG, "PERIODIC "); + if (smi_sts & (1 << 13)) printk(BIOS_DEBUG, "TCO "); + if (smi_sts & (1 << 12)) printk(BIOS_DEBUG, "DEVMON "); + if (smi_sts & (1 << 11)) printk(BIOS_DEBUG, "MCSMI "); + if (smi_sts & (1 << 10)) printk(BIOS_DEBUG, "GPI "); + if (smi_sts & (1 << 9)) printk(BIOS_DEBUG, "GPE0 "); + if (smi_sts & (1 << 8)) printk(BIOS_DEBUG, "PM1 "); + if (smi_sts & (1 << 6)) printk(BIOS_DEBUG, "SWSMI_TMR "); + if (smi_sts & (1 << 5)) printk(BIOS_DEBUG, "APM "); + if (smi_sts & (1 << 4)) printk(BIOS_DEBUG, "SLP_SMI "); + if (smi_sts & (1 << 3)) printk(BIOS_DEBUG, "LEGACY_USB "); + if (smi_sts & (1 << 2)) printk(BIOS_DEBUG, "BIOS "); + printk(BIOS_DEBUG, "\n"); +} + + +/** + * @brief read and clear GPE0_STS + * @return GPE0_STS register + */ +static u32 reset_gpe0_status(void) +{ + u32 reg32; + + reg32 = inl(pmbase + GPE0_STS); + /* set status bits are cleared by writing 1 to them */ + outl(reg32, pmbase + GPE0_STS); + + return reg32; +} + +static void dump_gpe0_status(u32 gpe0_sts) +{ + int i; + printk(BIOS_DEBUG, "GPE0_STS: "); + for (i=31; i>= 16; i--) { + if (gpe0_sts & (1 << i)) printk(BIOS_DEBUG, "GPIO%d ", (i-16)); + } + if (gpe0_sts & (1 << 14)) printk(BIOS_DEBUG, "USB4 "); + if (gpe0_sts & (1 << 13)) printk(BIOS_DEBUG, "PME_B0 "); + if (gpe0_sts & (1 << 12)) printk(BIOS_DEBUG, "USB3 "); + if (gpe0_sts & (1 << 11)) printk(BIOS_DEBUG, "PME "); + if (gpe0_sts & (1 << 10)) printk(BIOS_DEBUG, "EL_SCI/BATLOW "); + if (gpe0_sts & (1 << 9)) printk(BIOS_DEBUG, "PCI_EXP "); + if (gpe0_sts & (1 << 8)) printk(BIOS_DEBUG, "RI "); + if (gpe0_sts & (1 << 7)) printk(BIOS_DEBUG, "SMB_WAK "); + if (gpe0_sts & (1 << 6)) printk(BIOS_DEBUG, "TCO_SCI "); + if (gpe0_sts & (1 << 5)) printk(BIOS_DEBUG, "AC97 "); + if (gpe0_sts & (1 << 4)) printk(BIOS_DEBUG, "USB2 "); + if (gpe0_sts & (1 << 3)) printk(BIOS_DEBUG, "USB1 "); + if (gpe0_sts & (1 << 2)) printk(BIOS_DEBUG, "HOT_PLUG "); + if (gpe0_sts & (1 << 0)) printk(BIOS_DEBUG, "THRM "); + printk(BIOS_DEBUG, "\n"); +} + + +/** + * @brief read and clear ALT_GP_SMI_STS + * @return ALT_GP_SMI_STS register + */ +static u16 reset_alt_gp_smi_status(void) +{ + u16 reg16; + + reg16 = inl(pmbase + ALT_GP_SMI_STS); + /* set status bits are cleared by writing 1 to them */ + outl(reg16, pmbase + ALT_GP_SMI_STS); + + return reg16; +} + +static void dump_alt_gp_smi_status(u16 alt_gp_smi_sts) +{ + int i; + printk(BIOS_DEBUG, "ALT_GP_SMI_STS: "); + for (i=15; i>= 0; i--) { + if (alt_gp_smi_sts & (1 << i)) printk(BIOS_DEBUG, "GPI%d ", i); + } + printk(BIOS_DEBUG, "\n"); +} + + + +/** + * @brief read and clear TCOx_STS + * @return TCOx_STS registers + */ +static u32 reset_tco_status(void) +{ + u32 tcobase = pmbase + 0x60; + u32 reg32; + + reg32 = inl(tcobase + 0x04); + /* set status bits are cleared by writing 1 to them */ + outl(reg32 & ~(1<<18), tcobase + 0x04); // Don't clear BOOT_STS before SECOND_TO_STS + if (reg32 & (1 << 18)) + outl(reg32 & (1<<18), tcobase + 0x04); // clear BOOT_STS + + return reg32; +} + + +static void dump_tco_status(u32 tco_sts) +{ + printk(BIOS_DEBUG, "TCO_STS: "); + if (tco_sts & (1 << 20)) printk(BIOS_DEBUG, "SMLINK_SLV "); + if (tco_sts & (1 << 18)) printk(BIOS_DEBUG, "BOOT "); + if (tco_sts & (1 << 17)) printk(BIOS_DEBUG, "SECOND_TO "); + if (tco_sts & (1 << 16)) printk(BIOS_DEBUG, "INTRD_DET "); + if (tco_sts & (1 << 12)) printk(BIOS_DEBUG, "DMISERR "); + if (tco_sts & (1 << 10)) printk(BIOS_DEBUG, "DMISMI "); + if (tco_sts & (1 << 9)) printk(BIOS_DEBUG, "DMISCI "); + if (tco_sts & (1 << 8)) printk(BIOS_DEBUG, "BIOSWR "); + if (tco_sts & (1 << 7)) printk(BIOS_DEBUG, "NEWCENTURY "); + if (tco_sts & (1 << 3)) printk(BIOS_DEBUG, "TIMEOUT "); + if (tco_sts & (1 << 2)) printk(BIOS_DEBUG, "TCO_INT "); + if (tco_sts & (1 << 1)) printk(BIOS_DEBUG, "SW_TCO "); + if (tco_sts & (1 << 0)) printk(BIOS_DEBUG, "NMI2SMI "); + printk(BIOS_DEBUG, "\n"); +} + + + +/** + * @brief Set the EOS bit + */ +static void smi_set_eos(void) +{ + u8 reg8; + + reg8 = inb(pmbase + SMI_EN); + reg8 |= EOS; + outb(reg8, pmbase + SMI_EN); +} + +extern uint8_t smm_relocation_start, smm_relocation_end; + +static void smm_relocate(void) +{ + u32 smi_en; + u16 pm1_en; + u32 gpe0_en; + + printk(BIOS_DEBUG, "Initializing SMM handler..."); + + 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; + } + + /* copy the SMM relocation code */ + memcpy((void *)0x38000, &smm_relocation_start, + &smm_relocation_end - &smm_relocation_start); + + printk(BIOS_DEBUG, "\n"); + dump_smi_status(reset_smi_status()); + dump_pm1_status(reset_pm1_status()); + dump_gpe0_status(reset_gpe0_status()); + dump_alt_gp_smi_status(reset_alt_gp_smi_status()); + dump_tco_status(reset_tco_status()); + + /* 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); + + /** + * There are several methods of raising a controlled SMI# via + * software, among them: + * - Writes to io 0xb2 (APMC) + * - Writes to the Local Apic ICR with Delivery mode SMI. + * + * Using the local apic is a bit more tricky. According to + * AMD Family 11 Processor BKDG no destination shorthand must be + * used. + * The whole SMM initialization is quite a bit hardware specific, so + * I'm not too worried about the better of the methods at the moment + */ + + /* raise an SMI interrupt */ + printk(BIOS_SPEW, " ... raise SMI#\n"); + outb(0x00, 0xb2); +} + +static int smm_handler_copied = 0; + +static void smm_install(void) +{ + device_t dev = dev_find_slot(0, PCI_DEVFN(0, 0)); + u32 smm_base = 0xa0000; + struct ied_header ied = { + .signature = "INTEL RSVD", + .size = IED_SIZE, + .reserved = {0}, + }; + + /* The first CPU running this gets to copy the SMM handler. But not all + * of them. + */ + if (smm_handler_copied) + return; + smm_handler_copied = 1; + + /* enable the SMM memory window */ + pci_write_config8(dev, SMRAM, D_OPEN | G_SMRAME | C_BASE_SEG); + +#if CONFIG_SMM_TSEG + smm_base = pci_read_config32(dev, TSEG) & ~1; +#endif + + /* copy the real SMM handler */ + printk(BIOS_DEBUG, "Installing SMM handler to 0x%08x\n", smm_base); + memcpy((void *)smm_base, &_binary_smm_start, + (size_t)(&_binary_smm_end - &_binary_smm_start)); + + /* copy the IED header into place */ + if (CONFIG_SMM_TSEG_SIZE > IED_SIZE) { + /* Top of TSEG region */ + smm_base += CONFIG_SMM_TSEG_SIZE - IED_SIZE; + printk(BIOS_DEBUG, "Installing IED header to 0x%08x\n", + smm_base); + memcpy((void *)smm_base, &ied, sizeof(ied)); + } + wbinvd(); + + /* close the SMM memory window and enable normal SMM */ + pci_write_config8(dev, SMRAM, G_SMRAME | C_BASE_SEG); +} + +void smm_init(void) +{ +#if CONFIG_ELOG + /* Log events from chipset before clearing */ + pch_log_state(); +#endif + + /* Put SMM code to 0xa0000 */ + smm_install(); + + /* Put relocation code to 0x38000 and relocate SMBASE */ + smm_relocate(); + + /* We're done. Make sure SMIs can happen! */ + smi_set_eos(); +} + +void smm_lock(void) +{ + /* LOCK the SMM memory window and enable normal SMM. + * After running this function, only a full reset can + * make the SMM registers writable again. + */ + printk(BIOS_DEBUG, "Locking SMM.\n"); + pci_write_config8(dev_find_slot(0, PCI_DEVFN(0, 0)), SMRAM, + D_LCK | G_SMRAME | C_BASE_SEG); +} + +void smm_setup_structures(void *gnvs, void *tcg, void *smi1) +{ + /* + * 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/ibexpeak/smihandler.c b/src/southbridge/intel/ibexpeak/smihandler.c new file mode 100644 index 0000000000..5ee8ef9538 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/smihandler.c @@ -0,0 +1,848 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <types.h> +#include <arch/hlt.h> +#include <arch/io.h> +#include <console/console.h> +#include <cpu/x86/cache.h> +#include <device/pci_def.h> +#include <cpu/x86/smm.h> +#include <elog.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/nehalem/nehalem.h" +#include <arch/pci_mmio_cfg.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 *)0x0; +global_nvs_t *smm_get_gnvs(void) +{ + return gnvs; +} + +#if CONFIG_SMM_TSEG +static u32 tseg_base = 0; +u32 smi_get_tseg_base(void) +{ + if (!tseg_base) + tseg_base = pci_read_config32(PCI_DEV(0, 0, 0), TSEG) & ~1; + return tseg_base; +} +void tseg_relocate(void **ptr) +{ + /* Adjust pointer with TSEG base */ + if (*ptr && *ptr < (void*)smi_get_tseg_base()) + *ptr = (void *)(((u8*)*ptr) + smi_get_tseg_base()); +} +#endif + +/** + * @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 = pci_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 xhci_sleep(u8 slp_typ) +{ + u32 reg32, xhci_bar; + u16 reg16; + + switch (slp_typ) { + case SLP_TYP_S3: + case SLP_TYP_S4: + reg16 = pci_read_config16(PCH_XHCI_DEV, 0x74); + reg16 &= ~0x03UL; + pci_write_config32(PCH_XHCI_DEV, 0x74, reg16); + + reg32 = pci_read_config32(PCH_XHCI_DEV, PCI_COMMAND); + reg32 |= (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + pci_write_config32(PCH_XHCI_DEV, PCI_COMMAND, reg32); + + xhci_bar = pci_read_config32(PCH_XHCI_DEV, + PCI_BASE_ADDRESS_0) & ~0xFUL; + + if ((xhci_bar + 0x4C0) & 1) + pch_iobp_update(0xEC000082, ~0UL, (3 << 2)); + if ((xhci_bar + 0x4D0) & 1) + pch_iobp_update(0xEC000182, ~0UL, (3 << 2)); + if ((xhci_bar + 0x4E0) & 1) + pch_iobp_update(0xEC000282, ~0UL, (3 << 2)); + if ((xhci_bar + 0x4F0) & 1) + pch_iobp_update(0xEC000382, ~0UL, (3 << 2)); + + reg32 = pci_read_config32(PCH_XHCI_DEV, PCI_COMMAND); + reg32 &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + pci_write_config32(PCH_XHCI_DEV, PCI_COMMAND, reg32); + + reg16 = pci_read_config16(PCH_XHCI_DEV, 0x74); + reg16 |= 0x03; + pci_write_config16(PCH_XHCI_DEV, 0x74, reg16); + break; + + case SLP_TYP_S5: + reg16 = pci_read_config16(PCH_XHCI_DEV, 0x74); + reg16 |= ((1 << 8) | 0x03); + pci_write_config16(PCH_XHCI_DEV, 0x74, reg16); + break; + } +} + + +static void southbridge_smi_sleep(unsigned int node, smm_state_save_area_t *state_save) +{ + u8 reg8; + u32 reg32; + u8 slp_typ; + u8 s5pwr = CONFIG_MAINBOARD_POWER_ON_AFTER_POWER_FAIL; + + // save and recover RTC port values + u8 tmp70, tmp72; + tmp70 = inb(0x70); + tmp72 = inb(0x72); + get_option(&s5pwr, "power_on_after_fail"); + outb(tmp70, 0x70); + outb(tmp72, 0x72); + + void (*mainboard_sleep)(u8 slp_typ) = mainboard_smi_sleep; + + /* 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; + + if (smm_get_gnvs()->xhci) + xhci_sleep(slp_typ); + + /* Do any mainboard sleep handling */ + tseg_relocate((void **)&mainboard_sleep); + if (mainboard_sleep) + mainboard_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 = pci_read_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3); + if (s5pwr == MAINBOARD_POWER_ON) { + reg8 &= ~1; + } else { + reg8 |= 1; + } + pci_write_config8(PCI_DEV(0, 0x1f, 0), GEN_PMCON_3, reg8); + + /* also iterates over all bridges on bus 0 */ + busmaster_disable_on_bus(0); + break; + default: printk(BIOS_DEBUG, "SMI#: ERROR: SLP_TYP reserved\n"); break; + } + + /* Write back to the SLP register to cause the originally intended + * event again. We need to set BIT13 (SLP_EN) though to make the + * sleep happen. + */ + outl(reg32 | SLP_EN, pmbase + PM1_CNT); + + /* Make sure to stop executing code here for S3/S4/S5 */ + if (slp_typ > 1) + hlt(); + + /* In most sleep states, the code flow of this function ends at + * the line above. However, if we entered sleep state S1 and wake + * up again, we will continue to execute code in this function. + */ + reg32 = inl(pmbase + PM1_CNT); + if (reg32 & SCI_EN) { + /* The OS is not an ACPI OS, so we set the state to S0 */ + reg32 &= ~(SLP_EN | SLP_TYP); + outl(reg32, pmbase + PM1_CNT); + } +} + +/* + * 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; + u32 base = smi_get_tseg_base() + SMM_EM64T101_SAVE_STATE_OFFSET; + int node; + + /* Check all nodes looking for the one that issued the IO */ + for (node = 0; node < CONFIG_MAX_CPUS; node++) { + state = (em64t101_smm_state_save_area_t *) + (base - (node * 0x400)); + + /* 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(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 pmctrl; + u8 reg8; + int (*mainboard_apmc)(u8 apmc) = mainboard_smi_apmc; + 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 + } + + tseg_relocate((void **)&mainboard_apmc); + if (mainboard_apmc) + mainboard_apmc(reg8); +} + +static void southbridge_smi_pm1(unsigned int node, smm_state_save_area_t *state_save) +{ + 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(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 gpe0_sts; + + gpe0_sts = reset_gpe0_status(); + dump_gpe0_status(gpe0_sts); +} + +static void southbridge_smi_gpi(unsigned int node, smm_state_save_area_t *state_save) +{ + void (*mainboard_gpi)(u32 gpi_sts) = mainboard_smi_gpi; + u16 reg16; + reg16 = inw(pmbase + ALT_GP_SMI_STS); + outw(reg16, pmbase + ALT_GP_SMI_STS); + + reg16 &= inw(pmbase + ALT_GP_SMI_EN); + + tseg_relocate((void **)&mainboard_gpi); + if (mainboard_gpi) { + mainboard_gpi(reg16); + } else { + if (reg16) + printk(BIOS_DEBUG, "GPI (mask %04x)\n",reg16); + } + + outw(reg16, pmbase + ALT_GP_SMI_STS); +} + +static void southbridge_smi_mc(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 reg32; + + reg32 = inl(pmbase + SMI_EN); + + /* Are periodic SMIs enabled? */ + if ((reg32 & MCSMI_EN) == 0) + return; + + printk(BIOS_DEBUG, "Microcontroller SMI.\n"); +} + + + +static void southbridge_smi_tco(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 tco_sts; + + tco_sts = reset_tco_status(); + + /* Any TCO event? */ + if (!tco_sts) + return; + + if (tco_sts & (1 << 8)) { // BIOSWR + u8 bios_cntl; + + bios_cntl = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0xdc); + + if (bios_cntl & 1) { + /* BWE is RW, so the SMI was caused by a + * write to BWE, not by a write to the BIOS + */ + + /* This is the place where we notice someone + * is trying to tinker with the BIOS. We are + * trying to be nice and just ignore it. A more + * resolute answer would be to power down the + * box. + */ + printk(BIOS_DEBUG, "Switching back to RO\n"); + pci_write_config32(PCI_DEV(0, 0x1f, 0), 0xdc, (bios_cntl & ~1)); + } /* No else for now? */ + } else if (tco_sts & (1 << 3)) { /* TIMEOUT */ + /* Handle TCO timeout */ + printk(BIOS_DEBUG, "TCO Timeout.\n"); + } else if (!tco_sts) { + dump_tco_status(tco_sts); + } +} + +static void southbridge_smi_periodic(unsigned int node, smm_state_save_area_t *state_save) +{ + u32 reg32; + + reg32 = inl(pmbase + SMI_EN); + + /* Are periodic SMIs enabled? */ + if ((reg32 & PERIODIC_EN) == 0) + return; + + printk(BIOS_DEBUG, "Periodic SMI.\n"); +} + +static void southbridge_smi_monitor(unsigned int node, smm_state_save_area_t *state_save) +{ +#define IOTRAP(x) (trap_sts & (1 << x)) + u32 trap_sts, trap_cycle; + u32 data, mask = 0; + int i; + + trap_sts = RCBA32(0x1e00); // TRSR - Trap Status Register + RCBA32(0x1e00) = trap_sts; // Clear trap(s) in TRSR + + trap_cycle = RCBA32(0x1e10); + for (i=16; i<20; i++) { + if (trap_cycle & (1 << i)) + mask |= (0xff << ((i - 16) << 2)); + } + + + /* IOTRAP(3) SMI function call */ + if (IOTRAP(3)) { + if (gnvs && gnvs->smif) + io_trap_handler(gnvs->smif); // call function smif + return; + } + + /* IOTRAP(2) currently unused + * IOTRAP(1) currently unused */ + + /* IOTRAP(0) SMIC */ + if (IOTRAP(0)) { + if (!(trap_cycle & (1 << 24))) { // It's a write + printk(BIOS_DEBUG, "SMI1 command\n"); + data = RCBA32(0x1e18); + data &= mask; + // if (smi1) + // southbridge_smi_command(data); + // return; + } + // Fall through to debug + } + + printk(BIOS_DEBUG, " trapped io address = 0x%x\n", trap_cycle & 0xfffc); + for (i=0; i < 4; i++) if(IOTRAP(i)) printk(BIOS_DEBUG, " TRAPĀ = %d\n", i); + printk(BIOS_DEBUG, " AHBE = %x\n", (trap_cycle >> 16) & 0xf); + printk(BIOS_DEBUG, " MASK = 0x%08x\n", mask); + printk(BIOS_DEBUG, " read/write: %s\n", (trap_cycle & (1 << 24)) ? "read" : "write"); + + if (!(trap_cycle & (1 << 24))) { + /* Write Cycle */ + data = RCBA32(0x1e18); + printk(BIOS_DEBUG, " iotrap written data = 0x%08x\n", data); + } +#undef IOTRAP +} + +typedef void (*smi_handler_t)(unsigned int node, + smm_state_save_area_t *state_save); + +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# + * + * @param smm_revision revision of the smm state save map + */ + +void southbridge_smi_handler(unsigned int node, smm_state_save_area_t *state_save) +{ + int i, dump = 0; + u32 smi_sts; + + /* Update global variable pmbase */ + pmbase = pci_read_config16(PCI_DEV(0, 0x1f, 0), 0x40) & 0xfffc; + + /* We need to clear the SMI status registers, or we won't see what's + * happening in the following calls. + */ + smi_sts = reset_smi_status(); + + /* 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]) { +#if CONFIG_SMM_TSEG + smi_handler_t handler = (smi_handler_t) + ((u8*)southbridge_smi[i] + + smi_get_tseg_base()); + if (handler) + handler(node, state_save); +#else + southbridge_smi[i](node, state_save); +#endif + } 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/ibexpeak/spi.c b/src/southbridge/intel/ibexpeak/spi.c new file mode 100644 index 0000000000..e263b7f5f5 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/spi.c @@ -0,0 +1,746 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* This file is derived from the flashrom project. */ +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <delay.h> +#include <arch/io.h> +#include <console/console.h> +#include <device/pci_ids.h> + +#include <spi-generic.h> + +#define min(a, b) ((a)<(b)?(a):(b)) + +#ifdef __SMM__ +#include <arch/pci_mmio_cfg.h> +#define pci_read_config_byte(dev, reg, targ)\ + *(targ) = pci_read_config8(dev, reg) +#define pci_read_config_word(dev, reg, targ)\ + *(targ) = pci_read_config16(dev, reg) +#define pci_read_config_dword(dev, reg, targ)\ + *(targ) = pci_read_config32(dev, reg) +#define pci_write_config_byte(dev, reg, val)\ + pci_write_config8(dev, reg, val) +#define pci_write_config_word(dev, reg, val)\ + pci_write_config16(dev, reg, val) +#define pci_write_config_dword(dev, reg, val)\ + pci_write_config32(dev, reg, val) +#else /* !__SMM__ */ +#include <device/device.h> +#include <device/pci.h> +#define pci_read_config_byte(dev, reg, targ)\ + *(targ) = pci_read_config8(dev, reg) +#define pci_read_config_word(dev, reg, targ)\ + *(targ) = pci_read_config16(dev, reg) +#define pci_read_config_dword(dev, reg, targ)\ + *(targ) = pci_read_config32(dev, reg) +#define pci_write_config_byte(dev, reg, val)\ + pci_write_config8(dev, reg, val) +#define pci_write_config_word(dev, reg, val)\ + pci_write_config16(dev, reg, val) +#define pci_write_config_dword(dev, reg, val)\ + pci_write_config32(dev, reg, val) +#endif /* !__SMM__ */ + +typedef struct spi_slave ich_spi_slave; + +static int ichspi_lock = 0; + +typedef struct ich7_spi_regs { + uint16_t spis; + uint16_t spic; + uint32_t spia; + uint64_t spid[8]; + uint64_t _pad; + uint32_t bbar; + uint16_t preop; + uint16_t optype; + uint8_t opmenu[8]; +} __attribute__((packed)) ich7_spi_regs; + +typedef struct ich9_spi_regs { + uint32_t bfpr; + uint16_t hsfs; + uint16_t hsfc; + uint32_t faddr; + uint32_t _reserved0; + uint32_t fdata[16]; + uint32_t frap; + uint32_t freg[5]; + uint32_t _reserved1[3]; + uint32_t pr[5]; + uint32_t _reserved2[2]; + uint8_t ssfs; + uint8_t ssfc[3]; + uint16_t preop; + uint16_t optype; + uint8_t opmenu[8]; + uint32_t bbar; + uint8_t _reserved3[12]; + uint32_t fdoc; + uint32_t fdod; + uint8_t _reserved4[8]; + uint32_t afc; + uint32_t lvscc; + uint32_t uvscc; + uint8_t _reserved5[4]; + uint32_t fpb; + uint8_t _reserved6[28]; + uint32_t srdl; + uint32_t srdc; + uint32_t srd; +} __attribute__((packed)) ich9_spi_regs; + +typedef struct ich_spi_controller { + int locked; + + uint8_t *opmenu; + int menubytes; + uint16_t *preop; + uint16_t *optype; + uint32_t *addr; + uint8_t *data; + unsigned databytes; + uint8_t *status; + uint16_t *control; + uint32_t *bbar; +} ich_spi_controller; + +static ich_spi_controller cntlr; + +enum { + SPIS_SCIP = 0x0001, + SPIS_GRANT = 0x0002, + SPIS_CDS = 0x0004, + SPIS_FCERR = 0x0008, + SSFS_AEL = 0x0010, + SPIS_LOCK = 0x8000, + SPIS_RESERVED_MASK = 0x7ff0, + SSFS_RESERVED_MASK = 0x7fe2 +}; + +enum { + SPIC_SCGO = 0x000002, + SPIC_ACS = 0x000004, + SPIC_SPOP = 0x000008, + SPIC_DBC = 0x003f00, + SPIC_DS = 0x004000, + SPIC_SME = 0x008000, + SSFC_SCF_MASK = 0x070000, + SSFC_RESERVED = 0xf80000 +}; + +enum { + HSFS_FDONE = 0x0001, + HSFS_FCERR = 0x0002, + HSFS_AEL = 0x0004, + HSFS_BERASE_MASK = 0x0018, + HSFS_BERASE_SHIFT = 3, + HSFS_SCIP = 0x0020, + HSFS_FDOPSS = 0x2000, + HSFS_FDV = 0x4000, + HSFS_FLOCKDN = 0x8000 +}; + +enum { + HSFC_FGO = 0x0001, + HSFC_FCYCLE_MASK = 0x0006, + HSFC_FCYCLE_SHIFT = 1, + HSFC_FDBC_MASK = 0x3f00, + HSFC_FDBC_SHIFT = 8, + HSFC_FSMIE = 0x8000 +}; + +enum { + SPI_OPCODE_TYPE_READ_NO_ADDRESS = 0, + SPI_OPCODE_TYPE_WRITE_NO_ADDRESS = 1, + SPI_OPCODE_TYPE_READ_WITH_ADDRESS = 2, + SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS = 3 +}; + +#if CONFIG_DEBUG_SPI_FLASH + +static u8 readb_(const void *addr) +{ + u8 v = read8((unsigned long)addr); + printk(BIOS_DEBUG, "read %2.2x from %4.4x\n", + v, ((unsigned) addr & 0xffff) - 0xf020); + return v; +} + +static u16 readw_(const void *addr) +{ + u16 v = read16((unsigned long)addr); + printk(BIOS_DEBUG, "read %4.4x from %4.4x\n", + v, ((unsigned) addr & 0xffff) - 0xf020); + return v; +} + +static u32 readl_(const void *addr) +{ + u32 v = read32((unsigned long)addr); + printk(BIOS_DEBUG, "read %8.8x from %4.4x\n", + v, ((unsigned) addr & 0xffff) - 0xf020); + return v; +} + +static void writeb_(u8 b, const void *addr) +{ + write8((unsigned long)addr, b); + printk(BIOS_DEBUG, "wrote %2.2x to %4.4x\n", + b, ((unsigned) addr & 0xffff) - 0xf020); +} + +static void writew_(u16 b, const void *addr) +{ + write16((unsigned long)addr, b); + printk(BIOS_DEBUG, "wrote %4.4x to %4.4x\n", + b, ((unsigned) addr & 0xffff) - 0xf020); +} + +static void writel_(u32 b, const void *addr) +{ + write32((unsigned long)addr, b); + printk(BIOS_DEBUG, "wrote %8.8x to %4.4x\n", + b, ((unsigned) addr & 0xffff) - 0xf020); +} + +#else /* CONFIG_DEBUG_SPI_FLASH ^^^ enabled vvv NOT enabled */ + +#define readb_(a) read8((uint32_t)a) +#define readw_(a) read16((uint32_t)a) +#define readl_(a) read32((uint32_t)a) +#define writeb_(val, addr) write8((uint32_t)addr, val) +#define writew_(val, addr) write16((uint32_t)addr, val) +#define writel_(val, addr) write32((uint32_t)addr, val) + +#endif /* CONFIG_DEBUG_SPI_FLASH ^^^ NOT enabled */ + +static void write_reg(const void *value, void *dest, uint32_t size) +{ + const uint8_t *bvalue = value; + uint8_t *bdest = dest; + + while (size >= 4) { + writel_(*(const uint32_t *)bvalue, bdest); + bdest += 4; bvalue += 4; size -= 4; + } + while (size) { + writeb_(*bvalue, bdest); + bdest++; bvalue++; size--; + } +} + +static void read_reg(const void *src, void *value, uint32_t size) +{ + const uint8_t *bsrc = src; + uint8_t *bvalue = value; + + while (size >= 4) { + *(uint32_t *)bvalue = readl_(bsrc); + bsrc += 4; bvalue += 4; size -= 4; + } + while (size) { + *bvalue = readb_(bsrc); + bsrc++; bvalue++; size--; + } +} + +static void ich_set_bbar(uint32_t minaddr) +{ + const uint32_t bbar_mask = 0x00ffff00; + uint32_t ichspi_bbar; + + minaddr &= bbar_mask; + ichspi_bbar = readl_(cntlr.bbar) & ~bbar_mask; + ichspi_bbar |= minaddr; + writel_(ichspi_bbar, cntlr.bbar); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + printk(BIOS_DEBUG, "spi_cs_is_valid used but not implemented\n"); + return 0; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + ich_spi_slave *slave = malloc(sizeof(*slave)); + + if (!slave) { + printk(BIOS_DEBUG, "ICH SPI: Bad allocation\n"); + return NULL; + } + + memset(slave, 0, sizeof(*slave)); + + slave->bus = bus; + slave->cs = cs; + return slave; +} + +/* + * Check if this device ID matches one of supported Intel PCH devices. + * + * Return the ICH version if there is a match, or zero otherwise. + */ +static inline int get_ich_version(uint16_t device_id) +{ + if (device_id == PCI_DEVICE_ID_INTEL_TGP_LPC) + return 7; + + if ((device_id >= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MIN && + device_id <= PCI_DEVICE_ID_INTEL_COUGARPOINT_LPC_MAX) || + (device_id >= PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MIN && + device_id <= PCI_DEVICE_ID_INTEL_PANTHERPOINT_LPC_MAX) + || device_id == 0x3b07) + return 9; + + return 0; +} + +void spi_init(void) +{ + int ich_version = 0; + + uint8_t *rcrb; /* Root Complex Register Block */ + uint32_t rcba; /* Root Complex Base Address */ + uint8_t bios_cntl; + device_t dev; + uint32_t ids; + uint16_t vendor_id, device_id; + +#ifdef __SMM__ + dev = PCI_DEV(0, 31, 0); +#else + dev = dev_find_slot(0, PCI_DEVFN(31, 0)); +#endif + pci_read_config_dword(dev, 0, &ids); + vendor_id = ids; + device_id = (ids >> 16); + + if (vendor_id != PCI_VENDOR_ID_INTEL) { + printk(BIOS_DEBUG, "ICH SPI: No ICH found.\n"); + return; + } + + ich_version = get_ich_version(device_id); + + if (!ich_version) { + printk(BIOS_DEBUG, "ICH SPI: No known ICH found.\n"); + return; + } + + pci_read_config_dword(dev, 0xf0, &rcba); + /* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable. */ + rcrb = (uint8_t *)(rcba & 0xffffc000); + switch (ich_version) { + case 7: + { + const uint16_t ich7_spibar_offset = 0x3020; + ich7_spi_regs *ich7_spi = + (ich7_spi_regs *)(rcrb + ich7_spibar_offset); + + ichspi_lock = readw_(&ich7_spi->spis) & SPIS_LOCK; + cntlr.opmenu = ich7_spi->opmenu; + cntlr.menubytes = sizeof(ich7_spi->opmenu); + cntlr.optype = &ich7_spi->optype; + cntlr.addr = &ich7_spi->spia; + cntlr.data = (uint8_t *)ich7_spi->spid; + cntlr.databytes = sizeof(ich7_spi->spid); + cntlr.status = (uint8_t *)&ich7_spi->spis; + cntlr.control = &ich7_spi->spic; + cntlr.bbar = &ich7_spi->bbar; + cntlr.preop = &ich7_spi->preop; + break; + } + case 9: + { + const uint16_t ich9_spibar_offset = 0x3800; + ich9_spi_regs *ich9_spi = + (ich9_spi_regs *)(rcrb + ich9_spibar_offset); + ichspi_lock = readw_(&ich9_spi->hsfs) & HSFS_FLOCKDN; + cntlr.opmenu = ich9_spi->opmenu; + cntlr.menubytes = sizeof(ich9_spi->opmenu); + cntlr.optype = &ich9_spi->optype; + cntlr.addr = &ich9_spi->faddr; + cntlr.data = (uint8_t *)ich9_spi->fdata; + cntlr.databytes = sizeof(ich9_spi->fdata); + cntlr.status = &ich9_spi->ssfs; + cntlr.control = (uint16_t *)ich9_spi->ssfc; + cntlr.bbar = &ich9_spi->bbar; + cntlr.preop = &ich9_spi->preop; + break; + } + default: + printk(BIOS_DEBUG, "ICH SPI: Unrecognized ICH version %d.\n", ich_version); + } + + ich_set_bbar(0); + + /* Disable the BIOS write protect so write commands are allowed. */ + pci_read_config_byte(dev, 0xdc, &bios_cntl); + switch (ich_version) { + case 9: + /* Deassert SMM BIOS Write Protect Disable. */ + bios_cntl &= ~(1 << 5); + break; + + default: + break; + } + pci_write_config_byte(dev, 0xdc, bios_cntl | 0x1); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ +} + +void spi_cs_activate(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + /* Handled by ICH automatically. */ +} + +typedef struct spi_transaction { + const uint8_t *out; + uint32_t bytesout; + uint8_t *in; + uint32_t bytesin; + uint8_t type; + uint8_t opcode; + uint32_t offset; +} spi_transaction; + +static inline void spi_use_out(spi_transaction *trans, unsigned bytes) +{ + trans->out += bytes; + trans->bytesout -= bytes; +} + +static inline void spi_use_in(spi_transaction *trans, unsigned bytes) +{ + trans->in += bytes; + trans->bytesin -= bytes; +} + +static void spi_setup_type(spi_transaction *trans) +{ + trans->type = 0xFF; + + /* Try to guess spi type from read/write sizes. */ + if (trans->bytesin == 0) { + if (trans->bytesout > 4) + /* + * If bytesin = 0 and bytesout > 4, we presume this is + * a write data operation, which is accompanied by an + * address. + */ + trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS; + else + trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS; + return; + } + + if (trans->bytesout == 1) { /* and bytesin is > 0 */ + trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS; + return; + } + + if (trans->bytesout == 4) { /* and bytesin is > 0 */ + trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS; + } + + /* Fast read command is called with 5 bytes instead of 4 */ + if (trans->out[0] == SPI_OPCODE_FAST_READ && trans->bytesout == 5) { + trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS; + --trans->bytesout; + } +} + +static int spi_setup_opcode(spi_transaction *trans) +{ + uint16_t optypes; + uint8_t opmenu[cntlr.menubytes]; + + trans->opcode = trans->out[0]; + spi_use_out(trans, 1); + if (!ichspi_lock) { + /* The lock is off, so just use index 0. */ + writeb_(trans->opcode, cntlr.opmenu); + optypes = readw_(cntlr.optype); + optypes = (optypes & 0xfffc) | (trans->type & 0x3); + writew_(optypes, cntlr.optype); + return 0; + } else { + /* The lock is on. See if what we need is on the menu. */ + uint8_t optype; + uint16_t opcode_index; + + /* Write Enable is handled as atomic prefix */ + if (trans->opcode == SPI_OPCODE_WREN) + return 0; + + read_reg(cntlr.opmenu, opmenu, sizeof(opmenu)); + for (opcode_index = 0; opcode_index < cntlr.menubytes; + opcode_index++) { + if (opmenu[opcode_index] == trans->opcode) + break; + } + + if (opcode_index == cntlr.menubytes) { + printk(BIOS_DEBUG, "ICH SPI: Opcode %x not found\n", + trans->opcode); + return -1; + } + + optypes = readw_(cntlr.optype); + optype = (optypes >> (opcode_index * 2)) & 0x3; + if (trans->type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS && + optype == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS && + trans->bytesout >= 3) { + /* We guessed wrong earlier. Fix it up. */ + trans->type = optype; + } + if (optype != trans->type) { + printk(BIOS_DEBUG, "ICH SPI: Transaction doesn't fit type %d\n", + optype); + return -1; + } + return opcode_index; + } +} + +static int spi_setup_offset(spi_transaction *trans) +{ + /* Separate the SPI address and data. */ + switch (trans->type) { + case SPI_OPCODE_TYPE_READ_NO_ADDRESS: + case SPI_OPCODE_TYPE_WRITE_NO_ADDRESS: + return 0; + case SPI_OPCODE_TYPE_READ_WITH_ADDRESS: + case SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS: + trans->offset = ((uint32_t)trans->out[0] << 16) | + ((uint32_t)trans->out[1] << 8) | + ((uint32_t)trans->out[2] << 0); + spi_use_out(trans, 3); + return 1; + default: + printk(BIOS_DEBUG, "Unrecognized SPI transaction type %#x\n", trans->type); + return -1; + } +} + +/* + * Wait for up to 60ms til status register bit(s) turn 1 (in case wait_til_set + * below is True) or 0. In case the wait was for the bit(s) to set - write + * those bits back, which would cause resetting them. + * + * Return the last read status value on success or -1 on failure. + */ +static int ich_status_poll(u16 bitmask, int wait_til_set) +{ + int timeout = 6000; /* This will result in 60 ms */ + u16 status = 0; + + while (timeout--) { + status = readw_(cntlr.status); + if (wait_til_set ^ ((status & bitmask) == 0)) { + if (wait_til_set) + writew_((status & bitmask), cntlr.status); + return status; + } + udelay(10); + } + + printk(BIOS_DEBUG, "ICH SPI: SCIP timeout, read %x, expected %x\n", + status, bitmask); + return -1; +} + +int spi_xfer(struct spi_slave *slave, const void *dout, + unsigned int bitsout, void *din, unsigned int bitsin) +{ + uint16_t control; + int16_t opcode_index; + int with_address; + int status; + + spi_transaction trans = { + dout, bitsout / 8, + din, bitsin / 8, + 0xff, 0xff, 0 + }; + + /* There has to always at least be an opcode. */ + if (!bitsout || !dout) { + printk(BIOS_DEBUG, "ICH SPI: No opcode for transfer\n"); + return -1; + } + /* Make sure if we read something we have a place to put it. */ + if (bitsin != 0 && !din) { + printk(BIOS_DEBUG, "ICH SPI: Read but no target buffer\n"); + return -1; + } + /* Right now we don't support writing partial bytes. */ + if (bitsout % 8 || bitsin % 8) { + printk(BIOS_DEBUG, "ICH SPI: Accessing partial bytes not supported\n"); + return -1; + } + + if (ich_status_poll(SPIS_SCIP, 0) == -1) + return -1; + + writew_(SPIS_CDS | SPIS_FCERR, cntlr.status); + + spi_setup_type(&trans); + if ((opcode_index = spi_setup_opcode(&trans)) < 0) + return -1; + if ((with_address = spi_setup_offset(&trans)) < 0) + return -1; + + if (trans.opcode == SPI_OPCODE_WREN) { + /* + * Treat Write Enable as Atomic Pre-Op if possible + * in order to prevent the Management Engine from + * issuing a transaction between WREN and DATA. + */ + if (!ichspi_lock) + writew_(trans.opcode, cntlr.preop); + return 0; + } + + /* Preset control fields */ + control = SPIC_SCGO | ((opcode_index & 0x07) << 4); + + /* Issue atomic preop cycle if needed */ + if (readw_(cntlr.preop)) + control |= SPIC_ACS; + + if (!trans.bytesout && !trans.bytesin) { + /* SPI addresses are 24 bit only */ + if (with_address) + writel_(trans.offset & 0x00FFFFFF, cntlr.addr); + + /* + * This is a 'no data' command (like Write Enable), its + * bitesout size was 1, decremented to zero while executing + * spi_setup_opcode() above. Tell the chip to send the + * command. + */ + writew_(control, cntlr.control); + + /* wait for the result */ + status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1); + if (status == -1) + return -1; + + if (status & SPIS_FCERR) { + printk(BIOS_DEBUG, "ICH SPI: Command transaction error\n"); + return -1; + } + + return 0; + } + + /* + * Check if this is a write command attempting to transfer more bytes + * than the controller can handle. Iterations for writes are not + * supported here because each SPI write command needs to be preceded + * and followed by other SPI commands, and this sequence is controlled + * by the SPI chip driver. + */ + if (trans.bytesout > cntlr.databytes) { + printk(BIOS_DEBUG, "ICH SPI: Too much to write. Does your SPI chip driver use" + " CONTROLLER_PAGE_LIMIT?\n"); + return -1; + } + + /* + * Read or write up to databytes bytes at a time until everything has + * been sent. + */ + while (trans.bytesout || trans.bytesin) { + uint32_t data_length; + + /* SPI addresses are 24 bit only */ + writel_(trans.offset & 0x00FFFFFF, cntlr.addr); + + if (trans.bytesout) + data_length = min(trans.bytesout, cntlr.databytes); + else + data_length = min(trans.bytesin, cntlr.databytes); + + /* Program data into FDATA0 to N */ + if (trans.bytesout) { + write_reg(trans.out, cntlr.data, data_length); + spi_use_out(&trans, data_length); + if (with_address) + trans.offset += data_length; + } + + /* Add proper control fields' values */ + control &= ~((cntlr.databytes - 1) << 8); + control |= SPIC_DS; + control |= (data_length - 1) << 8; + + /* write it */ + writew_(control, cntlr.control); + + /* Wait for Cycle Done Status or Flash Cycle Error. */ + status = ich_status_poll(SPIS_CDS | SPIS_FCERR, 1); + if (status == -1) + return -1; + + if (status & SPIS_FCERR) { + printk(BIOS_DEBUG, "ICH SPI: Data transaction error\n"); + return -1; + } + + if (trans.bytesin) { + read_reg(cntlr.data, trans.in, data_length); + spi_use_in(&trans, data_length); + if (with_address) + trans.offset += data_length; + } + } + + /* Clear atomic preop now that xfer is done */ + writew_(0, cntlr.preop); + + return 0; +} diff --git a/src/southbridge/intel/ibexpeak/thermal.c b/src/southbridge/intel/ibexpeak/thermal.c new file mode 100644 index 0000000000..11b65554a8 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/thermal.c @@ -0,0 +1,85 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include "pch.h" +#include <usbdebug.h> +#include <arch/io.h> + +static void thermal_init(struct device *dev) +{ + struct resource *res; + + printk(BIOS_DEBUG, "Thermal init start.\n"); + + res = find_resource(dev, 0x10); + if (!res) + return; + + write32(res->base + 4, 0x3a2b); + write8(res->base + 0xe, 0x40); + write32(res->base + 0x12, 0x1a40); + write16(res->base + 0x16, 0x7746); + write16(res->base + 0x1a, 0x10f0); + write16(res->base + 0x56, 0xffff); + write16(res->base + 0x64, 0xffff); + write16(res->base + 0x66, 0xffff); + write16(res->base + 0x68, 0xfa); + + write8(res->base + 1, 0xb8); + + printk(BIOS_DEBUG, "Thermal init done.\n"); +} + +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 thermal_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = thermal_init, + .scan_bus = 0, + .ops_pci = &pci_ops, +}; + +static const unsigned short pci_device_ids[] = { 0x3b32, 0 }; + +static const struct pci_driver pch_thermal __pci_driver = { + .ops = &thermal_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/southbridge/intel/ibexpeak/usb_ehci.c b/src/southbridge/intel/ibexpeak/usb_ehci.c new file mode 100644 index 0000000000..d941438999 --- /dev/null +++ b/src/southbridge/intel/ibexpeak/usb_ehci.c @@ -0,0 +1,98 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * Copyright (C) 2013 Vladimir Serbinenko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include "pch.h" +#include <usbdebug.h> +#include <arch/io.h> + +static void usb_ehci_init(struct device *dev) +{ + u32 reg32; + + /* Disable Wake on Disconnect in RMH */ + reg32 = RCBA32(0x35b0); + reg32 |= 0x22; + RCBA32(0x35b0) = reg32; + + printk(BIOS_DEBUG, "EHCI: Setting up controller.. "); + + pci_write_config32(dev, 0x84, 0x130c8911); + pci_write_config32(dev, 0x88, 0xa0); + pci_write_config32(dev, 0xf4, 0x80808588); + pci_write_config32(dev, 0xf4, 0x00808588); + pci_write_config32(dev, 0xf4, 0x00808588); + pci_write_config32(dev, 0xfc, 0x301b1728); + + reg32 = pci_read_config32(dev, PCI_COMMAND); + reg32 |= PCI_COMMAND_MASTER; + //reg32 |= PCI_COMMAND_SERR; + pci_write_config32(dev, PCI_COMMAND, reg32); + + printk(BIOS_DEBUG, "done.\n"); +} + +static void usb_ehci_set_subsystem(device_t dev, unsigned vendor, + unsigned device) +{ + u8 access_cntl; + + access_cntl = pci_read_config8(dev, 0x80); + + /* Enable writes to protected registers. */ + pci_write_config8(dev, 0x80, access_cntl | 1); + + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & + 0xffff)); + } + + /* Restore protection. */ + pci_write_config8(dev, 0x80, access_cntl); +} + + +static struct pci_operations lops_pci = { + .set_subsystem = &usb_ehci_set_subsystem, +}; + +static struct device_operations usb_ehci_ops = { + .read_resources = pci_ehci_read_resources, + .set_resources = pci_dev_set_resources, + .init = usb_ehci_init, + .scan_bus = 0, + .ops_pci = &lops_pci, +}; + +static const unsigned short pci_device_ids[] = { 0x3b34, 0x3b3c, 0 }; + +static const struct pci_driver pch_usb_ehci __pci_driver = { + .ops = &usb_ehci_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pci_device_ids, +}; |