blob: 64d6c09157f5d5ff3f34b4df0233ad74e1e1a17a [file] [log] [blame]
/* Copyright (c) 2013 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 : hardware specific implementation */
#include "console.h"
#include "cpu.h"
#include "ec2i_chip.h"
#include "flash.h"
#include "host_command.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "util.h"
#include "version.h"
#include "watchdog.h"
void __no_hibernate(uint32_t seconds, uint32_t microseconds)
{
#ifdef CONFIG_COMMON_RUNTIME
/*
* Hibernate not implemented on this platform.
*
* Until then, treat this as a request to hard-reboot.
*/
cprints(CC_SYSTEM, "hibernate not supported, so rebooting");
cflush();
system_reset(SYSTEM_RESET_HARD);
#endif
}
void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
__attribute__((weak, alias("__no_hibernate")));
void system_hibernate(uint32_t seconds, uint32_t microseconds)
{
#ifdef CONFIG_HOSTCMD_PD
/* Inform the PD MCU that we are going to hibernate. */
host_command_pd_request_hibernate();
/* Wait to ensure exchange with PD before hibernating. */
msleep(100);
#endif
/* Flush console before hibernating */
cflush();
if (board_hibernate)
board_hibernate();
/* chip specific standby mode */
__enter_hibernate(seconds, microseconds);
}
static void check_reset_cause(void)
{
uint32_t flags = 0;
uint8_t raw_reset_cause = IT83XX_GCTRL_RSTS & 0x03;
uint8_t raw_reset_cause2 = IT83XX_GCTRL_SPCTRL4 & 0x07;
/* Clear reset cause. */
IT83XX_GCTRL_RSTS |= 0x03;
IT83XX_GCTRL_SPCTRL4 |= 0x07;
/* Determine if watchdog reset or power on reset. */
if (raw_reset_cause & 0x02) {
flags |= RESET_FLAG_WATCHDOG;
} else if (raw_reset_cause & 0x01) {
flags |= RESET_FLAG_POWER_ON;
} else {
if ((IT83XX_GCTRL_RSTS & 0xC0) == 0x80)
flags |= RESET_FLAG_POWER_ON;
}
if (raw_reset_cause2 & 0x04)
flags |= RESET_FLAG_RESET_PIN;
/* Restore then clear saved reset flags. */
if (!(flags & RESET_FLAG_POWER_ON)) {
flags |= BRAM_RESET_FLAGS << 24;
flags |= BRAM_RESET_FLAGS1 << 16;
flags |= BRAM_RESET_FLAGS2 << 8;
flags |= BRAM_RESET_FLAGS3;
/* watchdog module triggers these reset */
if (flags & (RESET_FLAG_HARD | RESET_FLAG_SOFT))
flags &= ~RESET_FLAG_WATCHDOG;
}
BRAM_RESET_FLAGS = 0;
BRAM_RESET_FLAGS1 = 0;
BRAM_RESET_FLAGS2 = 0;
BRAM_RESET_FLAGS3 = 0;
system_set_reset_flags(flags);
}
int system_is_reboot_warm(void)
{
uint32_t reset_flags;
/*
* Check reset cause here,
* gpio_pre_init is executed faster than system_pre_init
*/
check_reset_cause();
reset_flags = system_get_reset_flags();
if ((reset_flags & RESET_FLAG_RESET_PIN) ||
(reset_flags & RESET_FLAG_POWER_ON) ||
(reset_flags & RESET_FLAG_WATCHDOG) ||
(reset_flags & RESET_FLAG_HARD) ||
(reset_flags & RESET_FLAG_SOFT))
return 0;
else
return 1;
}
void system_pre_init(void)
{
/* No initialization required */
}
void system_reset(int flags)
{
uint32_t save_flags = 0;
/* Disable interrupts to avoid task swaps during reboot. */
interrupt_disable();
/* Save current reset reasons if necessary */
if (flags & SYSTEM_RESET_PRESERVE_FLAGS)
save_flags = system_get_reset_flags() | RESET_FLAG_PRESERVED;
/* Add in AP off flag into saved flags. */
if (flags & SYSTEM_RESET_LEAVE_AP_OFF)
save_flags |= RESET_FLAG_AP_OFF;
if (flags & SYSTEM_RESET_HARD)
save_flags |= RESET_FLAG_HARD;
else
save_flags |= RESET_FLAG_SOFT;
/* Store flags to battery backed RAM. */
BRAM_RESET_FLAGS = save_flags >> 24;
BRAM_RESET_FLAGS1 = (save_flags >> 16) & 0xff;
BRAM_RESET_FLAGS2 = (save_flags >> 8) & 0xff;
BRAM_RESET_FLAGS3 = save_flags & 0xff;
/*
* bit4, disable debug mode through SMBus.
* If we are in debug mode, we need disable it before triggering
* a soft reset or reset will fail.
*/
IT83XX_SMB_SLVISELR |= (1 << 4);
/*
* Writing invalid key to watchdog module triggers a soft reset. For
* now this is the only option, no hard reset.
*/
IT83XX_ETWD_ETWCFG |= 0x20;
IT83XX_ETWD_EWDKEYR = 0x00;
/* Spin and wait for reboot; should never return */
while (1)
;
}
int system_set_scratchpad(uint32_t value)
{
BRAM_SCRATCHPAD3 = (value >> 24) & 0xff;
BRAM_SCRATCHPAD2 = (value >> 16) & 0xff;
BRAM_SCRATCHPAD1 = (value >> 8) & 0xff;
BRAM_SCRATCHPAD = value & 0xff;
return EC_SUCCESS;
}
uint32_t system_get_scratchpad(void)
{
uint32_t value = 0;
value |= BRAM_SCRATCHPAD3 << 24;
value |= BRAM_SCRATCHPAD2 << 16;
value |= BRAM_SCRATCHPAD1 << 8;
value |= BRAM_SCRATCHPAD;
return value;
}
static uint16_t system_get_chip_id(void)
{
return (IT83XX_GCTRL_CHIPID1 << 8) | IT83XX_GCTRL_CHIPID2;
}
static uint8_t system_get_chip_version(void)
{
/* bit[3-0], chip version */
return IT83XX_GCTRL_CHIPVER & 0x0F;
}
static char to_hex(int x)
{
if (x >= 0 && x <= 9)
return '0' + x;
return 'a' + x - 10;
}
const char *system_get_chip_vendor(void)
{
return "ite";
}
const char *system_get_chip_name(void)
{
static char buf[7];
uint16_t chip_id = system_get_chip_id();
buf[0] = 'i';
buf[1] = 't';
buf[2] = to_hex((chip_id >> 12) & 0xf);
buf[3] = to_hex((chip_id >> 8) & 0xf);
buf[4] = to_hex((chip_id >> 4) & 0xf);
buf[5] = to_hex(chip_id & 0xf);
buf[6] = '\0';
return buf;
}
const char *system_get_chip_revision(void)
{
static char buf[3];
uint8_t rev = system_get_chip_version();
buf[0] = to_hex(rev + 0xa);
buf[1] = 'x';
buf[2] = '\0';
return buf;
}
int system_get_vbnvcontext(uint8_t *block)
{
int i;
for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
block[i] = IT83XX_BRAM_BANK0((BRAM_IDX_NVCONTEXT + i));
return EC_SUCCESS;
}
int system_set_vbnvcontext(const uint8_t *block)
{
int i;
for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++)
IT83XX_BRAM_BANK0((BRAM_IDX_NVCONTEXT + i)) = block[i];
return EC_SUCCESS;
}
#define BRAM_NVCONTEXT_SIZE (BRAM_IDX_NVCONTEXT_END - BRAM_IDX_NVCONTEXT + 1)
BUILD_ASSERT(EC_VBNV_BLOCK_SIZE <= BRAM_NVCONTEXT_SIZE);
uintptr_t system_get_fw_reset_vector(uintptr_t base)
{
uintptr_t reset_vector, num;
num = *(uintptr_t *)base;
reset_vector = ((num>>24)&0xff) | ((num<<8)&0xff0000) |
((num>>8)&0xff00) | ((num<<24)&0xff000000);
reset_vector = ((reset_vector & 0xffffff) << 1) + base;
return reset_vector;
}
static int command_rw_ec_reg(int argc, char **argv)
{
volatile uint8_t *addr;
uint8_t value = 0;
#ifdef CONFIG_EC2I
enum ec2i_message em;
enum logical_device_number ldn;
enum host_pnpcfg_index idx;
#endif
int i;
char *e;
if ((argc < 2) || (argc > 3))
return EC_ERROR_PARAM_COUNT;
addr = (uint8_t *)strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
if (argc == 3) {
value = (uint8_t)strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
}
/* access PNPCFG registers */
if (((uint32_t)addr & 0xffff0000) == 0xec210000) {
#ifdef CONFIG_EC2I
/* set LDN */
ldn = ((uint32_t)addr & 0xff00) >> 8;
idx = (uint32_t)addr & 0xff;
if (ec2i_write(HOST_INDEX_LDN, ldn) == EC2I_WRITE_ERROR)
return EC_ERROR_UNKNOWN;
/* ec2i write */
if (argc == 3) {
if (ec2i_write(idx, value) == EC2I_WRITE_ERROR)
return EC_ERROR_UNKNOWN;
em = ec2i_read(idx);
if (em == EC2I_READ_ERROR)
return EC_ERROR_UNKNOWN;
value = em & 0xff;
ccprintf("LDN%02X IDX%02X = %02x", ldn, idx, value);
/* ec2i read */
} else {
for (i = 0; i < 256; i++) {
em = ec2i_read(i);
if (em == EC2I_READ_ERROR)
return EC_ERROR_UNKNOWN;
value = em & 0xff;
if (0 == (i % 16))
ccprintf("\nLDN%02X IDX%02X: %02x",
ldn, i, value);
else
ccprintf(" %02x", value);
}
}
#else
return EC_ERROR_ACCESS_DENIED;
#endif
/* access EC registers */
} else {
/* write register */
if (argc == 3) {
*addr = value;
ccprintf("%08X = %02x", addr, *addr);
/* read registers */
} else {
for (i = 0; i < 256; i++) {
if (0 == (i % 16))
ccprintf("\n%08X: %02x", addr+i,
addr[i]);
else
ccprintf(" %02x", addr[i]);
}
}
}
ccprintf("\n");
cflush();
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(rwreg, command_rw_ec_reg,
"addr [value (byte)]",
"R/W EC/PNPCFG registers."
" addr 0xec21xxyy for R/W PNPCFG, xx is LDN yy is IDX");