blob: c05df75d785d08848aa5a1a48eaeb35b6f3f53ef [file] [log] [blame]
/*
* Copyright (c) 2012 The Chromium OS Authors. 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.
*
* gec.c: Generic CrOS EC functions and structures.
*/
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "mosys/alloc.h"
#include "mosys/kv_pair.h"
#include "mosys/log.h"
#include "mosys/platform.h"
#include "drivers/google/cros_ec.h"
#include "drivers/google/cros_ec_commands.h"
#include "lib/math.h"
int cros_ec_hello(struct platform_intf *intf)
{
struct ec_params_hello p;
struct ec_response_hello r;
int rv;
struct cros_ec_priv *priv = intf->cb->ec->priv;
p.in_data = 0xa0b0c0d0;
rv = priv->cmd(intf, EC_CMD_HELLO, 0, &p,
sizeof(p), &r, sizeof(r));
if (rv)
return rv;
if (r.out_data != 0xa1b2c3d4) {
lprintf(LOG_ERR, "Expected response 0x%08x, got 0x%08x\n",
0xa1b2c3d4, r.out_data);
rv = -1;
}
lprintf(LOG_DEBUG, "%s: EC says hello!\n", __func__);
return rv;
}
const char *cros_ec_version(struct platform_intf *intf)
{
static const char *const fw_copies[] = { "unknown", "RO", "RW" };
struct ec_response_get_version r;
const char *ret = NULL;
struct cros_ec_priv *priv = intf->cb->ec->priv;
if (priv->cmd(intf, EC_CMD_GET_VERSION, 0, &r, sizeof(r), NULL, 0))
return NULL;
/* Ensure versions are null-terminated before we print them */
r.version_string_ro[sizeof(r.version_string_ro) - 1] = '\0';
r.version_string_rw[sizeof(r.version_string_rw) - 1] = '\0';
/* Print versions */
lprintf(LOG_DEBUG, "RO version: %s\n", r.version_string_ro);
lprintf(LOG_DEBUG, "RW version: %s\n", r.version_string_rw);
lprintf(LOG_DEBUG, "Firmware copy: %s\n",
(r.current_image < ARRAY_SIZE(fw_copies) ?
fw_copies[r.current_image] : "?"));
switch (r.current_image) {
case EC_IMAGE_RO:
ret = mosys_strdup(r.version_string_ro);
break;
case EC_IMAGE_RW:
ret = mosys_strdup(r.version_string_rw);
break;
default:
lprintf(LOG_DEBUG, "%s: cannot determine version\n", __func__);
break;
}
return ret;
}
int cros_ec_chip_info(struct platform_intf *intf,
struct ec_response_get_chip_info *info)
{
int rc = 0;
struct cros_ec_priv *priv = intf->cb->ec->priv;
rc = priv->cmd(intf, EC_CMD_GET_CHIP_INFO, 0,
info, sizeof(*info), NULL, 0);
if (rc)
return rc;
lprintf(LOG_DEBUG, "CrOS EC vendor: %s\n", info->vendor);
lprintf(LOG_DEBUG, "CrOS EC name: %s\n", info->name);
lprintf(LOG_DEBUG, "CrOS EC revision: %s\n", info->revision);
return rc;
}
int cros_ec_flash_info(struct platform_intf *intf,
struct ec_response_flash_info *info)
{
int rc = 0;
struct cros_ec_priv *priv = intf->cb->ec->priv;
rc = priv->cmd(intf, EC_CMD_FLASH_INFO, 0,
info, sizeof(*info), NULL, 0);
if (rc)
return rc;
lprintf(LOG_DEBUG, "CrOS EC flash size: 0x%06x\n", info->flash_size);
lprintf(LOG_DEBUG, "CrOS EC write block size: 0x%06x\n",
info->write_block_size);
lprintf(LOG_DEBUG, "CrOS EC erase block size: 0x%06x\n",
info->erase_block_size);
lprintf(LOG_DEBUG, "CrOS EC protection block size: 0x%06x\n",
info->protect_block_size);
return rc;
}
/* returns 1 if EC detected, 0 if not, <0 to indicate failure */
int cros_ec_detect(struct platform_intf *intf)
{
struct ec_params_hello request;
struct ec_response_hello response;
int result = 0;
int ret = 0;
struct cros_ec_priv *priv;
if (!intf->cb || !intf->cb->ec || !intf->cb->ec->priv)
return -1;
priv = intf->cb->ec->priv;
/* Say hello to EC. */
request.in_data = 0xf0e0d0c0; /* Expect EC will add on 0x01020304. */
lprintf(LOG_DEBUG, "%s: sending HELLO request with 0x%08x\n",
__func__, request.in_data);
result = priv->cmd(intf, EC_CMD_HELLO, 0,
&response, sizeof(response),
&request, sizeof(request));
lprintf(LOG_DEBUG, "%s: response: 0x%08x\n",
__func__, response.out_data);
if (result || response.out_data != 0xf1e2d3c4) {
lprintf(LOG_DEBUG, "response.out_data is not 0xf1e2d3c4.\n"
"result=%d, request=0x%x response=0x%x\n",
result, request.in_data, response.out_data);
} else {
ret = 1;
}
return ret;
}
int cros_ec_vbnvcontext_read(struct platform_intf *intf, uint8_t *block)
{
struct ec_params_vbnvcontext request;
struct cros_ec_priv *priv;
int result;
if (!intf->cb || !intf->cb->ec || !intf->cb->ec->priv)
return -1;
priv = intf->cb->ec->priv;
lprintf(LOG_DEBUG, "%s: sending VBNV_CONTEXT read request\n",
__func__);
request.op = EC_VBNV_CONTEXT_OP_READ;
result = priv->cmd(intf, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT,
block, EC_VBNV_BLOCK_SIZE,
&request, sizeof(request.op));
if (result) {
lprintf(LOG_DEBUG, "%s: result=%d\n", __func__, result);
return -1;
}
return 0;
}
int cros_ec_vbnvcontext_write(struct platform_intf *intf, const uint8_t *block)
{
struct ec_params_vbnvcontext request;
struct cros_ec_priv *priv;
int result;
if (!intf->cb || !intf->cb->ec || !intf->cb->ec->priv)
return -1;
priv = intf->cb->ec->priv;
lprintf(LOG_DEBUG, "%s: sending VBNV_CONTEXT write request\n",
__func__);
request.op = EC_VBNV_CONTEXT_OP_WRITE;
memcpy(request.block, block, sizeof(request.block));
result = priv->cmd(intf, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT,
NULL, 0,
&request, sizeof(request));
if (result) {
lprintf(LOG_DEBUG, "%s: result=%d\n", __func__, result);
return -1;
}
return 0;
}
int cros_ec_vboot_read(struct platform_intf *intf)
{
struct kv_pair *kv;
uint8_t block[EC_VBNV_BLOCK_SIZE];
char hexstring[EC_VBNV_BLOCK_SIZE * 2 + 1];
int i, rc;
if (cros_ec_vbnvcontext_read(intf, block))
return -1;
for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
snprintf(hexstring + i * 2, 3, "%02x", block[i]);
kv = kv_pair_new();
kv_pair_fmt(kv, "vbnvcontext", "%s", hexstring);
rc = kv_pair_print(kv);
kv_pair_free(kv);
return rc;
}
int cros_ec_vboot_write(struct platform_intf *intf, const char *hexstring)
{
uint8_t block[EC_VBNV_BLOCK_SIZE];
char hexdigit[3];
int i, len;
len = strlen(hexstring);
if (len != EC_VBNV_BLOCK_SIZE * 2) {
lprintf(LOG_DEBUG, "%s: hexstring's length must "
"be %d (got %d)\n",
__func__, EC_VBNV_BLOCK_SIZE * 2, len);
return -1;
}
len /= 2;
hexdigit[2] = '\0';
for (i = 0; i < len; i++) {
hexdigit[0] = hexstring[i * 2];
hexdigit[1] = hexstring[i * 2 + 1];
block[i] = strtol(hexdigit, NULL, 16);
}
return cros_ec_vbnvcontext_write(intf, block);
}
struct nvram_cb cros_ec_nvram_cb = {
.vboot_read = cros_ec_vboot_read,
.vboot_write = cros_ec_vboot_write,
};