blob: b05390c3f3e28fced9472653660935d2d2c29db3 [file] [log] [blame]
/*
* 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