blob: 96b2978547665557cae239ceeec87ef93dada86a [file] [log] [blame]
/*
* Copyright 2012, 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.
*/
#include <inttypes.h>
#include <unistd.h>
#include "stout.h"
#include "mosys/log.h"
#include "mosys/platform.h"
#include "drivers/superio.h"
#include "drivers/ite/it8500.h"
#include "intf/io.h"
#include "lib/acpi.h"
#define STOUT_EC_CMD_TIMEOUT_MS 3000
static struct i8042_host_intf stout_acpi_intf_rw = {
/* use sideband ports to avoid racing with kernel ACPI driver */
.csr = 0x6c,
.data = 0x68,
};
static struct i8042_host_intf stout_acpi_intf_ro = {
/* try regular ports in case EC is in RO */
.csr = ACPI_EC_SC,
.data = ACPI_EC_DATA,
};
/* for i8042-style wait commands */
static struct i8042_host_intf *stout_acpi_intf = &stout_acpi_intf_rw;
static int stout_wait_ibf_clear(struct platform_intf *intf)
{
return i8042_wait_ibf_clear(intf, stout_acpi_intf,
STOUT_EC_CMD_TIMEOUT_MS);
}
static int stout_wait_obf_set(struct platform_intf *intf)
{
return i8042_wait_obf_set(intf, stout_acpi_intf,
STOUT_EC_CMD_TIMEOUT_MS);
}
/*
* ec_command - sends command to EC, with optional read/write parameters.
*
* @inf: platform interface
* command: EC command
* @input_data: optional input data to send (NULL if none)
* input_len: length of @input_data (0 if input_data == NULL)
* @output_data: optional output data to send (NULL if none)
* output_len: length of @output_data (0 if output_data == NULL)
*
* Returns 0 to indicate success, <0 to indicate failure.
*/
int ec_command(struct platform_intf *intf, stout_ec_command command,
uint8_t *input_data, uint8_t input_len,
uint8_t *output_data, uint8_t output_len )
{
int i;
if (stout_wait_ibf_clear(intf) != 1)
return -1;
if (io_write8(intf, stout_acpi_intf->csr, command) < 0)
return -1;
if (input_data != NULL) {
for (i = 0; i < input_len; i++) {
if (stout_wait_ibf_clear(intf) != 1)
return -1;
if (io_write8(intf, stout_acpi_intf->data,
input_data[i] ) < 0)
return -1;
}
}
if (output_data != NULL) {
for (i = 0; i < output_len; i++) {
if (stout_wait_obf_set(intf) != 1)
return -1;
if (io_read8(intf, stout_acpi_intf->data,
&output_data[i] ) < 0)
return -1;
}
}
return 0;
}
/* returns 0 to indicate success, <0 to indicate failure */
int ecram_read(struct platform_intf *intf,
stout_ec_mem_addr address, uint8_t *data,
stout_ec_command cmd)
{
return ec_command(intf, cmd, (uint8_t *)&address, 1,
data, 1);
}
/* returns 0 to indicate success, <0 to indicate failure */
int ecram_write(struct platform_intf *intf,
stout_ec_mem_addr address, uint8_t data)
{
uint8_t write_data[2] = {address, data};
return ec_command(intf, STOUT_ECCMD_MEM_WRITE, write_data, 2,
NULL, 0);
}
/*
* stout_ec_version - return allocated EC firmware version
*
* @intf: platform interface
*
* returns pointer to version string if successful
* returns NULL to indicate failure
*/
static const char *stout_ec_fw_version(struct platform_intf *intf)
{
int major, minor;
char revision;
static char version[7];
uint8_t msb, lsb;
stout_ec_command cmd = STOUT_ECCMD_MEM_READ;
if (ecram_read(intf, STOUT_ECMEM_FW_VERSION_MSB, &msb, cmd) < 0) {
/* Command failed, but we might be in RO. */
stout_acpi_intf = &stout_acpi_intf_ro;
cmd = ACPI_RD_EC;
lprintf(LOG_DEBUG, "%s: Retry w/ RO ports\n", __func__);
if (ecram_read(intf, STOUT_ECMEM_FW_VERSION_MSB, &msb, cmd) < 0)
return NULL;
}
if (ecram_read(intf, STOUT_ECMEM_FW_VERSION_LSB, &lsb, cmd) < 0)
return NULL;
major = (msb >> 4) & 0xf;
minor = ((msb & 0xf) << 4) | ((lsb >> 4) & 0xf);
/* Revision: Zero indicates no revision character */
if ((lsb & 0xf) == 0)
revision = '\0';
else
revision = 'A' + (lsb & 0xf) - 1;
memset(version, 0, sizeof(version));
sprintf(version, "%x.%02x%c", major, minor, revision);
return version;
}
/*
* stout_ec_name - return EC firmware name string
*
* @intf: platform interface
*
* returns 0 if successful
* returns <0 if failure
*/
static const char *stout_ec_name(struct platform_intf *intf)
{
const struct sio_id *id = NULL;
uint16_t port;
if (it8500_get_sioport(intf, &port) <= 0)
return "Unknown";
id = get_sio_id(intf, port);
if (!id)
return "Unknown";
return id->name;
}
/*
* stout_ec_vendor - return EC vendor string
*
* @intf: platform interface
*
* returns 0 if successful
* returns <0 if failure
*/
static const char *stout_ec_vendor(struct platform_intf *intf)
{
const struct sio_id *id = NULL;
uint16_t port;
if (it8500_get_sioport(intf, &port) <= 0)
return "Unknown";
id = get_sio_id(intf, port);
if (!id)
return "Unknown";
return id->vendor;
}
int stout_ec_setup(struct platform_intf *intf)
{
int rc = 0;
/* invert logic: it8500_detect will return 1 if it finds an it8500 EC */
if (!it8500_detect(intf))
rc = 1;
return rc;
}
struct legacy_ec_cb stout_ec_cb = {
.vendor = stout_ec_vendor,
.name = stout_ec_name,
.fw_version = stout_ec_fw_version,
};