summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/console.c207
-rw-r--r--common/flash_commands.c229
-rw-r--r--common/host_command.c203
-rw-r--r--common/i8042.c137
-rw-r--r--common/main.c94
-rw-r--r--common/port80.c74
-rw-r--r--common/shared_mem.c54
-rw-r--r--common/util.c151
-rw-r--r--common/vboot.c113
9 files changed, 1262 insertions, 0 deletions
diff --git a/common/console.c b/common/console.c
new file mode 100644
index 0000000000..bc558f5cf4
--- /dev/null
+++ b/common/console.c
@@ -0,0 +1,207 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Console module for Chrome EC */
+
+#include "console.h"
+#include "task.h"
+#include "uart.h"
+#include "registers.h"
+#include "util.h"
+
+#define MAX_COMMAND_GROUPS 20
+#define MAX_ARGS_PER_COMMAND 10
+
+#define PROMPT "> "
+
+static const struct console_group *group_list[MAX_COMMAND_GROUPS];
+static int group_count = 0;
+
+
+void console_has_input(void)
+{
+ /* Wake up the console task */
+ task_send_msg(TASK_ID_CONSOLE, TASK_ID_CONSOLE, 0);
+}
+
+
+int console_register_commands(const struct console_group *group)
+{
+ if (group_count >= MAX_COMMAND_GROUPS)
+ return EC_ERROR_OVERFLOW;
+
+ group_list[group_count++] = group;
+ return EC_SUCCESS;
+}
+
+
+/* Splits a line of input into words. Stores the count of words in
+ * <argc>. Stores pointers to the words in <argv>, which must be at
+ * least <max_argc> long. If more than <max_argc> words are found,
+ * discards the excess and returns EC_ERROR_OVERFLOW. */
+int split_words(char *input, int max_argc, int *argc, char **argv)
+{
+ char *c;
+ int in_word = 0;
+
+ /* Parse input into words */
+ *argc = 0;
+ for (c = input; *c; c++) {
+ if (isspace(*c)) {
+ if (in_word) {
+ /* Ending a word */
+ *c = '\0';
+ ++*argc;
+ in_word = 0;
+ }
+ } else if (*c == '#') {
+ /* After the hash sign is comment, ignored.
+ * TODO: Need more logic to suuport escaping. */
+ break;
+ } else {
+ if (!in_word) {
+ /* Starting a new word */
+ if (*argc >= max_argc)
+ return EC_ERROR_OVERFLOW;
+
+ argv[*argc] = c;
+ in_word = 1;
+ }
+ }
+ }
+ return EC_SUCCESS;
+}
+
+
+/* Finds a command by name. Returns the command structure, or NULL if
+ * no match found. */
+const struct console_command *find_command(char *name)
+{
+ const struct console_command *cmd;
+ int c, g;
+
+ /* Find the command in the command groups */
+ for (g = 0; g < group_count; g++) {
+ cmd = group_list[g]->commands;
+ for (c = group_list[g]->command_count; c > 0; c--, cmd++) {
+ if (!strcasecmp(name, cmd->name))
+ return cmd;
+ }
+ }
+
+ return NULL;
+}
+
+
+/* Handles a line of input containing a single command.
+ *
+ * Modifies the input string during parsing. */
+static int handle_command(char *input)
+{
+ const struct console_command *cmd;
+ char *argv[MAX_ARGS_PER_COMMAND];
+ int argc = 0;
+
+ /* Split input into words. Ignore words past our limit. */
+ split_words(input, MAX_ARGS_PER_COMMAND, &argc, argv);
+
+ /* If no command, nothing to do */
+ if (!argc)
+ return EC_SUCCESS;
+
+ cmd = find_command(argv[0]);
+ if (cmd)
+ return cmd->handler(argc, argv);
+
+ uart_printf("Command '%s' not found.\n", argv[0]);
+ return EC_ERROR_UNKNOWN;
+}
+
+
+static char last_input[80];
+static char input_buf[80];
+
+/* handle a console command */
+void console_process(void)
+{
+ int rv;
+
+ /* Process all the pending commands. Need to do this all at once
+ * since our interrupt may have been triggered multiple times. */
+ /* TODO: Go to sleep briefly between commands to give lower
+ * priority tasks a chance to run? */
+ while (uart_peek('\n') >= 0) {
+ uart_gets(input_buf, sizeof(input_buf));
+
+ /* "." repeats the last command, if any */
+ if (!strcasecmp(input_buf, ".\n"))
+ strzcpy(input_buf, last_input, sizeof(input_buf));
+ else if (!isspace(*input_buf))
+ strzcpy(last_input, input_buf, sizeof(last_input));
+
+ rv = handle_command(input_buf);
+ if (rv != EC_SUCCESS)
+ uart_printf("Command returned error %d\n", rv);
+ uart_puts(PROMPT);
+ }
+}
+
+void console_task(void)
+{
+ console_init();
+
+ while (1) {
+ console_process();
+ /* wait for the next command message */
+ task_wait_msg(-1);
+ }
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+/* Command handler - prints help. */
+static int command_help(int argc, char **argv)
+{
+ const struct console_command *cmd;
+ int c, g;
+
+ uart_puts("Known commands:\n");
+
+ for (g = 0; g < group_count; g++) {
+ cmd = group_list[g]->commands;
+ uart_printf("Group %s:\n", group_list[g]->group_name);
+ for (c = group_list[g]->command_count; c > 0; c--, cmd++)
+ uart_printf(" %s\n", cmd->name);
+ /* Generates enough output to overflow the buffer */
+ uart_flush_output();
+ }
+
+ uart_puts("'.' repeats the last command.\n");
+
+ return EC_SUCCESS;
+}
+
+static const struct console_command console_commands[] = {
+ {"help", command_help},
+ {"?", command_help},
+};
+static const struct console_group command_group = {
+ "Console", console_commands, ARRAY_SIZE(console_commands)
+};
+
+/*****************************************************************************/
+/* Initialization */
+
+int console_init(void)
+{
+ *last_input = '\0';
+ *input_buf = '\0';
+ uart_set_console_mode(1);
+ uart_printf("Console is enabled; type HELP for help.\n");
+ uart_puts(PROMPT);
+ /* Register our internal commands */
+ return console_register_commands(&command_group);
+}
diff --git a/common/flash_commands.c b/common/flash_commands.c
new file mode 100644
index 0000000000..c53f8e6d97
--- /dev/null
+++ b/common/flash_commands.c
@@ -0,0 +1,229 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Flash memory module for Chrome EC */
+
+#include "console.h"
+#include "flash.h"
+#include "flash_commands.h"
+#include "lpc_commands.h"
+#include "registers.h" /* TODO: remove; only for temp debugging */
+#include "shared_mem.h"
+#include "uart.h"
+#include "util.h"
+
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_flash_info(int argc, char **argv)
+{
+ uart_printf("Usable flash size: %d KB\n", flash_get_size());
+ return EC_SUCCESS;
+}
+
+
+static int command_flash_erase(int argc, char **argv)
+{
+ int offset = 0;
+ int size = FLASH_ERASE_BYTES;
+ char *endptr;
+ int rv;
+
+ if (argc < 2) {
+ uart_puts("Usage: flasherase <offset> [size]\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ offset = strtoi(argv[1], &endptr, 0);
+ if (*endptr) {
+ uart_puts("Invalid offset\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ if (argc > 2) {
+ size = strtoi(argv[2], &endptr, 0);
+ if (*endptr) {
+ uart_puts("Invalid size\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ }
+
+ uart_printf("Erasing %d bytes at offset 0x%x (%d)...\n",
+ size, offset, offset);
+ rv = flash_erase(offset, size);
+ if (rv == EC_SUCCESS)
+ uart_puts("done.\n");
+ else
+ uart_printf("failed. (error %d)\n", rv);
+
+ return rv;
+}
+
+
+static int command_flash_write(int argc, char **argv)
+{
+ char *data;
+ int offset = 0;
+ int size = 1024; /* Default size */
+ char *endptr;
+ int rv;
+ int i;
+
+ if (argc < 2) {
+ uart_puts("Usage: flashwrite <offset> <size>\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ offset = strtoi(argv[1], &endptr, 0);
+ if (*endptr) {
+ uart_puts("Invalid offset\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ if (argc > 2) {
+ size = strtoi(argv[2], &endptr, 0);
+ if (*endptr) {
+ uart_puts("Invalid size\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ if (size > shared_mem_size()) {
+ uart_puts("Truncating size\n");
+ size = sizeof(data);
+ }
+ }
+
+ /* Acquire the shared memory buffer */
+ rv = shared_mem_acquire(size, 0, &data);
+ if (rv) {
+ uart_printf("Unable to acquire %d byte buffer\n", size);
+ return rv;
+ }
+
+ /* Fill the data buffer with a pattern */
+ for (i = 0; i < size; i++)
+ data[i] = i;
+
+ uart_printf("Writing %d bytes to offset 0x%x (%d)...\n",
+ size, offset, offset);
+ rv = flash_write(offset, size, data);
+ if (rv == EC_SUCCESS)
+ uart_puts("done.\n");
+ else
+ uart_printf("failed. (error %d)\n", rv);
+
+ /* Free the buffer */
+ shared_mem_release(data);
+
+ return rv;
+}
+
+
+static int command_flash_wp(int argc, char **argv)
+{
+ int b = 0;
+ char *endptr;
+
+ if (argc < 2) {
+ uart_puts("Usage: flashwp [bitmask]\n");
+ uart_printf("(current value of FMPPE1: 0x%08x)\n",
+ LM4_FLASH_FMPPE1);
+ return EC_SUCCESS;
+ }
+
+ b = strtoi(argv[1], &endptr, 0);
+ if (*endptr) {
+ uart_puts("Invalid bitmask\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ uart_printf("FMPPE1 before: 0x%08x\n", LM4_FLASH_FMPPE1);
+ LM4_FLASH_FMPPE1 = b;
+ uart_printf("FMPPE1 after: 0x%08x\n", LM4_FLASH_FMPPE1);
+ return EC_SUCCESS;
+}
+
+
+static const struct console_command console_commands[] = {
+ {"flasherase", command_flash_erase},
+ {"flashinfo", command_flash_info},
+ {"flashwrite", command_flash_write},
+ {"flashwp", command_flash_wp},
+};
+static const struct console_group command_group = {
+ "Flash", console_commands, ARRAY_SIZE(console_commands)
+};
+
+
+
+/*****************************************************************************/
+/* Host commands */
+
+enum lpc_status flash_command_get_info(uint8_t *data)
+{
+ struct lpc_response_flash_info *r =
+ (struct lpc_response_flash_info *)data;
+
+ r->flash_size = flash_get_size();
+ r->write_block_size = FLASH_WRITE_BYTES;
+ r->erase_block_size = FLASH_ERASE_BYTES;
+ r->protect_block_size = FLASH_PROTECT_BYTES;
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+
+enum lpc_status flash_command_read(uint8_t *data)
+{
+ struct lpc_params_flash_read *p =
+ (struct lpc_params_flash_read *)data;
+ struct lpc_response_flash_read *r =
+ (struct lpc_response_flash_read *)data;
+
+ if (p->size > sizeof(r->data))
+ return EC_LPC_STATUS_ERROR;
+
+ if (flash_read(p->offset, p->size, r->data))
+ return EC_LPC_STATUS_ERROR;
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+
+enum lpc_status flash_command_write(uint8_t *data)
+{
+ struct lpc_params_flash_write *p =
+ (struct lpc_params_flash_write *)data;
+
+ if (p->size > sizeof(p->data))
+ return EC_LPC_STATUS_ERROR;
+
+ if (flash_write(p->offset, p->size, p->data))
+ return EC_LPC_STATUS_ERROR;
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+
+enum lpc_status flash_command_erase(uint8_t *data)
+{
+ struct lpc_params_flash_erase *p =
+ (struct lpc_params_flash_erase *)data;
+
+ if (flash_erase(p->offset, p->size))
+ return EC_LPC_STATUS_ERROR;
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Initialization */
+
+int flash_commands_init(void)
+{
+ /* Register our internal commands */
+ console_register_commands(&command_group);
+ return EC_SUCCESS;
+}
+
diff --git a/common/host_command.c b/common/host_command.c
new file mode 100644
index 0000000000..1f27196412
--- /dev/null
+++ b/common/host_command.c
@@ -0,0 +1,203 @@
+/* Copyright (c) 2011 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 "config.h"
+#include "console.h"
+#include "flash_commands.h"
+#include "host_command.h"
+#include "lpc.h"
+#include "lpc_commands.h"
+#include "system.h"
+#include "task.h"
+#include "uart.h"
+#include "registers.h"
+#include "util.h"
+
+static int host_command = 0;
+static uint8_t *host_data;
+
+/*****************************************************************************/
+/* Host commands */
+
+void host_command_received(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(EC_LPC_STATUS_ERROR);
+ return;
+ }
+
+ /* Save the command */
+ host_command = command;
+
+ /* Wake up the task to handle the command */
+ task_send_msg(TASK_ID_HOSTCMD, TASK_ID_HOSTCMD, 0);
+}
+
+
+static enum lpc_status HostCommandHello(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);
+
+ /* TODO - remove this delay. For now, pretend command takes a long
+ * time, so we can see the busy bit set on the host side. */
+ {
+ volatile int i __attribute__((unused));
+ int j;
+ for (j = 0; j < 10000000; j++)
+ i = j;
+ }
+ uart_puts("[LPC sending hello back]\n");
+
+ r->out_data = d + 0x01020304;
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+
+static enum lpc_status HostCommandGetVersion(uint8_t *data)
+{
+ struct lpc_response_get_version *r =
+ (struct lpc_response_get_version *)data;
+
+ uart_printf("[LPC GetVersion]\n");
+
+ strzcpy(r->version_string_ro, system_get_version(SYSTEM_IMAGE_RO),
+ sizeof(r->version_string_ro));
+ strzcpy(r->version_string_rw_a, system_get_version(SYSTEM_IMAGE_RW_A),
+ sizeof(r->version_string_rw_a));
+ strzcpy(r->version_string_rw_b, system_get_version(SYSTEM_IMAGE_RW_B),
+ sizeof(r->version_string_rw_b));
+
+ switch(system_get_image_copy()) {
+ case SYSTEM_IMAGE_RO:
+ r->current_image = EC_LPC_IMAGE_RO;
+ break;
+ case SYSTEM_IMAGE_RW_A:
+ r->current_image = EC_LPC_IMAGE_RW_A;
+ break;
+ case SYSTEM_IMAGE_RW_B:
+ r->current_image = EC_LPC_IMAGE_RW_B;
+ break;
+ default:
+ r->current_image = EC_LPC_IMAGE_UNKNOWN;
+ break;
+ }
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+
+static enum lpc_status HostCommandReadTest(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_STATUS_ERROR;
+
+ for (i = 0; i < size; i++)
+ r->data[i] = offset + i;
+
+ return EC_LPC_STATUS_SUCCESS;
+}
+
+
+/* handle a LPC command */
+static void command_process(void)
+{
+ uart_printf("[hostcmd 0x%02x]\n", host_command);
+
+ /* TODO: might be smaller to make this a table, once we get a bunch
+ * of commands. */
+ switch (host_command) {
+ case EC_LPC_COMMAND_HELLO:
+ lpc_send_host_response(HostCommandHello(host_data));
+ return;
+ case EC_LPC_COMMAND_GET_VERSION:
+ lpc_send_host_response(HostCommandGetVersion(host_data));
+ return;
+ case EC_LPC_COMMAND_READ_TEST:
+ lpc_send_host_response(HostCommandReadTest(host_data));
+ return;
+ case EC_LPC_COMMAND_FLASH_INFO:
+ lpc_send_host_response(flash_command_get_info(host_data));
+ return;
+ case EC_LPC_COMMAND_FLASH_READ:
+ lpc_send_host_response(flash_command_read(host_data));
+ return;
+ case EC_LPC_COMMAND_FLASH_WRITE:
+ lpc_send_host_response(flash_command_write(host_data));
+ return;
+ case EC_LPC_COMMAND_FLASH_ERASE:
+ lpc_send_host_response(flash_command_erase(host_data));
+ return;
+ default:
+ lpc_send_host_response(EC_LPC_STATUS_INVALID_COMMAND);
+ }
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+/* Command handler - prints EC version. */
+static int command_version(int argc, char **argv)
+{
+ uart_printf("RO version: %s\n",
+ system_get_version(SYSTEM_IMAGE_RO));
+ uart_printf("RW-A version: %s\n",
+ system_get_version(SYSTEM_IMAGE_RW_A));
+ uart_printf("RW-B version: %s\n",
+ system_get_version(SYSTEM_IMAGE_RW_B));
+ return EC_SUCCESS;
+}
+
+
+static const struct console_command console_commands[] = {
+ {"version", command_version},
+};
+
+static const struct console_group command_group = {
+ "Host commands", console_commands, ARRAY_SIZE(console_commands)
+};
+
+/*****************************************************************************/
+/* Initialization */
+
+int host_command_init(void)
+{
+ host_data = (uint8_t *)lpc_get_host_range();
+
+ console_register_commands(&command_group);
+ return EC_SUCCESS;
+}
+
+void host_command_task(void)
+{
+ host_command_init();
+
+ while (1) {
+ /* wait for the next command message */
+ task_wait_msg(-1);
+ /* process it */
+ command_process();
+ }
+}
diff --git a/common/i8042.c b/common/i8042.c
new file mode 100644
index 0000000000..42c6095ecc
--- /dev/null
+++ b/common/i8042.c
@@ -0,0 +1,137 @@
+/* Copyright (c) 2011 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.
+ *
+ * Chrome OS EC i8042 interface code.
+ */
+
+#include "board.h"
+#include "common.h"
+#include "i8042.h"
+#include "keyboard.h"
+/* TODO: Code in common.c should not directly access chip registers */
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+
+#define I8042_DEBUG 1
+
+#define MAX_QUEUED_KEY_PRESS 16
+
+/* Circular buffer to host.
+ * head: next to dequeqe
+ * tail: next to enqueue
+ * head == tail: empty.
+ * tail + 1 == head: full
+ */
+static int head_to_buffer = 0;
+static int tail_to_buffer = 0;
+#define HOST_BUFFER_SIZE (16)
+static uint8_t to_host_buffer[HOST_BUFFER_SIZE];
+
+
+void i8042_init()
+{
+ head_to_buffer = tail_to_buffer = 0;
+ LM4_LPC_ST(3) = 0; /* clear the TOH bit */
+}
+
+
+void i8042_receives_data(int data)
+{
+ int ret_len;
+ uint8_t output[MAX_SCAN_CODE_LEN];
+ enum ec_error_list ret;
+
+ ret_len = handle_keyboard_data(data, output);
+ ret = i8042_send_to_host(ret_len, output);
+ ASSERT(ret == EC_SUCCESS);
+}
+
+
+void i8042_receives_command(int cmd)
+{
+ int ret_len;
+ uint8_t output[MAX_SCAN_CODE_LEN];
+ enum ec_error_list ret;
+
+ ret_len = handle_keyboard_command(cmd, output);
+ ret = i8042_send_to_host(ret_len, output);
+ ASSERT(ret == EC_SUCCESS);
+}
+
+
+static void enq_to_host(int len, uint8_t *to_host)
+{
+ int from, to;
+
+ /* TODO: need atomic protection */
+ if ((tail_to_buffer + len) <= (head_to_buffer + HOST_BUFFER_SIZE - 1)) {
+ for (from = 0, to = tail_to_buffer; from < len;) {
+ to_host_buffer[to++] = to_host[from++];
+ to %= HOST_BUFFER_SIZE;
+ }
+ tail_to_buffer = (tail_to_buffer + len) % HOST_BUFFER_SIZE;
+ }
+ /* end of atomic protection */
+}
+
+
+void i8042_command_task(void)
+{
+ while (1) {
+ /* Either a new byte to host or host picking up can un-block. */
+ task_wait_msg(-1);
+
+ while (1) {
+ uint8_t chr;
+ int empty = 0;
+
+ /* TODO: need atomic protection */
+ if (head_to_buffer == tail_to_buffer) {
+ empty = 1; /* nothing to host */
+ }
+ /* end of atomic protection */
+ if (empty) break;
+
+ /* if the host still didn't read that away,
+ try next time. */
+ if (LM4_LPC_ST(3) & (1 << 0 /* TOH */)) {
+#if I8042_DEBUG >= 5
+ uart_printf("[%d] i8042_command_task() "
+ "cannot send to host due to TOH\n",
+ get_time().le.lo);
+#endif
+ break;
+ }
+
+ /* TODO: need atomic protection */
+ chr = to_host_buffer[head_to_buffer];
+ head_to_buffer =
+ (head_to_buffer + 1) % HOST_BUFFER_SIZE;
+ /* end of atomic protection */
+
+ /* Write to host. TOH is set automatically. */
+ LPC_POOL_KEYBOARD[1] = chr;
+#if I8042_DEBUG >= 4
+ uart_printf("[%d] i8042_command_task() "
+ "sends to host: 0x%02x\n",
+ get_time().le.lo, chr);
+#endif
+ }
+ }
+}
+
+
+enum ec_error_list i8042_send_to_host(int len, uint8_t *to_host)
+{
+ enq_to_host(len, to_host);
+
+ /* Wake up the task to handle the command */
+ task_send_msg(TASK_ID_I8042CMD, TASK_ID_I8042CMD, 0);
+
+ return EC_SUCCESS;
+}
diff --git a/common/main.c b/common/main.c
new file mode 100644
index 0000000000..c9e1bbe75d
--- /dev/null
+++ b/common/main.c
@@ -0,0 +1,94 @@
+/* Copyright (c) 2011 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.
+ * Copyright 2011 Google Inc.
+ *
+ * Example of EC main loop
+ */
+
+#include "registers.h"
+#include "board.h"
+
+#include "adc.h"
+#include "clock.h"
+#include "console.h"
+#include "eeprom.h"
+#include "flash.h"
+#include "flash_commands.h"
+#include "gpio.h"
+#include "i2c.h"
+#include "keyboard.h"
+#include "lpc.h"
+#include "memory_commands.h"
+#include "port80.h"
+#include "powerdemo.h"
+#include "pwm.h"
+#include "system.h"
+#include "task.h"
+#include "temp_sensor.h"
+#include "timer.h"
+#include "uart.h"
+#include "vboot.h"
+#include "watchdog.h"
+
+/* example task blinking the user LED */
+void UserLedBlink(void)
+{
+ while (1) {
+ gpio_set(EC_GPIO_DEBUG_LED, 1);
+ usleep(500000);
+ watchdog_reload();
+ gpio_set(EC_GPIO_DEBUG_LED, 0);
+ usleep(500000);
+ watchdog_reload();
+ }
+}
+
+
+int main(void)
+{
+ /* Configure the pin multiplexers */
+ configure_board();
+ /* Set the CPU clocks / PLLs */
+ clock_init();
+
+ /* Do system, gpio, and vboot pre-initialization so we can jump to
+ * another image if necessary. This must be done as early as
+ * possible, so that the minimum number of components get
+ * re-initialized if we jump to another image. */
+ system_pre_init();
+ gpio_pre_init();
+ vboot_pre_init();
+
+ /* TODO - race condition on enabling interrupts. Module inits
+ * should call task_IntEnable(int) when they're ready... */
+ task_init();
+
+ watchdog_init(1100);
+ timer_init();
+ uart_init();
+ system_init();
+ gpio_init();
+ flash_init();
+ eeprom_init();
+ port_80_init();
+ lpc_init();
+ flash_commands_init();
+ vboot_init();
+ pwm_init();
+ i2c_init();
+ temp_sensor_init();
+ memory_commands_init();
+ keyboard_init();
+ adc_init();
+
+ /* Print the reset cause */
+ uart_printf("\n\n--- Chrome EC initialized! ---\n");
+ uart_printf("(image: %s, version: %s, last reset: %s)\n",
+ system_get_image_copy_string(),
+ system_get_version(SYSTEM_IMAGE_UNKNOWN),
+ system_get_reset_cause_string());
+
+ /* Launch task scheduling (never returns) */
+ return task_start();
+}
diff --git a/common/port80.c b/common/port80.c
new file mode 100644
index 0000000000..68d3219aa0
--- /dev/null
+++ b/common/port80.c
@@ -0,0 +1,74 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Port 80 module for Chrome EC */
+
+#include "console.h"
+#include "port80.h"
+#include "uart.h"
+#include "util.h"
+
+
+#define HISTORY_LEN 16
+
+static uint8_t history[HISTORY_LEN];
+static int head = 0; /* Next index to use / oldest previous entry */
+static int last_data = -1; /* Last data written to port 80 */
+
+
+void port_80_write(int data)
+{
+ /* Ignore duplicate writes, since the linux kernel writes to port 80
+ * as a delay mechanism during boot. */
+ if (data == last_data)
+ return;
+
+ /* TODO: post to SWI and print from there? This currently
+ * prints from inside the LPC interrupt itself. */
+
+ uart_printf("[Port 80: 0x%02x]\n", data);
+ last_data = data;
+
+ history[head] = data;
+ head = (head + 1) & (HISTORY_LEN - 1);
+}
+
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_port80(int argc, char **argv)
+{
+ int h = head;
+ int i;
+
+ /* Technically, if a port 80 write comes in while we're
+ * printing this, we could print an incorrect history.
+ * Probably not worth the complexity to work around that. */
+
+ uart_puts("Last port 80 writes:");
+ for (i = 0; i < HISTORY_LEN; i++)
+ uart_printf(" %02x", history[(h + i) & (HISTORY_LEN - 1)]);
+ uart_puts(" <--newest\n");
+ return EC_SUCCESS;
+}
+
+
+static const struct console_command console_commands[] = {
+ {"port80", command_port80},
+};
+static const struct console_group command_group = {
+ "Port 80", console_commands, ARRAY_SIZE(console_commands)
+};
+
+/*****************************************************************************/
+/* Initialization */
+
+int port_80_init(void)
+{
+ console_register_commands(&command_group);
+ memset(history, 0, sizeof(history));
+ return EC_SUCCESS;
+}
diff --git a/common/shared_mem.c b/common/shared_mem.c
new file mode 100644
index 0000000000..bff3ec6da3
--- /dev/null
+++ b/common/shared_mem.c
@@ -0,0 +1,54 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Shared memory module for Chrome EC */
+
+#include "shared_mem.h"
+#include "uart.h"
+
+/* Size of shared memory buffer */
+#define SHARED_MEM_SIZE 4096
+
+static char shared_buf[SHARED_MEM_SIZE];
+static int buf_in_use = 0;
+
+
+int shared_mem_init(void)
+{
+ return EC_SUCCESS;
+}
+
+
+int shared_mem_size(void)
+{
+ return SHARED_MEM_SIZE;
+}
+
+
+int shared_mem_acquire(int size, int wait, char **dest_ptr)
+{
+ if (size > SHARED_MEM_SIZE || size <= 0)
+ return EC_ERROR_INVAL;
+
+ /* TODO: if task_start() hasn't been called, fail immediately
+ * if not available. */
+
+ /* TODO: wait if requested; for now, we fail immediately if
+ * not available. */
+ if (buf_in_use)
+ return EC_ERROR_BUSY;
+
+ /* TODO: atomically acquire buf_in_use. */
+ buf_in_use = 1;
+ *dest_ptr = shared_buf;
+ return EC_SUCCESS;
+}
+
+
+void shared_mem_release(void *ptr)
+{
+ /* TODO: use event to wake up a previously-blocking acquire */
+ buf_in_use = 0;
+}
diff --git a/common/util.c b/common/util.c
new file mode 100644
index 0000000000..5eefcbe6ff
--- /dev/null
+++ b/common/util.c
@@ -0,0 +1,151 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Utility functions for Chrome EC */
+
+#include "util.h"
+
+int strlen(const char *s)
+{
+ int len = 0;
+
+ while (*s++)
+ len++;
+
+ return len;
+}
+
+
+int isspace(int c)
+{
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+
+int isdigit(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+
+int tolower(int c)
+{
+ return c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c;
+}
+
+
+int strcasecmp(const char *s1, const char *s2)
+{
+ int diff;
+ do {
+ diff = tolower(*s1) - tolower(*s2);
+ if (diff)
+ return diff;
+ } while (*(s1++) && *(s2++));
+ return 0;
+}
+
+
+int atoi(const char *nptr)
+{
+ int result = 0;
+ int neg = 0;
+ char c = '\0';
+
+ while ((c = *nptr++) && isspace(c)) {}
+
+ if (c == '-') {
+ neg = 1;
+ c = *nptr++;
+ }
+
+ while (isdigit(c)) {
+ result = result * 10 + (c - '0');
+ c = *nptr++;
+ }
+
+ return neg ? -result : result;
+}
+
+
+/* Like strtol(), but for integers */
+int strtoi(const char *nptr, char **endptr, int base)
+{
+ int result = 0;
+ int neg = 0;
+ int c = '\0';
+
+ if (endptr)
+ *endptr = (char *)nptr;
+
+ while((c = *nptr++) && isspace(c)) {}
+
+ if (c == '0' && *nptr == 'x' && (base == 0 || base == 16)) {
+ base = 16;
+ c = nptr[1];
+ nptr += 2;
+ } else {
+ base = 10;
+ if (c == '-') {
+ neg = 1;
+ c = *nptr++;
+ }
+ }
+
+ while (c) {
+ if (c >= '0' && c < '0' + MIN(base, 10))
+ result = result * base + (c - '0');
+ else if (c >= 'A' && c < 'A' + base - 10)
+ result = result * base + (c - 'A' + 10);
+ else if (c >= 'a' && c < 'a' + base - 10)
+ result = result * base + (c - 'a' + 10);
+ else
+ break;
+
+ if (endptr)
+ *endptr = (char *)nptr;
+ c = *nptr++;
+ }
+
+ return neg ? -result : result;
+}
+
+
+void *memcpy(void *dest, const void *src, int len)
+{
+ /* TODO: optimized version using LDM/STM would be much faster */
+ char *d = (char *)dest;
+ const char *s = (const char *)src;
+ while (len > 0) {
+ *(d++) = *(s++);
+ len--;
+ }
+ return dest;
+}
+
+
+void *memset(void *dest, int c, int len)
+{
+ /* TODO: optimized version using STM would be much faster */
+ char *d = (char *)dest;
+ while (len > 0) {
+ *(d++) = c;
+ len--;
+ }
+ return dest;
+}
+
+
+/* Like strncpy(), but guarantees null termination */
+char *strzcpy(char *dest, const char *src, int len)
+{
+ char *d = dest;
+ while (len > 1 && *src) {
+ *(d++) = *(src++);
+ len--;
+ }
+ *d = '\0';
+ return dest;
+}
diff --git a/common/vboot.c b/common/vboot.c
new file mode 100644
index 0000000000..ebf514d79d
--- /dev/null
+++ b/common/vboot.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Verified boot module for Chrome EC */
+
+#include "console.h"
+#include "system.h"
+#include "uart.h"
+#include "util.h"
+#include "vboot.h"
+
+
+#define SCRATCHPAD_EMPTY 0
+#define SCRATCHPAD_REQUEST_A 0xb00daaaa
+#define SCRATCHPAD_REQUEST_B 0xb00dbbbb
+#define SCRATCHPAD_SELECTED_A 0x0000d1da
+#define SCRATCHPAD_SELECTED_B 0x0000d1db
+#define SCRATCHPAD_SELECTED_RO 0x0000d1d0
+#define SCRATCHPAD_FAILED_A 0x0000eeea
+#define SCRATCHPAD_FAILED_B 0x0000eeeb
+
+
+/* Jumps to one of the RW images if necessary. */
+static void jump_to_other_image(void)
+{
+ int s;
+
+ if (system_get_image_copy() != SYSTEM_IMAGE_RO)
+ return; /* Not in RO firmware, so ignore scratchpad */
+
+ if (system_get_reset_cause() != SYSTEM_RESET_SOFT_COLD) {
+ /* In RO firmware, but not because of a warm boot.
+ * Stay in RO regardless of scratchpad, and clear it
+ * so we don't use it on the next boot. */
+ system_set_scratchpad(SCRATCHPAD_EMPTY);
+ return;
+ }
+
+ /* TODO: check recovery button; if it's pressed, stay in RO */
+
+ /* Check for a scratchpad value we recognize. Clear the
+ * scratchpad before jumping, so we only do this once. */
+ s = system_get_scratchpad();
+ if (s == SCRATCHPAD_REQUEST_A) {
+ system_set_scratchpad(SCRATCHPAD_SELECTED_A);
+ system_run_image_copy(SYSTEM_IMAGE_RW_A);
+ /* Shouldn't normally return; if we did, flag error */
+ system_set_scratchpad(SCRATCHPAD_FAILED_A);
+ } else if (s == SCRATCHPAD_REQUEST_B) {
+ system_set_scratchpad(SCRATCHPAD_SELECTED_B);
+ system_run_image_copy(SYSTEM_IMAGE_RW_B);
+ /* Shouldn't normally return; if we did, flag error */
+ system_set_scratchpad(SCRATCHPAD_FAILED_B);
+ } else {
+ system_set_scratchpad(SCRATCHPAD_EMPTY);
+ }
+}
+
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_reboot(int argc, char **argv)
+{
+ /* Handle request to boot to a specific image */
+ if (argc >= 2) {
+ if (!strcasecmp(argv[1], "a")) {
+ uart_puts("Rebooting to image A!\n\n\n");
+ system_set_scratchpad(SCRATCHPAD_REQUEST_A);
+ } else if (!strcasecmp(argv[1], "b")) {
+ uart_puts("Rebooting to image B!\n\n\n");
+ system_set_scratchpad(SCRATCHPAD_REQUEST_B);
+ } else {
+ uart_puts("Usage: reboot [ A | B ]\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ } else {
+ uart_puts("Rebooting to RO!\n\n\n");
+ }
+
+ uart_flush_output();
+ /* TODO - param to specify warm/cold */
+ system_reset(1);
+ return EC_SUCCESS;
+}
+
+static const struct console_command console_commands[] = {
+ {"reboot", command_reboot},
+};
+
+static const struct console_group command_group = {
+ "Verified boot", console_commands, ARRAY_SIZE(console_commands)
+};
+
+/*****************************************************************************/
+/* Initialization */
+
+int vboot_pre_init(void)
+{
+ /* Jump to a different image if necessary; this may not return */
+ jump_to_other_image();
+ return EC_SUCCESS;
+}
+
+
+int vboot_init(void)
+{
+ /* Register our internal commands */
+ console_register_commands(&command_group);
+ return EC_SUCCESS;
+}