From f4a9e75f3aa860a4e311edc4c78462d24a5e94c1 Mon Sep 17 00:00:00 2001 From: stefanct Date: Thu, 25 Jul 2013 22:58:56 +0000 Subject: sbxxx: Handle active IMCs in AMD chipsets. Detect and temporarily disable the IMC while accessing the flash. Disable writes on default, but allow the user to enforce it. Signed-off-by: Rudolf Marek Signed-off-by: Stefan Tauner Acked-by: Stefan Tauner Acked-by: David Hendricks git-svn-id: https://code.coreboot.org/svn/flashrom/trunk@1704 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- amd_imc.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 amd_imc.c (limited to 'amd_imc.c') diff --git a/amd_imc.c b/amd_imc.c new file mode 100644 index 0000000..b05390c --- /dev/null +++ b/amd_imc.c @@ -0,0 +1,159 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2013 Rudolf Marek + * Copyright (C) 2013 Stefan Tauner + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if defined(__i386__) || defined(__x86_64__) + +#include "flash.h" +#include "programmer.h" +#include "hwaccess.h" +#include "spi.h" + +/* same as serverengines */ +static void enter_conf_mode_ec(uint16_t port) +{ + OUTB(0x5a, port); +} + +static void exit_conf_mode_ec(uint16_t port) +{ + OUTB(0xa5, port); +} + +static uint16_t get_sio_port(struct pci_dev *dev) +{ + uint16_t ec_port; + + if (!dev) { + return 0; + } + + ec_port = pci_read_word(dev, 0xa4); + + /* EcPortActive? */ + if (!(ec_port & 0x1)) + return 0; + + ec_port &= ~0x1; + + return ec_port; +} + +/* Wait for up to 10 ms for a response. */ +static int mbox_wait_ack(uint16_t mbox_port) +{ + int i = 10; + while (sio_read(mbox_port, 0x82) != 0xfa) { + if (--i == 0) { + msg_pwarn("IMC MBOX: Timeout!\n"); + return 1; + } + programmer_delay(1000); + } + return 0; +} + +static uint16_t mbox_get_port(uint16_t sio_port) +{ + uint16_t mbox_port; + + enter_conf_mode_ec(sio_port); + + /* Go to LDN 9, mailbox */ + sio_write(sio_port, 7, 9); + + /* MBOX inactive? */ + if ((sio_read(sio_port, 0x30) & 1) == 0) { + exit_conf_mode_ec(sio_port); + return 0; + } + + mbox_port = sio_read(sio_port, 0x60) << 8; + mbox_port |= sio_read(sio_port, 0x61); + + exit_conf_mode_ec(sio_port); + return mbox_port; +} + +/* Returns negative values when IMC is inactive, positive values on errors */ +static int imc_send_cmd(struct pci_dev *dev, uint8_t cmd) +{ + uint16_t sio_port; + uint16_t mbox_port; + + /* IntegratedEcPresent? */ + if (!(pci_read_byte(dev, 0x40) & (1 << 7))) + return -1; + + sio_port = get_sio_port(dev); + if (!sio_port) + return -1; + + msg_pdbg2("IMC SIO is at 0x%x.\n", sio_port); + mbox_port = mbox_get_port(sio_port); + if (!mbox_port) + return -1; + msg_pdbg2("IMC MBOX is at 0x%x.\n", mbox_port); + + sio_write(mbox_port, 0x82, 0x0); + sio_write(mbox_port, 0x83, cmd); + sio_write(mbox_port, 0x84, 0x0); + /* trigger transfer 0x96 with subcommand cmd */ + sio_write(mbox_port, 0x80, 0x96); + + return mbox_wait_ack(mbox_port); +} + +static int imc_resume(void *data) +{ + struct pci_dev *dev = data; + int ret = imc_send_cmd(dev, 0xb5); + + if (ret != 0) + msg_pinfo("Resuming IMC failed)\n"); + else + msg_pdbg2("IMC resumed.\n"); + return ret; +} + +int amd_imc_shutdown(struct pci_dev *dev) +{ + /* Try to put IMC to sleep */ + int ret = imc_send_cmd(dev, 0xb4); + + /* No IMC activity detectable, assume we are fine */ + if (ret < 0) { + msg_pdbg2("No IMC found.\n"); + return 0; + } + + if (ret != 0) { + msg_perr("Shutting down IMC failed.\n"); + return ret; + } + msg_pdbg2("Shutting down IMC successful.\n"); + + if (register_shutdown(imc_resume, dev)) + return 1; + + return ret; +} + +#endif -- cgit v1.2.1