| /* 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. |
| */ |
| |
| /* Host event commands for Chrome EC */ |
| |
| #include "atomic.h" |
| #include "common.h" |
| #include "console.h" |
| #include "host_command.h" |
| #include "lpc.h" |
| #include "mkbp_event.h" |
| #include "util.h" |
| |
| /* Console output macros */ |
| #define CPUTS(outstr) cputs(CC_EVENTS, outstr) |
| #define CPRINTS(format, args...) cprints(CC_EVENTS, format, ## args) |
| |
| /* |
| * 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 to track events at a non-interrupt level (for |
| * example, so a user-level process can find out what events have happened |
| * since the last call, even though a kernel-level process is consuming events |
| * from the first copy). |
| * |
| * Setting an event sets both copies. Copies are cleared separately. |
| */ |
| static uint32_t events; |
| static uint32_t events_copy_b; |
| |
| uint32_t host_get_events(void) |
| { |
| return events; |
| } |
| |
| void host_set_events(uint32_t mask) |
| { |
| /* Only print if something's about to change */ |
| if ((events & mask) != mask || (events_copy_b & mask) != mask) |
| CPRINTS("event set 0x%08x", mask); |
| |
| atomic_or(&events, mask); |
| atomic_or(&events_copy_b, mask); |
| |
| #ifdef CONFIG_LPC |
| lpc_set_host_event_state(events); |
| #else |
| *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = events; |
| #endif |
| |
| #ifdef CONFIG_MKBP_EVENT |
| mkbp_send_event(EC_MKBP_EVENT_HOST_EVENT); |
| #endif |
| } |
| |
| void host_clear_events(uint32_t mask) |
| { |
| /* Only print if something's about to change */ |
| if (events & mask) |
| CPRINTS("event clear 0x%08x", mask); |
| |
| atomic_clear(&events, mask); |
| |
| #ifdef CONFIG_LPC |
| lpc_set_host_event_state(events); |
| #else |
| *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = events; |
| #endif |
| |
| #ifdef CONFIG_MKBP_EVENT |
| mkbp_send_event(EC_MKBP_EVENT_HOST_EVENT); |
| #endif |
| } |
| |
| static int host_get_next_event(uint8_t *out) |
| { |
| uint32_t event_out = events; |
| memcpy(out, &event_out, sizeof(event_out)); |
| atomic_clear(&events, event_out); |
| return sizeof(event_out); |
| } |
| DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_HOST_EVENT, host_get_next_event); |
| |
| /** |
| * 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(uint32_t mask) |
| { |
| /* Only print if something's about to change */ |
| if (events_copy_b & mask) |
| CPRINTS("event clear B 0x%08x", mask); |
| |
| 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); |
| } |
| |
| /*****************************************************************************/ |
| /* Console commands */ |
| |
| static int command_host_event(int argc, char **argv) |
| { |
| /* Handle sub-commands */ |
| if (argc == 3) { |
| char *e; |
| int i = strtoi(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_LPC |
| 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); |
| #endif |
| else |
| return EC_ERROR_PARAM1; |
| } |
| |
| /* Print current SMI/SCI status */ |
| ccprintf("Events: 0x%08x\n", host_get_events()); |
| ccprintf("Events-B: 0x%08x\n", events_copy_b); |
| #ifdef CONFIG_LPC |
| ccprintf("SMI mask: 0x%08x\n", |
| lpc_get_host_event_mask(LPC_HOST_EVENT_SMI)); |
| ccprintf("SCI mask: 0x%08x\n", |
| lpc_get_host_event_mask(LPC_HOST_EVENT_SCI)); |
| ccprintf("Wake mask: 0x%08x\n", |
| lpc_get_host_event_mask(LPC_HOST_EVENT_WAKE)); |
| #endif |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(hostevent, command_host_event, |
| "[set | clear | clearb | smi | sci | wake] [mask]", |
| "Print / set host event state", |
| NULL); |
| |
| /*****************************************************************************/ |
| /* Host commands */ |
| |
| #ifdef CONFIG_LPC |
| |
| static int host_event_get_smi_mask(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_host_event_mask *r = args->response; |
| |
| r->mask = 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 int host_event_get_sci_mask(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_host_event_mask *r = args->response; |
| |
| r->mask = 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 int host_event_get_wake_mask(struct host_cmd_handler_args *args) |
| { |
| struct ec_response_host_event_mask *r = args->response; |
| |
| r->mask = 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 int 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 int 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 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); |
| return EC_RES_SUCCESS; |
| } |
| DECLARE_HOST_COMMAND(EC_CMD_HOST_EVENT_SET_WAKE_MASK, |
| host_event_set_wake_mask, |
| EC_VER_MASK(0)); |
| |
| #endif /* CONFIG_LPC */ |
| |
| static int 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 int 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 int 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)); |