diff --git a/common/host_event_commands.c b/common/host_event_commands.c index 0f092f6bea..587fa12df2 100644 --- a/common/host_event_commands.c +++ b/common/host_event_commands.c @@ -11,6 +11,7 @@ #include "host_command.h" #include "lpc.h" #include "mkbp_event.h" +#include "power.h" #include "system.h" #include "task.h" #include "util.h" @@ -83,10 +84,17 @@ static void host_event_set_bit(host_event_t *ev, uint8_t bit) 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) @@ -210,6 +218,17 @@ void lpc_s3_resume_clear_masks(void) static host_event_t events; static host_event_t events_copy_b; +/* Lazy wake masks */ +#ifdef CONFIG_LPC +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; @@ -512,12 +531,18 @@ static int 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_LPC */ static int host_event_get_b(struct host_cmd_handler_args *args) @@ -554,3 +579,188 @@ static int host_event_clear_b(struct host_cmd_handler_args *args) DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_CLEAR_B, host_event_clear_b, EC_VER_MASK(0)); + +static int 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_LPC + 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 int 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_LPC + 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 int 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 int 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_LPC +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 diff --git a/include/ec_commands.h b/include/ec_commands.h index 741b7234b7..367b625ffe 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -3233,6 +3233,8 @@ struct __ec_align1 ec_response_temp_sensor_get_info { /*****************************************************************************/ /* Host event commands */ + +/* Obsolete. New implementation should use EC_CMD_HOST_EVENT instead */ /* * Host event mask params and response structures, shared by all of the host * event commands below. @@ -3258,6 +3260,86 @@ struct __ec_align4 ec_response_host_event_mask { #define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x008E #define EC_CMD_HOST_EVENT_CLEAR_B 0x008F +/* + * Unified host event programming interface - Should be used by newer versions + * of BIOS/OS to program host events and masks + */ + +struct __ec_align4 ec_params_host_event { + + /* Action requested by host - one of enum ec_host_event_action. */ + uint8_t action; + + /* + * Mask type that the host requested the action on - one of + * enum ec_host_event_mask_type. + */ + uint8_t mask_type; + + /* Set to 0, ignore on read */ + uint16_t reserved; + + /* Value to be used in case of set operations. */ + uint64_t value; +}; + +/* + * Response structure returned by EC_CMD_HOST_EVENT. + * Update the value on a GET request. Set to 0 on GET/CLEAR + */ + +struct __ec_align4 ec_response_host_event { + + /* Mask value in case of get operation */ + uint64_t value; +}; + +enum ec_host_event_action { + /* + * params.value is ignored. Value of mask_type populated + * in response.value + */ + EC_HOST_EVENT_GET, + + /* Bits in params.value are set */ + EC_HOST_EVENT_SET, + + /* Bits in params.value are cleared */ + EC_HOST_EVENT_CLEAR, +}; + +enum ec_host_event_mask_type { + + /* Main host event copy */ + EC_HOST_EVENT_MAIN, + + /* Copy B of host events */ + EC_HOST_EVENT_B, + + /* SCI Mask */ + EC_HOST_EVENT_SCI_MASK, + + /* SMI Mask */ + EC_HOST_EVENT_SMI_MASK, + + /* Mask of events that should be always reported in hostevents */ + EC_HOST_EVENT_ALWAYS_REPORT_MASK, + + /* Active wake mask */ + EC_HOST_EVENT_ACTIVE_WAKE_MASK, + + /* Lazy wake mask for S0ix */ + EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX, + + /* Lazy wake mask for S3 */ + EC_HOST_EVENT_LAZY_WAKE_MASK_S3, + + /* Lazy wake mask for S5 */ + EC_HOST_EVENT_LAZY_WAKE_MASK_S5, +}; + +#define EC_CMD_HOST_EVENT 0x00A4 + /*****************************************************************************/ /* Switch commands */ diff --git a/include/host_command.h b/include/host_command.h index 6fdbc639e9..f83ffcc616 100644 --- a/include/host_command.h +++ b/include/host_command.h @@ -10,6 +10,7 @@ #include "common.h" #include "ec_commands.h" +enum power_state; /* Args for host command handler */ struct host_cmd_handler_args { @@ -182,6 +183,28 @@ host_event_t host_get_events(void); * @return true if is set or false otherwise */ int host_is_event_set(enum host_event_code event); + +#ifdef CONFIG_LPC + +/* + * Get lazy wake masks for the sleep state provided + * + * @param state Sleep state + * @param mask Lazy wake mask. + * + * @return EC_SUCCESS for success and EC_ERROR_INVAL for error + */ + +int get_lazy_wake_mask(enum power_state state, host_event_t *mask); + +/* + * Check if active wake mask set by host + * + * + * @return 1 if active wake mask set by host else return 0 + */ +uint8_t lpc_is_active_wm_set_by_host(void); +#endif #endif /** diff --git a/power/common.c b/power/common.c index 452aa7f9a9..ed1ffcc0e5 100644 --- a/power/common.c +++ b/power/common.c @@ -13,6 +13,7 @@ #include "gpio.h" #include "hooks.h" #include "host_command.h" +#include "lpc.h" #include "power.h" #include "system.h" #include "task.h" @@ -196,6 +197,43 @@ void power_set_state(enum power_state new_state) want_g3_exit = 0; } +#ifdef CONFIG_LPC + +/* If host doesn't program s0ix lazy wake mask, use default s0ix mask */ +#define DEFAULT_WAKE_MASK_S0IX (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) | \ + EC_HOST_EVENT_MASK(EC_HOST_EVENT_MODE_CHANGE)) + /* + * Set wake mask on edge of sleep state entry + * 1. On transition to S0, wake mask is reset. + * 2. In non-S0 states, active mask set by host gets a higher preference. + * 3. If host has not set any active mask, then check if a lazy mask exists + * for the current power state. + * 4. If state is S0ix and no lazy or active wake mask is set, then use default + * S0ix mask to be compatible with older BIOS versions. + * + * @param state New sleep state + */ +static void power_set_active_wake_mask(enum power_state state) +{ + host_event_t wake_mask; + + if (state == POWER_S0) + wake_mask = 0; + else if (lpc_is_active_wm_set_by_host()) + return; + else if (get_lazy_wake_mask(state, &wake_mask)) + return; +#ifdef CONFIG_POWER_S0IX + if ((state == POWER_S0ix) && (wake_mask == 0)) + wake_mask = DEFAULT_WAKE_MASK_S0IX; +#endif + + lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, wake_mask); +} +#else +static void power_set_active_wake_mask(enum power_state state) { } +#endif + /** * Common handler for steady states * @@ -414,8 +452,10 @@ void chipset_task(void *u) new_state = power_common_state(state); /* Handle state changes */ - if (new_state != state) + if (new_state != state) { power_set_state(new_state); + power_set_active_wake_mask(new_state); + } } } diff --git a/power/intel_x86.c b/power/intel_x86.c index 59b2d529aa..8233261661 100644 --- a/power/intel_x86.c +++ b/power/intel_x86.c @@ -155,47 +155,6 @@ static void s0ix_transition(int check_state, int hook_id) s0ix_notify = S0IX_NOTIFY_NONE; } -/* - * In AP S0 -> S3 & S0ix transitions, - * the chipset_suspend is called. - * - * The chipset_in_state(CHIPSET_STATE_STANDBY | CHIPSET_STATE_ON) - * is used to detect the S0ix transiton. - * - * During S0ix entry, the wake mask for lid open and tablet mode is enabled. - */ -static void s0ix_lpc_enable_wake_mask(void) -{ - if (chipset_in_state(CHIPSET_STATE_STANDBY | CHIPSET_STATE_ON)) { - host_event_t mask; - - mask = lpc_get_host_event_mask(LPC_HOST_EVENT_WAKE) | - EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) | - EC_HOST_EVENT_MASK(EC_HOST_EVENT_MODE_CHANGE); - - lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, mask); - } -} - -/* - * In AP S0ix & S3 -> S0 transitions, - * the chipset_resume hook is called. - * - * During S0ix exit, the wake mask for lid open and tablet mode is disabled. - */ -static void s0ix_lpc_disable_wake_mask(void) -{ - if (chipset_in_state(CHIPSET_STATE_STANDBY | CHIPSET_STATE_ON)) { - host_event_t mask; - - mask = lpc_get_host_event_mask(LPC_HOST_EVENT_WAKE) & - ~EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) & - ~EC_HOST_EVENT_MASK(EC_HOST_EVENT_MODE_CHANGE); - - lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, mask); - } -} - static void handle_chipset_reset(void) { if (chipset_in_state(CHIPSET_STATE_STANDBY)) { @@ -428,9 +387,6 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state) #ifdef CONFIG_POWER_S0IX case POWER_S0S0ix: - - s0ix_lpc_enable_wake_mask(); - /* * Call hooks only if we haven't notified listeners of S0ix * suspend. @@ -442,13 +398,9 @@ enum power_state common_intel_x86_power_handle_state(enum power_state state) * to go into deep sleep in S0ix. */ enable_sleep(SLEEP_MASK_AP_RUN); - return POWER_S0ix; - case POWER_S0ixS0: - s0ix_lpc_disable_wake_mask(); - /* * Disable idle task deep sleep. This means that the low * power idle task will not go into deep sleep while in S0.