814 lines
21 KiB
C
814 lines
21 KiB
C
/* Copyright 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.
|
|
*/
|
|
|
|
/* Host event commands for Chrome EC */
|
|
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "hooks.h"
|
|
#include "host_command.h"
|
|
#include "lpc.h"
|
|
#include "mkbp_event.h"
|
|
#include "power.h"
|
|
#include "system.h"
|
|
#include "task.h"
|
|
#include "util.h"
|
|
|
|
/* Console output macros */
|
|
#define CPUTS(outstr) cputs(CC_EVENTS, outstr)
|
|
#define CPRINTS(format, args...) cprints(CC_EVENTS, format, ## args)
|
|
|
|
/*
|
|
* This is used to avoid 64-bit shifts which might require a new library
|
|
* function.
|
|
*/
|
|
#define HOST_EVENT_32BIT_MASK(x) (1UL << ((x) - 1))
|
|
static void host_event_set_bit(host_event_t *ev, uint8_t bit)
|
|
{
|
|
uint32_t *ptr = (uint32_t *)ev;
|
|
|
|
*ev = 0;
|
|
|
|
/*
|
|
* Host events are 1-based, so return early if event 0 is requested to
|
|
* be set.
|
|
*/
|
|
if (bit == 0)
|
|
return;
|
|
|
|
#ifdef CONFIG_HOST_EVENT64
|
|
if (bit > 32)
|
|
*(ptr + 1) = HOST_EVENT_32BIT_MASK(bit - 32);
|
|
else
|
|
#endif
|
|
*ptr = HOST_EVENT_32BIT_MASK(bit);
|
|
}
|
|
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
|
|
#define LPC_SYSJUMP_TAG 0x4c50 /* "LP" */
|
|
#define LPC_SYSJUMP_OLD_VERSION 1
|
|
#define LPC_SYSJUMP_VERSION 2
|
|
|
|
/*
|
|
* Always report mask includes mask of host events that need to be reported in
|
|
* host event always irrespective of the state of SCI, SMI and wake masks.
|
|
*
|
|
* Events that indicate critical shutdown/reboots that have occurred:
|
|
* - EC_HOST_EVENT_THERMAL_SHUTDOWN
|
|
* - EC_HOST_EVENT_BATTERY_SHUTDOWN
|
|
* - EC_HOST_EVENT_HANG_REBOOT
|
|
* - EC_HOST_EVENT_PANIC
|
|
*
|
|
* Events that are consumed by BIOS:
|
|
* - EC_HOST_EVENT_KEYBOARD_RECOVERY
|
|
* - EC_HOST_EVENT_KEYBOARD_FASTBOOT
|
|
* - EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT
|
|
*
|
|
* Events that are buffered and have separate data maintained of their own:
|
|
* - EC_HOST_EVENT_MKBP
|
|
*
|
|
*/
|
|
#define LPC_HOST_EVENT_ALWAYS_REPORT_DEFAULT_MASK \
|
|
(EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEYBOARD_RECOVERY) | \
|
|
EC_HOST_EVENT_MASK(EC_HOST_EVENT_THERMAL_SHUTDOWN) | \
|
|
EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_SHUTDOWN) | \
|
|
EC_HOST_EVENT_MASK(EC_HOST_EVENT_HANG_REBOOT) | \
|
|
EC_HOST_EVENT_MASK(EC_HOST_EVENT_PANIC) | \
|
|
EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEYBOARD_FASTBOOT) | \
|
|
EC_HOST_EVENT_MASK(EC_HOST_EVENT_MKBP) | \
|
|
EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT))
|
|
|
|
static host_event_t lpc_host_events;
|
|
static host_event_t lpc_host_event_mask[LPC_HOST_EVENT_COUNT];
|
|
|
|
/* Indicates if active wake mask set by host */
|
|
static uint8_t active_wm_set_by_host;
|
|
|
|
void lpc_set_host_event_mask(enum lpc_host_event_type type, host_event_t mask)
|
|
{
|
|
lpc_host_event_mask[type] = mask;
|
|
lpc_update_host_event_status();
|
|
|
|
/* mask 0 indicates wake mask not set by host */
|
|
if ((type == LPC_HOST_EVENT_WAKE) && (mask == 0))
|
|
active_wm_set_by_host = 0;
|
|
}
|
|
|
|
host_event_t lpc_get_host_event_mask(enum lpc_host_event_type type)
|
|
{
|
|
return lpc_host_event_mask[type];
|
|
}
|
|
|
|
static host_event_t lpc_get_all_host_event_masks(void)
|
|
{
|
|
host_event_t or_mask = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < LPC_HOST_EVENT_COUNT; i++)
|
|
or_mask |= lpc_get_host_event_mask(i);
|
|
|
|
return or_mask;
|
|
}
|
|
|
|
static void lpc_set_host_event_state(host_event_t events)
|
|
{
|
|
if (events == lpc_host_events)
|
|
return;
|
|
|
|
lpc_host_events = events;
|
|
lpc_update_host_event_status();
|
|
}
|
|
|
|
host_event_t lpc_get_host_events_by_type(enum lpc_host_event_type type)
|
|
{
|
|
return lpc_host_events & lpc_get_host_event_mask(type);
|
|
}
|
|
|
|
host_event_t lpc_get_host_events(void)
|
|
{
|
|
return lpc_host_events;
|
|
}
|
|
|
|
int lpc_get_next_host_event(void)
|
|
{
|
|
host_event_t ev;
|
|
int evt_idx = __builtin_ffs(lpc_host_events);
|
|
|
|
#ifdef CONFIG_HOST_EVENT64
|
|
if (evt_idx == 0) {
|
|
int evt_idx_high = __builtin_ffs(lpc_host_events >> 32);
|
|
|
|
if (evt_idx_high)
|
|
evt_idx = 32 + evt_idx_high;
|
|
}
|
|
#endif
|
|
|
|
if (evt_idx) {
|
|
host_event_set_bit(&ev, evt_idx);
|
|
host_clear_events(ev);
|
|
}
|
|
return evt_idx;
|
|
}
|
|
|
|
static void lpc_sysjump_save_mask(void)
|
|
{
|
|
system_add_jump_tag(LPC_SYSJUMP_TAG, LPC_SYSJUMP_VERSION,
|
|
sizeof(lpc_host_event_mask), lpc_host_event_mask);
|
|
}
|
|
DECLARE_HOOK(HOOK_SYSJUMP, lpc_sysjump_save_mask, HOOK_PRIO_DEFAULT);
|
|
|
|
/*
|
|
* Restore various LPC masks if they were saved before the sysjump.
|
|
*
|
|
* Returns:
|
|
* 1 = All masks were restored
|
|
* 0 = No masks were stashed before sysjump or EC performing sysjump did not
|
|
* support always report mask.
|
|
*/
|
|
static int lpc_post_sysjump_restore_mask(void)
|
|
{
|
|
const host_event_t *prev_mask;
|
|
int size, version;
|
|
|
|
prev_mask = (const host_event_t *)system_get_jump_tag(LPC_SYSJUMP_TAG,
|
|
&version, &size);
|
|
if (!prev_mask || size != sizeof(lpc_host_event_mask) ||
|
|
(version != LPC_SYSJUMP_VERSION &&
|
|
version != LPC_SYSJUMP_OLD_VERSION))
|
|
return 0;
|
|
|
|
memcpy(lpc_host_event_mask, prev_mask, sizeof(lpc_host_event_mask));
|
|
|
|
return version == LPC_SYSJUMP_VERSION;
|
|
}
|
|
|
|
host_event_t __attribute__((weak)) lpc_override_always_report_mask(void)
|
|
{
|
|
return LPC_HOST_EVENT_ALWAYS_REPORT_DEFAULT_MASK;
|
|
}
|
|
|
|
void lpc_init_mask(void)
|
|
{
|
|
/*
|
|
* First check if masks were stashed before sysjump. If no masks were
|
|
* stashed or if the EC image performing sysjump does not support always
|
|
* report mask, then set always report mask now.
|
|
*/
|
|
if (!lpc_post_sysjump_restore_mask())
|
|
lpc_host_event_mask[LPC_HOST_EVENT_ALWAYS_REPORT] =
|
|
lpc_override_always_report_mask();
|
|
}
|
|
|
|
void lpc_s3_resume_clear_masks(void)
|
|
{
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, 0);
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, 0);
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, 0);
|
|
}
|
|
|
|
/*
|
|
* Clear events that are not part of SCI/SMI mask so as to prevent
|
|
* premature wakes on next suspend. This is needed because A.P only queries
|
|
* SCI events after resume. We do not clear SCI/SMI events as they help
|
|
* kernel identify the wake reason on resume.
|
|
* For events that are not set in SCI mask but are part of WAKE(S0ix/S3)
|
|
* masks, kernel drivers should have other ways (physical/virtual interrupt)
|
|
* pin to identify when they trigger wakes.
|
|
*/
|
|
void clear_non_sci_events(void)
|
|
{
|
|
host_clear_events(~lpc_get_host_event_mask(LPC_HOST_EVENT_SCI) &
|
|
~lpc_get_host_event_mask(LPC_HOST_EVENT_SMI));
|
|
}
|
|
DECLARE_HOOK(HOOK_CHIPSET_RESUME, clear_non_sci_events, HOOK_PRIO_DEFAULT);
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Maintain two copies of the events that are set.
|
|
*
|
|
* The primary copy is mirrored in mapped memory and used to trigger interrupts
|
|
* on the host via ACPI/SCI/SMI/GPIO.
|
|
*
|
|
* The secondary (B) copy is used by entities other than ACPI to query the state
|
|
* of host events on EC. Currently events_copy_b is used for
|
|
* 1. Logging recovery mode switch in coreboot.
|
|
* 2. Used by depthcharge on devices with no 8042 and no MKBP interrupt.
|
|
* 3. Logging wake reason in coreboot.
|
|
* Current query of a event from copy_b is immediately followed by clear of the
|
|
* same event. Further uses of copy_b should make sure this semantics is
|
|
* followed and none of the above mentioned use cases are broken.
|
|
*
|
|
* Setting an event sets both copies. Copies are cleared separately.
|
|
*/
|
|
static host_event_t events;
|
|
static host_event_t events_copy_b;
|
|
|
|
/* Lazy wake masks */
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
static struct lazy_wake_masks {
|
|
host_event_t s3_lazy_wm;
|
|
host_event_t s5_lazy_wm;
|
|
#ifdef CONFIG_POWER_S0IX
|
|
host_event_t s0ix_lazy_wm;
|
|
#endif
|
|
} lazy_wm;
|
|
#endif
|
|
|
|
static void host_events_atomic_or(host_event_t *e, host_event_t m)
|
|
{
|
|
uint32_t *ptr = (uint32_t *)e;
|
|
|
|
atomic_or(ptr, (uint32_t)m);
|
|
#ifdef CONFIG_HOST_EVENT64
|
|
atomic_or(ptr + 1, (uint32_t)(m >> 32));
|
|
#endif
|
|
}
|
|
|
|
static void host_events_atomic_clear(host_event_t *e, host_event_t m)
|
|
{
|
|
uint32_t *ptr = (uint32_t *)e;
|
|
|
|
atomic_clear(ptr, (uint32_t)m);
|
|
#ifdef CONFIG_HOST_EVENT64
|
|
atomic_clear(ptr + 1, (uint32_t)(m >> 32));
|
|
#endif
|
|
}
|
|
|
|
#if !defined(CONFIG_HOSTCMD_X86) && defined(CONFIG_MKBP_EVENT)
|
|
static void host_events_send_mkbp_event(host_event_t e)
|
|
{
|
|
#ifdef CONFIG_HOST_EVENT64
|
|
/*
|
|
* If event bits in the upper 32-bit are set, indicate 64-bit host
|
|
* event.
|
|
*/
|
|
if (!(uint32_t)e)
|
|
mkbp_send_event(EC_MKBP_EVENT_HOST_EVENT64);
|
|
else
|
|
#endif
|
|
mkbp_send_event(EC_MKBP_EVENT_HOST_EVENT);
|
|
}
|
|
#endif
|
|
|
|
host_event_t host_get_events(void)
|
|
{
|
|
return events;
|
|
}
|
|
|
|
void host_set_events(host_event_t mask)
|
|
{
|
|
/* ignore host events the rest of board doesn't care about */
|
|
#ifdef CONFIG_HOST_EVENT64
|
|
mask &= CONFIG_HOST_EVENT64_REPORT_MASK;
|
|
#else
|
|
mask &= CONFIG_HOST_EVENT_REPORT_MASK;
|
|
#endif
|
|
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
/*
|
|
* Host only cares about the events for which the masks are set either
|
|
* in wake mask, SCI mask or SMI mask. In addition to that, there are
|
|
* certain events that need to be always reported (Please see
|
|
* LPC_HOST_EVENT_ALWAYS_REPORT_DEFAULT_MASK). Thus, when a new host
|
|
* event is being set, ensure that it is present in one of these
|
|
* masks. Else, there is no need to process that event.
|
|
*/
|
|
mask &= lpc_get_all_host_event_masks();
|
|
#endif
|
|
|
|
/* exit now if nothing has changed */
|
|
if (!((events & mask) != mask || (events_copy_b & mask) != mask))
|
|
return;
|
|
|
|
HOST_EVENT_CPRINTS("event set", mask);
|
|
|
|
host_events_atomic_or(&events, mask);
|
|
host_events_atomic_or(&events_copy_b, mask);
|
|
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
lpc_set_host_event_state(events);
|
|
#else
|
|
*(host_event_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = events;
|
|
#ifdef CONFIG_MKBP_EVENT
|
|
#ifdef CONFIG_MKBP_USE_HOST_EVENT
|
|
#error "Config error: MKBP must not be on top of host event"
|
|
#endif
|
|
host_events_send_mkbp_event(events);
|
|
#endif /* CONFIG_MKBP_EVENT */
|
|
#endif /* !CONFIG_HOSTCMD_X86 */
|
|
}
|
|
|
|
void host_set_single_event(enum host_event_code event)
|
|
{
|
|
host_event_t ev = 0;
|
|
|
|
host_event_set_bit(&ev, event);
|
|
host_set_events(ev);
|
|
}
|
|
|
|
int host_is_event_set(enum host_event_code event)
|
|
{
|
|
host_event_t ev = 0;
|
|
|
|
host_event_set_bit(&ev, event);
|
|
return events & ev;
|
|
}
|
|
|
|
void host_clear_events(host_event_t mask)
|
|
{
|
|
/* ignore host events the rest of board doesn't care about */
|
|
#ifdef CONFIG_HOST_EVENT64
|
|
mask &= CONFIG_HOST_EVENT64_REPORT_MASK;
|
|
#else
|
|
mask &= CONFIG_HOST_EVENT_REPORT_MASK;
|
|
#endif
|
|
|
|
/* return early if nothing changed */
|
|
if (!(events & mask))
|
|
return;
|
|
|
|
HOST_EVENT_CPRINTS("event clear", mask);
|
|
|
|
host_events_atomic_clear(&events, mask);
|
|
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
lpc_set_host_event_state(events);
|
|
#else
|
|
*(host_event_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = events;
|
|
#ifdef CONFIG_MKBP_EVENT
|
|
host_events_send_mkbp_event(events);
|
|
#endif
|
|
#endif /* !CONFIG_HOSTCMD_X86 */
|
|
}
|
|
|
|
#ifndef CONFIG_HOSTCMD_X86
|
|
static int host_get_next_event(uint8_t *out)
|
|
{
|
|
uint32_t event_out = (uint32_t)events;
|
|
memcpy(out, &event_out, sizeof(event_out));
|
|
host_events_atomic_clear(&events, event_out);
|
|
*(host_event_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = events;
|
|
return sizeof(event_out);
|
|
}
|
|
DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_HOST_EVENT, host_get_next_event);
|
|
|
|
#ifdef CONFIG_HOST_EVENT64
|
|
static int host_get_next_event64(uint8_t *out)
|
|
{
|
|
host_event_t event_out = events;
|
|
|
|
memcpy(out, &event_out, sizeof(event_out));
|
|
host_events_atomic_clear(&events, event_out);
|
|
*(host_event_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = events;
|
|
return sizeof(event_out);
|
|
}
|
|
DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_HOST_EVENT64, host_get_next_event64);
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
* Clear one or more host event bits from copy B.
|
|
*
|
|
* @param mask Event bits to clear (use EC_HOST_EVENT_MASK()).
|
|
* Write 1 to a bit to clear it.
|
|
*/
|
|
static void host_clear_events_b(host_event_t mask)
|
|
{
|
|
/* Only print if something's about to change */
|
|
if (events_copy_b & mask)
|
|
HOST_EVENT_CPRINTS("event clear B", mask);
|
|
|
|
host_events_atomic_clear(&events_copy_b, mask);
|
|
}
|
|
|
|
/**
|
|
* Politely ask the CPU to enable/disable its own throttling.
|
|
*
|
|
* @param throttle Enable (!=0) or disable(0) throttling
|
|
*/
|
|
test_mockable void host_throttle_cpu(int throttle)
|
|
{
|
|
if (throttle)
|
|
host_set_single_event(EC_HOST_EVENT_THROTTLE_START);
|
|
else
|
|
host_set_single_event(EC_HOST_EVENT_THROTTLE_STOP);
|
|
}
|
|
|
|
/*
|
|
* Events copy b is used by coreboot for logging the wake reason. For this to
|
|
* work, events_copy_b needs to be cleared on every suspend.
|
|
*/
|
|
void clear_events_copy_b(void)
|
|
{
|
|
events_copy_b = 0;
|
|
}
|
|
DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, clear_events_copy_b, HOOK_PRIO_DEFAULT);
|
|
|
|
/*****************************************************************************/
|
|
/* Console commands */
|
|
static int command_host_event(int argc, char **argv)
|
|
{
|
|
/* Handle sub-commands */
|
|
if (argc == 3) {
|
|
char *e;
|
|
host_event_t i = strtoul(argv[2], &e, 0);
|
|
if (*e)
|
|
return EC_ERROR_PARAM2;
|
|
|
|
if (!strcasecmp(argv[1], "set"))
|
|
host_set_events(i);
|
|
else if (!strcasecmp(argv[1], "clear"))
|
|
host_clear_events(i);
|
|
else if (!strcasecmp(argv[1], "clearb"))
|
|
host_clear_events_b(i);
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
else if (!strcasecmp(argv[1], "smi"))
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, i);
|
|
else if (!strcasecmp(argv[1], "sci"))
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, i);
|
|
else if (!strcasecmp(argv[1], "wake"))
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, i);
|
|
else if (!strcasecmp(argv[1], "always_report"))
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_ALWAYS_REPORT,
|
|
i);
|
|
#endif
|
|
else
|
|
return EC_ERROR_PARAM1;
|
|
}
|
|
|
|
/* Print current SMI/SCI status */
|
|
HOST_EVENT_CCPRINTF("Events: ", host_get_events());
|
|
HOST_EVENT_CCPRINTF("Events-B: ", events_copy_b);
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
HOST_EVENT_CCPRINTF("SMI mask: ",
|
|
lpc_get_host_event_mask(LPC_HOST_EVENT_SMI));
|
|
HOST_EVENT_CCPRINTF("SCI mask: ",
|
|
lpc_get_host_event_mask(LPC_HOST_EVENT_SCI));
|
|
HOST_EVENT_CCPRINTF("Wake mask: ",
|
|
lpc_get_host_event_mask(LPC_HOST_EVENT_WAKE));
|
|
HOST_EVENT_CCPRINTF("Always report mask: ",
|
|
lpc_get_host_event_mask(LPC_HOST_EVENT_ALWAYS_REPORT));
|
|
#endif
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(hostevent, command_host_event,
|
|
"[set | clear | clearb | smi | sci | wake | always_report] [mask]",
|
|
"Print / set host event state");
|
|
|
|
/*****************************************************************************/
|
|
/* Host commands */
|
|
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
|
|
static enum ec_status
|
|
host_event_get_smi_mask(struct host_cmd_handler_args *args)
|
|
{
|
|
struct ec_response_host_event_mask *r = args->response;
|
|
|
|
r->mask = (uint32_t)lpc_get_host_event_mask(LPC_HOST_EVENT_SMI);
|
|
args->response_size = sizeof(*r);
|
|
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_GET_SMI_MASK,
|
|
host_event_get_smi_mask,
|
|
EC_VER_MASK(0));
|
|
|
|
static enum ec_status
|
|
host_event_get_sci_mask(struct host_cmd_handler_args *args)
|
|
{
|
|
struct ec_response_host_event_mask *r = args->response;
|
|
|
|
r->mask = (uint32_t)lpc_get_host_event_mask(LPC_HOST_EVENT_SCI);
|
|
args->response_size = sizeof(*r);
|
|
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_GET_SCI_MASK,
|
|
host_event_get_sci_mask,
|
|
EC_VER_MASK(0));
|
|
|
|
static enum ec_status
|
|
host_event_get_wake_mask(struct host_cmd_handler_args *args)
|
|
{
|
|
struct ec_response_host_event_mask *r = args->response;
|
|
|
|
r->mask = (uint32_t)lpc_get_host_event_mask(LPC_HOST_EVENT_WAKE);
|
|
args->response_size = sizeof(*r);
|
|
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_GET_WAKE_MASK,
|
|
host_event_get_wake_mask,
|
|
EC_VER_MASK(0));
|
|
|
|
static enum ec_status
|
|
host_event_set_smi_mask(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_host_event_mask *p = args->params;
|
|
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, p->mask);
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_SET_SMI_MASK,
|
|
host_event_set_smi_mask,
|
|
EC_VER_MASK(0));
|
|
|
|
static enum ec_status
|
|
host_event_set_sci_mask(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_host_event_mask *p = args->params;
|
|
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, p->mask);
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_SET_SCI_MASK,
|
|
host_event_set_sci_mask,
|
|
EC_VER_MASK(0));
|
|
|
|
static enum ec_status
|
|
host_event_set_wake_mask(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_host_event_mask *p = args->params;
|
|
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, p->mask);
|
|
active_wm_set_by_host = !!p->mask;
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_SET_WAKE_MASK,
|
|
host_event_set_wake_mask,
|
|
EC_VER_MASK(0));
|
|
|
|
uint8_t lpc_is_active_wm_set_by_host(void)
|
|
{
|
|
return active_wm_set_by_host;
|
|
}
|
|
|
|
#endif /* CONFIG_HOSTCMD_X86 */
|
|
|
|
static enum ec_status host_event_get_b(struct host_cmd_handler_args *args)
|
|
{
|
|
struct ec_response_host_event_mask *r = args->response;
|
|
|
|
r->mask = events_copy_b;
|
|
args->response_size = sizeof(*r);
|
|
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_GET_B,
|
|
host_event_get_b,
|
|
EC_VER_MASK(0));
|
|
|
|
static enum ec_status host_event_clear(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_host_event_mask *p = args->params;
|
|
|
|
host_clear_events(p->mask);
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_CLEAR,
|
|
host_event_clear,
|
|
EC_VER_MASK(0));
|
|
|
|
static enum ec_status host_event_clear_b(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_host_event_mask *p = args->params;
|
|
|
|
host_clear_events_b(p->mask);
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_CLEAR_B,
|
|
host_event_clear_b,
|
|
EC_VER_MASK(0));
|
|
|
|
static enum ec_status host_event_action_get(struct host_cmd_handler_args *args)
|
|
{
|
|
struct ec_response_host_event *r = args->response;
|
|
const struct ec_params_host_event *p = args->params;
|
|
int result = EC_RES_SUCCESS;
|
|
|
|
args->response_size = sizeof(*r);
|
|
memset(r, 0, sizeof(*r));
|
|
|
|
switch (p->mask_type) {
|
|
case EC_HOST_EVENT_B:
|
|
r->value = events_copy_b;
|
|
break;
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
case EC_HOST_EVENT_SCI_MASK:
|
|
r->value = lpc_get_host_event_mask(LPC_HOST_EVENT_SCI);
|
|
break;
|
|
case EC_HOST_EVENT_SMI_MASK:
|
|
r->value = lpc_get_host_event_mask(LPC_HOST_EVENT_SMI);
|
|
break;
|
|
case EC_HOST_EVENT_ALWAYS_REPORT_MASK:
|
|
r->value = lpc_get_host_event_mask
|
|
(LPC_HOST_EVENT_ALWAYS_REPORT);
|
|
break;
|
|
case EC_HOST_EVENT_ACTIVE_WAKE_MASK:
|
|
r->value = lpc_get_host_event_mask(LPC_HOST_EVENT_WAKE);
|
|
break;
|
|
#ifdef CONFIG_POWER_S0IX
|
|
case EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX:
|
|
r->value = lazy_wm.s0ix_lazy_wm;
|
|
break;
|
|
#endif
|
|
case EC_HOST_EVENT_LAZY_WAKE_MASK_S3:
|
|
r->value = lazy_wm.s3_lazy_wm;
|
|
break;
|
|
case EC_HOST_EVENT_LAZY_WAKE_MASK_S5:
|
|
r->value = lazy_wm.s5_lazy_wm;
|
|
break;
|
|
#endif
|
|
default:
|
|
result = EC_RES_INVALID_PARAM;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static enum ec_status host_event_action_set(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_host_event *p = args->params;
|
|
int result = EC_RES_SUCCESS;
|
|
host_event_t mask_value __unused = (host_event_t)(p->value);
|
|
|
|
switch (p->mask_type) {
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
case EC_HOST_EVENT_SCI_MASK:
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, mask_value);
|
|
break;
|
|
case EC_HOST_EVENT_SMI_MASK:
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, mask_value);
|
|
break;
|
|
case EC_HOST_EVENT_ALWAYS_REPORT_MASK:
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_ALWAYS_REPORT,
|
|
mask_value);
|
|
break;
|
|
case EC_HOST_EVENT_ACTIVE_WAKE_MASK:
|
|
active_wm_set_by_host = !!mask_value;
|
|
lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, mask_value);
|
|
break;
|
|
#ifdef CONFIG_POWER_S0IX
|
|
case EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX:
|
|
lazy_wm.s0ix_lazy_wm = mask_value;
|
|
break;
|
|
#endif
|
|
case EC_HOST_EVENT_LAZY_WAKE_MASK_S3:
|
|
lazy_wm.s3_lazy_wm = mask_value;
|
|
break;
|
|
case EC_HOST_EVENT_LAZY_WAKE_MASK_S5:
|
|
lazy_wm.s5_lazy_wm = mask_value;
|
|
break;
|
|
#endif
|
|
default:
|
|
result = EC_RES_INVALID_PARAM;
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static enum ec_status
|
|
host_event_action_clear(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_host_event *p = args->params;
|
|
int result = EC_RES_SUCCESS;
|
|
host_event_t mask_value = (host_event_t)(p->value);
|
|
|
|
switch (p->mask_type) {
|
|
case EC_HOST_EVENT_MAIN:
|
|
host_clear_events(mask_value);
|
|
break;
|
|
case EC_HOST_EVENT_B:
|
|
host_clear_events_b(mask_value);
|
|
break;
|
|
default:
|
|
result = EC_RES_INVALID_PARAM;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static enum ec_status
|
|
host_command_host_event(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_host_event *p = args->params;
|
|
|
|
args->response_size = 0;
|
|
|
|
switch (p->action) {
|
|
case EC_HOST_EVENT_GET:
|
|
return host_event_action_get(args);
|
|
case EC_HOST_EVENT_SET:
|
|
return host_event_action_set(args);
|
|
case EC_HOST_EVENT_CLEAR:
|
|
return host_event_action_clear(args);
|
|
default:
|
|
return EC_RES_INVALID_PARAM;
|
|
}
|
|
}
|
|
|
|
DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT,
|
|
host_command_host_event,
|
|
EC_VER_MASK(0));
|
|
|
|
#define LAZY_WAKE_MASK_SYSJUMP_TAG 0x4C4D /* LM - Lazy Mask*/
|
|
#define LAZY_WAKE_MASK_HOOK_VERSION 1
|
|
|
|
#ifdef CONFIG_HOSTCMD_X86
|
|
int get_lazy_wake_mask(enum power_state state, host_event_t *mask)
|
|
{
|
|
int ret = EC_SUCCESS;
|
|
|
|
switch (state) {
|
|
case POWER_S5:
|
|
*mask = lazy_wm.s5_lazy_wm;
|
|
break;
|
|
case POWER_S3:
|
|
*mask = lazy_wm.s3_lazy_wm;
|
|
break;
|
|
#ifdef CONFIG_POWER_S0IX
|
|
case POWER_S0ix:
|
|
*mask = lazy_wm.s0ix_lazy_wm;
|
|
break;
|
|
#endif
|
|
default:
|
|
*mask = 0;
|
|
ret = EC_ERROR_INVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void preserve_lazy_wm(void)
|
|
{
|
|
system_add_jump_tag(LAZY_WAKE_MASK_SYSJUMP_TAG,
|
|
LAZY_WAKE_MASK_HOOK_VERSION,
|
|
sizeof(lazy_wm),
|
|
&lazy_wm);
|
|
}
|
|
DECLARE_HOOK(HOOK_SYSJUMP, preserve_lazy_wm, HOOK_PRIO_DEFAULT);
|
|
|
|
static void restore_lazy_wm(void)
|
|
{
|
|
const struct lazy_wake_masks *wm_state;
|
|
int version, size;
|
|
|
|
wm_state = (const struct lazy_wake_masks *)
|
|
system_get_jump_tag(LAZY_WAKE_MASK_SYSJUMP_TAG,
|
|
&version, &size);
|
|
|
|
if (wm_state && (version == LAZY_WAKE_MASK_HOOK_VERSION) &&
|
|
(size == sizeof(lazy_wm))) {
|
|
lazy_wm = *wm_state;
|
|
}
|
|
}
|
|
DECLARE_HOOK(HOOK_INIT, restore_lazy_wm, HOOK_PRIO_INIT_CHIPSET + 1);
|
|
#endif
|