mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-28 02:35:28 +00:00
Signed-off-by: Randall Spangler <rspangler@chromium.org> BUG=chrome-os-partner:7461 TEST=manual make BOARD={bds,link,daisy} make tests flash link system and make sure it boots Change-Id: I1241a1895c083e387e38ddab01ac346ca4474eb9
174 lines
4.3 KiB
C
174 lines
4.3 KiB
C
/* 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 command module for Chrome EC */
|
|
|
|
#include "console.h"
|
|
#include "host_command.h"
|
|
#include "lpc.h"
|
|
#include "lpc_commands.h"
|
|
#include "system.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "uart.h"
|
|
#include "util.h"
|
|
|
|
#define TASK_EVENT_SLOT(n) TASK_EVENT_CUSTOM(1 << n)
|
|
|
|
static int host_command[2];
|
|
|
|
/* Host commands are described in a special section */
|
|
extern const struct host_command __hcmds[];
|
|
extern const struct host_command __hcmds_end[];
|
|
|
|
/*****************************************************************************/
|
|
/* Host commands */
|
|
|
|
void host_command_received(int slot, int command)
|
|
{
|
|
/* TODO: should warn if we already think we're in a command */
|
|
|
|
/* If this is the reboot command, reboot immediately. This gives
|
|
* the host processor a way to unwedge the EC even if it's busy with
|
|
* some other command. */
|
|
if (command == EC_LPC_COMMAND_REBOOT) {
|
|
system_reset(1);
|
|
/* Reset should never return; if it does, post an error */
|
|
lpc_send_host_response(slot, EC_LPC_RESULT_ERROR);
|
|
return;
|
|
}
|
|
|
|
/* Save the command */
|
|
host_command[slot] = command;
|
|
|
|
/* Wake up the task to handle the command for the slot */
|
|
task_set_event(TASK_ID_HOSTCMD, TASK_EVENT_SLOT(slot), 0);
|
|
}
|
|
|
|
|
|
static enum lpc_status host_command_hello(uint8_t *data)
|
|
{
|
|
struct lpc_params_hello *p = (struct lpc_params_hello *)data;
|
|
struct lpc_response_hello *r = (struct lpc_response_hello *)data;
|
|
uint32_t d = p->in_data;
|
|
|
|
uart_printf("[LPC Hello 0x%08x]\n", d);
|
|
|
|
#ifdef DELAY_HELLO_RESPONSE
|
|
/* Pretend command takes a long time, so we can see the busy
|
|
* bit set on the host side. */
|
|
/* TODO: remove in production. Or maybe hello should take a
|
|
* param with how long the delay should be; that'd be more
|
|
* useful. */
|
|
usleep(1000000);
|
|
#endif
|
|
|
|
uart_puts("[LPC sending hello back]\n");
|
|
|
|
r->out_data = d + 0x01020304;
|
|
return EC_LPC_RESULT_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_LPC_COMMAND_HELLO, host_command_hello);
|
|
|
|
|
|
static enum lpc_status host_command_read_test(uint8_t *data)
|
|
{
|
|
struct lpc_params_read_test *p = (struct lpc_params_read_test *)data;
|
|
struct lpc_response_read_test *r =
|
|
(struct lpc_response_read_test *)data;
|
|
|
|
int offset = p->offset;
|
|
int size = p->size / sizeof(uint32_t);
|
|
int i;
|
|
|
|
if (size > ARRAY_SIZE(r->data))
|
|
return EC_LPC_RESULT_ERROR;
|
|
|
|
for (i = 0; i < size; i++)
|
|
r->data[i] = offset + i;
|
|
|
|
return EC_LPC_RESULT_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_LPC_COMMAND_READ_TEST, host_command_read_test);
|
|
|
|
|
|
/* ACPI query event handler. Note that the returned value is NOT actually
|
|
* an EC_LPC_RESULT enum; it's 0 if no event was pending, or the 1-based
|
|
* index of the lowest bit which was set. */
|
|
static enum lpc_status host_command_acpi_query_event(uint8_t *data)
|
|
{
|
|
uint32_t events = lpc_get_host_events();
|
|
int i;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
if (events & (1 << i)) {
|
|
lpc_clear_host_events(1 << i);
|
|
return (enum lpc_status)(i + 1);
|
|
}
|
|
}
|
|
|
|
/* No events pending */
|
|
return (enum lpc_status)0;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_LPC_COMMAND_ACPI_QUERY_EVENT,
|
|
host_command_acpi_query_event);
|
|
|
|
|
|
/* Finds a command by command number. Returns the command structure, or NULL if
|
|
* no match found. */
|
|
static const struct host_command *find_host_command(int command)
|
|
{
|
|
const struct host_command *cmd;
|
|
|
|
for (cmd = __hcmds; cmd < __hcmds_end; cmd++) {
|
|
if (command == cmd->command)
|
|
return cmd;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Handle a LPC command */
|
|
static void command_process(int slot)
|
|
{
|
|
int command = host_command[slot];
|
|
uint8_t *data = lpc_get_host_range(slot);
|
|
const struct host_command *cmd = find_host_command(command);
|
|
|
|
uart_printf("[hostcmd%d 0x%02x]\n", slot, command);
|
|
|
|
if (cmd)
|
|
lpc_send_host_response(slot, cmd->handler(data));
|
|
else
|
|
lpc_send_host_response(slot, EC_LPC_RESULT_INVALID_COMMAND);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Initialization / task */
|
|
|
|
static int host_command_init(void)
|
|
{
|
|
host_command[0] = host_command[1] = -1;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
|
|
void host_command_task(void)
|
|
{
|
|
host_command_init();
|
|
|
|
while (1) {
|
|
/* wait for the next command event */
|
|
int evt = task_wait_event(-1);
|
|
/* process it */
|
|
if (evt & TASK_EVENT_SLOT(0))
|
|
command_process(0);
|
|
if (evt & TASK_EVENT_SLOT(1))
|
|
command_process(1);
|
|
}
|
|
}
|