Files
OpenCellular/common/system.c
Randall Spangler e85cb93715 Add LPC command to get EC build info
Useful when debugging to determine if a user has an official build or
not, particularly early in the devel process where we're handing
builds to everyone.  Particularly useful for proto1, since not all
those systems will be case-open servo-attached.

Also move get-version LPC command into system.c, where it's closer to
the system functions it calls (matches what we do for other host
commands).

Signed-off-by: Randall Spangler <rspangler@chromium.org>

BUG=none
TEST=none

Change-Id: Idb0f6edf31ca00e32f083be0b0d3f23ab79c5fba
2012-03-05 11:05:15 -08:00

264 lines
6.4 KiB
C

/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* System module for Chrome EC : common functions */
#include "console.h"
#include "host_command.h"
#include "lpc_commands.h"
#include "system.h"
#include "uart.h"
#include "util.h"
#include "version.h"
static enum system_reset_cause_t reset_cause = SYSTEM_RESET_UNKNOWN;
enum system_reset_cause_t system_get_reset_cause(void)
{
return reset_cause;
}
void system_set_reset_cause(enum system_reset_cause_t cause)
{
reset_cause = cause;
}
const char *system_get_reset_cause_string(void)
{
static const char * const cause_descs[] = {
"unknown", "other", "brownout", "power-on", "reset pin",
"soft cold", "soft warm", "watchdog", "rtc alarm", "wake pin",
"low battery"};
return reset_cause < ARRAY_SIZE(cause_descs) ?
cause_descs[reset_cause] : "?";
}
enum system_image_copy_t system_get_image_copy(void)
{
int copy = ((uint32_t)system_get_image_copy - CONFIG_FLASH_BASE) /
CONFIG_FW_IMAGE_SIZE;
switch (copy) {
case 0:
return SYSTEM_IMAGE_RO;
case 1:
return SYSTEM_IMAGE_RW_A;
case 2:
return SYSTEM_IMAGE_RW_B;
default:
return SYSTEM_IMAGE_UNKNOWN;
}
}
const char *system_get_image_copy_string(void)
{
static const char * const copy_descs[] = {"unknown", "RO", "A", "B"};
int copy = system_get_image_copy();
return copy < ARRAY_SIZE(copy_descs) ? copy_descs[copy] : "?";
}
int system_run_image_copy(enum system_image_copy_t copy)
{
uint32_t init_addr;
void (*resetvec)(void);
/* Fail if we're not in RO firmware */
if (system_get_image_copy() != SYSTEM_IMAGE_RO)
return EC_ERROR_UNKNOWN;
/* Load the appropriate reset vector */
if (copy == SYSTEM_IMAGE_RW_A)
init_addr = *(uint32_t *)(CONFIG_FW_A_OFF + 4);
#ifndef CONFIG_NO_RW_B
else if (copy == SYSTEM_IMAGE_RW_B)
init_addr = *(uint32_t *)(CONFIG_FW_B_OFF + 4);
#endif
else
return EC_ERROR_UNKNOWN;
/* TODO: sanity checks (crosbug.com/p/7468)
*
* Fail if called outside of pre-init.
*
* Fail if reboot reason is not soft reboot. Power-on
* reset cause must run RO firmware; if it wants to move to RW
* firmware, it must go through a soft reboot first
*
* Sanity check reset vector; must be inside the appropriate
* image. */
/* Jump to the reset vector */
resetvec = (void(*)(void))init_addr;
resetvec();
/* Should never get here */
return EC_ERROR_UNIMPLEMENTED;
}
const char *system_get_version(enum system_image_copy_t copy)
{
int imoffset;
const struct version_struct *v;
/* Handle version of current image */
if (copy == system_get_image_copy() || copy == SYSTEM_IMAGE_UNKNOWN)
return version_data.version;
switch (copy) {
case SYSTEM_IMAGE_RO:
imoffset = CONFIG_FW_RO_OFF;
break;
case SYSTEM_IMAGE_RW_A:
imoffset = CONFIG_FW_A_OFF;
break;
#ifndef CONFIG_NO_RW_B
case SYSTEM_IMAGE_RW_B:
imoffset = CONFIG_FW_B_OFF;
break;
#endif
default:
return "";
}
/* The version string is always located after the reset vectors */
v = (const struct version_struct *)((uint8_t *)&version_data
+ imoffset);
if (v->cookie1 == version_data.cookie1 &&
v->cookie2 == version_data.cookie2)
return v->version;
return "";
}
const char *system_get_build_info(void)
{
return build_info;
}
/*****************************************************************************/
/* Console commands */
static int command_sysinfo(int argc, char **argv)
{
uart_printf("Reset cause: %d (%s)\n",
system_get_reset_cause(),
system_get_reset_cause_string());
uart_printf("Scratchpad: 0x%08x\n", system_get_scratchpad());
uart_printf("Firmware copy: %s\n", system_get_image_copy_string());
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(sysinfo, command_sysinfo);
static int command_set_scratchpad(int argc, char **argv)
{
int s;
char *e;
if (argc < 2) {
uart_puts("Usage: scratchpad <value>\n");
return EC_ERROR_UNKNOWN;
}
s = strtoi(argv[1], &e, 0);
if (*e) {
uart_puts("Invalid scratchpad value\n");
return EC_ERROR_UNKNOWN;
}
uart_printf("Setting scratchpad to 0x%08x\n", s);
return system_set_scratchpad(s);
}
DECLARE_CONSOLE_COMMAND(setscratchpad, command_set_scratchpad);
static int command_hibernate(int argc, char **argv)
{
int seconds;
int microseconds = 0;
if (argc < 2) {
uart_puts("Usage: hibernate <seconds> [<microseconds>]\n");
return EC_ERROR_UNKNOWN;
}
seconds = strtoi(argv[1], NULL, 0);
if (argc >= 3)
microseconds = strtoi(argv[2], NULL, 0);
uart_printf("Hibernating for %d.%06d s ...\n", seconds, microseconds);
uart_flush_output();
system_hibernate(seconds, microseconds);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(hibernate, command_hibernate);
static int command_version(int argc, char **argv)
{
uart_printf("RO version: %s\n",
system_get_version(SYSTEM_IMAGE_RO));
uart_printf("RW-A version: %s\n",
system_get_version(SYSTEM_IMAGE_RW_A));
uart_printf("RW-B version: %s\n",
system_get_version(SYSTEM_IMAGE_RW_B));
uart_printf("Current build: %s\n", system_get_build_info());
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(version, command_version);
/*****************************************************************************/
/* Host commands */
static enum lpc_status host_command_get_version(uint8_t *data)
{
struct lpc_response_get_version *r =
(struct lpc_response_get_version *)data;
strzcpy(r->version_string_ro, system_get_version(SYSTEM_IMAGE_RO),
sizeof(r->version_string_ro));
strzcpy(r->version_string_rw_a, system_get_version(SYSTEM_IMAGE_RW_A),
sizeof(r->version_string_rw_a));
strzcpy(r->version_string_rw_b, system_get_version(SYSTEM_IMAGE_RW_B),
sizeof(r->version_string_rw_b));
switch(system_get_image_copy()) {
case SYSTEM_IMAGE_RO:
r->current_image = EC_LPC_IMAGE_RO;
break;
case SYSTEM_IMAGE_RW_A:
r->current_image = EC_LPC_IMAGE_RW_A;
break;
case SYSTEM_IMAGE_RW_B:
r->current_image = EC_LPC_IMAGE_RW_B;
break;
default:
r->current_image = EC_LPC_IMAGE_UNKNOWN;
break;
}
return EC_LPC_RESULT_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_LPC_COMMAND_GET_VERSION, host_command_get_version);
static enum lpc_status host_command_build_info(uint8_t *data)
{
struct lpc_response_get_build_info *r =
(struct lpc_response_get_build_info *)data;
strzcpy(r->build_string, system_get_build_info(),
sizeof(r->build_string));
return EC_LPC_RESULT_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_LPC_COMMAND_GET_BUILD_INFO, host_command_build_info);