| /* |
| * 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. |
| */ |
| |
| #include "flash.h" |
| #include "programmer.h" |
| #include "hwaccess_x86_io.h" |
| #include "spi.h" |
| #include "platform/pci.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; |
| } |