/* * This file is part of the flashrom project. * * Copyright (C) 2013 Rudolf Marek * * 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; } if (!(pci_read_byte(dev, 0x40) & (1 << 7))) return 0; ec_port = pci_read_word(dev, 0xa4); if (!(ec_port & 0x1)) return 0; ec_port &= ~0x1; return ec_port; } static void write_data(uint16_t port, uint8_t off, uint8_t data) { outb(off, port); outb(data, port + 1); } static uint8_t read_data(uint16_t port, uint8_t off) { outb(off, port); return inb(port + 1); } static uint16_t get_mbox_port(uint16_t sio_port) { uint16_t mbox_port; enter_conf_mode_ec(sio_port); /* go to LDN 9, mailbox */ write_data(sio_port, 7, 9); /* MBOX not active ? */ if (!(read_data(sio_port, 0x30) & 1)) { exit_conf_mode_ec(sio_port); return 0; } mbox_port = read_data(sio_port, 0x60) << 8; mbox_port |= read_data(sio_port, 0x61); exit_conf_mode_ec(sio_port); return mbox_port; } static int mbox_wait_ack(uint16_t mbox_port) { int i; for (i = 0; i < 10; i++) { /* sleep 1ms */ programmer_delay(1000); if (read_data(mbox_port, 0x82) == 0xfa) { return 0; } } msg_pinfo("IMC MBOX: Timeout!\n"); return 1; } static int send_cmd(uint16_t mbox_port, uint8_t cmd) { write_data(mbox_port, 0x82, 0x0); write_data(mbox_port, 0x83, cmd); /* trigger transfer 0x96 with subcommand cmd */ write_data(mbox_port, 0x80, 0x96); return mbox_wait_ack(mbox_port); } static int imc_cmd(struct pci_dev *dev, uint8_t cmd) { uint16_t sio_port; uint16_t mbox_port; sio_port = get_sio_port(dev); if (!sio_port) return -1; msg_pdbg("\nEC SIO is at 0x%x\n", sio_port); mbox_port = get_mbox_port(sio_port); if (!mbox_port) return -1; msg_pdbg("EC MBOX is at 0x%x\n", mbox_port); /* put IMC to sleep */ return send_cmd(mbox_port, cmd); } static int imc_try_resume(void *data) { struct pci_dev *dev = data; int ret = imc_cmd(dev, 0xb5); msg_pinfo("(IMC resume%s)\n", ret ? " failed!" : "d"); return ret; } int imc_try_shutdown(struct pci_dev *dev) { int ret = imc_cmd(dev, 0xb4); /* No IMC active, fake OK */ if (ret < 0) return 0; msg_pinfo("(Shutting down IMC%s)\n", ret ? " failed!" : ""); /* restore IMC on exit */ if ((!ret) && (register_shutdown(imc_try_resume, dev))) return 1; return ret; } #endif