blob: feb1d9355278909b086510cfe50e21b2b59f270c [file] [log] [blame]
/* 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));