summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2011-12-07 18:51:09 +0000
committerVincent Palatin <vpalatin@chromium.org>2011-12-07 19:10:02 +0000
commitbdf7da5b082f6d18dd27f1e5d8cca0b12154a28c (patch)
tree6f14312a6cc70d056efc2bede8728c0868266719
parentabe5786058f4b60dc6d30e7d7c964aae850caa1f (diff)
downloadchrome-ec-bdf7da5b082f6d18dd27f1e5d8cca0b12154a28c.tar.gz
Initial sources import 1/3
source files mainly done by Randall. Signed-off-by: Randall Spangler <rspangler@chromium.org> Change-Id: Iaff83a842b17f3350fb6f2a3f1597ad4c29bd12a
-rw-r--r--board/bds/board.h50
-rw-r--r--board/link/board.h26
-rw-r--r--chip/lm4/adc.c166
-rw-r--r--chip/lm4/eeprom.c256
-rw-r--r--chip/lm4/flash.c183
-rw-r--r--chip/lm4/gpio.c58
-rw-r--r--chip/lm4/i2c.c224
-rw-r--r--chip/lm4/keyboard_scan.c231
-rw-r--r--chip/lm4/lpc.c210
-rw-r--r--chip/lm4/pwm.c284
-rw-r--r--chip/lm4/registers.h312
-rw-r--r--chip/lm4/system.c371
-rw-r--r--chip/lm4/temp_sensor.c197
-rw-r--r--chip/lm4/uart.c563
-rw-r--r--chip/lm4/x86_power.c150
-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
-rw-r--r--include/adc.h35
-rw-r--r--include/common.h35
-rw-r--r--include/console.h41
-rw-r--r--include/eeprom.h35
-rw-r--r--include/flash.h88
-rw-r--r--include/flash_commands.h23
-rw-r--r--include/gpio.h41
-rw-r--r--include/host_command.h19
-rw-r--r--include/i2c.h29
-rw-r--r--include/keyboard_scan.h16
-rw-r--r--include/lpc.h26
-rw-r--r--include/lpc_commands.h133
-rw-r--r--include/port80.h19
-rw-r--r--include/powerdemo.h16
-rw-r--r--include/pwm.h28
-rw-r--r--include/shared_mem.h39
-rw-r--r--include/system.h100
-rw-r--r--include/temp_sensor.h26
-rw-r--r--include/uart.h112
-rw-r--r--include/vboot.h20
-rw-r--r--include/version.h16
-rw-r--r--include/x86_power.h16
-rw-r--r--util/ectool.c465
47 files changed, 5921 insertions, 0 deletions
diff --git a/board/bds/board.h b/board/bds/board.h
new file mode 100644
index 0000000000..d485980b8f
--- /dev/null
+++ b/board/bds/board.h
@@ -0,0 +1,50 @@
+/* Stellaris EKB-LM4F-EAC board configuration */
+
+#ifndef __BOARD_H
+#define __BOARD_H
+
+/* 66.667 Mhz clock frequency */
+#define CPU_CLOCK 66666667
+
+/* Fan PWM channels */
+#define FAN_CH_KBLIGHT 1 /* Keyboard backlight */
+#define FAN_CH_POWER_LED 3 /* Power adapter LED */
+#define FAN_CH_CPU 4 /* CPU fan */
+
+/* LPC channels */
+#define LPC_CH_KERNEL 0 /* Kernel commands */
+#define LPC_CH_PORT80 1 /* Port 80 debug output */
+#define LPC_CH_CMD_DATA 2 /* Data for kernel/user-mode commands */
+#define LPC_CH_KEYBOARD 3 /* 8042 keyboard emulation */
+#define LPC_CH_USER 4 /* User-mode commands */
+#define LPC_CH_COMX 7 /* UART emulation */
+/* LPC pool offsets */
+#define LPC_POOL_OFFS_KERNEL 0 /* Kernel commands - 0=in, 1=out */
+#define LPC_POOL_OFFS_PORT80 4 /* Port 80 - 4=in, 5=out */
+#define LPC_POOL_OFFS_COMX 8 /* UART emulation range - 8-15 */
+#define LPC_POOL_OFFS_KEYBOARD 16 /* Keyboard - 16=in, 17=out */
+#define LPC_POOL_OFFS_CMD_DATA 512 /* Data range for commands - 512-1023 */
+/* LPC pool data pointers */
+#define LPC_POOL_KERNEL (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_KERNEL)
+#define LPC_POOL_PORT80 (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_PORT80)
+#define LPC_POOL_COMX (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_COMX)
+#define LPC_POOL_KEYBOARD (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_KEYBOARD)
+#define LPC_POOL_CMD_DATA (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_CMD_DATA)
+
+/* ADC inputs */
+/* TODO: really just need a lookup table for channels to inputs */
+#define ADC_IN_POT 0 /* Turn POT on badger board */
+
+/* I2C ports */
+#define I2C_PORT_BATTERY 5 // port 0 / PB2:3 on Link, open on badger
+#define I2C_PORT_CHARGER 5 // port 1 / PA6:7 on Link, user LED on badger
+#define I2C_PORT_THERMAL 5 // port 5 / PB6:7 on link, but PG6:7 on badger
+/* I2C port speeds in kbps */
+#define I2C_SPEED_BATTERY 100
+#define I2C_SPEED_CHARGER 100
+#define I2C_SPEED_THERMAL 400 /* TODO: TMP007 supports 3.4Mbps
+ operation; use faster speed? */
+
+void configure_board(void);
+
+#endif /* __BOARD_H */
diff --git a/board/link/board.h b/board/link/board.h
new file mode 100644
index 0000000000..45ea36833e
--- /dev/null
+++ b/board/link/board.h
@@ -0,0 +1,26 @@
+/* configuration for Link mainboard */
+
+#ifndef __BOARD_H
+#define __BOARD_H
+
+/* 66.667 Mhz clock frequency */
+#define CPU_CLOCK 66666667
+
+/* Fan PWM channels */
+#define FAN_CH_CPU 0 /* CPU fan */
+#define FAN_CH_KBLIGHT 1 /* Keyboard backlight */
+#define FAN_CH_POWER_LED 5 /* Power adapter LED */
+
+/* I2C ports */
+#define I2C_PORT_BATTERY 0
+#define I2C_PORT_CHARGER 1
+#define I2C_PORT_THERMAL 5
+/* I2C port speeds in kbps */
+#define I2C_SPEED_BATTERY 100
+#define I2C_SPEED_CHARGER 100
+#define I2C_SPEED_THERMAL 400 /* TODO: TMP007 supports 3.4Mbps
+ operation; use faster speed? */
+
+void configure_board(void);
+
+#endif /* __BOARD_H */
diff --git a/chip/lm4/adc.c b/chip/lm4/adc.c
new file mode 100644
index 0000000000..fab5c43cef
--- /dev/null
+++ b/chip/lm4/adc.c
@@ -0,0 +1,166 @@
+/* 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.
+ */
+
+/* ADC module for Chrome EC */
+
+#include "board.h"
+#include "console.h"
+#include "adc.h"
+#include "timer.h"
+#include "registers.h"
+#include "uart.h"
+#include "util.h"
+
+
+static void configure_gpio(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable GPIOE module and delay a few clocks */
+ LM4_SYSTEM_RCGCGPIO |= 0x0010;
+ scratch = LM4_SYSTEM_RCGCGPIO;
+
+ /* Use analog function for PE3 (AIN0) */
+ LM4_GPIO_DEN(E) &= ~0x08;
+ LM4_GPIO_AMSEL(E) |= 0x08;
+}
+
+
+int adc_read(enum adc_channel ch)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* TODO: right now we have only a single channel so this is
+ * simple. When we have multiple channels, should we...
+ *
+ * 1) Read them all using a timer interrupt, and then return
+ * the most recent value? This is lowest-latency for the
+ * caller, but won't return accurate data if read frequently.
+ *
+ * 2) Reserve SS3 for reading a single value, and configure it
+ * on each read? Needs mutex if we could have multiple
+ * callers; doesn't matter if just used for debugging.
+ *
+ * 3) Both? */
+ if (ch != ADC_CH_POT)
+ return ADC_READ_ERROR;
+
+ /* Empty the FIFO of any previous results */
+ while (!(LM4_ADC_SSFSTAT(0) & 0x100))
+ scratch = LM4_ADC_SSFIFO(0);
+
+ /* Clear the interrupt status */
+ LM4_ADC_ADCISC |= 0x01;
+
+ /* Initiate sample sequence */
+ LM4_ADC_ADCPSSI |= 0x01;
+
+ /* Wait for interrupt */
+ /* TODO: use a real interrupt */
+ while (!(LM4_ADC_ADCRIS & 0x01));
+
+ /* Read the FIFO */
+ return LM4_ADC_SSFIFO(0);
+}
+
+
+int adc_read_ec_temperature(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+ int a;
+
+ /* Empty the FIFO of any previous results */
+ while (!(LM4_ADC_SSFSTAT(3) & 0x100))
+ scratch = LM4_ADC_SSFIFO(3);
+
+ /* Clear the interrupt status */
+ LM4_ADC_ADCISC |= 0x08;
+
+ /* Initiate sample sequence */
+ LM4_ADC_ADCPSSI |= 0x08;
+
+ /* Wait for interrupt */
+ /* TODO: use a real interrupt */
+ /* TODO: timeout */
+ while (!(LM4_ADC_ADCRIS & 0x08));
+
+ /* Read the FIFO and convert to temperature */
+ a = LM4_ADC_SSFIFO(3);
+ return 273 + (295 - (225 * 2 * a) / ADC_READ_MAX) / 2;
+}
+
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_adc(int argc, char **argv)
+{
+ uart_printf("ADC POT channel = 0x%03x\n",adc_read(ADC_CH_POT));
+ return EC_SUCCESS;
+}
+
+
+static int command_ectemp(int argc, char **argv)
+{
+ int t = adc_read_ec_temperature();
+ uart_printf("EC temperature is %d K = %d C\n", t, t-273);
+ return EC_SUCCESS;
+}
+
+
+static const struct console_command console_commands[] = {
+ {"adc", command_adc},
+ {"ectemp", command_ectemp},
+};
+static const struct console_group command_group = {
+ "ADC", console_commands, ARRAY_SIZE(console_commands)
+};
+
+
+/*****************************************************************************/
+/* Initialization */
+
+int adc_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable ADC0 module and delay a few clocks */
+ LM4_SYSTEM_RCGCADC |= 0x01;
+ scratch = LM4_SYSTEM_RCGCADC;
+
+ /* Configure GPIOs */
+ configure_gpio();
+
+ /* Use external voltage references (VREFA+, VREFA-) instead of
+ * VDDA and GNDA. */
+ LM4_ADC_ADCCTL = 0x01;
+
+ /* TODO: set up clock using ADCCC register? */
+
+ /* Configure sample sequencer 0 */
+ LM4_ADC_ADCACTSS &= ~0x01;
+ /* Trigger SS0 by processor request */
+ LM4_ADC_ADCEMUX = (LM4_ADC_ADCEMUX & 0xfffffff0) | 0x00;
+ /* Sample AIN0 only */
+ LM4_ADC_SSMUX(0) = ADC_IN_POT & 0x0f;
+ LM4_ADC_SSEMUX(0) = (ADC_IN_POT >> 4) & 0x0f;
+ LM4_ADC_SSCTL(0) = 0x06; /* IE0 | END0 */
+ /* Enable sample sequencer 0 */
+ LM4_ADC_ADCACTSS |= 0x01;
+
+ /* Configure sample sequencer 3 */
+ LM4_ADC_ADCACTSS &= ~0x08;
+ /* Trigger SS3 by processor request */
+ LM4_ADC_ADCEMUX = (LM4_ADC_ADCEMUX & 0xffffff0f) | 0x00;
+ /* Sample internal temp sensor */
+ LM4_ADC_SSMUX(3) = 0x00;
+ LM4_ADC_SSEMUX(3) = 0x00;
+ LM4_ADC_SSCTL(3) = 0x0e; /* TS0 | IE0 | END0 */
+ /* Enable sample sequencer 3 */
+ LM4_ADC_ADCACTSS |= 0x08;
+
+ console_register_commands(&command_group);
+ return EC_SUCCESS;
+}
diff --git a/chip/lm4/eeprom.c b/chip/lm4/eeprom.c
new file mode 100644
index 0000000000..266dbe3d44
--- /dev/null
+++ b/chip/lm4/eeprom.c
@@ -0,0 +1,256 @@
+/* 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.
+ */
+
+/* EEPROM module for Chrome EC */
+
+#include "eeprom.h"
+#include "console.h"
+#include "uart.h"
+#include "registers.h"
+#include "util.h"
+
+/* Size of EEPROM block in bytes */
+#define EEPROM_BLOCK_SIZE 64
+
+/* Count of EEPROM blocks */
+static int block_count;
+
+
+/* Waits for the current EEPROM operation to finish. */
+static int wait_for_done(void)
+{
+ /* TODO: how long is a reasonable timeout? */
+ int i;
+ for (i = 0; i < 1000000; i++) {
+ if (!(LM4_EEPROM_EEDONE & 0x01))
+ return EC_SUCCESS;
+ }
+ return EC_ERROR_UNKNOWN;
+}
+
+
+int eeprom_get_block_count(void)
+{
+ return block_count;
+}
+
+
+int eeprom_get_block_size(void)
+{
+ return EEPROM_BLOCK_SIZE;
+}
+
+
+int eeprom_read(int block, int offset, int size, char *data)
+{
+ uint32_t *d = (uint32_t *)data;
+ int rv;
+
+ if (block < 0 || block >= block_count ||
+ offset < 0 || offset > EEPROM_BLOCK_SIZE || offset & 3 ||
+ size < 0 || offset + size >= EEPROM_BLOCK_SIZE || size & 3)
+ return EC_ERROR_UNKNOWN;
+
+ rv = wait_for_done();
+ if (rv)
+ return rv;
+
+ LM4_EEPROM_EEBLOCK = block;
+ if (LM4_EEPROM_EEBLOCK != block)
+ return EC_ERROR_UNKNOWN; /* Error setting block */
+
+ LM4_EEPROM_EEOFFSET = offset >> 2;
+
+ for ( ; size; size -= sizeof(uint32_t))
+ *(d++) = LM4_EEPROM_EERDWRINC;
+
+ return EC_SUCCESS;
+}
+
+
+int eeprom_write(int block, int offset, int size, const char *data)
+{
+ uint32_t *d = (uint32_t *)data;
+ int rv;
+
+ if (block < 0 || block >= block_count ||
+ offset < 0 || offset > EEPROM_BLOCK_SIZE || offset & 3 ||
+ size < 0 || offset + size >= EEPROM_BLOCK_SIZE || size & 3)
+ return EC_ERROR_UNKNOWN;
+
+ rv = wait_for_done();
+ if (rv)
+ return rv;
+
+ LM4_EEPROM_EEBLOCK = block;
+ if (LM4_EEPROM_EEBLOCK != block)
+ return EC_ERROR_UNKNOWN; /* Error setting block */
+
+ LM4_EEPROM_EEOFFSET = offset >> 2;
+
+ /* Write 32 bits at a time; wait for each write to complete */
+ for ( ; size; size -= sizeof(uint32_t)) {
+ LM4_EEPROM_EERDWRINC = *(d++);
+ rv = wait_for_done();
+ if (rv)
+ return rv;
+ if (LM4_EEPROM_EEDONE)
+ return EC_ERROR_UNKNOWN;
+ }
+
+ return EC_SUCCESS;
+}
+
+
+int eeprom_hide(int block)
+{
+ /* Block 0 can't be hidden */
+ if (block <= 0 || block >= block_count)
+ return EC_ERROR_UNKNOWN;
+
+ LM4_EEPROM_EEHIDE |= 1 << block;
+ return EC_SUCCESS;
+}
+
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_eeprom_info(int argc, char **argv)
+{
+ uart_printf("EEPROM: %d blocks of %d bytes\n",
+ eeprom_get_block_count(), eeprom_get_block_size());
+ uart_printf(" Block-hide flags: 0x%08x\n", LM4_EEPROM_EEHIDE);
+ return EC_SUCCESS;
+}
+
+
+static int command_eeprom_read(int argc, char **argv)
+{
+ int block = 0;
+ int offset = 0;
+ char *e;
+ int rv;
+ uint32_t d;
+
+ if (argc < 2) {
+ uart_puts("Usage: eeread <block> [offset]\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ block = strtoi(argv[1], &e, 0);
+ if (*e) {
+ uart_puts("Invalid block\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ if (argc > 2) {
+ offset = strtoi(argv[2], &e, 0);
+ if (*e) {
+ uart_puts("Invalid offset\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ }
+
+ rv = eeprom_read(block, offset, sizeof(d), (char *)&d);
+ if (rv == EC_SUCCESS)
+ uart_printf("Block %d offset %d = 0x%08x\n",
+ block, offset, d);
+ return rv;
+}
+
+
+static int command_eeprom_write(int argc, char **argv)
+{
+ int block = 0;
+ int offset = 0;
+ char *e;
+ int rv;
+ uint32_t d;
+
+ if (argc < 4) {
+ uart_puts("Usage: eeread <block> <offset> <data>\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ block = strtoi(argv[1], &e, 0);
+ if (*e) {
+ uart_puts("Invalid block\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ offset = strtoi(argv[2], &e, 0);
+ if (*e) {
+ uart_puts("Invalid offset\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ d = strtoi(argv[3], &e, 0);
+ if (*e) {
+ uart_puts("Invalid data\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ uart_printf("Writing 0x%08x to block %d offset %d...\n",
+ d, block, offset);
+ rv = eeprom_write(block, offset, sizeof(d), (char *)&d);
+ if (rv == EC_SUCCESS)
+ uart_puts("done.\n");
+ return rv;
+}
+
+
+static int command_eeprom_hide(int argc, char **argv)
+{
+ int block = 0;
+ char *e;
+ int rv;
+
+ if (argc < 2) {
+ uart_puts("Usage: eehide <block>\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ block = strtoi(argv[1], &e, 0);
+ if (*e) {
+ uart_puts("Invalid block\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ uart_printf("Hiding EEPROM block %d...\n", block);
+ rv = eeprom_hide(block);
+ if (rv == EC_SUCCESS)
+ uart_printf("Done.\n");
+ return rv;
+}
+
+
+static const struct console_command console_commands[] = {
+ {"eeinfo", command_eeprom_info},
+ {"eeread", command_eeprom_read},
+ {"eewrite", command_eeprom_write},
+ {"eehide", command_eeprom_hide},
+};
+static const struct console_group command_group = {
+ "EEPROM", console_commands, ARRAY_SIZE(console_commands)
+};
+
+
+/*****************************************************************************/
+/* Initialization */
+
+
+int eeprom_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable the EEPROM module and delay a few clocks */
+ LM4_SYSTEM_RCGCEEPROM = 1;
+ scratch = LM4_SYSTEM_RCGCEEPROM;
+
+ wait_for_done();
+ block_count = LM4_EEPROM_EESIZE >> 16;
+
+ console_register_commands(&command_group);
+ return EC_SUCCESS;
+}
diff --git a/chip/lm4/flash.c b/chip/lm4/flash.c
new file mode 100644
index 0000000000..115747d7d9
--- /dev/null
+++ b/chip/lm4/flash.c
@@ -0,0 +1,183 @@
+/* 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 "flash.h"
+#include "gpio.h"
+#include "uart.h"
+#include "registers.h"
+#include "util.h"
+
+
+static int usable_flash_size;
+
+
+int flash_get_size(void)
+{
+ return usable_flash_size;
+}
+
+
+int flash_get_write_block_size(void)
+{
+ return FLASH_WRITE_BYTES;
+}
+
+
+int flash_get_erase_block_size(void)
+{
+ return FLASH_ERASE_BYTES;
+}
+
+
+int flash_get_protect_block_size(void)
+{
+ return FLASH_PROTECT_BYTES;
+}
+
+
+int flash_read(int offset, int size, char *data)
+{
+ if (size < 0 || offset > usable_flash_size ||
+ offset + size > usable_flash_size)
+ return EC_ERROR_UNKNOWN; /* Invalid range */
+
+ /* Just read the flash from its memory window. */
+ /* TODO: is this affected by data cache? That is, if we read a
+ * block, then alter it, then read it again, do we get the old
+ * data? */
+ memcpy(data, (char *)offset, size);
+ return EC_SUCCESS;
+}
+
+
+/* Performs a write-buffer operation. Buffer (FWB) and address (FMA)
+ * must be pre-loaded. */
+static int WriteBuffer(void)
+{
+ if (!LM4_FLASH_FWBVAL)
+ return EC_SUCCESS; /* Nothing to do */
+
+ /* Clear previous error status */
+ LM4_FLASH_FCMISC = LM4_FLASH_FCRIS;
+
+ /* Start write operation at page boundary */
+ LM4_FLASH_FMC2 = 0xa4420001;
+
+ /* Wait for write to complete */
+ while (LM4_FLASH_FMC2 & 0x01) {}
+
+ /* Check for error conditions - program failed, erase needed,
+ * voltage error. */
+ if (LM4_FLASH_FCRIS & 0x2600)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+}
+
+
+int flash_write(int offset, int size, const char *data)
+{
+ const uint32_t *data32 = (const uint32_t *)data;
+ int rv;
+ int i;
+
+ if (size < 0 || offset > usable_flash_size ||
+ offset + size > usable_flash_size ||
+ (offset | size) & (FLASH_WRITE_BYTES - 1))
+ return EC_ERROR_UNKNOWN; /* Invalid range */
+
+ /* TODO - safety check - don't allow writing to the image we're
+ * running from */
+
+ /* Get initial page and write buffer index */
+ LM4_FLASH_FMA = offset & ~(FLASH_FWB_BYTES - 1);
+ i = (offset >> 2) & (FLASH_FWB_WORDS - 1);
+
+ /* Copy words into buffer */
+ for ( ; size > 0; size -= 4) {
+ LM4_FLASH_FWB[i++] = *data32++;
+ if (i == FLASH_FWB_WORDS) {
+ rv = WriteBuffer();
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* Advance to next page */
+ i = 0;
+ LM4_FLASH_FMA += FLASH_FWB_BYTES;
+ }
+ }
+
+ /* Handle final partial page, if any */
+ if (i > 0) {
+ rv = WriteBuffer();
+ if (rv != EC_SUCCESS)
+ return rv;
+ }
+ return EC_SUCCESS;
+}
+
+
+int flash_erase(int offset, int size)
+{
+ if (size < 0 || offset > usable_flash_size ||
+ offset + size > usable_flash_size ||
+ (offset | size) & (FLASH_ERASE_BYTES - 1))
+ return EC_ERROR_UNKNOWN; /* Invalid range */
+
+ /* TODO - safety check - don't allow erasing the image we're running
+ * from */
+
+ LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; /* Clear previous error status */
+ LM4_FLASH_FMA = offset;
+
+ for ( ; size > 0; size -= FLASH_ERASE_BYTES) {
+ /* Start erase */
+ LM4_FLASH_FMC = 0xa4420002;
+
+ /* Wait for erase to complete */
+ while (LM4_FLASH_FMC & 0x02) {}
+
+ /* Check for error conditions - erase failed, voltage error */
+ if (LM4_FLASH_FCRIS & 0x0a00)
+ return EC_ERROR_UNKNOWN;
+
+ LM4_FLASH_FMA += FLASH_ERASE_BYTES;
+ }
+
+ return EC_SUCCESS;
+}
+
+
+int flash_get_write_protect_range(int *offset, int *size)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+
+int flash_set_write_protect_range(int offset, int size)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+
+int flash_get_write_protect_status(void)
+{
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+
+int flash_init(void)
+{
+ /* Calculate usable flash size. Reserve one protection block
+ * at the top to hold the write protect range. FSIZE already
+ * returns one less than the number of protection pages. */
+ usable_flash_size = LM4_FLASH_FSIZE * FLASH_PROTECT_BYTES;
+
+ /* TODO - check WP# GPIO. If it's set and the flash protect range
+ * is set, write the flash protection registers. */
+ return EC_SUCCESS;
+}
diff --git a/chip/lm4/gpio.c b/chip/lm4/gpio.c
new file mode 100644
index 0000000000..2ec64ce8af
--- /dev/null
+++ b/chip/lm4/gpio.c
@@ -0,0 +1,58 @@
+/* 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.
+ */
+
+/* GPIO module for Chrome EC */
+
+#include "gpio.h"
+#include "registers.h"
+
+
+int gpio_pre_init(void)
+{
+ /* Enable clock to GPIO block A */
+ LM4_SYSTEM_RCGCGPIO |= 0x0001;
+
+ /* Turn off the LED before we make it an output */
+ gpio_set(EC_GPIO_DEBUG_LED, 0);
+
+ /* Clear GPIOAFSEL bits for block A pin 7 */
+ LM4_GPIO_AFSEL(A) &= ~(0x80);
+
+ /* Set GPIO to digital enable, output */
+ LM4_GPIO_DEN(A) |= 0x80;
+ LM4_GPIO_DIR(A) |= 0x80;
+
+ return EC_SUCCESS;
+}
+
+
+int gpio_init(void)
+{
+ return EC_SUCCESS;
+}
+
+
+int gpio_get(enum gpio_signal signal, int *value_ptr)
+{
+ switch (signal) {
+ case EC_GPIO_DEBUG_LED:
+ *value_ptr = (LM4_GPIO_DATA_BITS(A, 0x200) & 0x80 ? 1 : 0);
+ return EC_SUCCESS;
+ default:
+ return EC_ERROR_UNKNOWN;
+ }
+}
+
+
+int gpio_set(enum gpio_signal signal, int value)
+{
+ switch (signal) {
+ case EC_GPIO_DEBUG_LED:
+ LM4_GPIO_DATA_BITS(A, 0x200) = (value ? 0x80 : 0);
+ return EC_SUCCESS;
+ default:
+ return EC_ERROR_UNKNOWN;
+ }
+}
diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c
new file mode 100644
index 0000000000..401bfdf6b9
--- /dev/null
+++ b/chip/lm4/i2c.c
@@ -0,0 +1,224 @@
+/* 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.
+ */
+
+/* Temperature sensor module for Chrome EC */
+
+#include "board.h"
+#include "console.h"
+#include "i2c.h"
+#include "timer.h"
+#include "registers.h"
+#include "uart.h"
+#include "util.h"
+
+/* I2C ports */
+#define I2C_PORT_THERMAL 5 // port 5 / PB6:7 on link, but PG6:7 on badger
+#define I2C_PORT_BATTERY 5 // port 0 / PB2:3 on Link, open on badger
+#define I2C_PORT_CHARGER 5 // port 1 / PA6:7 on Link, user LED on badger
+/* I2C port speed in kbps */
+#define I2C_SPEED_THERMAL 400 /* TODO: TMP007 supports 3.4Mbps
+ operation; use faster speed? */
+#define I2C_SPEED_BATTERY 100
+#define I2C_SPEED_CHARGER 100
+
+
+/* Waits for the I2C bus to go idle */
+static int wait_idle(int port)
+{
+ int s, i;
+
+ /* Spin waiting for busy flag to go away */
+ /* TODO: should use interrupt handler */
+ for (i = 1000; i >= 0; i--) {
+ s = LM4_I2C_MCS(port);
+ if (s & 0x01) {
+ /* Still busy */
+ udelay(1000);
+ continue;
+ }
+
+ /* Check for errors */
+ if (s & 0x02)
+ return EC_ERROR_UNKNOWN;
+
+ return EC_SUCCESS;
+ }
+ return EC_ERROR_TIMEOUT;
+}
+
+
+int i2c_read16(int port, int slave_addr, int offset, int* data)
+{
+ int rv;
+ int d;
+
+ *data = 0;
+
+ /* Transmit the offset address to the slave; leave the master in
+ * transmit state. */
+ LM4_I2C_MSA(port) = (slave_addr & 0xff) | 0x00;
+ LM4_I2C_MDR(port) = offset & 0xff;
+ LM4_I2C_MCS(port) = 0x03;
+
+ rv = wait_idle(port);
+ if (rv)
+ return rv;
+
+ /* Send repeated start followed by receive */
+ LM4_I2C_MSA(port) = (slave_addr & 0xff) | 0x01;
+ LM4_I2C_MCS(port) = 0x0b;
+
+ rv = wait_idle(port);
+ if (rv)
+ return rv;
+
+ /* Read the first byte */
+ d = LM4_I2C_MDR(port) & 0xff;
+
+ /* Issue another read and then a stop. */
+ LM4_I2C_MCS(port) = 0x05;
+
+ rv = wait_idle(port);
+ if (rv)
+ return rv;
+
+ /* Read the second byte */
+ if (slave_addr & I2C_FLAG_BIG_ENDIAN)
+ *data = (d << 8) | (LM4_I2C_MDR(port) & 0xff);
+ else
+ *data = ((LM4_I2C_MDR(port) & 0xff) << 8) | d;
+ return EC_SUCCESS;
+}
+
+
+int i2c_write16(int port, int slave_addr, int offset, int data)
+{
+ int rv;
+
+ /* Transmit the offset address to the slave; leave the master in
+ * transmit state. */
+ LM4_I2C_MDR(port) = offset & 0xff;
+ LM4_I2C_MSA(port) = (slave_addr & 0xff) | 0x00;
+ LM4_I2C_MCS(port) = 0x03;
+
+ rv = wait_idle(port);
+ if (rv)
+ return rv;
+
+ /* Transmit the first byte */
+ if (slave_addr & I2C_FLAG_BIG_ENDIAN)
+ LM4_I2C_MDR(port) = (data >> 8) & 0xff;
+ else
+ LM4_I2C_MDR(port) = data & 0xff;
+ LM4_I2C_MCS(port) = 0x01;
+
+ rv = wait_idle(port);
+ if (rv)
+ return rv;
+
+ /* Transmit the second byte and then a stop */
+ if (slave_addr & I2C_FLAG_BIG_ENDIAN)
+ LM4_I2C_MDR(port) = data & 0xff;
+ else
+ LM4_I2C_MDR(port) = (data >> 8) & 0xff;
+ LM4_I2C_MCS(port) = 0x05;
+
+ return wait_idle(port);
+}
+
+
+/*****************************************************************************/
+/* Console commands */
+
+
+static void scan_bus(int port, char *desc)
+{
+ int rv;
+ int a;
+
+ uart_printf("Scanning %s I2C bus...\n", desc);
+
+ for (a = 0; a < 0x100; a += 2) {
+ uart_puts(".");
+
+ /* Do a single read */
+ LM4_I2C_MSA(port) = a | 0x01;
+ LM4_I2C_MCS(port) = 0x07;
+ rv = wait_idle(port);
+ if (rv == EC_SUCCESS)
+ uart_printf("\nFound device at 0x%02x\n", a);
+ }
+ uart_puts("\n");
+}
+
+
+static int command_scan(int argc, char **argv)
+{
+ scan_bus(I2C_PORT_THERMAL, "thermal");
+ scan_bus(I2C_PORT_BATTERY, "battery");
+ scan_bus(I2C_PORT_CHARGER, "charger");
+ uart_puts("done.\n");
+ return EC_SUCCESS;
+}
+
+
+static const struct console_command console_commands[] = {
+ {"i2cscan", command_scan},
+};
+static const struct console_group command_group = {
+ "I2C", console_commands, ARRAY_SIZE(console_commands)
+};
+
+
+/*****************************************************************************/
+/* Initialization */
+
+/* Configures GPIOs for the module. */
+static void configure_gpio(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable GPIOG module and delay a few clocks */
+ LM4_SYSTEM_RCGCGPIO |= 0x0040;
+ scratch = LM4_SYSTEM_RCGCGPIO;
+
+ /* Use alternate function 3 for PG6:7 */
+ LM4_GPIO_AFSEL(G) |= 0xc0;
+ LM4_GPIO_PCTL(G) = (LM4_GPIO_PCTL(N) & 0x00ffffff) | 0x33000000;
+ LM4_GPIO_DEN(G) |= 0xc0;
+ /* Configure SDA as open-drain. SCL should not be open-drain,
+ * since it has an internal pull-up. */
+ LM4_GPIO_ODR(G) |= 0x80;
+}
+
+
+int i2c_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable I2C5 module and delay a few clocks */
+ LM4_SYSTEM_RCGCI2C |= (1 << I2C_PORT_THERMAL) |
+ (1 << I2C_PORT_BATTERY) | (1 << I2C_PORT_CHARGER);
+ scratch = LM4_SYSTEM_RCGCI2C;
+
+ /* Configure GPIOs */
+ configure_gpio();
+
+ /* Initialize ports as master */
+ LM4_I2C_MCR(I2C_PORT_THERMAL) = 0x10;
+ LM4_I2C_MTPR(I2C_PORT_THERMAL) =
+ (CPU_CLOCK / (I2C_SPEED_THERMAL * 10 * 2)) - 1;
+
+ LM4_I2C_MCR(I2C_PORT_BATTERY) = 0x10;
+ LM4_I2C_MTPR(I2C_PORT_BATTERY) =
+ (CPU_CLOCK / (I2C_SPEED_BATTERY * 10 * 2)) - 1;
+
+ LM4_I2C_MCR(I2C_PORT_CHARGER) = 0x10;
+ LM4_I2C_MTPR(I2C_PORT_CHARGER) =
+ (CPU_CLOCK / (I2C_SPEED_CHARGER * 10 * 2)) - 1;
+
+ console_register_commands(&command_group);
+ return EC_SUCCESS;
+}
diff --git a/chip/lm4/keyboard_scan.c b/chip/lm4/keyboard_scan.c
new file mode 100644
index 0000000000..e78e8a5241
--- /dev/null
+++ b/chip/lm4/keyboard_scan.c
@@ -0,0 +1,231 @@
+/* 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.
+ */
+
+/* Keyboard scanner module for Chrome EC */
+
+#include "console.h"
+#include "keyboard.h"
+#include "keyboard_scan.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+#define KB_COLS 13
+
+/* Notes:
+ *
+ * Columns (outputs):
+ * KSO0 - KSO7 = PQ0:7
+ * KSO8 - KSO11 = PK0:3
+ * KSO12 = PN2
+ * Rows (inputs):
+ * KSI0 - KSI7 = PH0:7
+ * Other:
+ * PWR_BTN# = PC5
+ */
+
+static uint8_t raw_state[KB_COLS];
+
+/* Mask with 1 bits only for keys that actually exist */
+static const uint8_t *actual_key_mask;
+
+/* All actual key masks (todo: move to keyboard matrix definition */
+/* TODO: fill in real key mask with 0-bits for coords that aren't keys */
+static const uint8_t actual_key_masks[4][KB_COLS] = {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0},
+ {0},
+ {0},
+ };
+
+/* Drives the specified column low; other columns are tri-stated */
+static void select_column(int col)
+{
+#if defined(EVT)
+ if (col < 0) {
+ LM4_GPIO_DIR(P) &= ~0xff;
+ LM4_GPIO_DIR(Q) &= ~0x1f;
+ } else if (col < 8) {
+ LM4_GPIO_DIR(P) &= ~0xff;
+ LM4_GPIO_DIR(Q) &= ~0x1f;
+ LM4_GPIO_DATA_BITS(P, 0xff << 2) = ~(1 << col);
+ LM4_GPIO_DIR(P) = (1 << col) & 0xff;
+ } else {
+ LM4_GPIO_DIR(P) &= ~0xff;
+ LM4_GPIO_DIR(Q) &= ~0x1f;
+ LM4_GPIO_DATA_BITS(Q, 0xff << 2) = ~(1 << (col - 8));
+ LM4_GPIO_DIR(Q) |= 1 << (col - 8);
+ }
+#else /* BDS definition */
+ /* Somehow the col 10 and 11 are swapped on bds. */
+ if (col == 10) {
+ col = 11;
+ } else if (col == 11) {
+ col = 10;
+ }
+
+ if (col < 0) {
+ /* All tri-stated */
+ LM4_GPIO_DIR(Q) = 0;
+ LM4_GPIO_DIR(K) &= ~0x0f;
+ LM4_GPIO_DIR(N) &= ~0x04;
+ } else if (col < 8) {
+ LM4_GPIO_DIR(Q) = 1 << col;
+ LM4_GPIO_DIR(K) &= ~0x0f;
+ LM4_GPIO_DIR(N) &= ~0x04;
+ LM4_GPIO_DATA_BITS(Q, 0xff << 2) = ~(1 << col);
+ } else if (col < 12) {
+ LM4_GPIO_DIR(Q) = 0;
+ LM4_GPIO_DIR(K) = (LM4_GPIO_DIR(K) & ~0x0f) | (1 << (col - 8));
+ LM4_GPIO_DIR(N) &= ~0x04;
+ LM4_GPIO_DATA_BITS(K, 0x0f << 2) = ~(1 << (col - 8));
+ } else { /* col == 12 */
+ LM4_GPIO_DIR(Q) = 0;
+ LM4_GPIO_DIR(K) &= ~0x0f;
+ LM4_GPIO_DIR(N) |= 0x04;
+ LM4_GPIO_DATA_BITS(N, 0x04 << 2) = ~0x04;
+ }
+#endif
+}
+
+int keyboard_scan_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+ int i;
+
+ /* Enable GPIOs */
+#if defined(EVT)
+ /* Enable clock to GPIO modules C,H,K,N,P,Q */
+ LM4_SYSTEM_RCGCGPIO |= 0x7284;
+#else
+ /* Enable clock to GPIO modules C,H,K,N,Q */
+ LM4_SYSTEM_RCGCGPIO |= 0x5284;
+#endif
+ scratch = LM4_SYSTEM_RCGCGPIO;
+
+ /* Clear GPIOAFSEL and enable digital function for PC5, PH0:7,
+ * PK0:3, PN2, PQ0:7. */
+ LM4_GPIO_AFSEL(C) &= ~0x20;
+ LM4_GPIO_DEN(C) |= 0x20;
+#if defined(EVT)
+ LM4_GPIO_AFSEL(N) &= 0xff; /* KSI[7:0] */
+ LM4_GPIO_DEN(N) |= 0xff;
+ LM4_GPIO_AFSEL(P) &= 0xff; /* KSO[7:0] */
+ LM4_GPIO_DEN(P) |= 0xff;
+ LM4_GPIO_AFSEL(Q) &= 0x1f; /* KSO[12:8] */
+ LM4_GPIO_DEN(Q) |= 0x1f;
+#else
+ LM4_GPIO_AFSEL(H) = 0;
+ LM4_GPIO_DEN(H) = 0xff;
+ LM4_GPIO_AFSEL(K) &= ~0x0f;
+ LM4_GPIO_DEN(K) |= 0x0f;
+ LM4_GPIO_AFSEL(N) &= ~0x04;
+ LM4_GPIO_DEN(N) |= 0x04;
+ LM4_GPIO_AFSEL(Q) = 0;
+ LM4_GPIO_DEN(Q) = 0xff;
+#endif
+
+#if defined(EVT)
+ /* Set PN0:7 as inputs with pull-up */
+ LM4_GPIO_DIR(N) = 0;
+ LM4_GPIO_PUR(N) = 0xff;
+#else
+ /* Set PH0:7 as inputs with pull-up */
+ LM4_GPIO_DIR(H) = 0;
+ LM4_GPIO_PUR(H) = 0xff;
+#endif
+
+ /* Set PC5 as input with pull-up. */
+ /* TODO: no need for pull-up on real circuit, since it'll be
+ * externally pulled up. */
+ LM4_GPIO_DIR(C) &= ~0x04;
+ LM4_GPIO_PUR(C) |= 0x04;
+
+ /* Tri-state the columns */
+ select_column(-1);
+
+ /* Initialize raw state */
+ for (i = 0; i < KB_COLS; i++)
+ raw_state[i] = 0;
+
+ /* TODO: method to set which keyboard we have, so we set the actual
+ * key mask properly */
+ actual_key_mask = actual_key_masks[0];
+
+ return EC_SUCCESS;
+}
+
+
+void check_keys_down(void)
+{
+ int c;
+ uint8_t r;
+ int change = 0;
+
+ for (c = 0; c < KB_COLS; c++) {
+ /* Select column, then wait a bit for it to settle */
+ select_column(c);
+ usleep(20);
+ /* Read the row state */
+#if defined(EVT)
+ r = LM4_GPIO_DATA_BITS(N, 0xff << 2);
+#else
+ r = LM4_GPIO_DATA_BITS(H, 0xff << 2);
+#endif
+ /* Invert it so 0=not pressed, 1=pressed */
+ r ^= 0xff;
+ /* Mask off keys that don't exist so they never show
+ * as pressed */
+ r &= actual_key_mask[c];
+
+#ifdef OR_WITH_CURRENT_STATE_FOR_TESTING
+ /* KLUDGE - or current state in, so we can make sure
+ * all the lines are hooked up */
+ r |= raw_state[c];
+#endif
+
+ /* Check for changes */
+ if (r != raw_state[c]) {
+ int i;
+ for (i = 0; i < 8; ++i) {
+ uint8_t prev = (raw_state[c] >> i) & 1;
+ uint8_t now = (r >> i) & 1;
+ if (prev != now) {
+ keyboard_state_changed(i, c, now);
+ }
+ }
+ raw_state[c] = r;
+ change = 1;
+ }
+ }
+ select_column(-1);
+
+ if (change) {
+ uart_puts("[Keyboard state:");
+ for (c = 0; c < KB_COLS; c++) {
+ if (raw_state[c])
+ uart_printf(" %02x", raw_state[c]);
+ else
+ uart_puts(" --");
+ }
+ uart_puts("]\n");
+ }
+}
+
+
+void keyboard_scan_task(void)
+{
+ keyboard_scan_init();
+
+ while (1) {
+ /* Sleep for a while */
+ usleep(25000);
+ /* Check for keys down */
+ check_keys_down();
+ }
+}
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c
new file mode 100644
index 0000000000..a377890c41
--- /dev/null
+++ b/chip/lm4/lpc.c
@@ -0,0 +1,210 @@
+/* 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.
+ */
+
+/* LPC module for Chrome EC */
+
+#include "board.h"
+#include "host_command.h"
+#include "i8042.h"
+#include "lpc.h"
+#include "port80.h"
+#include "registers.h"
+#include "task.h"
+#include "uart.h"
+
+
+/* Configures GPIOs for module. */
+static void configure_gpio(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable clocks to GPIO modules L, M (p. 404) */
+ LM4_SYSTEM_RCGCGPIO |= 0x0c00;
+ scratch = LM4_SYSTEM_RCGCGPIO;
+
+ /* Set digital alternate function 15 for PL0:5, PM0:2, PM4:5 pins. */
+ LM4_GPIO_AFSEL(L) |= 0x3f;
+ LM4_GPIO_AFSEL(M) |= 0x37;
+ LM4_GPIO_PCTL(L) |= 0x00ffffff;
+ LM4_GPIO_PCTL(M) |= 0x00ff0fff;
+ LM4_GPIO_DEN(L) |= 0x3f;
+ LM4_GPIO_DEN(M) |= 0x37;
+
+ /* Set the drive strength to 8mA. */
+ /* TODO: Only necessary on BDS because the cabling to the x86
+ is long and flaky; remove this for Link. */
+ LM4_GPIO_DR8R(L) |= 0x0000003f;
+ LM4_GPIO_DR8R(M) |= 0x00000037;
+}
+
+
+int lpc_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable RGCGLPC then delay a few clocks. */
+ LM4_SYSTEM_RCGCLPC = 1;
+ scratch = LM4_SYSTEM_RCGCLPC;
+
+ /* Configure GPIOs */
+ configure_gpio();
+
+ /* Set LPC channel 0 to I/O address 0x62 (data) / 0x66 (command),
+ * single endpoint, offset 0 for host command/writes and 1 for EC
+ * data writes, pool bytes 0(data)/1(cmd) */
+ LM4_LPC_ADR(0) = 0x62;
+ LM4_LPC_CTL(0) = (LPC_POOL_OFFS_KERNEL << (5 - 1));
+
+ /* Set LPC channel 1 to I/O address 0x80 (data), single endpoint,
+ * pool bytes 4(data)/5(cmd). */
+ LM4_LPC_ADR(1) = 0x80;
+ LM4_LPC_CTL(1) = (LPC_POOL_OFFS_PORT80 << (5 - 1));
+
+ /* Set LPC channel 2 to I/O address 0x800, range endpoint,
+ * arbitration disabled, pool bytes 512-1023. To access this from
+ * x86, use the following commands to set GEN_LPC2 and GEN_LPC3:
+ *
+ * pci_write32 0 0x1f 0 0x88 0x007c0801
+ * pci_write32 0 0x1f 0 0x8c 0x007c0901
+ */
+ LM4_LPC_ADR(2) = 0x800;
+ LM4_LPC_CTL(2) = 0x801D | (LPC_POOL_OFFS_CMD_DATA << (5 - 1));
+
+ /* Set LPC channel 3 to I/O address 0x60 (data) / 0x64 (command),
+ * single endpoint, offset 0 for host command/writes and 1 for EC
+ * data writes, pool bytes 0(data)/1(cmd) */
+ LM4_LPC_ADR(3) = 0x60;
+ LM4_LPC_CTL(3) = (1 << 24/* IRQSEL1 */) |
+ (1 << 18/* IRQEN1 */) |
+ (LPC_POOL_OFFS_KEYBOARD << (5 - 1));
+ LM4_LPC_ST(3) = 0;
+
+ /* Set LPC channel 7 to I/O address 0x2F8 (COM2), bytes 8-15.
+ * Channel 7 ignores the TYPE bit. */
+ LM4_LPC_ADR(7) = 0x2f8;
+ /* TODO: could configure IRQSELs and set IRQEN2/CX, and then the host
+ * can enable IRQs on its own. */
+ LM4_LPC_CTL(7) = 0x0004 | (LPC_POOL_OFFS_COMX << (5 - 1));
+
+#ifdef USE_LPC_COMx_DMA
+ /* TODO: haven't been able to get this to work yet */
+ /* COMx UART DMA mode */
+ LM4_LPC_LPCDMACX = 0x00070000;
+
+ /* TODO: set up DMA */
+ LM4_SYSTEM_RCGCDMA = 1;
+ /* Wait 3 clocks before accessing other DMA regs */
+ LM4_SYSTEM_RCGCDMA = 1;
+ LM4_SYSTEM_RCGCDMA = 1;
+ LM4_SYSTEM_RCGCDMA = 1;
+ /* Enable master */
+ LM4_DMA_DMACFG = 1;
+ /* TODO: hope we don't need the channel control structs; we're just
+ * throwing this somewhere in memory. Shouldn't need it if we leave
+ * all the channel disabled, though. */
+ LM4_DMA_DMACTLBASE = 0x20004000;
+ /* Map UART and LPC DMA functions to channels */
+ LM4_DMA_DMACHMAP0 = 0x00003000; /* Channel 3 encoding 3 = LPC0 Ch3 */
+ LM4_DMA_DMACHMAP1 = 0x00000011; /* Channels 8,9 encoding 1 = UART1 */
+#else
+ /* Use our LPC interrupt handler to notify COMxIM on write-from-host */
+ LM4_LPC_LPCDMACX = 0x00110000;
+ LM4_LPC_LPCIM |= 0x20000000;
+#endif
+
+ /* Unmask interrupt bits for channel 0 command write (CH0IM2),
+ * channel 1 data write (CH1IM1) and channel 3 data write (CH3IM3). */
+ LM4_LPC_LPCIM |= 0x7024;
+
+ /* Enable LPC channels 0, 1, 2, 3, 7 in LPCCTL */
+ LM4_LPC_LPCCTL |= 0x8F;
+
+ return EC_SUCCESS;
+}
+
+
+volatile uint8_t *lpc_get_host_range(void)
+{
+ return LPC_POOL_CMD_DATA;
+}
+
+
+void lpc_send_host_response(int status)
+{
+ /* Set status nibble (bits 7:4 from host side) and clear the busy
+ * bit (0x1000) (bit 2 from host side) */
+ LM4_LPC_ST(0) = (LM4_LPC_ST(0) & 0xffffe0ff) | ((status & 0xf) << 8);
+
+ /* Write dummy value to data byte. This sets the TOH bit in the
+ * status byte and triggers an IRQ on the host so the host can read
+ * the status. */
+ /* TODO: or it would, if we actually set up host IRQs */
+ LPC_POOL_KERNEL[1] = 0;
+}
+
+
+/* LPC interrupt handler */
+static void lpc_interrupt(void)
+{
+ uint32_t mis = LM4_LPC_LPCMIS;
+
+ /* Clear the interrupt bits we're handling */
+ LM4_LPC_LPCIC = mis;
+
+ /* Handle port 66 command writes */
+ if (mis & 0x04) {
+ /* Set the busy bit and clear the status */
+ LM4_LPC_ST(0) = (LM4_LPC_ST(0) & 0xffffe0ff) | 0x1000;
+
+ /* Read the command byte and pass to the host command handler.
+ * This clears the FRMH bit in the status byte. */
+ host_command_received(LPC_POOL_KERNEL[0]);
+ }
+
+ /* Handle port 80 writes (CH0MIS1) */
+ if (mis & 0x20)
+ port_80_write(LPC_POOL_PORT80[0]);
+
+ /* Handle port 60 command (CH3MIS2) and data (CH3MIS1) */
+ if (mis & 0x2000) {
+ /* Read the data byte and pass to the i8042 handler.
+ * This clears the FRMH bit in the status byte. */
+ i8042_receives_data(LPC_POOL_KEYBOARD[0]);
+ }
+ if (mis & 0x4000) {
+ /* Read the command byte and pass to the i8042 handler.
+ * This clears the FRMH bit in the status byte. */
+ i8042_receives_command(LPC_POOL_KEYBOARD[0]);
+ }
+ if (mis & 0x1000) {
+ /* Host picks up the data, try to send remaining bytes */
+ task_send_msg(TASK_ID_I8042CMD, TASK_ID_I8042CMD, 0);
+ }
+
+ /* Handle COMx */
+ if (mis & 0x20000000) {
+ uint32_t cis = LM4_LPC_LPCDMACX;
+ /* Clear the interrupt reasons we're handling */
+ LM4_LPC_LPCDMACX = cis;
+
+ /* Handle host writes */
+ if (LM4_LPC_ST(7) & 0x02) {
+ if (LM4_UART_FR(1) & 0x20) {
+ /* FIFO is full, so enable transmit
+ * interrupt to let us know when it
+ * empties */
+ LM4_UART_IM(1) |= 0x20;
+ } else {
+ /* Space in FIFO, so copy byte */
+ LM4_UART_DR(1) = LPC_POOL_COMX[0];
+ }
+ }
+
+ /* TODO: if host read the from-host data, see if
+ * there's another byte still waiting on UART1. */
+ }
+}
+
+DECLARE_IRQ(108, lpc_interrupt, 2);
diff --git a/chip/lm4/pwm.c b/chip/lm4/pwm.c
new file mode 100644
index 0000000000..da9acb5e89
--- /dev/null
+++ b/chip/lm4/pwm.c
@@ -0,0 +1,284 @@
+/* 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.
+ */
+
+/* PWM control module for Chrome EC */
+
+#include "board.h"
+#include "console.h"
+#include "pwm.h"
+#include "registers.h"
+#include "uart.h"
+#include "util.h"
+
+/* Maximum RPM for fan controller */
+#define MAX_RPM 0x1fff
+/* Max PWM for fan controller */
+#define MAX_PWM 0x1ff
+
+
+/* Configures the GPIOs for the fan module. */
+/* TODO: this is currently hard-coded for BDS; needs to do the right
+ thing for Link. */
+static void configure_gpios(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable GPIO K,N modules and delay a few clocks */
+ LM4_SYSTEM_RCGCGPIO |= 0x1200;
+ scratch = LM4_SYSTEM_RCGCGPIO;
+
+ /* Use alternate function 1 for PN4 (channel 3 PWM) and PN6:7
+ (channel 4 PWM/tach) */
+ // TODO: depends on which PWMs we're using
+ LM4_GPIO_AFSEL(N) |= 0xd0;
+ LM4_GPIO_PCTL(N) = (LM4_GPIO_PCTL(N) & 0x00f0ffff) | 0x11010000;
+ LM4_GPIO_DEN(N) |= 0xd0;
+
+ /* Use alternate function 1 for PK6 (channel 1 PWM) */
+ // TODO: GPIO module depends on fan channel
+ LM4_GPIO_AFSEL(K) |= 0x40;
+ LM4_GPIO_PCTL(K) = (LM4_GPIO_PCTL(K) & 0xf0ffffff) | 0x01000000;
+ LM4_GPIO_DEN(K) |= 0x40;
+
+}
+
+
+int pwm_get_fan_rpm(void)
+{
+ return LM4_FAN_FANCST(FAN_CH_CPU) & MAX_RPM;
+}
+
+
+int pwm_set_fan_target_rpm(int rpm)
+{
+ /* Treat out-of-range requests as requests for maximum fan speed */
+ if (rpm < 0 || rpm > MAX_RPM)
+ rpm = MAX_RPM;
+
+ LM4_FAN_FANCMD(FAN_CH_CPU) = rpm;
+ return EC_SUCCESS;
+}
+
+
+int pwm_set_keyboard_backlight(int percent)
+{
+ LM4_FAN_FANCMD(FAN_CH_KBLIGHT) = ((percent * MAX_PWM) / 100) << 16;
+ return EC_SUCCESS;
+}
+
+
+int pwm_set_power_led(int percent)
+{
+ LM4_FAN_FANCMD(FAN_CH_POWER_LED) = ((percent * MAX_PWM) / 100) << 16;
+ return EC_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_fan_info(int argc, char **argv)
+{
+ uart_printf("Fan actual speed: %4d rpm\n", pwm_get_fan_rpm());
+ uart_printf(" target speed: %4d rpm\n",
+ LM4_FAN_FANCMD(FAN_CH_CPU) & MAX_RPM);
+ uart_printf(" duty cycle: %d%%\n",
+ ((LM4_FAN_FANCMD(FAN_CH_CPU) >> 16)) * 100 / MAX_PWM);
+ uart_printf(" status: %d\n",
+ (LM4_FAN_FANSTS >> (2 * FAN_CH_CPU)) & 0x03);
+ return EC_SUCCESS;
+}
+
+
+static int command_fan_set(int argc, char **argv)
+{
+ int rpm = 0;
+ char *e;
+ int rv;
+
+ if (argc < 2) {
+ uart_puts("Usage: fanset <rpm>\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ rpm = strtoi(argv[1], &e, 0);
+ if (*e) {
+ uart_puts("Invalid speed\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ uart_printf("Setting fan speed to %d rpm...\n", rpm);
+
+ /* Move the fan to automatic control */
+ if (LM4_FAN_FANCH(FAN_CH_CPU) & 0x0001) {
+ LM4_FAN_FANCTL &= ~(1 << FAN_CH_CPU);
+ LM4_FAN_FANCH(FAN_CH_CPU) &= ~0x0001;
+ LM4_FAN_FANCTL |= (1 << FAN_CH_CPU);
+ }
+
+ rv = pwm_set_fan_target_rpm(rpm);
+ if (rv == EC_SUCCESS)
+ uart_printf("Done.\n");
+ return rv;
+}
+
+
+/* TODO: this is a temporary command for debugging tach issues */
+static int command_fan_duty(int argc, char **argv)
+{
+ int d = 0, pwm;
+ char *e;
+
+ if (argc < 2) {
+ uart_puts("Usage: fanduty <percent>\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ d = strtoi(argv[1], &e, 0);
+ if (*e) {
+ uart_puts("Invalid duty cycle\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ pwm = (MAX_PWM * d) / 100;
+ uart_printf("Setting fan duty cycle to %d%% = 0x%x...\n", d, pwm);
+
+ /* Move the fan to manual control */
+ if (!(LM4_FAN_FANCH(FAN_CH_CPU) & 0x0001)) {
+ LM4_FAN_FANCTL &= ~(1 << FAN_CH_CPU);
+ LM4_FAN_FANCH(FAN_CH_CPU) |= 0x0001;
+ LM4_FAN_FANCTL |= (1 << FAN_CH_CPU);
+ }
+
+ /* Set the duty cycle */
+ LM4_FAN_FANCMD(FAN_CH_CPU) = pwm << 16;
+
+ return EC_SUCCESS;
+}
+
+
+static int command_kblight(int argc, char **argv)
+{
+ char *e;
+ int rv;
+ int i;
+
+ if (argc < 2) {
+ uart_puts("Usage: kblight <percent>\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ i = strtoi(argv[1], &e, 0);
+ if (*e) {
+ uart_puts("Invalid percent\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ uart_printf("Setting keyboard backlight to %d%%...\n", i);
+ rv = pwm_set_keyboard_backlight(i);
+ if (rv == EC_SUCCESS)
+ uart_printf("Done.\n");
+ return rv;
+}
+
+
+static int command_powerled(int argc, char **argv)
+{
+ char *e;
+ int rv;
+ int i;
+
+ if (argc < 2) {
+ uart_puts("Usage: powerled <percent>\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ i = strtoi(argv[1], &e, 0);
+ if (*e) {
+ uart_puts("Invalid percent\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ uart_printf("Setting power LED to %d%%...\n", i);
+ rv = pwm_set_keyboard_backlight(i);
+ if (rv == EC_SUCCESS)
+ uart_printf("Done.\n");
+ return rv;
+}
+
+
+static const struct console_command console_commands[] = {
+ {"fanduty", command_fan_duty},
+ {"faninfo", command_fan_info},
+ {"fanset", command_fan_set},
+ {"kblight", command_kblight},
+ {"powerled", command_powerled},
+};
+static const struct console_group command_group = {
+ "PWM", console_commands, ARRAY_SIZE(console_commands)
+};
+
+
+/*****************************************************************************/
+/* Initialization */
+
+int pwm_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable the fan module and delay a few clocks */
+ LM4_SYSTEM_RCGCFAN = 1;
+ scratch = LM4_SYSTEM_RCGCFAN;
+
+ /* Configure GPIOs */
+ configure_gpios();
+
+ /* Disable all fans */
+ LM4_FAN_FANCTL = 0;
+
+ /* Configure CPU fan:
+ * 0x8000 = bit 15 = auto-restart
+ * 0x0000 = bit 14 = slow acceleration
+ * 0x0000 = bits 13:11 = no hysteresis
+ * 0x0000 = bits 10:8 = start period (2<<0) edges
+ * 0x0000 = bits 7:6 = no fast start
+ * 0x0020 = bits 5:4 = average 4 edges when calculating RPM
+ * 0x0008 = bits 3:2 = 4 pulses per revolution
+ * 0x0000 = bit 0 = automatic control */
+ LM4_FAN_FANCH(FAN_CH_CPU) = 0x8028;
+
+ /* Configure keyboard backlight:
+ * 0x0000 = bit 15 = auto-restart
+ * 0x0000 = bit 14 = slow acceleration
+ * 0x0000 = bits 13:11 = no hysteresis
+ * 0x0000 = bits 10:8 = start period (2<<0) edges
+ * 0x0000 = bits 7:6 = no fast start
+ * 0x0000 = bits 5:4 = average 4 edges when calculating RPM
+ * 0x0000 = bits 3:2 = 4 pulses per revolution
+ * 0x0001 = bit 0 = manual control */
+ LM4_FAN_FANCH(FAN_CH_KBLIGHT) = 0x0001;
+
+ /* Configure power LED:
+ * 0x0000 = bit 15 = auto-restart
+ * 0x0000 = bit 14 = slow acceleration
+ * 0x0000 = bits 13:11 = no hysteresis
+ * 0x0000 = bits 10:8 = start period (2<<0) edges
+ * 0x0000 = bits 7:6 = no fast start
+ * 0x0000 = bits 5:4 = average 4 edges when calculating RPM
+ * 0x0000 = bits 3:2 = 4 pulses per revolution
+ * 0x0001 = bit 0 = manual control */
+ LM4_FAN_FANCH(FAN_CH_POWER_LED) = 0x0001;
+
+ /* Set initial fan speed to maximum, backlight off, power LED off */
+ pwm_set_fan_target_rpm(-1);
+ pwm_set_keyboard_backlight(0);
+ pwm_set_power_led(0);
+
+ /* Enable CPU fan and keyboard backlight */
+ LM4_FAN_FANCTL |= (1 << FAN_CH_CPU) | (1 << FAN_CH_KBLIGHT) |
+ (1 << FAN_CH_POWER_LED);
+
+ console_register_commands(&command_group);
+ return EC_SUCCESS;
+}
diff --git a/chip/lm4/registers.h b/chip/lm4/registers.h
new file mode 100644
index 0000000000..c2c214dbd1
--- /dev/null
+++ b/chip/lm4/registers.h
@@ -0,0 +1,312 @@
+/* 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.
+ *
+ * Register map for LM4x processor
+ */
+
+#ifndef __LM4_REGISTERS
+#define __LM4_REGISTERS
+
+#include <stdint.h>
+
+/* Macro to access 32-bit registers */
+#define LM4REG(addr) (*(volatile uint32_t*)(addr))
+
+#define LM4_UART_CH0_BASE 0x4000c000
+#define LM4_UART_CH1_BASE 0x4000d000
+#define LM4UARTREG(ch, offset) LM4REG(LM4_UART_CH##ch##_BASE + (offset))
+#define LM4_UART_DR(ch) LM4UARTREG(ch, 0x000)
+#define LM4_UART_FR(ch) LM4UARTREG(ch, 0x018)
+#define LM4_UART_IBRD(ch) LM4UARTREG(ch, 0x024)
+#define LM4_UART_FBRD(ch) LM4UARTREG(ch, 0x028)
+#define LM4_UART_LCRH(ch) LM4UARTREG(ch, 0x02c)
+#define LM4_UART_CTL(ch) LM4UARTREG(ch, 0x030)
+#define LM4_UART_IFLS(ch) LM4UARTREG(ch, 0x034)
+#define LM4_UART_IM(ch) LM4UARTREG(ch, 0x038)
+#define LM4_UART_ICR(ch) LM4UARTREG(ch, 0x044)
+#define LM4_UART_DMACTL(ch) LM4UARTREG(ch, 0x048)
+
+#define LM4_ADC_ADCACTSS LM4REG(0x40038000)
+#define LM4_ADC_ADCRIS LM4REG(0x40038004)
+#define LM4_ADC_ADCIM LM4REG(0x40038008)
+#define LM4_ADC_ADCISC LM4REG(0x4003800C)
+#define LM4_ADC_ADCOSTAT LM4REG(0x40038010)
+#define LM4_ADC_ADCEMUX LM4REG(0x40038014)
+#define LM4_ADC_ADCUSTAT LM4REG(0x40038018)
+#define LM4_ADC_ADCSSPRI LM4REG(0x40038020)
+#define LM4_ADC_ADCSPC LM4REG(0x40038024)
+#define LM4_ADC_ADCPSSI LM4REG(0x40038028)
+#define LM4_ADC_ADCSAC LM4REG(0x40038030)
+#define LM4_ADC_ADCCTL LM4REG(0x40038038)
+#define LM4_ADC_SS0_BASE 0x40038040
+#define LM4_ADC_SS1_BASE 0x40038060
+#define LM4_ADC_SS2_BASE 0x40038080
+#define LM4_ADC_SS3_BASE 0x400380a0
+#define LM4ADCREG(ss, offset) LM4REG(LM4_ADC_SS##ss##_BASE + (offset))
+#define LM4_ADC_SSMUX(ss) LM4ADCREG(ss, 0x000)
+#define LM4_ADC_SSCTL(ss) LM4ADCREG(ss, 0x004)
+#define LM4_ADC_SSFIFO(ss) LM4ADCREG(ss, 0x008)
+#define LM4_ADC_SSFSTAT(ss) LM4ADCREG(ss, 0x00c)
+#define LM4_ADC_SSOP(ss) LM4ADCREG(ss, 0x010)
+#define LM4_ADC_SSEMUX(ss) LM4ADCREG(ss, 0x018)
+
+#define LM4_LPC_LPCCTL LM4REG(0x40080000)
+#define LM4_LPC_LPCSTS LM4REG(0x40080004)
+#define LM4_LPC_LPCIRQCTL LM4REG(0x40080008)
+#define LM4_LPC_LPCIRQST LM4REG(0x4008000c)
+#define LM4_LPC_LPCIM LM4REG(0x40080100)
+#define LM4_LPC_LPCRIS LM4REG(0x40080104)
+#define LM4_LPC_LPCMIS LM4REG(0x40080108)
+#define LM4_LPC_LPCIC LM4REG(0x4008010C)
+#define LM4_LPC_LPCDMACX LM4REG(0x40080120)
+#define LM4_LPC_CH0_BASE 0x40080010
+#define LM4_LPC_CH1_BASE 0x40080020
+#define LM4_LPC_CH2_BASE 0x40080030
+#define LM4_LPC_CH3_BASE 0x40080040
+#define LM4_LPC_CH4_BASE 0x40080050
+#define LM4_LPC_CH5_BASE 0x40080060
+#define LM4_LPC_CH6_BASE 0x40080070
+#define LM4_LPC_CH7_BASE 0x40080080
+#define LM4LPCREG(ch, offset) LM4REG(LM4_LPC_CH##ch##_BASE + (offset))
+#define LM4_LPC_CTL(ch) LM4LPCREG(ch, 0x000)
+#define LM4_LPC_ST(ch) LM4LPCREG(ch, 0x004)
+#define LM4_LPC_ADR(ch) LM4LPCREG(ch, 0x008)
+#define LM4_LPC_POOL_BYTES 1024 /* Size of LPCPOOL in bytes */
+#define LM4_LPC_LPCPOOL ((volatile unsigned char*)0x40080400)
+
+#define LM4_FAN_FANSTS LM4REG(0x40084000)
+#define LM4_FAN_FANCTL LM4REG(0x40084004)
+#define LM4_FAN_CH0_BASE 0x40084010
+#define LM4_FAN_CH1_BASE 0x40084020
+#define LM4_FAN_CH2_BASE 0x40084030
+#define LM4_FAN_CH3_BASE 0x40084040
+#define LM4_FAN_CH4_BASE 0x40084050
+#define LM4_FAN_CH5_BASE 0x40084060
+#define LM4FANREG(ch, offset) LM4REG(LM4_FAN_CH##ch##_BASE + (offset))
+#define LM4_FAN_FANCH(ch) LM4FANREG(ch, 0x000)
+#define LM4_FAN_FANCMD(ch) LM4FANREG(ch, 0x004)
+#define LM4_FAN_FANCST(ch) LM4FANREG(ch, 0x008)
+
+#define LM4_EEPROM_EESIZE LM4REG(0x400af000)
+#define LM4_EEPROM_EEBLOCK LM4REG(0x400af004)
+#define LM4_EEPROM_EEOFFSET LM4REG(0x400af008)
+#define LM4_EEPROM_EERDWR LM4REG(0x400af010)
+#define LM4_EEPROM_EERDWRINC LM4REG(0x400af014)
+#define LM4_EEPROM_EEDONE LM4REG(0x400af018)
+#define LM4_EEPROM_EESUPP LM4REG(0x400af01c)
+#define LM4_EEPROM_EEUNLOCK LM4REG(0x400af020)
+#define LM4_EEPROM_EEPROT LM4REG(0x400af030)
+#define LM4_EEPROM_EEPASS0 LM4REG(0x400af034)
+#define LM4_EEPROM_EEPASS1 LM4REG(0x400af038)
+#define LM4_EEPROM_EEPASS2 LM4REG(0x400af03c)
+#define LM4_EEPROM_EEINT LM4REG(0x400af040)
+#define LM4_EEPROM_EEHIDE LM4REG(0x400af050)
+
+#define LM4_HIBERNATE_HIBRTCC LM4REG(0x400fc000)
+#define LM4_HIBERNATE_HIBRTCM0 LM4REG(0x400fc004)
+#define LM4_HIBERNATE_HIBRTCLD LM4REG(0x400fc00c)
+#define LM4_HIBERNATE_HIBCTL LM4REG(0x400fc010)
+#define LM4_HIBERNATE_HIBIM LM4REG(0x400fc014)
+#define LM4_HIBERNATE_HIBRIS LM4REG(0x400fc018)
+#define LM4_HIBERNATE_HIBIC LM4REG(0x400fc020)
+#define LM4_HIBERNATE_HIBRTCT LM4REG(0x400fc024)
+#define LM4_HIBERNATE_HIBRTCSS LM4REG(0x400fc028)
+#define LM4_HIBERNATE_HIBDATA LM4REG(0x400fc030)
+
+#define LM4_FLASH_FMA LM4REG(0x400fd000)
+#define LM4_FLASH_FMD LM4REG(0x400fd004)
+#define LM4_FLASH_FMC LM4REG(0x400fd008)
+#define LM4_FLASH_FCRIS LM4REG(0x400fd00c)
+#define LM4_FLASH_FCMISC LM4REG(0x400fd014)
+#define LM4_FLASH_FMC2 LM4REG(0x400fd020)
+#define LM4_FLASH_FWBVAL LM4REG(0x400fd030)
+/* FWB size is 32 words = 128 bytes */
+#define LM4_FLASH_FWB ((volatile uint32_t*)0x400fd100)
+#define LM4_FLASH_FSIZE LM4REG(0x400fdfc0)
+#define LM4_FLASH_FMPRE0 LM4REG(0x400fe200)
+#define LM4_FLASH_FMPRE1 LM4REG(0x400fe204)
+#define LM4_FLASH_FMPRE2 LM4REG(0x400fe208)
+#define LM4_FLASH_FMPRE3 LM4REG(0x400fe20c)
+#define LM4_FLASH_FMPPE0 LM4REG(0x400fe400)
+#define LM4_FLASH_FMPPE1 LM4REG(0x400fe404)
+#define LM4_FLASH_FMPPE2 LM4REG(0x400fe408)
+#define LM4_FLASH_FMPPE3 LM4REG(0x400fe40c)
+
+#define LM4_SYSTEM_RIS LM4REG(0x400fe050)
+#define LM4_SYSTEM_MISC LM4REG(0x400fe058)
+#define LM4_SYSTEM_RESC LM4REG(0x400fe05c)
+#define LM4_SYSTEM_RCC LM4REG(0x400fe060)
+#define LM4_SYSTEM_RCC2 LM4REG(0x400fe070)
+#define LM4_SYSTEM_PLLSTAT LM4REG(0x400fe168)
+#define LM4_SYSTEM_RCGCWD LM4REG(0x400fe600)
+#define LM4_SYSTEM_RCGCTIMER LM4REG(0x400fe604)
+#define LM4_SYSTEM_RCGCGPIO LM4REG(0x400fe608)
+#define LM4_SYSTEM_RCGCDMA LM4REG(0x400fe60c)
+#define LM4_SYSTEM_RCGCHIB LM4REG(0x400fe614)
+#define LM4_SYSTEM_RCGCUART LM4REG(0x400fe618)
+#define LM4_SYSTEM_RCGCI2C LM4REG(0x400fe620)
+#define LM4_SYSTEM_RCGCADC LM4REG(0x400fe638)
+#define LM4_SYSTEM_RCGCLPC LM4REG(0x400fe648)
+#define LM4_SYSTEM_RCGCFAN LM4REG(0x400fe654)
+#define LM4_SYSTEM_RCGCEEPROM LM4REG(0x400fe658)
+#define LM4_SYSTEM_RCGCWTIMER LM4REG(0x400fe65c)
+
+#define LM4_DMA_DMACFG LM4REG(0x400ff004)
+#define LM4_DMA_DMACTLBASE LM4REG(0x400ff008)
+#define LM4_DMA_DMACHMAP0 LM4REG(0x400ff510)
+#define LM4_DMA_DMACHMAP1 LM4REG(0x400ff514)
+#define LM4_DMA_DMACHMAP2 LM4REG(0x400ff518)
+#define LM4_DMA_DMACHMAP3 LM4REG(0x400ff51c)
+
+#define LM4_NVIC_EN(x) LM4REG(0xe000e100 + 4 * (x))
+#define LM4_NVIC_DIS(x) LM4REG(0xe000e180 + 4 * (x))
+#define LM4_NVIC_PRI(x) LM4REG(0xe000e400 + 4 * (x))
+#define LM4_NVIC_APINT LM4REG(0xe000ed0c)
+#define LM4_NVIC_SWTRIG LM4REG(0xe000ef00)
+
+#define LM4_SCB_SYSCTRL LM4REG(0xe000ed10)
+
+/* GPIO */
+#define LM4_GPIO_PORTA_BASE 0x40004000
+#define LM4_GPIO_PORTB_BASE 0x40005000
+#define LM4_GPIO_PORTC_BASE 0x40006000
+#define LM4_GPIO_PORTD_BASE 0x40007000
+#define LM4_GPIO_PORTE_BASE 0x40024000
+#define LM4_GPIO_PORTF_BASE 0x40025000
+#define LM4_GPIO_PORTG_BASE 0x40026000
+#define LM4_GPIO_PORTH_BASE 0x40027000
+#define LM4_GPIO_PORTK_BASE 0x40061000
+#define LM4_GPIO_PORTL_BASE 0x40062000
+#define LM4_GPIO_PORTM_BASE 0x40063000
+#define LM4_GPIO_PORTN_BASE 0x40064000
+#define LM4_GPIO_PORTP_BASE 0x40065000
+#define LM4_GPIO_PORTQ_BASE 0x40066000
+#define LM4_GPIO_PORTA_AHB_BASE 0x40058000
+#define LM4_GPIO_PORTB_AHB_BASE 0x40059000
+#define LM4_GPIO_PORTC_AHB_BASE 0x4005A000
+#define LM4_GPIO_PORTD_AHB_BASE 0x4005B000
+#define LM4_GPIO_PORTE_AHB_BASE 0x4005C000
+#define LM4_GPIO_PORTF_AHB_BASE 0x4005D000
+#define LM4_GPIO_PORTG_AHB_BASE 0x4005E000
+#define LM4_GPIO_PORTH_AHB_BASE 0x4005F000
+#define LM4_GPIO_PORTJ_AHB_BASE 0x40060000
+
+#define LM4GPIOREG(port, offset) LM4REG(LM4_GPIO_PORT##port##_BASE + (offset))
+
+#define LM4_GPIO_DATA_BITS(port, off) LM4GPIOREG(port, 0x000 + (off))
+#define LM4_GPIO_DIR(port) LM4GPIOREG(port, 0x400)
+#define LM4_GPIO_IS(port) LM4GPIOREG(port, 0x404)
+#define LM4_GPIO_IBE(port) LM4GPIOREG(port, 0x408)
+#define LM4_GPIO_IM(port) LM4GPIOREG(port, 0x410)
+#define LM4_GPIO_RIS(port) LM4GPIOREG(port, 0x414)
+#define LM4_GPIO_MIS(port) LM4GPIOREG(port, 0x418)
+#define LM4_GPIO_ICR(port) LM4GPIOREG(port, 0x41c)
+#define LM4_GPIO_AFSEL(port) LM4GPIOREG(port, 0x420)
+#define LM4_GPIO_DR2R(port) LM4GPIOREG(port, 0x500)
+#define LM4_GPIO_DR4R(port) LM4GPIOREG(port, 0x504)
+#define LM4_GPIO_DR8R(port) LM4GPIOREG(port, 0x508)
+#define LM4_GPIO_ODR(port) LM4GPIOREG(port, 0x50c)
+#define LM4_GPIO_PUR(port) LM4GPIOREG(port, 0x510)
+#define LM4_GPIO_PDR(port) LM4GPIOREG(port, 0x514)
+#define LM4_GPIO_SLR(port) LM4GPIOREG(port, 0x518)
+#define LM4_GPIO_DEN(port) LM4GPIOREG(port, 0x51c)
+#define LM4_GPIO_LOCK(port) LM4GPIOREG(port, 0x520)
+#define LM4_GPIO_CR(port) LM4GPIOREG(port, 0x524)
+#define LM4_GPIO_AMSEL(port) LM4GPIOREG(port, 0x528)
+#define LM4_GPIO_PCTL(port) LM4GPIOREG(port, 0x52c)
+
+
+/* I2C */
+#define LM4_I2C0_BASE 0x40020000
+#define LM4_I2C1_BASE 0x40021000
+#define LM4_I2C2_BASE 0x40022000
+#define LM4_I2C3_BASE 0x40023000
+#define LM4_I2C4_BASE 0x400c0000
+#define LM4_I2C5_BASE 0x400c1000
+#define LM4_I2C_BASESEP 0x00001000
+/* I2C base address by port. Compiles to a constant in gcc if port
+ and offset are constant. */
+static inline int lm4_i2c_addr(int port, int offset)
+{
+ return offset + (port < 4 ?
+ LM4_I2C0_BASE + LM4_I2C_BASESEP * port :
+ LM4_I2C4_BASE + LM4_I2C_BASESEP * (port - 4));
+}
+#define LM4I2CREG(port, offset) LM4REG(lm4_i2c_addr(port, offset))
+#define LM4_I2C_MSA(port) LM4I2CREG(port, 0x000)
+#define LM4_I2C_MCS(port) LM4I2CREG(port, 0x004)
+#define LM4_I2C_MDR(port) LM4I2CREG(port, 0x008)
+#define LM4_I2C_MTPR(port) LM4I2CREG(port, 0x00c)
+#define LM4_I2C_MIMR(port) LM4I2CREG(port, 0x010)
+#define LM4_I2C_MRIS(port) LM4I2CREG(port, 0x014)
+#define LM4_I2C_MMIS(port) LM4I2CREG(port, 0x018)
+#define LM4_I2C_MICR(port) LM4I2CREG(port, 0x01c)
+#define LM4_I2C_MCR(port) LM4I2CREG(port, 0x020)
+#define LM4_I2C_MCLKOCNT(port) LM4I2CREG(port, 0x024)
+#define LM4_I2C_MBMON(port) LM4I2CREG(port, 0x02c)
+
+
+/* Timers */
+#define LM4TIMERREG(tmr, offset) LM4REG(LM4_TIMER##tmr##_BASE + (offset))
+
+#define LM4_TIMER0_BASE 0x40030000
+#define LM4_TIMER1_BASE 0x40031000
+#define LM4_TIMER2_BASE 0x40032000
+#define LM4_TIMER3_BASE 0x40033000
+#define LM4_TIMER4_BASE 0x40034000
+#define LM4_TIMER5_BASE 0x40035000
+#define LM4_TIMERW0_BASE 0x40036000
+#define LM4_TIMERW1_BASE 0x40037000
+#define LM4_TIMERW2_BASE 0x4004C000
+#define LM4_TIMERW3_BASE 0x4004D000
+#define LM4_TIMERW4_BASE 0x4004E000
+#define LM4_TIMERW5_BASE 0x4004F000
+
+#define LM4_TIMER_CFG(tmr) LM4TIMERREG(tmr, 0x00)
+#define LM4_TIMER_TAMR(tmr) LM4TIMERREG(tmr, 0x04)
+#define LM4_TIMER_TBMR(tmr) LM4TIMERREG(tmr, 0x08)
+#define LM4_TIMER_CTL(tmr) LM4TIMERREG(tmr, 0x0C)
+#define LM4_TIMER_SYNC(tmr) LM4TIMERREG(tmr, 0x10)
+#define LM4_TIMER_IMR(tmr) LM4TIMERREG(tmr, 0x18)
+#define LM4_TIMER_RIS(tmr) LM4TIMERREG(tmr, 0x1C)
+#define LM4_TIMER_MIS(tmr) LM4TIMERREG(tmr, 0x20)
+#define LM4_TIMER_ICR(tmr) LM4TIMERREG(tmr, 0x24)
+#define LM4_TIMER_TAILR(tmr) LM4TIMERREG(tmr, 0x28)
+#define LM4_TIMER_TBILR(tmr) LM4TIMERREG(tmr, 0x2C)
+#define LM4_TIMER_TAMATCHR(tmr) LM4TIMERREG(tmr, 0x30)
+#define LM4_TIMER_TBMATCHR(tmr) LM4TIMERREG(tmr, 0x34)
+#define LM4_TIMER_TAPR(tmr) LM4TIMERREG(tmr, 0x38)
+#define LM4_TIMER_TBPR(tmr) LM4TIMERREG(tmr, 0x3C)
+#define LM4_TIMER_TAPMR(tmr) LM4TIMERREG(tmr, 0x40)
+#define LM4_TIMER_TBPMR(tmr) LM4TIMERREG(tmr, 0x44)
+#define LM4_TIMER_TAR(tmr) LM4TIMERREG(tmr, 0x48)
+#define LM4_TIMER_TBR(tmr) LM4TIMERREG(tmr, 0x4C)
+#define LM4_TIMER_TAV(tmr) LM4TIMERREG(tmr, 0x50)
+#define LM4_TIMER_TBV(tmr) LM4TIMERREG(tmr, 0x54)
+#define LM4_TIMER_RTCPD(tmr) LM4TIMERREG(tmr, 0x58)
+#define LM4_TIMER_TAPS(tmr) LM4TIMERREG(tmr, 0x5C)
+#define LM4_TIMER_TBPS(tmr) LM4TIMERREG(tmr, 0x60)
+#define LM4_TIMER_TAPV(tmr) LM4TIMERREG(tmr, 0x64)
+#define LM4_TIMER_TBPV(tmr) LM4TIMERREG(tmr, 0x68)
+
+#define LM4_SYSTICK_CTRL LM4REG(0xE000E010)
+#define LM4_SYSTICK_RELOAD LM4REG(0xE000E014)
+#define LM4_SYSTICK_CURRENT LM4REG(0xE000E018)
+
+/* Watchdogs */
+#define LM4WDTREG(num, offset) LM4REG(LM4_WATCHDOG##num##_BASE + (offset))
+
+#define LM4_WATCHDOG0_BASE 0x40000000
+#define LM4_WATCHDOG1_BASE 0x40001000
+
+#define LM4_WATCHDOG_LOAD(n) LM4WDTREG(n, 0x000)
+#define LM4_WATCHDOG_VALUE(n) LM4WDTREG(n, 0x004)
+#define LM4_WATCHDOG_CTL(n) LM4WDTREG(n, 0x008)
+#define LM4_WATCHDOG_ICR(n) LM4WDTREG(n, 0x00C)
+#define LM4_WATCHDOG_RIS(n) LM4WDTREG(n, 0x010)
+#define LM4_WATCHDOG_TEST(n) LM4WDTREG(n, 0x418)
+#define LM4_WATCHDOG_LOCK(n) LM4WDTREG(n, 0xC00)
+
+#endif /* __LM4_REGISTERS */
diff --git a/chip/lm4/system.c b/chip/lm4/system.c
new file mode 100644
index 0000000000..a6d74e32cd
--- /dev/null
+++ b/chip/lm4/system.c
@@ -0,0 +1,371 @@
+/* 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.
+ */
+
+/* System module for Chrome EC */
+
+#include "console.h"
+#include "registers.h"
+#include "system.h"
+#include "uart.h"
+#include "util.h"
+#include "version.h"
+
+/* Forward declarations for console commands */
+static int command_sysinfo(int argc, char **argv);
+static int command_set_scratchpad(int argc, char **argv);
+static int command_hibernate(int argc, char **argv);
+
+static const struct console_command console_commands[] = {
+ {"setscratch", command_set_scratchpad},
+ {"sysinfo", command_sysinfo},
+ {"hibernate", command_hibernate}
+};
+static const struct console_group command_group = {
+ "System", console_commands, ARRAY_SIZE(console_commands)
+};
+
+struct version_struct {
+ uint32_t cookie1;
+ char version[32];
+ uint32_t cookie2;
+} __attribute__ ((packed));
+
+static const struct version_struct version_data = {
+ 0xce112233,
+ CROS_EC_VERSION_STRING,
+ 0xce445566
+};
+
+static uint32_t raw_reset_cause = 0;
+static enum system_reset_cause_t reset_cause = SYSTEM_RESET_UNKNOWN;
+
+
+static int wait_for_hibctl_wc(void)
+{
+ int i;
+ /* Wait for write-capable */
+ for (i = 0; i < 1000000; i++) {
+ if (LM4_HIBERNATE_HIBCTL & 0x80000000)
+ return EC_SUCCESS;
+ }
+ return EC_ERROR_UNKNOWN;
+}
+
+
+static void check_reset_cause(void)
+{
+ enum system_image_copy_t copy = system_get_image_copy();
+ uint32_t hib_status = LM4_HIBERNATE_HIBRIS;
+
+ /* Read the raw reset cause */
+ raw_reset_cause = LM4_SYSTEM_RESC;
+
+ if (hib_status & 0x0d) {
+ /* the hibernation module wakes up the system */
+ if (hib_status & 0x8)
+ reset_cause = SYSTEM_RESET_WAKE_PIN;
+ else if (hib_status & 0x1)
+ reset_cause = SYSTEM_RESET_RTC_ALARM;
+ else if (hib_status & 0x4)
+ reset_cause = SYSTEM_RESET_LOW_BATTERY;
+ /* clear the pending interrupt */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBIC = hib_status;
+ } else if (copy == SYSTEM_IMAGE_RW_A || copy == SYSTEM_IMAGE_RW_B) {
+ /* If we're in image A or B, the only way we can get there is
+ * via a warm reset. */
+ reset_cause = SYSTEM_RESET_SOFT_WARM;
+ } else if (raw_reset_cause & 0x28) {
+ /* Watchdog timer 0 or 1 */
+ reset_cause = SYSTEM_RESET_WATCHDOG;
+ } else if (raw_reset_cause & 0x10) {
+ reset_cause = SYSTEM_RESET_SOFT_COLD;
+ } else if (raw_reset_cause & 0x04) {
+ reset_cause = SYSTEM_RESET_BROWNOUT;
+ } else if (raw_reset_cause & 0x02) {
+ reset_cause = SYSTEM_RESET_POWER_ON;
+ } else if (raw_reset_cause & 0x01) {
+ reset_cause = SYSTEM_RESET_RESET_PIN;
+ } else if (raw_reset_cause) {
+ reset_cause = SYSTEM_RESET_OTHER;
+ } else {
+ reset_cause = SYSTEM_RESET_UNKNOWN;
+ }
+}
+
+
+void system_hibernate(uint32_t seconds, uint32_t microseconds)
+{
+ /* clear pending interrupt */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBIC = LM4_HIBERNATE_HIBRIS;
+ /* set RTC alarm match */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBRTCM0 = seconds;
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBRTCSS = (microseconds * 512 / 15625) << 16;
+
+ /* start counting toward the alarm */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBRTCLD = 0;
+ /* go to hibernation and wake on RTC match or WAKE pin */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBCTL = 0x5B;
+ /* we are going to hibernate ... */
+ while (1) ;
+}
+
+
+int system_pre_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable clocks to the hibernation module */
+ LM4_SYSTEM_RCGCHIB = 1;
+ /* Wait 3 clock cycles before using the module */
+ scratch = LM4_SYSTEM_RCGCHIB;
+
+ /* Enable the hibernation oscillator, if it's not already enabled. We
+ * use this to hold our scratchpad value across reboots. */
+ if (!(LM4_HIBERNATE_HIBCTL & 0x40)) {
+ int rv, i;
+ rv = wait_for_hibctl_wc();
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* Enable clock to hibernate module */
+ LM4_HIBERNATE_HIBCTL |= 0x40;
+ /* Wait for write-complete */
+ for (i = 0; i < 1000000; i++) {
+ if (LM4_HIBERNATE_HIBRIS & 0x10)
+ break;
+ }
+ }
+ /* initialize properly registers after reset (cf errata) */
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBRTCT = 0x7fff;
+ wait_for_hibctl_wc();
+ LM4_HIBERNATE_HIBIM = 0;
+
+ check_reset_cause();
+
+ return EC_SUCCESS;
+}
+
+
+int system_init(void)
+{
+ /* Clear the hardware reset cause, now that we've committed to running
+ * this image. */
+ LM4_SYSTEM_RESC = 0;
+
+ /* Register our internal commands */
+ return console_register_commands(&command_group);
+}
+
+
+enum system_reset_cause_t system_get_reset_cause(void)
+{
+ return reset_cause;
+}
+
+
+const char *system_get_reset_cause_string(void)
+{
+ static const char * const cause_descs[] = {
+ "unknown", "other", "brownout", "power-on", "reset pin",
+ "soft cold", "soft warm", "watchdog", "rtc alarm", "wake pin",
+ "low battery"};
+
+ return reset_cause < ARRAY_SIZE(cause_descs) ?
+ cause_descs[reset_cause] : "?";
+}
+
+
+enum system_image_copy_t system_get_image_copy(void)
+{
+ int copy = (uint32_t)system_get_image_copy / CONFIG_FW_IMAGE_SIZE;
+ switch (copy) {
+ case 0:
+ return SYSTEM_IMAGE_RO;
+ case 1:
+ return SYSTEM_IMAGE_RW_A;
+ case 2:
+ return SYSTEM_IMAGE_RW_B;
+ default:
+ return SYSTEM_IMAGE_UNKNOWN;
+ }
+}
+
+
+const char *system_get_image_copy_string(void)
+{
+ static const char * const copy_descs[] = {"unknown", "RO", "A", "B"};
+ int copy = system_get_image_copy();
+ return copy < ARRAY_SIZE(copy_descs) ? copy_descs[copy] : "?";
+}
+
+
+int system_run_image_copy(enum system_image_copy_t copy)
+{
+ uint32_t init_addr;
+ void (*resetvec)(void);
+
+ /* Fail if we're not in RO firmware */
+ if (system_get_image_copy() != SYSTEM_IMAGE_RO)
+ return EC_ERROR_UNKNOWN;
+
+ /* TODO: fail if we've already started up interrupts, etc. */
+
+ /* Load the appropriate reset vector */
+ if (copy == SYSTEM_IMAGE_RW_A)
+ init_addr = *(uint32_t *)(CONFIG_FW_A_OFF + 4);
+ else if (copy == SYSTEM_IMAGE_RW_B)
+ init_addr = *(uint32_t *)(CONFIG_FW_B_OFF + 4);
+ else
+ return EC_ERROR_UNKNOWN;
+
+ /* TODO: sanity check reset vector; must be inside the
+ * appropriate image. */
+
+ /* Jump to the reset vector */
+ resetvec = (void(*)(void))init_addr;
+ resetvec();
+
+ /* Should never get here */
+ return EC_ERROR_UNIMPLEMENTED;
+}
+
+
+int system_reset(int is_cold)
+{
+ /* TODO - warm vs. cold */
+ LM4_NVIC_APINT = 0x05fa0004;
+
+ /* Spin and wait for reboot; should never return */
+ /* TODO: should disable task swaps while waiting */
+ while (1) {}
+
+ return EC_ERROR_UNKNOWN;
+}
+
+
+int system_set_scratchpad(uint32_t value)
+{
+ int rv;
+
+ /* Wait for ok-to-write */
+ rv = wait_for_hibctl_wc();
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* Write scratchpad */
+ /* TODO: might be more elegant to have a write_hibernate_reg() method
+ * which takes an address and data and does the delays */
+ LM4_HIBERNATE_HIBDATA = value;
+
+ /* Wait for write-complete */
+ return wait_for_hibctl_wc();
+}
+
+
+uint32_t system_get_scratchpad(void)
+{
+ return LM4_HIBERNATE_HIBDATA;
+}
+
+
+const char *system_get_version(enum system_image_copy_t copy)
+{
+ int imoffset;
+ const uint32_t *p, *pend;
+ const struct version_struct *v;
+
+ /* Handle version of current image */
+ if (copy == system_get_image_copy() || copy == SYSTEM_IMAGE_UNKNOWN)
+ return version_data.version;
+
+ switch (copy) {
+ case SYSTEM_IMAGE_RO:
+ imoffset = CONFIG_FW_RO_OFF;
+ break;
+ case SYSTEM_IMAGE_RW_A:
+ imoffset = CONFIG_FW_A_OFF;
+ break;
+ case SYSTEM_IMAGE_RW_B:
+ imoffset = CONFIG_FW_B_OFF;
+ break;
+ default:
+ return "";
+ }
+
+ /* Search for version cookies in target image */
+ /* TODO: could be smarter about where to search if we stuffed
+ * the version data into a predefined area of the image - for
+ * example, immediately following the reset vectors. */
+ pend = (uint32_t *)(imoffset + CONFIG_FW_IMAGE_SIZE
+ - sizeof(version_data));
+ for (p = (uint32_t *)imoffset; p <= pend; p++) {
+ v = (const struct version_struct *)p;
+ if (v->cookie1 == version_data.cookie1 &&
+ v->cookie2 == version_data.cookie2)
+ return v->version;
+ }
+
+ return "";
+}
+
+
+static int command_sysinfo(int argc, char **argv)
+{
+ uart_printf("Reset cause: %d (%s)\n",
+ system_get_reset_cause(),
+ system_get_reset_cause_string());
+ uart_printf("Raw reset cause: 0x%x\n", raw_reset_cause);
+ uart_printf("Scratchpad: 0x%08x\n", system_get_scratchpad());
+ uart_printf("Firmware copy: %s\n", system_get_image_copy_string());
+ return EC_SUCCESS;
+}
+
+
+static int command_set_scratchpad(int argc, char **argv)
+{
+ int s;
+ char *e;
+
+ if (argc < 2) {
+ uart_puts("Usage: scratchpad <value>\n");
+ return EC_ERROR_UNKNOWN;
+ }
+
+ s = strtoi(argv[1], &e, 0);
+ if (*e) {
+ uart_puts("Invalid scratchpad value\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ uart_printf("Setting scratchpad to 0x%08x\n", s);
+ return system_set_scratchpad(s);
+}
+
+static int command_hibernate(int argc, char **argv)
+{
+ int seconds;
+ int microseconds = 0;
+
+ if (argc < 2) {
+ uart_puts("Usage: hibernate <seconds> [<microseconds>]\n");
+ return EC_ERROR_UNKNOWN;
+ }
+ seconds = strtoi(argv[1], NULL, 0);
+ if (argc >= 3)
+ microseconds = strtoi(argv[2], NULL, 0);
+
+ uart_printf("Hibernating for %d.%06d s ...\n", seconds, microseconds);
+ uart_flush_output();
+
+ system_hibernate(seconds, microseconds);
+
+ return EC_SUCCESS;
+}
diff --git a/chip/lm4/temp_sensor.c b/chip/lm4/temp_sensor.c
new file mode 100644
index 0000000000..bdb8b42ea6
--- /dev/null
+++ b/chip/lm4/temp_sensor.c
@@ -0,0 +1,197 @@
+/* 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.
+ */
+
+/* Temperature sensor module for Chrome EC */
+
+#include "adc.h"
+#include "board.h"
+#include "console.h"
+#include "i2c.h"
+#include "temp_sensor.h"
+#include "uart.h"
+#include "util.h"
+
+
+/* Address of first temp sensor in system */
+#define TEMP0_ADDR ((0x40 << 1) | I2C_FLAG_BIG_ENDIAN)
+/* TODO: support all 6 temp sensors */
+
+/* Address of battery charger */
+#define CHARGER_ADDR 0x12
+
+/* Address of battery */
+#define BATTERY_ADDR 0x16
+
+
+int temp_sensor_read(enum temp_sensor_id id)
+{
+ int traw, t;
+ int rv;
+
+ switch(id) {
+ case TEMP_SENSOR_CASE:
+ /* TODO: fix temperature correction factor. For now,
+ * just return the die temperature. */
+ return temp_sensor_read(TEMP_SENSOR_CASE_DIE);
+
+ case TEMP_SENSOR_CASE_DIE:
+ rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x01, &traw);
+ if (rv)
+ return -1;
+ t = (int)(int16_t)traw / 128;
+ return t + 273;
+
+ case TEMP_SENSOR_EC_INTERNAL:
+ return adc_read_ec_temperature();
+ }
+
+ /* If we're still here, we don't handle that sensor */
+ return -1;
+}
+
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_temps(int argc, char **argv)
+{
+ int vraw, v;
+ int traw, t;
+ int rv;
+ int d;
+
+ uart_puts("Reading temperature sensors...\n");
+
+ uart_printf(" Temp from die: %d K\n\n",
+ temp_sensor_read(TEMP_SENSOR_CASE_DIE));
+
+ uart_puts("Debug data:\n");
+ rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0xfe, &d);
+ if (rv)
+ return rv;
+ uart_printf(" Manufacturer ID: 0x%04x\n", d);
+
+ rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0xff, &d);
+ uart_printf(" Device ID: 0x%04x\n", d);
+
+ rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x02, &d);
+ uart_printf(" Config: 0x%04x\n", d);
+
+ rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x00, &vraw);
+ v = ((int)(int16_t)vraw * 15625) / 100;
+ uart_printf(" Voltage: 0x%04x = %d nV\n", vraw, v);
+
+ rv = i2c_read16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x01, &traw);
+ t = ((int)(int16_t)traw * 100) / 128;
+ uart_printf(" Temperature: 0x%04x = %d.%02d C\n",
+ traw, t / 100, t > 0 ? t % 100 : 100 - (t % 100));
+
+ /* TODO: correction factor from voltage offset */
+
+ return EC_SUCCESS;
+}
+
+
+/* TODO: the battery charger would normally be on a separate I2C bus.
+ * For evaluation, it's on the same bus as the thermal sensor, so I
+ * put the debug command here for now. */
+static int command_charger(int argc, char **argv)
+{
+ int rv;
+ int d;
+
+ uart_puts("Reading battery charger...\n");
+
+ rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0xfe, &d);
+ if (rv)
+ return rv;
+ uart_printf(" Manufacturer ID: 0x%04x\n", d);
+
+ rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0xff, &d);
+ uart_printf(" Device ID: 0x%04x\n", d);
+
+ rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0x12, &d);
+ uart_printf(" Option: 0x%04x\n", d);
+
+ rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0x14, &d);
+ uart_printf(" Charge current: 0x%04x\n", d);
+
+ rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0x15, &d);
+ uart_printf(" Charge voltage: 0x%04x\n", d);
+
+ rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR, 0x3f, &d);
+ uart_printf(" Input current: 0x%04x\n", d);
+
+ return EC_SUCCESS;
+}
+
+
+/* TODO: the battery would normally be on a separate I2C bus. For
+ * evaluation, it's on the same bus as the thermal sensor so I put the
+ * debug command here for now. */
+static int command_battery(int argc, char **argv)
+{
+ int rv;
+ int d;
+
+ uart_puts("Reading battery...\n");
+
+ rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x08, &d);
+ if (rv)
+ return rv;
+ uart_printf(" Temperature: 0x%04x = %d C\n",
+ d, (d-2731)/10);
+
+ rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x09, &d);
+ uart_printf(" Voltage: 0x%04x = %d mV\n", d, d);
+
+ rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x0f, &d);
+ uart_printf(" Remaining capacity: 0x%04x = %d mAh\n", d, d);
+ rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x10, &d);
+ uart_printf(" Full charge capacity: 0x%04x = %d mAh\n", d, d);
+
+ rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x14, &d);
+ uart_printf(" Desired charge current: 0x%04x = %d mA\n", d, d);
+ rv = i2c_read16(I2C_PORT_BATTERY, BATTERY_ADDR, 0x15, &d);
+ uart_printf(" Desired charge voltage: 0x%04x = %d mV\n", d, d);
+
+
+ return EC_SUCCESS;
+}
+
+
+static const struct console_command console_commands[] = {
+ {"temps", command_temps},
+ {"charger", command_charger},
+ {"battery", command_battery},
+};
+static const struct console_group command_group = {
+ "Temp sensor", console_commands, ARRAY_SIZE(console_commands)
+};
+
+
+/*****************************************************************************/
+/* Initialization */
+
+int temp_sensor_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+#ifdef CONFIG_SENSOR
+ /* TODO: not necessary since these are the power-on defaults,
+ * except for the DRDY pin. It's unclear DRDY will be used
+ * anyway. */
+
+ /* Configure the sensor:
+ * 0x7000 = bits 14:12 = continuous conversion
+ * 0x0400 = bits 11:9 = ADC conversion rate (1/sec)
+ * 0x0100 = bit 8 = DRDY pin enabled */
+ /* TODO: support shutdown mode for power-saving? */
+ i2c_write16(I2C_PORT_THERMAL, TEMP0_ADDR, 0x02, 0x7500);
+#endif
+
+ console_register_commands(&command_group);
+ return EC_SUCCESS;
+}
diff --git a/chip/lm4/uart.c b/chip/lm4/uart.c
new file mode 100644
index 0000000000..49115e418c
--- /dev/null
+++ b/chip/lm4/uart.c
@@ -0,0 +1,563 @@
+/* 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.
+ */
+
+/* UART module for Chrome EC */
+
+#include <stdarg.h>
+
+#include "board.h"
+#include "console.h"
+#include "registers.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+
+/* Baud rate for UARTs */
+#define BAUD_RATE 115200
+
+/* Buffer sizes; should be power of 2 */
+#define TX_BUF_SIZE 512
+#define RX_BUF_SIZE 128 /* suggest larger than 80 to copy&paste script. */
+
+/* Macros to advance in the circular transmit and receive buffers */
+#define TX_BUF_NEXT(i) (((i) + 1) & (TX_BUF_SIZE - 1))
+#define RX_BUF_NEXT(i) (((i) + 1) & (RX_BUF_SIZE - 1))
+#define RX_BUF_PREV(i) (((i) - 1) & (RX_BUF_SIZE - 1))
+
+/* Transmit and receive buffers */
+static volatile char tx_buf[TX_BUF_SIZE];
+static volatile int tx_buf_head = 0;
+static volatile int tx_buf_tail = 0;
+static volatile char rx_buf[RX_BUF_SIZE];
+static volatile int rx_buf_head = 0;
+static volatile int rx_buf_tail = 0;
+static int last_rx_was_cr = 0;
+
+static int console_mode = 1;
+
+/* TODO: should have an API to set raw mode for the UART. In raw
+ * mode, we don't do CRLF translation or echo input. */
+
+
+/* Put a single character into the transmit buffer. Does not enable
+ * the transmit interrupt; assumes that happens elsewhere. Returns
+ * zero if the character was transmitted, 1 if it was dropped. */
+static int __tx_char(int c)
+{
+ int tx_buf_next;
+
+ /* Do newline to CRLF translation */
+ if (console_mode && c == '\n' && __tx_char('\r'))
+ return 1;
+
+ tx_buf_next = TX_BUF_NEXT(tx_buf_head);
+ if (tx_buf_next == tx_buf_tail)
+ return 1;
+
+ tx_buf[tx_buf_head] = c;
+ tx_buf_head = tx_buf_next;
+ return 0;
+}
+
+
+/* Interrupt handler for UART0 */
+static void uart_0_interrupt(void)
+{
+ /* Clear transmit and receive interrupt status */
+ LM4_UART_ICR(0) = 0x70;
+
+ /* Copy input from buffer until RX fifo empty */
+ while (!(LM4_UART_FR(0) & 0x10)) {
+ int c = LM4_UART_DR(0);
+
+ /* Handle console mode echoing and translation */
+ if (console_mode) {
+ /* Translate CR and CRLF to LF (newline) */
+ if (c == '\r') {
+ last_rx_was_cr = 1;
+ c = '\n';
+ } else if (c == '\n' && last_rx_was_cr) {
+ last_rx_was_cr = 0;
+ continue;
+ } else {
+ last_rx_was_cr = 0;
+ }
+
+ /* Echo characters directly to the transmit
+ * FIFO so we don't interfere with the
+ * transmit buffer. This means that if a lot
+ * of output is happening, input characters
+ * won't always be properly echoed. */
+ if (console_mode && c == '\n')
+ LM4_UART_DR(0) = '\r';
+ LM4_UART_DR(0) = c;
+
+ /* Handle backspace if we can */
+ if (c == '\b') {
+ if (rx_buf_head != rx_buf_tail) {
+ /* Delete the previous
+ * character (and space over
+ * it on the output) */
+ LM4_UART_DR(0) = ' ';
+ LM4_UART_DR(0) = '\b';
+ rx_buf_head = RX_BUF_PREV(rx_buf_head);
+ }
+ continue;
+ }
+ }
+
+ rx_buf[rx_buf_head] = c;
+ rx_buf_head = RX_BUF_NEXT(rx_buf_head);
+ /* On overflow, discard oldest output */
+ if (rx_buf_head == rx_buf_tail)
+ rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
+
+ /* Call console callback on newline, if in console mode */
+ if (console_mode && c == '\n')
+ console_has_input();
+ }
+
+ /* Copy output from buffer until TX fifo full or output buffer empty */
+ while (!(LM4_UART_FR(0) & 0x20) && (tx_buf_head != tx_buf_tail)) {
+ LM4_UART_DR(0) = tx_buf[tx_buf_tail];
+ tx_buf_tail = TX_BUF_NEXT(tx_buf_tail);
+ }
+
+ /* If output buffer is empty, disable transmit interrupt */
+ if (tx_buf_tail == tx_buf_head)
+ LM4_UART_IM(0) &= ~0x20;
+}
+DECLARE_IRQ(5, uart_0_interrupt, 1);
+
+
+/* Interrupt handler for UART1 */
+static void uart_1_interrupt(void)
+{
+ /* Clear transmit and receive interrupt status */
+ LM4_UART_ICR(1) = 0x70;
+
+ /* TODO: handle input */
+
+ /* If we have space in our FIFO and a character is pending in LPC,
+ * grab it.
+ *
+ * TODO: move UART1 interrupt to the LPC module? */
+ if (!(LM4_UART_FR(1) & 0x20) && (LM4_LPC_ST(7) & 0x02)) {
+ /* TODO: this clears the receive-ready interrupt too,
+ * which will be ok once we're handing input as well.
+ * But we're not yet. */
+ LM4_LPC_LPCDMACX = LM4_LPC_LPCDMACX;
+ /* Copy the next byte */
+ LM4_UART_DR(1) = LPC_POOL_COMX[0];
+ /* Disable transmit interrupt */
+ LM4_UART_IM(1) &= ~0x20;
+ }
+}
+/* Must be same prio as LPC interrupt handler so they don't preempt */
+DECLARE_IRQ(6, uart_1_interrupt, 2);
+
+
+/* Configure GPIOs for the UART module. */
+/* TODO: board-dependent; on Link UART1 is PC4/PC5, not PB0/PB1. */
+static void configure_gpio(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable clocks to GPIO blocks A and B, then delay a few clocks. */
+ LM4_SYSTEM_RCGCGPIO |= 0x0003;
+
+ /* UART0 setup; RX and TX are GPIO PA0 and PA1 */
+ /* Enable alternate function */
+ LM4_GPIO_AFSEL(A) |= 0x03;
+ /* Alternate function 1 */
+ LM4_GPIO_PCTL(A) = (LM4_GPIO_PCTL(A) & 0xffffff00) | 0x11;
+ /* Enable digital function */
+ LM4_GPIO_DEN(A) |= 0x03;
+
+ /* UART1 setup; RX and TX are GPIO PB0 and PB1 */
+ /* Enable alternate function */
+ LM4_GPIO_AFSEL(B) |= 0x03;
+ /* Alternate function 1 */
+ LM4_GPIO_PCTL(B) = (LM4_GPIO_PCTL(B) & 0xffffff00) | 0x11;
+ /* Enable digital function */
+ LM4_GPIO_DEN(B) = 0x03;
+}
+
+
+int uart_init(void)
+{
+ volatile uint32_t scratch __attribute__((unused));
+
+ /* Enable UART0 and UART1 and delay a few clocks */
+ LM4_SYSTEM_RCGCUART |= 0x03;
+ scratch = LM4_SYSTEM_RCGCUART;
+
+ /* Configure GPIOs */
+ configure_gpio();
+
+ /* UART0 setup */
+ /* Disable the port via UARTCTL */
+ LM4_UART_CTL(0) = 0x0300;
+ /* Set the baud rate divisor */
+ LM4_UART_IBRD(0) = (CPU_CLOCK / 16) / BAUD_RATE;
+ LM4_UART_FBRD(0) =
+ (((CPU_CLOCK / 16) % BAUD_RATE) * 64 + BAUD_RATE / 2) /
+ BAUD_RATE;
+ /* Set UARTLCRH to 8-N-1, FIFO enabled. Must be done after setting
+ * the divisor for the new divisor to take effect. */
+ LM4_UART_LCRH(0) = 0x70;
+ /* Interrupt when RX fifo at minimum (>= 1/8 full), and TX fifo when
+ * <= 1/4 full */
+ LM4_UART_IFLS(0) = 0x01;
+ /* Unmask receive-FIFO, receive-timeout. We need
+ * receive-timeout because the minimum RX FIFO depth is 1/8 =
+ * 2 bytes; without the receive-timeout we'd never be notified
+ * about single received characters. */
+ LM4_UART_IM(0) = 0x50;
+ /* Enable the port */
+ LM4_UART_CTL(0) |= 0x0001;
+
+ /* UART1 setup */
+ /* Disable the port via UARTCTL */
+ LM4_UART_CTL(1) = 0x0300;
+ /* Set the baud rate divisor */
+ LM4_UART_IBRD(1) = (CPU_CLOCK / 16) / BAUD_RATE;
+ LM4_UART_FBRD(1) =
+ (((CPU_CLOCK / 16) % BAUD_RATE) * 64 + BAUD_RATE / 2) /
+ BAUD_RATE;
+ /* Set UARTLCRH to 8-N-1, FIFO enabled. Must be done after setting
+ * the divisor for the new divisor to take effect. */
+ LM4_UART_LCRH(1) = 0x70;
+ /* Unmask receive-FIFO, receive-timeout. We need
+ * receive-timeout because the minimum RX FIFO depth is 1/8 =
+ * 2 bytes; without the receive-timeout we'd never be notified
+ * about single received characters. */
+ LM4_UART_IM(1) = 0x50;
+#ifdef USE_UART_DMA
+ /* No interrupts on UART1 */
+ LM4_UART_IM(1) = 0x00;
+ /* Enable RX and TX DMA */
+ LM4_UART_DMACTL(1) = 0x03;
+#endif
+ /* Enable the port */
+ LM4_UART_CTL(1) |= 0x0001;
+
+ /* Print hello on UART1 for debugging */
+ {
+ const char *c = "Hello on UART1\r\n";
+ while (*c)
+ LM4_UART_DR(1) = *c++;
+ }
+
+ return EC_SUCCESS;
+}
+
+
+void uart_set_console_mode(int enable)
+{
+ console_mode = enable;
+}
+
+
+int uart_puts(const char *outstr)
+{
+ int was_empty = (tx_buf_head == tx_buf_tail);
+
+ /* Put all characters in the output buffer */
+ while (*outstr) {
+ if (__tx_char(*outstr++) != 0)
+ break;
+ }
+
+ if (was_empty) {
+ /* Re-enable the transmit interrupt, then forcibly trigger the
+ * interrupt. This works around a hardware problem with the
+ * UART where the FIFO only triggers the interrupt when its
+ * threshold is _crossed_, not just met. */
+ LM4_UART_IM(0) |= 0x20;
+ LM4_NVIC_SWTRIG = 5;
+ }
+
+ /* Successful if we consumed all output */
+ return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS;
+}
+
+
+int uart_printf(const char *format, ...)
+{
+ static const char int_chars[] = "0123456789abcdef";
+ static const char error_str[] = "ERROR";
+ char intbuf[21]; /* Longest uint64 */
+ int dropped_chars = 0;
+ int is_left;
+ int pad_zero;
+ int pad_width;
+ int was_empty = (tx_buf_head == tx_buf_tail);
+ va_list args;
+ char *vstr;
+ int vlen;
+
+ va_start(args, format);
+
+ while (*format && !dropped_chars) {
+ int c = *format++;
+
+ /* Copy normal characters */
+ if (c != '%') {
+ dropped_chars |= __tx_char(c);
+ continue;
+ }
+
+ /* Get first format character */
+ c = *format++;
+
+ /* Send "%" for "%%" input */
+ if (c == '%' || c == '\0') {
+ dropped_chars |= __tx_char('%');
+ continue;
+ }
+
+ /* Handle %c */
+ if (c == 'c') {
+ c = va_arg(args, int);
+ dropped_chars |= __tx_char(c);
+ continue;
+ }
+
+ /* Handle left-justification ("%-5s") */
+ is_left = (c == '-');
+ if (is_left)
+ c = *format++;
+
+ /* Handle padding with 0's */
+ pad_zero = (c == '0');
+ if (pad_zero)
+ c = *format++;
+
+ /* Count padding length */
+ pad_width = 0;
+ while (c >= '0' && c <= '9') {
+ pad_width = (10 * pad_width) + c - '0';
+ c = *format++;
+ }
+ if (pad_width > 80) {
+ /* Sanity check for width failed */
+ format = error_str;
+ continue;
+ }
+
+ if (c == 's') {
+ vstr = va_arg(args, char *);
+ if (vstr == NULL)
+ vstr = "(NULL)";
+ } else {
+ uint32_t v;
+ int is_negative = 0;
+ int base = 10;
+
+ /* TODO: handle "%l" prefix for uint64_t */
+
+ v = va_arg(args, uint32_t);
+
+ switch (c) {
+ case 'd':
+ if ((int)v < 0) {
+ is_negative = 1;
+ v = -v;
+ }
+ break;
+ case 'u':
+ break;
+ case 'x':
+ case 'p':
+ base = 16;
+ break;
+ default:
+ format = error_str;
+ }
+ if (format == error_str)
+ continue; /* Bad format specifier */
+
+ /* Convert integer to string, starting at end of
+ * buffer and working backwards. */
+ vstr = intbuf + sizeof(intbuf) - 1;
+ *(vstr) = '\0';
+
+ if (!v)
+ *(--vstr) = '0';
+
+ while (v) {
+ *(--vstr) = int_chars[v % base];
+ v /= base;
+ }
+ if (is_negative)
+ *(--vstr) = '-';
+ }
+
+ /* Copy string (or stringified integer) */
+ vlen = strlen(vstr);
+ while (vlen < pad_width && !is_left) {
+ dropped_chars |= __tx_char(pad_zero ? '0' : ' ');
+ vlen++;
+ }
+ while (*vstr)
+ dropped_chars |= __tx_char(*vstr++);
+ while (vlen < pad_width && is_left) {
+ dropped_chars |= __tx_char(' ');
+ vlen++;
+ }
+ }
+ va_end(args);
+
+ if (was_empty) {
+ /* Re-enable the transmit interrupt, then forcibly trigger the
+ * interrupt. This works around a hardware problem with the
+ * UART where the FIFO only triggers the interrupt when its
+ * threshold is _crossed_, not just met. */
+ LM4_UART_IM(0) |= 0x20;
+ LM4_NVIC_SWTRIG = 5;
+ }
+
+ /* Successful if we consumed all output */
+ return dropped_chars ? EC_ERROR_OVERFLOW : EC_SUCCESS;
+}
+
+
+void uart_flush_output(void)
+{
+ /* Wait for buffer to empty */
+ while (tx_buf_head != tx_buf_tail) {
+ /* It's possible we're in some other interrupt, and the
+ * previous context was doing a printf() or puts() but hadn't
+ * enabled the UART interrupt. Check if the interrupt is
+ * disabled, and if so, re-enable and trigger it. Note that
+ * this check is inside the while loop, so we'll be safe even
+ * if the context switches away from us to another partial
+ * printf() and back. */
+ if (!(LM4_UART_IM(0) & 0x20)) {
+ LM4_UART_IM(0) |= 0x20;
+ LM4_NVIC_SWTRIG = 5;
+ }
+ }
+
+ /* Wait for transmit FIFO empty */
+ while (!(LM4_UART_FR(0) & 0x80)) {}
+}
+
+void uart_emergency_flush(void)
+{
+ do {
+ /* Copy output from buffer until TX fifo full
+ * or output buffer empty
+ */
+ while (!(LM4_UART_FR(0) & 0x20) &&
+ (tx_buf_head != tx_buf_tail)) {
+ LM4_UART_DR(0) = tx_buf[tx_buf_tail];
+ tx_buf_tail = TX_BUF_NEXT(tx_buf_tail);
+ }
+ /* Wait for transmit FIFO empty */
+ while (!(LM4_UART_FR(0) & 0x80)) {}
+ } while (tx_buf_head != tx_buf_tail);
+}
+
+void uart_flush_input(void)
+{
+ /* Disable interrupts */
+ LM4_NVIC_DIS(0) = (1 << 5);
+
+ /* Call interrupt handler to empty the hardware FIFO */
+ uart_0_interrupt();
+
+ /* Clear the input buffer */
+ rx_buf_tail = rx_buf_head;
+
+ /* Re-enable interrupts */
+ LM4_NVIC_EN(0) = (1 << 5);
+}
+
+
+int uart_peek(int c)
+{
+ int index = -1;
+ int i = 0;
+
+ /* Disable interrupts while we pull characters out, because the
+ * interrupt handler can also modify the tail pointer. */
+ LM4_NVIC_DIS(0) = (1 << 5);
+
+ /* Call interrupt handler to empty the hardware FIFO. The minimum
+ * FIFO trigger depth is 1/8 (2 chars), so this is the only way to
+ * ensure we've pulled the very last character out of the FIFO. */
+ uart_0_interrupt();
+
+ for (i = rx_buf_tail; i != rx_buf_head; i = RX_BUF_NEXT(i)) {
+ if (rx_buf[i] == c) {
+ index = (RX_BUF_SIZE + i - rx_buf_tail) &
+ (RX_BUF_SIZE - 1);
+ break;
+ }
+ }
+
+ /* Re-enable interrupts */
+ LM4_NVIC_EN(0) = (1 << 5);
+
+ return index;
+}
+
+
+int uart_getc(void)
+{
+ int c;
+
+ /* Disable interrupts */
+ LM4_NVIC_DIS(0) = (1 << 5);
+
+ /* Call interrupt handler to empty the hardware FIFO */
+ uart_0_interrupt();
+
+ if (rx_buf_tail == rx_buf_head) {
+ c = -1; /* No pending input */
+ } else {
+ c = rx_buf[rx_buf_tail];
+ rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
+ }
+
+ /* Re-enable interrupts */
+ LM4_NVIC_EN(0) = (1 << 5);
+
+ return c;
+}
+
+
+int uart_gets(char *dest, int size)
+{
+ int got = 0;
+ int c;
+
+ /* Disable interrupts while we pull characters out, because the
+ * interrupt handler can also modify the tail pointer. */
+ LM4_NVIC_DIS(0) = (1 << 5);
+
+ /* Call interrupt handler to empty the hardware FIFO */
+ uart_0_interrupt();
+
+ /* Read characters */
+ while (got < size - 1 && rx_buf_tail != rx_buf_head) {
+ c = rx_buf[rx_buf_tail];
+ dest[got++] = c;
+ rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
+ if (c == '\n')
+ break; /* Stop on newline */
+ }
+
+ /* Re-enable interrupts */
+ LM4_NVIC_EN(0) = (1 << 5);
+
+ /* Null-terminate */
+ dest[got] = '\0';
+
+ /* Return the length we got */
+ return got;
+}
diff --git a/chip/lm4/x86_power.c b/chip/lm4/x86_power.c
new file mode 100644
index 0000000000..cda5dcd0eb
--- /dev/null
+++ b/chip/lm4/x86_power.c
@@ -0,0 +1,150 @@
+/* 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.
+ */
+
+/* x86 power control module for Chrome EC */
+
+#include "registers.h"
+#include "x86_power.h"
+
+/* Signals to/from EC. These are ALL the signals on the schematic
+ * that seem related. From the 11/14 schematic. */
+#if 0
+enum ec_signal {
+};
+
+/* Signals from Chief River Platform Power Sequence document. Directions are
+ * from the EC's point of view. */
+enum x86_signal {
+ // Schematic GPIO dir Intel doc name
+ CPU1.5V_S3_GATE, PH5, out /* Connected to SUSP# via resistor. Can
+ * disable VDDQ when driven low. */
+ PBTN_OUT#, PG3, out PWRBTN#
+ PCH_DPWROK, PG0, out, DPWROK
+ PCH_PWROK, PC5, out, PWROK /* AND'd with VGATE (CPU_CORE
+ * good), then connected to
+ * PWROK. Also * connected to
+ * APWROK via resistor */
+ PCH_RSMRST#, PF1, out, RSMRST# /* Also connected to
+ * PCH_DPWROK via resistor */
+ PM_SLP_SUS#, PD3, inp, SLP_SUS
+ SLP_A#, PG5, i/o, SLP_A# /* Intel claims inp; why does
+ * schematic claim I/O? */
+ SLP_ME_CSW_DEV#, PG4, i/o, SLP_ME_CSW_DEV# /* Intel also claims
+ * inp? */
+ PM_SLP_S3#, PJ0, inp, SLP_S3#
+ PM_SLP_S4#, PJ1, inp, SLP_S4#
+ PM_SLP_S5#, PJ2, inp, SLP_S5#
+ SUSACK#, PD2, out, SUS_ACK# /* Also connected to SUSWARN#
+ * via (no-load) resistor */
+ SUSWARN#, PH1, inp, SUSWARN#
+
+ /* On EC schematic, but not mentioned directly in doc */
+ ACOFF, PG2, inp /* Not connected? */
+ BKOFF#, PH4, out /* Turns off display when pulled low.
+ * Can tri-state when not using. */
+ EC_ACIN, PC7, inp /* Connected to ACIN, which comes from
+ * ACOK on charger */
+ EC_KBRST#, PQ7, out, RCIN#
+ EC_LID_OUT#, PF0, out, GPIO15
+ EC_ON, PC6, out
+ ENBKL, PF4, inp, L_BKLTEN /* From Panther Point LVDS */
+ GATEA20, PQ6, out, A20GATE
+ HDA_SDO, PG1, out, HDA_SDO
+ H_PROCHOT#_EC, PF2, out, PROCHOT# /* Ivy Bridge. Has pullup. */
+ SA_PGOOD, PF3, inp /* Set by power control when VccSA is
+ * good */
+ SMI#, PJ3, out, GPIO8
+ SUSP#, PG6, out /* Disables VCCP, VDDQ, +5VS, +3VS,
+ * +1.5VS, +0.75VS, +1.8VS */
+ SYSON, PB6, out /* Enables +1.5VP, VCCP */
+ VR_ON, PB7, out /* Enables CPU_CORE, VGFX_CORE */
+};
+
+
+/* Signals from the Ivy Bridge power sequencing document that aren't connected
+ * to the EC (at least, directly) */
+enum x86_signal_no_control {
+ // Platform to PCH
+ ACPRESENT, /* Comes from ACIN, which comes from ACOK on charger.
+ * There's an EC_ACIN line mentioned, but it doesn't seem
+ * to go to the EC(!) */
+ IMVP7_VR_EN, /* (not on schematic) */
+ RTCRST#, /* Can short to ground via jumper */
+ SA_VR_PWROK, /* (not on schematic) */
+ SYS_PWROK,
+ VR_VDDPWRGD,
+
+ // PCH to platform
+ CL_RST#
+ SUSCLK,
+ SUS_STAT#,
+ SLP_LAN#
+
+ // Platform to platform
+ ALL_SYS_PWRGD,
+
+ // Power rails
+ VccASW,
+ VccAXG,
+ VccCore (CPU), // aka Vboot
+ VccCore (PCH),
+ VccDSW,
+ VccRTC,
+ VCCP, // aka VccIO
+ VccSA,
+ VccSPI,
+ VccSUS,
+ Vcc_WLAN,
+ VDDQ,
+ // (+ all platform rails?)
+};
+
+/* Signals we explicitly don't care about */
+enum x86_signal_dont_care {
+ SUSPWRDNACK, // Only applicable if deep sleep well not supported
+
+};
+
+
+#endif
+
+
+
+
+/* Signal definitions are messy, split across multiple GPIOs.
+ * Fortunately, we only need to set them one at a time. */
+struct signal_gpio {
+ int is_output;
+
+};
+
+struct signal_gpio gpios[] = {
+
+
+
+};
+
+
+
+
+int x86_power_init(void)
+{
+#if 0
+ /* Enable clock to GPIO block A */
+ LM4_SYSTEM_RCGCGPIO |= 0x0001;
+
+ /* Turn off the LED before we make it an output */
+ gpio_set(EC_GPIO_DEBUG_LED, 0);
+
+ /* Clear GPIOAFSEL bits for block A pin 7 */
+ LM4_GPIO_AFSEL(A) &= ~(0x80);
+
+ /* Set GPIO to digital enable, output */
+ LM4_GPIO_DEN(A) |= 0x80;
+ LM4_GPIO_DIR(A) |= 0x80;
+#endif
+ return EC_SUCCESS;
+}
+
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;
+}
diff --git a/include/adc.h b/include/adc.h
new file mode 100644
index 0000000000..1f01f2c08f
--- /dev/null
+++ b/include/adc.h
@@ -0,0 +1,35 @@
+/* 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.
+ */
+
+/* ADC interface for Chrome EC */
+
+#ifndef __CROS_EC_ADC_H
+#define __CROS_EC_ADC_H
+
+#include "common.h"
+
+/* Value returned by adc_read_*() methods if the read failed. */
+#define ADC_READ_ERROR -1
+
+/* Minimum and maximum values returned by adc_read(). */
+#define ADC_READ_MIN 0
+#define ADC_READ_MAX 4095
+
+/* ADC channels */
+/* TODO: channel mapping is board-specific */
+enum adc_channel {
+ ADC_CH_POT = 0,
+};
+
+/* Initializes the module. */
+int adc_init(void);
+
+/* Reads an ADC channel. Returns the ADC value, or ADC_READ_ERROR if error. */
+int adc_read(enum adc_channel ch);
+
+/* Returns the internal EC temperature in K, or ADC_READ_ERROR if error. */
+int adc_read_ec_temperature(void);
+
+#endif /* __CROS_EC_ADC_H */
diff --git a/include/common.h b/include/common.h
new file mode 100644
index 0000000000..ceb2bc873f
--- /dev/null
+++ b/include/common.h
@@ -0,0 +1,35 @@
+/* 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.
+ */
+
+/* common.h - Common includes for Chrome EC */
+
+#ifndef __CROS_EC_COMMON_H
+#define __CROS_EC_COMMON_H
+
+#include <stdint.h>
+
+/* List of common error codes that can be returned */
+enum ec_error_list {
+ /* Success - no error */
+ EC_SUCCESS = 0,
+ /* Unknown error */
+ EC_ERROR_UNKNOWN = 1,
+ /* Function not implemented yet */
+ EC_ERROR_UNIMPLEMENTED = 2,
+ /* Overflow error; too much input provided. */
+ EC_ERROR_OVERFLOW = 3,
+ /* Timeout */
+ EC_ERROR_TIMEOUT = 4,
+ /* Invalid argument */
+ EC_ERROR_INVAL = 5,
+ /* Already in use */
+ EC_ERROR_BUSY = 6,
+
+ /* Module-internal error codes may use this range. */
+ EC_ERROR_INTERNAL_FIRST = 0x10000,
+ EC_ERROR_INTERNAL_LAST = 0x1FFFF
+};
+
+#endif /* __CROS_EC_COMMON_H */
diff --git a/include/console.h b/include/console.h
new file mode 100644
index 0000000000..f8f76860da
--- /dev/null
+++ b/include/console.h
@@ -0,0 +1,41 @@
+/* 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.h - Debug console for Chrome EC */
+
+#ifndef __CROS_EC_CONSOLE_H
+#define __CROS_EC_CONSOLE_H
+
+#include "common.h"
+
+/* Console command */
+struct console_command {
+ /* Command name. Case-insensitive. */
+ const char *name;
+ /* Handler for the command. argv[0] will be the command name. */
+ int (*handler)(int argc, char **argv);
+};
+
+
+/* Console command group */
+struct console_group {
+ const char *group_name; /* Name of the command group */
+ const struct console_command *commands; /* List of commands */
+ int command_count; /* Number of commands in list */
+};
+
+
+/* Initializes the console module. */
+int console_init(void);
+
+
+/* Called by UART when a line of input is pending. */
+void console_has_input(void);
+
+
+/* Registers a group of console commands. */
+int console_register_commands(const struct console_group *group);
+
+#endif /* __CROS_EC_CONSOLE_H */
diff --git a/include/eeprom.h b/include/eeprom.h
new file mode 100644
index 0000000000..70290e17d4
--- /dev/null
+++ b/include/eeprom.h
@@ -0,0 +1,35 @@
+/* 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.
+ */
+
+/* EEPROM module for Chrome EC */
+
+#ifndef __CROS_EC_EEPROM_H
+#define __CROS_EC_EEPROM_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int eeprom_init(void);
+
+/* Returns the number of EEPROM blocks on the system. */
+int eeprom_get_block_count(void);
+
+/* Returns the EEPROM block size in bytes. */
+int eeprom_get_block_size(void);
+
+/* Reads <size> bytes of data from <offset> in <block> of EEPROM. Offset
+ * and size must be a multiple of 32 bits. */
+int eeprom_read(int block, int offset, int size, char *data);
+
+/* Writes <size> bytes of data to <offset> in <block> of EEPROM. Offset
+ * and size must be a multiple of 32 bits. */
+int eeprom_write(int block, int offset, int size, const char *data);
+
+/* Hides an EEPROM block until the next reset. */
+int eeprom_hide(int block);
+
+/* TODO: write protect */
+
+#endif /* __CROS_EC_EEPROM_H */
diff --git a/include/flash.h b/include/flash.h
new file mode 100644
index 0000000000..38944e43cb
--- /dev/null
+++ b/include/flash.h
@@ -0,0 +1,88 @@
+/* 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 */
+
+#ifndef __CROS_EC_FLASH_H
+#define __CROS_EC_FLASH_H
+
+#include "common.h"
+
+
+#define FLASH_WRITE_BYTES 4
+#define FLASH_FWB_WORDS 32
+#define FLASH_FWB_BYTES (FLASH_FWB_WORDS * 4)
+#define FLASH_ERASE_BYTES 1024
+#define FLASH_PROTECT_BYTES 2048
+
+
+/* Initializes the module. */
+int flash_init(void);
+
+/* Returns the usable size of flash in bytes. Note that this is
+ * smaller than the actual flash size, */
+int flash_get_size(void);
+
+/* Returns the write / erase / protect block size, in bytes.
+ * Operations must be aligned to and multiples of the granularity.
+ * For example, erase operations must have offset and size which are
+ * multiples of the erase block size. */
+int flash_get_write_block_size(void);
+int flash_get_erase_block_size(void);
+int flash_get_protect_block_size(void);
+
+/* Reads <size> bytes of data from offset <offset> into <data>. */
+int flash_read(int offset, int size, char *data);
+
+/* Writes <size> bytes of data to flash at byte offset <offset>.
+ * <data> must be 32-bit aligned. */
+int flash_write(int offset, int size, const char *data);
+
+/* Erases <size> bytes of flash at byte offset <offset>. */
+int flash_erase(int offset, int size);
+
+/* TODO: not super happy about the following APIs yet.
+ *
+ * The theory of operation is that we'll use the last page of flash to
+ * hold the write protect range, and the flag for whether the last
+ * page itself should be protected. Then when flash_init() is called,
+ * it checks if the write protect pin is asserted, and if so, it
+ * writes (but does not commit) the flash protection registers.
+ *
+ * This simulates what a SPI flash does, where the status register
+ * holds the write protect range, and a bit which protects the status
+ * register itself. The bit is only obeyed if the write protect pin
+ * is enabled.
+ *
+ * It's an imperfect simulation, because in a SPI flash, as soon as
+ * you deassert the pin you can alter the status register, where here
+ * it'll take a cold boot to clear protection. Also, here protection
+ * gets written to the registers as soon as you set the write protect
+ * lock, which is different than SPI, where it's effective as soon as
+ * you set the write protect range. */
+
+/* Gets or sets the write protect range in bytes. This setting is
+ * stored in flash, and persists across reboots. If size is non-zero,
+ * the write protect range is also locked, and may not be subsequently
+ * altered until after a cold boot with the write protect pin
+ * deasserted. */
+int flash_get_write_protect_range(int *offset, int *size);
+int flash_set_write_protect_range(int offset, int size);
+
+/* The write protect range has been stored into the chip registers
+ * this boot. The flash is write protected and the range cannot be
+ * changed without rebooting. */
+#define EC_FLASH_WP_RANGE_LOCKED 0x01
+/* The write protect pin was asserted at init time. */
+#define EC_FLASH_WP_PIN_ASSERTED_AT_INIT 0x02
+/* The write protect pin is asserted now. */
+#define EC_FLASH_WP_PIN_ASSERTED_NOW 0x04
+
+/* Returns the current write protect status; see EC_FLASH_WP_*
+ * for valid flags. */
+int flash_get_write_protect_status(void);
+
+
+#endif /* __CROS_EC_FLASH_H */
diff --git a/include/flash_commands.h b/include/flash_commands.h
new file mode 100644
index 0000000000..e74fef1318
--- /dev/null
+++ b/include/flash_commands.h
@@ -0,0 +1,23 @@
+/* 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 commands for Chrome EC */
+
+#ifndef __CROS_EC_FLASH_COMMANDS_H
+#define __CROS_EC_FLASH_COMMANDS_H
+
+#include "common.h"
+#include "lpc_commands.h"
+
+/* Initializes the module. */
+int flash_commands_init(void);
+
+/* Host command handlers. */
+enum lpc_status flash_command_get_info(uint8_t *data);
+enum lpc_status flash_command_read(uint8_t *data);
+enum lpc_status flash_command_write(uint8_t *data);
+enum lpc_status flash_command_erase(uint8_t *data);
+
+#endif /* __CROS_EC_FLASH_COMMANDS_H */
diff --git a/include/gpio.h b/include/gpio.h
new file mode 100644
index 0000000000..05123c4eb6
--- /dev/null
+++ b/include/gpio.h
@@ -0,0 +1,41 @@
+/* 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.
+ */
+
+/* GPIO module for Chrome EC */
+
+#ifndef __CROS_EC_GPIO_H
+#define __CROS_EC_GPIO_H
+
+#include "common.h"
+
+/* GPIO signal definitions. */
+enum gpio_signal {
+ /* Firmware write protect */
+ EC_GPIO_WRITE_PROTECT = 0,
+ /* Recovery switch */
+ EC_GPIO_RECOVERY_SWITCH,
+ /* Debug LED */
+ EC_GPIO_DEBUG_LED
+};
+
+
+/* Pre-initializes the module. This occurs before clocks or tasks are
+ * set up. */
+int gpio_pre_init(void);
+
+/* Initializes the GPIO module. */
+int gpio_init(void);
+
+/* Functions should return an error if the requested signal is not
+ * supported / not present on the board. */
+
+/* Gets the current value of a signal (0=low, 1=hi). */
+int gpio_get(enum gpio_signal signal, int *value_ptr);
+
+/* Sets the current value of a signal. Returns error if the signal is
+ * not supported or is an input signal. */
+int gpio_set(enum gpio_signal signal, int value);
+
+#endif /* __CROS_EC_GPIO_H */
diff --git a/include/host_command.h b/include/host_command.h
new file mode 100644
index 0000000000..fc39683c06
--- /dev/null
+++ b/include/host_command.h
@@ -0,0 +1,19 @@
+/* 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 */
+
+#ifndef __CROS_EC_HOST_COMMAND_H
+#define __CROS_EC_HOST_COMMAND_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int host_command_init(void);
+
+/* Called by LPC module when a command is written to port 66h. */
+void host_command_received(int command);
+
+#endif /* __CROS_EC_HOST_COMMAND_H */
diff --git a/include/i2c.h b/include/i2c.h
new file mode 100644
index 0000000000..34076b1f34
--- /dev/null
+++ b/include/i2c.h
@@ -0,0 +1,29 @@
+/* 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.
+ */
+
+/* I2C interface for Chrome EC */
+
+#ifndef __CROS_EC_I2C_H
+#define __CROS_EC_I2C_H
+
+#include "common.h"
+
+/* Flags for slave address field, in addition to the 8-bit address */
+#define I2C_FLAG_BIG_ENDIAN 0x100 /* 16 byte values are MSB-first */
+
+/* Initializes the module. */
+int i2c_init(void);
+
+/* Reads a 16-bit register from the slave at 8-bit slave address
+ * <slaveaddr>, at the specified 8-bit <offset> in the slave's address
+ * space. */
+int i2c_read16(int port, int slave_addr, int offset, int* data);
+
+/* Writes a 16-bit register to the slave at 8-bit slave address
+ * <slaveaddr>, at the specified 8-bit <offset> in the slave's address
+ * space. */
+int i2c_write16(int port, int slave_addr, int offset, int data);
+
+#endif /* __CROS_EC_I2C_H */
diff --git a/include/keyboard_scan.h b/include/keyboard_scan.h
new file mode 100644
index 0000000000..8bc10c2c17
--- /dev/null
+++ b/include/keyboard_scan.h
@@ -0,0 +1,16 @@
+/* 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.
+ */
+
+/* Keyboard scanner module for Chrome EC */
+
+#ifndef __CROS_EC_KEYBOARD_SCAN_H
+#define __CROS_EC_KEYBOARD_SCAN_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int keyboard_scan_init(void);
+
+#endif /* __CROS_KEYBOARD_SCAN_H */
diff --git a/include/lpc.h b/include/lpc.h
new file mode 100644
index 0000000000..ac0b56c137
--- /dev/null
+++ b/include/lpc.h
@@ -0,0 +1,26 @@
+/* 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.
+ */
+
+/* LPC module for Chrome EC */
+
+#ifndef __CROS_EC_LPC_H
+#define __CROS_EC_LPC_H
+
+#include "common.h"
+
+/* Initializes the LPC module. */
+int lpc_init(void);
+
+/* Returns a pointer to the host command data buffer. This buffer
+ * must only be accessed between a notification to
+ * host_command_received() and a subsequent call to
+ * lpc_SendHostResponse(). */
+volatile uint8_t *lpc_get_host_range(void);
+
+/* Sends a response to a host command. The bottom 4 bits of <status>
+ * are sent in the status byte. */
+void lpc_send_host_response(int status);
+
+#endif /* __CROS_EC_LPC_H */
diff --git a/include/lpc_commands.h b/include/lpc_commands.h
new file mode 100644
index 0000000000..4923db4adb
--- /dev/null
+++ b/include/lpc_commands.h
@@ -0,0 +1,133 @@
+/* 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.
+ */
+
+/* LPC command constants for Chrome EC */
+
+#ifndef __CROS_EC_LPC_COMMANDS_H
+#define __CROS_EC_LPC_COMMANDS_H
+
+#include <stdint.h>
+
+/* LPC command status byte masks */
+/* EC is busy processing a command. This covers both bit 0x04, which
+ * is the busy-bit, and 0x02, which is the bit which indicates the
+ * host has written a byte but the EC hasn't picked it up yet. */
+#define EC_LPC_BUSY_MASK 0x06
+#define EC_LPC_STATUS_MASK 0xF0 /* Mask for status codes in status byte */
+#define EC_LPC_GET_STATUS(x) (((x) & EC_LPC_STATUS_MASK) >> 4)
+
+/* LPC command response codes */
+enum lpc_status {
+ EC_LPC_STATUS_SUCCESS = 0,
+ EC_LPC_STATUS_INVALID_COMMAND = 1,
+ EC_LPC_STATUS_ERROR = 2,
+ EC_LPC_STATUS_INVALID_PARAM = 3,
+};
+
+
+/* Notes on commands:
+ *
+ * Each command is an 8-byte command value. Commands which take
+ * params or return response data specify structs for that data. If
+ * no struct is specified, the command does not input or output data,
+ * respectively. */
+
+/* Reboot. This command will work even when the EC LPC interface is
+ * busy, because the reboot command is processed at interrupt
+ * level. Note that when the EC reboots, the host will reboot too, so
+ * there is no response to this command. */
+#define EC_LPC_COMMAND_REBOOT 0xD1 /* Think "die" */
+
+
+/* Hello. This is a simple command to test the EC is responsive to
+ * commands. */
+#define EC_LPC_COMMAND_HELLO 0x01
+struct lpc_params_hello {
+ uint32_t in_data; /* Pass anything here */
+} __attribute__ ((packed));
+struct lpc_response_hello {
+ uint32_t out_data; /* Output will be in_data + 0x01020304 */
+} __attribute__ ((packed));
+
+
+/* Get version number */
+#define EC_LPC_COMMAND_GET_VERSION 0x02
+enum lpc_current_image {
+ EC_LPC_IMAGE_UNKNOWN = 0,
+ EC_LPC_IMAGE_RO,
+ EC_LPC_IMAGE_RW_A,
+ EC_LPC_IMAGE_RW_B
+};
+struct lpc_response_get_version {
+ /* Null-terminated version strings for RO, RW-A, RW-B */
+ char version_string_ro[32];
+ char version_string_rw_a[32];
+ char version_string_rw_b[32];
+ uint32_t current_image; /* One of lpc_current_image */
+} __attribute__ ((packed));
+
+
+/* Read test */
+#define EC_LPC_COMMAND_READ_TEST 0x03
+struct lpc_params_read_test {
+ uint32_t offset; /* Starting value for read buffer */
+ uint32_t size; /* Size to read in bytes */
+} __attribute__ ((packed));
+struct lpc_response_read_test {
+ uint32_t data[32];
+} __attribute__ ((packed));
+
+/*****************************************************************************/
+/* Flash commands */
+
+/* Maximum bytes that can be read/written in a single command */
+#define EC_LPC_FLASH_SIZE_MAX 128
+
+/* Get flash info */
+#define EC_LPC_COMMAND_FLASH_INFO 0x10
+struct lpc_response_flash_info {
+ /* Usable flash size, in bytes */
+ uint32_t flash_size;
+ /* Write block size. Write offset and size must be a multiple
+ * of this. */
+ uint32_t write_block_size;
+ /* Erase block size. Erase offset and size must be a multiple
+ * of this. */
+ uint32_t erase_block_size;
+ /* Protection block size. Protection offset and size must be a
+ * multiple of this. */
+ uint32_t protect_block_size;
+} __attribute__ ((packed));
+
+
+/* Read flash */
+#define EC_LPC_COMMAND_FLASH_READ 0x11
+struct lpc_params_flash_read {
+ uint32_t offset; /* Byte offset to read */
+ uint32_t size; /* Size to read in bytes */
+} __attribute__ ((packed));
+struct lpc_response_flash_read {
+ uint8_t data[EC_LPC_FLASH_SIZE_MAX];
+} __attribute__ ((packed));
+
+
+/* Write flash */
+#define EC_LPC_COMMAND_FLASH_WRITE 0x12
+struct lpc_params_flash_write {
+ uint32_t offset; /* Byte offset to erase */
+ uint32_t size; /* Size to erase in bytes */
+ uint8_t data[EC_LPC_FLASH_SIZE_MAX];
+} __attribute__ ((packed));
+
+
+/* Erase flash */
+#define EC_LPC_COMMAND_FLASH_ERASE 0x13
+struct lpc_params_flash_erase {
+ uint32_t offset; /* Byte offset to erase */
+ uint32_t size; /* Size to erase in bytes */
+} __attribute__ ((packed));
+
+
+#endif /* __CROS_EC_LPC_COMMANDS_H */
diff --git a/include/port80.h b/include/port80.h
new file mode 100644
index 0000000000..060ee7bbd8
--- /dev/null
+++ b/include/port80.h
@@ -0,0 +1,19 @@
+/* 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 */
+
+#ifndef __CROS_EC_PORT80_H
+#define __CROS_EC_PORT80_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int port_80_init(void);
+
+/* Called by LPC module when a byte of data is written to port 80. */
+void port_80_write(int data);
+
+#endif /* __CROS_EC_PORT80_H */
diff --git a/include/powerdemo.h b/include/powerdemo.h
new file mode 100644
index 0000000000..78acfc095b
--- /dev/null
+++ b/include/powerdemo.h
@@ -0,0 +1,16 @@
+/* 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.
+ */
+
+/* Power state machine demo module for Chrome EC */
+
+#ifndef __CROS_EC_POWERDEMO_H
+#define __CROS_EC_POWERDEMO_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int power_demo_init(void);
+
+#endif /* __CROS_EC_POWERDEMO_H */
diff --git a/include/pwm.h b/include/pwm.h
new file mode 100644
index 0000000000..50534f2434
--- /dev/null
+++ b/include/pwm.h
@@ -0,0 +1,28 @@
+/* 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.
+ */
+
+/* PWM module for Chrome EC */
+
+#ifndef __CROS_EC_PWM_H
+#define __CROS_EC_PWM_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int pwm_init(void);
+
+/* Gets the current fan RPM. */
+int pwm_get_fan_rpm(void);
+
+/* Sets the target fan RPM. Pass -1 to set fan to maximum. */
+int pwm_set_fan_target_rpm(int rpm);
+
+/* Sets the keyboard backlight percentage (0=off, 100=max). */
+int pwm_set_keyboard_backlight(int percent);
+
+/* Sets the power LED brightness to the specified percent (0=off, 100=max). */
+int pwm_set_power_led(int percent);
+
+#endif /* __CROS_EC_PWM_H */
diff --git a/include/shared_mem.h b/include/shared_mem.h
new file mode 100644
index 0000000000..14b6613a9a
--- /dev/null
+++ b/include/shared_mem.h
@@ -0,0 +1,39 @@
+/* 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 interface for Chrome EC.
+ *
+ * This is intended to supply a relatively large block of memory for
+ * use by a task for a relatively short amount of time. For example,
+ * verified boot may need a buffer to hold signature data during a
+ * verification operation. It is NOT intended for allocating
+ * long-term buffers; those should in general be static variables
+ * allocated at compile-time. It is NOT a full-featured replacement
+ * for malloc() / free(). */
+
+#ifndef __CROS_EC_SHARED_MEM_H
+#define __CROS_EC_SHARED_MEM_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int shared_mem_init(void);
+
+/* Returns the maximum amount of shared memory which can be acquired,
+ * in bytes. */
+int shared_mem_size(void);
+
+/* Acquires a shared memory area of the requested size in bytes. If
+ * wait != 0, will wait for the area to be available; if wait == 0,
+ * will fail with EC_ERROR_BUSY if the request cannot be fulfilled
+ * immediately. On success, sets *dest_ptr to the start of the memory
+ * area and returns EC_SUCCESS. */
+int shared_mem_acquire(int size, int wait, char **dest_ptr);
+
+/* Releases a shared memory area previously allocated via
+ * shared_mem_acquire(). */
+void shared_mem_release(void *ptr);
+
+#endif /* __CROS_EC_SHARED_MEM_H */
diff --git a/include/system.h b/include/system.h
new file mode 100644
index 0000000000..46cb5bf730
--- /dev/null
+++ b/include/system.h
@@ -0,0 +1,100 @@
+/* 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.
+ */
+
+/* System module for Chrome EC */
+
+#ifndef __CROS_EC_SYSTEM_H
+#define __CROS_EC_SYSTEM_H
+
+#include "common.h"
+
+/* Reset causes */
+enum system_reset_cause_t {
+ /* Unknown reset cause */
+ SYSTEM_RESET_UNKNOWN = 0,
+ /* System reset cause is known, but not one of the causes
+ * listed below */
+ SYSTEM_RESET_OTHER,
+ /* Brownout */
+ SYSTEM_RESET_BROWNOUT,
+ /* Power-on reset */
+ SYSTEM_RESET_POWER_ON,
+ /* Reset caused by asserting reset (RST#) pin */
+ SYSTEM_RESET_RESET_PIN,
+ /* Software requested cold reset */
+ SYSTEM_RESET_SOFT_COLD,
+ /* Software requested warm reset */
+ SYSTEM_RESET_SOFT_WARM,
+ /* Watchdog timer reset */
+ SYSTEM_RESET_WATCHDOG,
+ /* the RTC alarm triggered power on */
+ SYSTEM_RESET_RTC_ALARM,
+ /* the Wake pin triggered power on */
+ SYSTEM_RESET_WAKE_PIN,
+ /* the low battery detection triggered power on */
+ SYSTEM_RESET_LOW_BATTERY,
+};
+
+/* System images */
+enum system_image_copy_t {
+ SYSTEM_IMAGE_UNKNOWN = 0,
+ SYSTEM_IMAGE_RO,
+ SYSTEM_IMAGE_RW_A,
+ SYSTEM_IMAGE_RW_B
+};
+
+/* Pre-initializes the module. This occurs before clocks or tasks are
+ * set up. */
+int system_pre_init(void);
+
+/* Initializes the system module. */
+int system_init(void);
+
+/* Returns the cause of the last reset, or SYSTEM_RESET_UNKNOWN if
+ * the cause is not known. */
+enum system_reset_cause_t system_get_reset_cause(void);
+
+/* Returns a text description of the last reset cause. */
+const char *system_get_reset_cause_string(void);
+
+/* Returns the image copy which is currently running. */
+enum system_image_copy_t system_get_image_copy(void);
+
+/* Returns a text description of the image copy which is currently running. */
+const char *system_get_image_copy_string(void);
+
+/* Jumps to the specified image copy. Only works from RO firmware. */
+int system_run_image_copy(enum system_image_copy_t copy);
+
+/* Returns the version string for an image copy, or an empty string if
+ * error. If copy==SYSTEM_IMAGE_UNKNOWN, returns the version for the
+ * currently-running image. */
+const char *system_get_version(enum system_image_copy_t copy);
+
+/* Resets the system. If is_cold!=0, performs a cold reset (which
+ * resets on-chip peripherals); else performs a warm reset (which does
+ * not reset on-chip peripherals). If successful, does not return.
+ * Returns error if the reboot type cannot be requested (e.g. brownout
+ * or reset pin). */
+int system_reset(int is_cold);
+
+/* Sets a scratchpad register to the specified value. The scratchpad
+ * register must maintain its contents across a software-requested
+ * warm reset. */
+int system_set_scratchpad(uint32_t value);
+
+/* Returns the current scratchpad register value. */
+uint32_t system_get_scratchpad(void);
+
+/* TODO: request sleep. How do we want to handle transitioning
+ * to/from low-power states? */
+
+/* put the system in hibernation for the specified duration */
+void system_hibernate(uint32_t seconds, uint32_t microseconds);
+
+/* minimum duration to get proper hibernation */
+#define SYSTEM_HIB_MINIMUM_DURATION 0, 1000
+
+#endif /* __CROS_EC_SYSTEM_H */
diff --git a/include/temp_sensor.h b/include/temp_sensor.h
new file mode 100644
index 0000000000..863f9d7d51
--- /dev/null
+++ b/include/temp_sensor.h
@@ -0,0 +1,26 @@
+/* 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.
+ */
+
+/* Temperature sensor module for Chrome EC */
+
+#ifndef __CROS_EC_TEMP_SENSOR_H
+#define __CROS_EC_TEMP_SENSOR_H
+
+#include "common.h"
+
+enum temp_sensor_id {
+ TEMP_SENSOR_CASE = 0, /* Case temperature */
+ TEMP_SENSOR_CASE_DIE, /* Case temperature sensor die */
+ TEMP_SENSOR_EC_INTERNAL, /* EC internal temperature sensor */
+};
+
+/* Initializes the module. */
+int temp_sensor_init(void);
+
+/* Returns the most recently measured temperature for the sensor in K,
+ * or -1 if error. */
+int temp_sensor_read(enum temp_sensor_id id);
+
+#endif /* __CROS_EC_TEMP_SENSOR_H */
diff --git a/include/uart.h b/include/uart.h
new file mode 100644
index 0000000000..12ecf7a1a6
--- /dev/null
+++ b/include/uart.h
@@ -0,0 +1,112 @@
+/* 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.
+ */
+
+/* uart.h - UART module for Chrome EC */
+
+#ifndef __CROS_EC_UART_H
+#define __CROS_EC_UART_H
+
+#include "common.h"
+
+
+/* Initializes the UART module. */
+int uart_init(void);
+
+
+/* Enables console mode if <enable>!=0. In console mode:
+ * - Input is echoed
+ * - Input CRLF and CR are translated to LF
+ * - Input backspace will remove characters from the input buffer (which
+ * is pretty much only useful if the input handler is only triggered on
+ * newline)
+ * - Output LF is translated to CRLF */
+void uart_set_console_mode(int enable);
+
+/*****************************************************************************/
+/* Output functions
+ *
+ * Output is buffered. If the buffer overflows, subsequent output is
+ * discarded. */
+
+/* Put a null-terminated string to the UART, like fputs().
+ *
+ * Returns error if output was truncated. */
+int uart_puts(const char *outstr);
+
+/* Print formatted output to the UART, like printf().
+ *
+ * Returns error if output was truncated.
+ *
+ * Must support format strings for:
+ * char (%c)
+ * string (%s)
+ * native int (signed/unsigned) (%d / %u / %x)
+ * int32_t / uint32_t (%d / %x)
+ * int64_t / uint64_t (%ld / %lu / %lx)
+ * pointer (%p)
+ * including padding (%-5s, %8d, %08x)
+ *
+ * Note: Floating point output (%f / %g) is not required.
+ */
+int uart_printf(const char *format, ...);
+
+/* Flushes output. Blocks until UART has transmitted all output. */
+void uart_flush_output(void);
+
+/* Flushes output.
+ *
+ * Blocks until UART has transmitted all output,
+ * even if we are in high priority interrupt context
+ */
+void uart_emergency_flush(void);
+
+/*****************************************************************************/
+/* Input functions
+ *
+ * Input is buffered. If the buffer overflows, the oldest input in
+ * the buffer is discarded to make room for the new input.
+ *
+ * Input lines may be terminated by CR ('\r'), LF ('\n'), or CRLF; all
+ * are translated to newline. */
+
+/* Flushes input buffer, discarding all input. */
+void uart_flush_input(void);
+
+/* Non-destructively checks for a character in the input buffer.
+ *
+ * Returns the offset into the input buffer of character <c>, or -1 if
+ * it is not in the input buffer. */
+int uart_peek(int c);
+
+/* Reads a single character of input, similar to fgetc(). Returns the
+ * character, or -1 if no input waiting. */
+int uart_getc(void);
+
+/* Reads characters from the UART, similar to fgets().
+ *
+ * Reads input until one of the following conditions is met:
+ * (1) <size-1> characters have been read.
+ * (2) A newline ('\n') has been read.
+ * (3) The input buffer is empty.
+ *
+ * Condition (3) means this call never blocks. This is important
+ * because it prevents a race condition where the caller calls
+ * UartPeek() to see if input is waiting, or is notified by the
+ * callack that input is waiting, but then the input buffer overflows
+ * or someone else grabs the input before UartGets() is called.
+ *
+ * Characters are stored in <dest> and are null-terminated.
+ * Characters include the newline if present, so that the caller can
+ * distinguish between a complete line and a truncated one. If the
+ * input buffer is empty, a null-terminated empty string ("") is
+ * returned.
+ *
+ * Returns the number of characters read (not counting the terminating
+ * null). */
+int uart_gets(char *dest, int size);
+
+/* TODO: getc(), putc() equivalents? */
+
+#endif /* __CROS_EC_UART_H */
diff --git a/include/vboot.h b/include/vboot.h
new file mode 100644
index 0000000000..430c617a52
--- /dev/null
+++ b/include/vboot.h
@@ -0,0 +1,20 @@
+/* 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 */
+
+#ifndef __CROS_EC_VBOOT_H
+#define __CROS_EC_VBOOT_H
+
+#include "common.h"
+
+/* Pre-initializes the module. This occurs before clocks or tasks are
+ * set up. */
+int vboot_pre_init(void);
+
+/* Initializes the module. */
+int vboot_init(void);
+
+#endif /* __CROS_EC_VBOOT_H */
diff --git a/include/version.h b/include/version.h
new file mode 100644
index 0000000000..0031339c34
--- /dev/null
+++ b/include/version.h
@@ -0,0 +1,16 @@
+/* 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.
+ */
+
+/* Version number for Chrome EC */
+
+#ifndef __CROS_EC_VERSION_H
+#define __CROS_EC_VERSION_H
+
+#define CROS_EC_VERSION_MAJOR 0
+#define CROS_EC_VERSION_MINOR 1
+#define CROS_EC_VERSION_SUBMINOR 2
+#define CROS_EC_VERSION_STRING "Link.0.1.2"
+
+#endif /* __CROS_EC_VERSION_H */
diff --git a/include/x86_power.h b/include/x86_power.h
new file mode 100644
index 0000000000..3343b237e2
--- /dev/null
+++ b/include/x86_power.h
@@ -0,0 +1,16 @@
+/* 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.
+ */
+
+/* x86 power module for Chrome EC */
+
+#ifndef __CROS_EC_X86_POWER_H
+#define __CROS_EC_X86_POWER_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int x86_power_init(void);
+
+#endif /* __CROS_EC_X86_POWER_H */
diff --git a/util/ectool.c b/util/ectool.c
new file mode 100644
index 0000000000..42820a1f58
--- /dev/null
+++ b/util/ectool.c
@@ -0,0 +1,465 @@
+/* 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/io.h>
+#include <unistd.h>
+
+#include "lpc_commands.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define MIN(a, b) (a < b ? a : b);
+
+#define EC_LPC_CMD_ADDR 0x66
+#define EC_LPC_DATA_ADDR 0x800
+#define EC_LPC_DATA_SIZE 512
+
+const char help_str[] =
+ "Commands:\n"
+ " flashinfo\n"
+ " Prints information on the EC flash\n"
+ " flashread <offset> <size> <outfile>\n"
+ " Reads from EC flash to a file\n"
+ " flashwrite <offset> <infile>\n"
+ " Writes to EC flash from a file\n"
+ " flasherase <offset> <size>\n"
+ " Erases EC flash\n"
+ " hello\n"
+ " Checks for basic communication with EC\n"
+ " readtest <patternoffset> <size>\n"
+ " Reads a pattern from the EC via LPC\n"
+ " sertest\n"
+ " Serial output test for COM2\n"
+ " version\n"
+ " Prints EC version\n"
+ "";
+
+
+
+/* Waits for the EC to be unbusy. Returns 0 if unbusy, non-zero if
+ * timeout. */
+int wait_for_ec(int timeout_usec)
+{
+ int i;
+ for (i = 0; i < timeout_usec; i += 10) {
+ usleep(10); /* Delay first, in case we just sent a command */
+ if (!(inb(EC_LPC_CMD_ADDR) & EC_LPC_BUSY_MASK))
+ return 0;
+ }
+ return -1; /* Timeout */
+}
+
+
+/* Sends a command to the EC. Returns the command status code, or
+ * -1 if other error. */
+int ec_command(int command, const void *indata, int insize,
+ void *outdata, int outsize) {
+ uint8_t *d;
+ int i;
+
+ outb(0x42, 0x80);
+
+ if (insize > EC_LPC_DATA_SIZE || outsize > EC_LPC_DATA_SIZE) {
+ fprintf(stderr, "Data size too big\n");
+ return -1;
+ }
+
+ if (wait_for_ec(1000000)) {
+ fprintf(stderr, "Timeout waiting for EC ready\n");
+ return -1;
+ }
+
+ /* Write data, if any */
+ /* TODO: optimized copy using outl() */
+ for (i = 0, d = (uint8_t *)indata; i < insize; i++, d++)
+ outb(*d, EC_LPC_DATA_ADDR + i);
+
+ outb(command, EC_LPC_CMD_ADDR);
+
+ if (wait_for_ec(1000000)) {
+ fprintf(stderr, "Timeout waiting for EC response\n");
+ return -1;
+ }
+
+ /* Check status */
+ i = inb(EC_LPC_CMD_ADDR);
+ i = EC_LPC_GET_STATUS(i);
+ if (i) {
+ fprintf(stderr, "EC returned error status %d\n", i);
+ return i;
+ }
+
+ /* Read data, if any */
+ for (i = 0, d = (uint8_t *)outdata; i < outsize; i++, d++)
+ *d = inb(EC_LPC_DATA_ADDR + i);
+
+ return 0;
+}
+
+
+void print_help(const char *prog)
+{
+ printf("Usage: %s <command> [params]\n\n", prog);
+ puts(help_str);
+}
+
+
+int cmd_hello(void)
+{
+ struct lpc_params_hello p;
+ struct lpc_response_hello r;
+ int rv;
+
+ p.in_data = 0xa0b0c0d0;
+
+ rv = ec_command(EC_LPC_COMMAND_HELLO, &p, sizeof(p), &r, sizeof(r));
+ if (rv)
+ return rv;
+
+ if (r.out_data != 0xa1b2c3d4) {
+ fprintf(stderr, "Expected response 0x%08x, got 0x%08x\n",
+ 0xa1b2c3d4, r.out_data);
+ return -1;
+ }
+
+ printf("EC says hello!\n");
+ return 0;
+}
+
+
+int cmd_version(void)
+{
+ static const char * const fw_copies[] = {"unknown", "RO", "A", "B"};
+ struct lpc_response_get_version r;
+ int rv;
+
+ rv = ec_command(EC_LPC_COMMAND_GET_VERSION, NULL, 0, &r, sizeof(r));
+ if (rv)
+ return rv;
+
+ /* Ensure versions are null-terminated before we print them */
+ r.version_string_ro[sizeof(r.version_string_ro) - 1] = '\0';
+ r.version_string_rw_a[sizeof(r.version_string_rw_a) - 1] = '\0';
+ r.version_string_rw_b[sizeof(r.version_string_rw_b) - 1] = '\0';
+
+ /* Print versions */
+ printf("RO version: %s\n", r.version_string_ro);
+ printf("RW-A version: %s\n", r.version_string_rw_a);
+ printf("RW-B version: %s\n", r.version_string_rw_b);
+ printf("Firmware copy: %s\n",
+ (r.current_image < ARRAY_SIZE(fw_copies) ?
+ fw_copies[r.current_image] : "?"));
+ return 0;
+}
+
+
+int cmd_read_test(int argc, char *argv[])
+{
+ struct lpc_params_read_test p;
+ struct lpc_response_read_test r;
+ int offset, size;
+ int errors = 0;
+ int rv;
+ int i;
+ char *e;
+ char *buf;
+ uint32_t *b;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: readtest <pattern_offset> <size>\n");
+ return -1;
+ }
+ offset = strtol(argv[0], &e, 0);
+ size = strtol(argv[1], &e, 0);
+ if ((e && *e) || size <= 0 || size > 0x100000) {
+ fprintf(stderr, "Bad size.\n");
+ return -1;
+ }
+ printf("Reading %d bytes with pattern offset 0x%x...\n", size, offset);
+
+ buf = (char *)malloc(size);
+ if (!buf) {
+ fprintf(stderr, "Unable to allocate buffer.\n");
+ return -1;
+ }
+
+ /* Read data in chunks */
+ for (i = 0; i < size; i += sizeof(r.data)) {
+ p.offset = offset + i / sizeof(uint32_t);
+ p.size = MIN(size - i, sizeof(r.data));
+ rv = ec_command(EC_LPC_COMMAND_READ_TEST, &p, sizeof(p),
+ &r, sizeof(r));
+ if (rv) {
+ fprintf(stderr, "Read error at offset %d\n", i);
+ free(buf);
+ return -1;
+ }
+ memcpy(buf + i, r.data, p.size);
+ }
+
+ /* Check data */
+ for (i = 0, b = (uint32_t *)buf; i < size / 4; i++, b++) {
+ if (*b != i + offset) {
+ printf("Mismatch at byte offset 0x%x: "
+ "expected 0x%08x, got 0x%08x\n",
+ (int)(i * sizeof(uint32_t)), i + offset, *b);
+ errors++;
+ }
+ }
+
+ free(buf);
+ if (errors) {
+ printf("Found %d errors\n", errors);
+ return -1;
+ }
+
+ printf("done.\n");
+ return 0;
+}
+
+
+int cmd_flash_info(void)
+{
+ struct lpc_response_flash_info r;
+ int rv;
+
+ rv = ec_command(EC_LPC_COMMAND_FLASH_INFO, NULL, 0, &r, sizeof(r));
+ if (rv)
+ return rv;
+
+ printf("FlashSize %d\nWriteSize %d\nEraseSize %d\nProtectSize %d\n",
+ r.flash_size, r.write_block_size, r.erase_block_size,
+ r.protect_block_size);
+
+ return 0;
+}
+
+
+int cmd_flash_read(int argc, char *argv[])
+{
+ struct lpc_params_flash_read p;
+ struct lpc_response_flash_read r;
+ int offset, size;
+ int rv;
+ int i;
+ char *e;
+ char *buf;
+ FILE *f;
+
+ if (argc < 3) {
+ fprintf(stderr,
+ "Usage: flashread <offset> <size> <filename>\n");
+ return -1;
+ }
+ offset = strtol(argv[0], &e, 0);
+ if ((e && *e) || offset < 0 || offset > 0x100000) {
+ fprintf(stderr, "Bad offset.\n");
+ return -1;
+ }
+ size = strtol(argv[1], &e, 0);
+ if ((e && *e) || size <= 0 || size > 0x100000) {
+ fprintf(stderr, "Bad size.\n");
+ return -1;
+ }
+ printf("Reading %d bytes at offset %d...\n", size, offset);
+
+ buf = (char *)malloc(size);
+ if (!buf) {
+ fprintf(stderr, "Unable to allocate buffer.\n");
+ return -1;
+ }
+
+ /* Read data in chunks */
+ for (i = 0; i < size; i += EC_LPC_FLASH_SIZE_MAX) {
+ p.offset = offset + i;
+ p.size = MIN(size - i, EC_LPC_FLASH_SIZE_MAX);
+ rv = ec_command(EC_LPC_COMMAND_FLASH_READ,
+ &p, sizeof(p), &r, sizeof(r));
+ if (rv) {
+ fprintf(stderr, "Read error at offset %d\n", i);
+ free(buf);
+ return -1;
+ }
+ memcpy(buf + i, r.data, p.size);
+ }
+
+ /* Write to file */
+ f = fopen(argv[2], "wb");
+ if (!f) {
+ perror("Error opening output file");
+ free(buf);
+ return -1;
+ }
+ i = fwrite(buf, 1, size, f);
+ fclose(f);
+ free(buf);
+ if (i != size) {
+ perror("Error writing to file");
+ return -1;
+ }
+ printf("done.\n");
+ return 0;
+}
+
+
+int cmd_flash_write(int argc, char *argv[])
+{
+ struct lpc_params_flash_write p;
+ int offset, size;
+ int rv;
+ int i;
+ char *e;
+ char *buf;
+ FILE *f;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: flashwrite <offset> <filename>\n");
+ return -1;
+ }
+ offset = strtol(argv[0], &e, 0);
+ if ((e && *e) || offset < 0 || offset > 0x100000) {
+ fprintf(stderr, "Bad offset.\n");
+ return -1;
+ }
+
+ /* Read the input file */
+ f = fopen(argv[1], "rb");
+ if (!f) {
+ perror("Error opening input file");
+ return -1;
+ }
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ rewind(f);
+ if (size > 0x100000) {
+ fprintf(stderr, "File seems unreasonably large\n");
+ fclose(f);
+ return -1;
+ }
+
+ buf = (char *)malloc(size);
+ if (!buf) {
+ fprintf(stderr, "Unable to allocate buffer.\n");
+ fclose(f);
+ return -1;
+ }
+
+ printf("Reading %d bytes from %s...\n", size, argv[1]);
+ i = fread(buf, 1, size, f);
+ if (i != size) {
+ perror("Error reading file");
+ free(buf);
+ return -1;
+ }
+
+ printf("Writing to offset %d...\n", offset);
+
+ /* Write data in chunks */
+ for (i = 0; i < size; i += EC_LPC_FLASH_SIZE_MAX) {
+ p.offset = offset + i;
+ p.size = MIN(size - i, EC_LPC_FLASH_SIZE_MAX);
+ memcpy(p.data, buf + i, p.size);
+ rv = ec_command(EC_LPC_COMMAND_FLASH_WRITE,
+ &p, sizeof(p), NULL, 0);
+ if (rv) {
+ fprintf(stderr, "Write error at offset %d\n", i);
+ free(buf);
+ return -1;
+ }
+ }
+
+ free(buf);
+ printf("done.\n");
+ return 0;
+}
+
+
+int cmd_flash_erase(int argc, char *argv[])
+{
+ struct lpc_params_flash_erase p;
+ char *e;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: flasherase <offset> <size>\n");
+ return -1;
+ }
+ p.offset = strtol(argv[0], &e, 0);
+ if ((e && *e) || p.offset < 0 || p.offset > 0x100000) {
+ fprintf(stderr, "Bad offset.\n");
+ return -1;
+ }
+ p.size = strtol(argv[1], &e, 0);
+ if ((e && *e) || p.size <= 0 || p.size > 0x100000) {
+ fprintf(stderr, "Bad size.\n");
+ return -1;
+ }
+
+ printf("Erasing %d bytes at offset %d...\n", p.size, p.offset);
+ if (ec_command(EC_LPC_COMMAND_FLASH_ERASE, &p, sizeof(p), NULL, 0))
+ return -1;
+
+ printf("done.\n");
+ return 0;
+}
+
+
+int cmd_serial_test(int argc, char *argv[])
+{
+ const char *c = "COM2 sample serial output from host!\r\n";
+
+ printf("Writing sample serial output to COM2\n");
+
+ while (*c) {
+ /* Wait for space in transmit FIFO */
+ while (!(inb(0x2fd) & 0x20)) {}
+
+ /* Put the next character */
+ outb(*c++, 0x2f8);
+ }
+
+ printf("done.\n");
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2 || !strcasecmp(argv[1], "-?") ||
+ !strcasecmp(argv[1], "help")) {
+ print_help(argv[0]);
+ return -2;
+ }
+
+ /* Request I/O privilege */
+ if (iopl(3) < 0) {
+ perror("Error getting I/O privilege");
+ return -3;
+ }
+
+ /* Handle commands */
+ if (!strcasecmp(argv[1], "flashinfo"))
+ return cmd_flash_info();
+ if (!strcasecmp(argv[1], "flashread"))
+ return cmd_flash_read(argc - 2, argv + 2);
+ if (!strcasecmp(argv[1], "flashwrite"))
+ return cmd_flash_write(argc - 2, argv + 2);
+ if (!strcasecmp(argv[1], "flasherase"))
+ return cmd_flash_erase(argc - 2, argv + 2);
+ if (!strcasecmp(argv[1], "hello"))
+ return cmd_hello();
+ if (!strcasecmp(argv[1], "readtest"))
+ return cmd_read_test(argc - 2, argv + 2);
+ if (!strcasecmp(argv[1], "sertest"))
+ return cmd_serial_test(argc - 2, argv + 2);
+ if (!strcasecmp(argv[1], "version"))
+ return cmd_version();
+
+ /* If we're still here, command was unknown */
+ fprintf(stderr, "Unknown command '%s'\n\n", argv[1]);
+ print_help(argv[0]);
+ return -2;
+}