blob: fd4d9ad43f435fb25cf73fa2322ef9324043efa5 [file] [log] [blame]
/*
* Copyright 2011, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Note: This file shares some code with the Flashrom project.
*
* mec1308_mbx.c: Shared EC mailbox interface code.
*/
#include <ctype.h>
#include <unistd.h>
#include "mosys/log.h"
#include "mosys/platform.h"
#include "drivers/smsc/mec1308.h"
#include "drivers/superio.h"
#include "intf/io.h"
#define MEC1308_MBX_REG_CMD 0x82
#define MEC1308_MBX_REG_EXTCMD 0x83
#define MEC1308_MBX_CMD_FW_VERSION 0x83
#define MEC1308_MBX_CMD_FAN_RPM 0xBB
#define MEC1308_MBX_ALT_CMD_FW_VERSION 0x73
#define MEC1308_MBX_ALT_CMD_FAN_RPM 0x4B
#define MEC1308_MAX_TIMEOUT_US 2000000 /* arbitrarily picked */
#define MEC1308_DELAY_US 5000
#define MEC1308_MBX_CMD_PASSTHRU 0x55 /* start command */
#define MEC1308_MBX_CMD_PASSTHRU_SUCCESS 0xaa /* success code */
#define MEC1308_MBX_CMD_PASSTHRU_FAIL 0xfe /* failure code */
#define MEC1308_MBX_CMD_PASSTHRU_ENTER "PathThruMode" /* not a typo */
#define MEC1308_MBX_CMD_PASSTHRU_START "Start"
#define MEC1308_MBX_CMD_PASSTHRU_EXIT "End_Mode"
static uint16_t ec_port;
static uint16_t mbx_idx;
static uint16_t mbx_data;
static uint8_t mbx_read(struct platform_intf *intf, uint8_t idx)
{
uint8_t data;
io_write8(intf, mbx_idx, idx);
io_read8(intf, mbx_data, &data);
return data;
}
static int mbx_wait(struct platform_intf *intf, unsigned int us)
{
int rc = 0;
int us_elapsed = 0;
uint8_t data;
data = mbx_read(intf, MEC1308_MBX_REG_CMD);
while (data) {
if (us_elapsed >= MEC1308_MAX_TIMEOUT_US) {
lprintf(LOG_ERR, "%s: EC timed out\n", __func__);
return -1;
}
/* FIXME: This delay adds determinism to the delay period. It
was determined arbitrarily. */
usleep(us);
us_elapsed += us;
data = mbx_read(intf, MEC1308_MBX_REG_CMD);
}
lprintf(LOG_SPEW, "%s: elapsed time: %d\n", __func__, us_elapsed);
return rc;
}
static int mbx_write(struct platform_intf *intf, uint8_t idx, uint8_t data)
{
int rc = 0;
io_write8(intf, mbx_idx, idx);
io_write8(intf, mbx_data, data);
if (idx == MEC1308_MBX_REG_CMD)
rc = mbx_wait(intf, MEC1308_DELAY_US);
return rc;
}
static void mbx_clear(struct platform_intf *intf)
{
int reg;
for (reg = MEC1308_MBX_REG_DATA_START;
reg < MEC1308_MBX_REG_DATA_END;
reg++)
mbx_write(intf, reg, 0x00);
mbx_write(intf, MEC1308_MBX_REG_CMD, 0x00);
}
int mec1308_mbx_setup(struct platform_intf *intf)
{
if (mec1308_get_sioport(intf, &ec_port) <= 0)
return -1;
mbx_idx = mec1308_get_iobad(intf, ec_port, MEC1308_LDN_MBX);
mbx_data = mbx_idx + 1;
lprintf(LOG_DEBUG, "%s: ec_port: 0x%04x, mbx_idx: 0x%04x, mbx_data: "
"0x%04x\n", __func__, ec_port, mbx_idx, mbx_data);
return 0;
}
void mec1308_mbx_teardown(struct platform_intf *intf)
{
mec1308_sio_exit(intf, ec_port);
}
int mec1308_mbx_fw_version(struct platform_intf *intf, uint8_t *buf, int len)
{
int i;
uint8_t cmd = MEC1308_MBX_CMD_FW_VERSION;
if (intf->cb->sys && intf->cb->sys->firmware_vendor) {
const char *bios = intf->cb->sys->firmware_vendor(intf);
if (bios && !strcasecmp(bios, "coreboot"))
cmd = MEC1308_MBX_ALT_CMD_FW_VERSION;
free((void *)bios);
}
memset(buf, 0, len);
mbx_clear(intf);
if (mbx_write(intf, MEC1308_MBX_REG_CMD, cmd) < 0)
return -1;
for (i = 0; i < len; i++) {
uint8_t tmp = mbx_read(intf, MEC1308_MBX_REG_DATA_START + i);
if (isalnum(tmp))
buf[i] = tmp;
else
buf[i] = '\0';
}
return 0;
}
int mec1308_mbx_exit_passthru_mode(struct platform_intf *intf)
{
int i;
uint8_t tmp8;
/* attempt to get out of passthru mode (assuming we're in it) */
for (i = 0; i < strlen(MEC1308_MBX_CMD_PASSTHRU_EXIT); i++) {
mbx_write(intf, MEC1308_MBX_REG_DATA_START + i,
MEC1308_MBX_CMD_PASSTHRU_EXIT[i]);
}
if (mbx_write(intf,
MEC1308_MBX_REG_CMD,
MEC1308_MBX_CMD_PASSTHRU)) {
lprintf(LOG_DEBUG, "%s: exit passthru command timed out\n",
__func__);
return -1;
}
tmp8 = mbx_read(intf, MEC1308_MBX_REG_DATA_START);
lprintf(LOG_DEBUG, "%s: result: 0x%02x ", __func__, tmp8);
if (tmp8 == MEC1308_MBX_CMD_PASSTHRU_SUCCESS) {
lprintf(LOG_DEBUG, "(exited passthru mode)\n");
} else if (tmp8 == MEC1308_MBX_CMD_PASSTHRU_FAIL) {
lprintf(LOG_DEBUG, "(failed to exit passthru mode)\n");
}
return 0;
}