summaryrefslogtreecommitdiffstats
path: root/amd_imc.c
diff options
context:
space:
mode:
Diffstat (limited to 'amd_imc.c')
-rw-r--r--amd_imc.c159
1 files changed, 159 insertions, 0 deletions
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 <r.marek@assembler.cz>
+ * 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
OpenPOWER on IntegriCloud