Merge development work from private blizzard.git repo.

BUG=chrome-os-partner:7564
TEST=None

Conflicts:
	.gitignore
diff --git a/.gitignore b/.gitignore
index 0aa56f6..440fd12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
-board/*/
 build/
-vendor/
+*.map
+*.bin
+*.elf
+*.swp
+*.pyc
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..076f743
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,48 @@
+# 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.
+#
+# Embedded Controller firmware build system
+#
+
+BOARD ?= bds
+
+PROJECT?=ec
+
+# output directory for build objects
+out?=build/$(BOARD)
+
+include Makefile.toolchain
+
+# Get CHIP name
+include board/$(BOARD)/build.mk
+
+# Transform the configuration into make variables
+_tsk_lst:=$(shell echo "CONFIG_TASK_LIST" | $(CPP) -P -Iboard/$(BOARD) -Itest \
+	  -D"TASK(n, r, d)=n" -imacros $(PROJECT).tasklist)
+_tsk_cfg:=$(foreach t,$(_tsk_lst),CONFIG_TASK_$(t))
+_flag_cfg:=$(shell $(CPP) -P -dN chip/$(CHIP)/config.h | grep -o "CONFIG_.*") \
+	   $(shell $(CPP) -P -dN board/$(BOARD)/board.h | grep -o "CONFIG_.*")
+$(foreach c,$(_tsk_cfg) $(_flag_cfg),$(eval $(c)=y))
+CPPFLAGS+=$(foreach t,$(_tsk_cfg),-D$(t))
+
+# Get build configuration from sub-directories
+include board/$(BOARD)/build.mk
+include chip/$(CHIP)/build.mk
+include core/$(CORE)/build.mk
+include common/build.mk
+include test/build.mk
+include util/build.mk
+
+objs_from_dir=$(foreach obj,$(2), $(out)/$(1)/$(obj))
+
+# Get all sources to build
+all-y=$(call objs_from_dir,core/$(CORE),$(core-y))
+all-y+=$(call objs_from_dir,chip/$(CHIP),$(chip-y))
+all-y+=$(call objs_from_dir,board/$(BOARD),$(board-y))
+all-y+=$(call objs_from_dir,common,$(common-y))
+all-y+=$(call objs_from_dir,test,$($(PROJECT)-y))
+dirs=core/$(CORE) chip/$(CHIP) board/$(BOARD) common test util
+includes=include $(dirs)
+
+include Makefile.rules
diff --git a/Makefile.rules b/Makefile.rules
new file mode 100644
index 0000000..b92e9a6
--- /dev/null
+++ b/Makefile.rules
@@ -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.
+#
+# Embedded Controller firmware build system - common targets
+#
+
+objs := $(all-y)
+deps := $(objs:%.o=%.o.d)
+build-utils := $(foreach u,$(build-util-bin),$(out)/util/$(u))
+host-utils := $(foreach u,$(host-util-bin),$(out)/util/$(u))
+
+# Create output directories if necessary
+_dir_create := $(foreach d,$(dirs),$(shell [ -d $(out)/$(d) ] || \
+	    mkdir -p $(out)/$(d)))
+
+section = $(subst .,,$(suffix $(1)))
+
+# Decrease verbosity unless you pass V=1
+quiet = $(if $(V),,@echo '  $(2)' $(subst $(out)/,,$@) ; )$(cmd_$(1))
+silent = $(if $(V),,1>/dev/null)
+
+# commands to build all targets
+cmd_lds = $(CPP) -P -C -MMD -MF $@.d -MT $@ $(CPPFLAGS) \
+          -DSECTION=$(call section,$*) $< -o $@
+cmd_obj_to_bin = $(OBJCOPY) --gap-fill=0xff -O binary $^ $@
+cmd_flat_to_obj = $(CC) -T $(out)/firmware_image.lds -nostdlib $(CPPFLAGS) \
+                  -DPROJECT=$* -Wl,--build-id=none -o $@ $<
+cmd_elf_to_flat = $(OBJCOPY) -O binary $^ $@
+cmd_elf_to_dis = $(OBJDUMP) -D $< > $@
+cmd_elf = $(LD) $(LDFLAGS) $(objs) -o $@ -T $< -Map $(out)/$*.map
+cmd_c_to_o = $(CC) $(CFLAGS) -MMD -MF $@.d -c $< -o $@
+cmd_c_to_build = $(BUILDCC) $(BUILD_CFLAGS) $(BUILD_LDFLAGS) \
+	         -MMD -MF $@.d $< -o $@
+cmd_c_to_host = $(HOSTCC) $(HOST_CFLAGS) -MMD -MF $@.d $< -o $@
+cmd_qemu = ./util/run_qemu_test --image=build/$(BOARD)/$*/$*.bin test/$*.py \
+	   $(silent)
+
+
+.PHONY: all tests utils
+all: $(out)/$(PROJECT).bin $(foreach s,A B RO,$(out)/$(PROJECT).$(s).dis) utils
+
+utils: $(build-utils) $(host-utils)
+
+test-targets=$(foreach t,$(test-list),test-$(t))
+qemu-test-targets=$(foreach t,$(test-list),qemu-$(t))
+.PHONY: $(qemu-test-target) $(test-targets)
+
+$(test-targets): test-%:
+	@set -e ; \
+	echo "  BUILD   $(out)/$*" ; \
+	$(MAKE) --no-print-directory BOARD=$(BOARD) PROJECT=$* \
+	        V=$(V) out=$(out)/$*
+
+$(qemu-test-targets): qemu-%: test-%
+	$(call quiet,qemu,TEST   )
+
+tests: $(test-targets)
+qemu-tests: $(qemu-test-targets)
+
+$(out)/firmware_image.lds: common/firmware_image.lds.S
+	$(call quiet,lds,LDS    )
+$(out)/%.lds: core/$(CORE)/ec.lds.S
+	$(call quiet,lds,LDS    )
+
+$(out)/%.bin: $(out)/%.obj
+	$(call quiet,obj_to_bin,OBJCOPY)
+
+$(out)/%.obj: common/firmware_image.S $(out)/firmware_image.lds \
+              $(out)/%.RO.flat $(out)/%.A.flat $(out)/%.B.flat
+	$(call quiet,flat_to_obj,CAT    )
+
+$(out)/%.dis: $(out)/%.elf
+	$(call quiet,elf_to_dis,OBJDUMP)
+
+$(out)/%.flat: $(out)/%.elf
+	$(call quiet,elf_to_flat,OBJCOPY)
+
+$(out)/%.elf: $(out)/%.lds $(objs)
+	$(call quiet,elf,LD     )
+
+$(out)/%.o:%.c
+	$(call quiet,c_to_o,CC     )
+
+$(out)/%.o:%.S
+	$(call quiet,c_to_o,AS     )
+
+$(build-utils): $(out)/%:%.c
+	$(call quiet,c_to_build,BUILDCC)
+
+$(host-utils): $(out)/%:%.c
+	$(call quiet,c_to_host,HOSTCC )
+
+.PHONY: clean
+clean:
+	-rm -rf $(out)
+
+.SECONDARY:
+
+-include $(deps)
diff --git a/Makefile.toolchain b/Makefile.toolchain
new file mode 100644
index 0000000..6f75b65
--- /dev/null
+++ b/Makefile.toolchain
@@ -0,0 +1,34 @@
+# 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.
+#
+# Toolchain configuration build system
+#
+
+# Toolchain configuration
+CROSS_COMPILE ?= armv7a-cros-linux-gnueabi-
+HOST_CROSS_COMPILE ?= i686-pc-linux-gnu-
+
+CC=$(CROSS_COMPILE)gcc
+CPP=$(CROSS_COMPILE)cpp
+LD=$(CROSS_COMPILE)ld
+OBJCOPY=$(CROSS_COMPILE)objcopy
+OBJDUMP=$(CROSS_COMPILE)objdump
+BUILDCC?=gcc
+HOSTCC?=$(HOST_CROSS_COMPILE)gcc
+
+CFLAGS_WARN=-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs              \
+            -fno-strict-aliasing -fno-common                              \
+            -Werror-implicit-function-declaration -Wno-format-security    \
+            -fno-delete-null-pointer-checks -Wdeclaration-after-statement \
+            -Wno-pointer-sign -fno-strict-overflow -fconserve-stack
+CFLAGS_DEBUG= -g
+CFLAGS_INCLUDE=$(foreach i,$(includes),-I$(i) )
+CFLAGS_DEFINE=-DOUTDIR=$(out) -DCHIP=$(CHIP) -DTASKFILE=$(PROJECT).tasklist \
+              -DBOARD=$(BOARD) -DBOARD_$(BOARD) -DCORE=$(CORE)
+CPPFLAGS=$(CFLAGS_DEFINE) $(CFLAGS_INCLUDE)
+CFLAGS=$(CPPFLAGS) $(CFLAGS_CPU) $(CFLAGS_DEBUG) $(CFLAGS_WARN)
+BUILD_CFLAGS=$(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN)
+HOST_CFLAGS=$(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN)
+LDFLAGS=-nostdlib -X
+BUILD_LDFLAGS=-lftdi
diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg
new file mode 100644
index 0000000..2e8cdb6
--- /dev/null
+++ b/PRESUBMIT.cfg
@@ -0,0 +1,6 @@
+[Hook Overrides]
+
+# We are using Linux style indentation with tabs
+# The indentation is checked by checkpatch not the python script
+tab_check: false
+
diff --git a/README b/README
new file mode 100644
index 0000000..9f8a5c3
--- /dev/null
+++ b/README
@@ -0,0 +1,26 @@
+- EC Lib
+
+This wraps Blizzard driverlib and implements the EC chip interface defined
+by Google. See below diagram for architecture.
+
+
+  +--------------------+
+  |   Host BIOS/OS     |
+  +--------------------+
+
+ ---- host interface ----
+
+  +--------------------+
+  | Google EC features |
+  +--------------------+
+
+ ---- chip interface ----  The interface is defined in
+                           src/platform/ec/chip_interface/*.
+  +--------------------+   But the real implementation is in EC Lib.
+  |       EC Lib       |
+  +--------------------+
+  | Blizzard low level |
+  |   driver, the      |
+  |   driverlib.       |
+  +--------------------+
+
diff --git a/board/bds/board.c b/board/bds/board.c
new file mode 100644
index 0000000..2e2a191
--- /dev/null
+++ b/board/bds/board.c
@@ -0,0 +1,76 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* Stellaris EKB-LM4F-EAC board-specific configuration */
+
+#include "board.h"
+#include "gpio.h"
+#include "power_button.h"
+#include "registers.h"
+#include "util.h"
+#include "lm4_adc.h"
+#include "adc.h"
+
+/* ADC channels. Must be in the exactly same order as in enum adc_channel. */
+const struct adc_t adc_channels[ADC_CH_COUNT] =
+{
+	/* EC internal temperature is calculated by
+	 * 273 + (295 - 450 * ADC_VALUE / ADC_READ_MAX) / 2
+	 * = -225 * ADC_VALUE / ADC_READ_MAX + 420.5
+	 */
+	{"ECTemp", LM4_ADC_SEQ0, -225, ADC_READ_MAX, 420,
+	 LM4_AIN_NONE, 0x0e /* TS0 | IE0 | END0 */},
+
+	/* Charger current is mapped from 0~4000mA to 0~1.6V.
+	 * And ADC maps 0~3.3V to ADC_READ_MAX.
+	 */
+	{"ChargerCurrent", LM4_ADC_SEQ1, 33 * 4000, ADC_READ_MAX * 16, 0,
+	 LM4_AIN(ADC_IN0), 0x06 /* IE0 | END0 */},
+};
+
+
+/* GPIO signal list.  Must match order from enum gpio_signal. */
+const struct gpio_info gpio_list[GPIO_COUNT] = {
+	/* Inputs with interrupt handlers are first for efficiency */
+	{"POWER_BUTTONn", LM4_GPIO_C, (1<<5), GPIO_PULL_UP | GPIO_INT_BOTH,
+	 power_button_interrupt},
+	{"LID_SWITCHn",   LM4_GPIO_D, (1<<0), GPIO_PULL_UP | GPIO_INT_BOTH,
+	 power_button_interrupt},
+	/* Other inputs */
+	/* Outputs */
+	{"DEBUG_LED",    LM4_GPIO_A, (1<<7), GPIO_OUT_LOW, NULL},
+	/* Unimplemented signals which we need to emulate for now */
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_LID_SWITCHn"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_PWRBTNn"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_BKLTEN"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_SLP_An"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_SLP_ME_CSW_DEVn"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_SLP_S3n"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_SLP_S4n"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_SLP_S5n"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_SLP_SUSn"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_SUSWARNn"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PGOOD_1_5V_DDR"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PGOOD_1_5V_PCH"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PGOOD_1_8VS"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PGOOD_5VALW"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PGOOD_CPU_CORE"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PGOOD_VCCP"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PGOOD_VCCSA"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PGOOD_VGFX_CORE"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("ENABLE_1_5V_DDR"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("ENABLE_BACKLIGHT"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("ENABLE_VCORE"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("ENABLE_VS"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_DPWROK"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_PWROK"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_RSMRSTn"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("PCH_SUSACKn"),
+	GPIO_SIGNAL_NOT_IMPLEMENTED("SHUNT_1_5V_DDR"),
+};
+
+
+void configure_board(void)
+{
+}
diff --git a/board/bds/board.h b/board/bds/board.h
new file mode 100644
index 0000000..e5cab1f
--- /dev/null
+++ b/board/bds/board.h
@@ -0,0 +1,129 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* 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 */
+
+/* TODO: these should really only be used inside lpc.c; once they are, remove
+ * from board header files. */
+/* 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_USER      20  /* User commands - 20=in, 21=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)
+#define LPC_POOL_USER     (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_USER)
+/* LPC COMx I/O address (in x86 I/O address space) */
+#define LPC_COMX_ADDR 0x2f8  /* COM2, since superIO uses COM1 */
+
+/* ADC inputs */
+/* TODO: really just need a lookup table for channels to inputs */
+#define ADC_IN0 0  /* Turn POT on badger board */
+
+enum adc_channel
+{
+	/* EC internal die temperature in degrees K. */
+	ADC_CH_EC_TEMP = 0,
+	/* Treat BDS pot input as charger current. */
+	ADC_CH_CHARGER_CURRENT,
+
+	ADC_CH_COUNT
+};
+
+/* 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? */
+
+/* Keyboard scanner uses an entire GPIO bank for row inputs */
+#define KB_SCAN_ROW_IRQ  LM4_IRQ_GPIOH
+#define KB_SCAN_ROW_GPIO LM4_GPIO_H
+
+/* USB charge port */
+#define USB_CHARGE_PORT_COUNT 0
+
+/* GPIO signal list */
+enum gpio_signal {
+	/* Inputs with interrupt handlers are first for efficiency */
+	GPIO_POWER_BUTTONn = 0,   /* Power button */
+	GPIO_LID_SWITCHn,         /* Lid switch */
+	/* Other inputs */
+	/* Outputs */
+	GPIO_DEBUG_LED,           /* Debug LED */
+	/* Signals which aren't implemented on BDS but we'll emulate anyway, to
+	 * make it more convenient to debug other code. */
+	GPIO_PCH_LID_SWITCHn,     /* Lid switch output to PCH */
+	GPIO_PCH_PWRBTNn,         /* Power button output to PCH */
+
+	GPIO_PCH_BKLTEN,          /* Backlight enable signal from PCH */
+	GPIO_PCH_SLP_An,          /* SLP_A# signal from PCH */
+	GPIO_PCH_SLP_ME_CSW_DEVn, /* SLP_ME_CSW_DEV# signal from PCH */
+	GPIO_PCH_SLP_S3n,         /* SLP_S3# signal from PCH */
+	GPIO_PCH_SLP_S4n,         /* SLP_S4# signal from PCH */
+	GPIO_PCH_SLP_S5n,         /* SLP_S5# signal from PCH */
+	GPIO_PCH_SLP_SUSn,        /* SLP_SUS# signal from PCH */
+	GPIO_PCH_SUSWARNn,        /* SUSWARN# signal from PCH */
+	GPIO_PGOOD_1_5V_DDR,      /* Power good on +1.5V_DDR */
+	GPIO_PGOOD_1_5V_PCH,      /* Power good on +1.5V_PCH */
+	GPIO_PGOOD_1_8VS,         /* Power good on +1.8VS */
+	GPIO_PGOOD_5VALW,         /* Power good on +5VALW */
+	GPIO_PGOOD_CPU_CORE,      /* Power good on +CPU_CORE */
+	GPIO_PGOOD_VCCP,          /* Power good on +VCCP */
+	GPIO_PGOOD_VCCSA,         /* Power good on +VCCSA */
+	GPIO_PGOOD_VGFX_CORE,     /* Power good on +VGFX_CORE */
+	GPIO_ENABLE_1_5V_DDR,     /* Enable +1.5V_DDR supply */
+	GPIO_ENABLE_BACKLIGHT,    /* Enable backlight power */
+	GPIO_ENABLE_VCORE,        /* Enable +CPU_CORE and +VGFX_CORE */
+	GPIO_ENABLE_VS,           /* Enable VS power supplies */
+	GPIO_PCH_DPWROK,          /* DPWROK signal to PCH */
+	GPIO_PCH_PWROK,           /* PWROK / APWROK signals to PCH */
+	GPIO_PCH_RSMRSTn,         /* Reset PCH resume power plane logic */
+	GPIO_PCH_SUSACKn,         /* Acknowledge PCH SUSWARN# signal */
+	GPIO_SHUNT_1_5V_DDR,      /* Shunt +1.5V_DDR; may also enable +3V_TP
+				   * depending on stuffing. */
+
+	/* Number of GPIOs; not an actual GPIO */
+	GPIO_COUNT
+};
+
+enum temp_sensor_id {
+  TEMP_SENSOR_EC_INTERNAL = 0, /* EC internal temperature sensor */
+  TEMP_SENSOR_CASE_DIE,
+
+  TEMP_SENSOR_COUNT
+};
+
+void configure_board(void);
+
+#endif /* __BOARD_H */
diff --git a/board/bds/board_temp_sensor.c b/board/bds/board_temp_sensor.c
new file mode 100644
index 0000000..65b7300
--- /dev/null
+++ b/board/bds/board_temp_sensor.c
@@ -0,0 +1,22 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* BDS-specific temp sensor module for Chrome EC */
+
+#include "temp_sensor.h"
+#include "chip_temp_sensor.h"
+#include "board.h"
+#include "i2c.h"
+
+#define TEMP_CASE_DIE_REG_ADDR ((0x40 << 1) | I2C_FLAG_BIG_ENDIAN)
+#define TEMP_CASE_DIE_ADDR \
+	TMP006_ADDR(I2C_PORT_THERMAL, TEMP_CASE_DIE_REG_ADDR)
+
+const struct temp_sensor_t temp_sensors[TEMP_SENSOR_COUNT] = {
+	{"ECInternal", TEMP_SENSOR_EC_INTERNAL, TEMP_SENSOR_NO_ADDR,
+		chip_temp_sensor_read, TEMP_SENSOR_NO_PRINT},
+	{"CaseDie", TEMP_SENSOR_CASE_DIE, TEMP_CASE_DIE_ADDR,
+		temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print}
+};
diff --git a/board/bds/build.mk b/board/bds/build.mk
new file mode 100644
index 0000000..73f1937
--- /dev/null
+++ b/board/bds/build.mk
@@ -0,0 +1,13 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Board specific files build
+#
+
+# the IC is TI Stellaris LM4
+CHIP:=lm4
+
+board-y=board.o
+board-$(CONFIG_CHARGER)+=dummy_charger.o
+board-$(CONFIG_TEMP_SENSOR)+=board_temp_sensor.o
diff --git a/board/bds/dummy_charger.c b/board/bds/dummy_charger.c
new file mode 100644
index 0000000..c4ee308
--- /dev/null
+++ b/board/bds/dummy_charger.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "board.h"
+#include "charger.h"
+#include "console.h"
+#include "i2c.h"
+#include "uart.h"
+#include "util.h"
+
+/* Address of battery charger */
+#define CHARGER_ADDR 0x12
+
+/* Address of battery */
+#define BATTERY_ADDR 0x16
+
+int charger_init(void)
+{
+	return EC_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(charger, command_charger);
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(battery, command_battery);
+
diff --git a/board/bds/ec.tasklist b/board/bds/ec.tasklist
new file mode 100644
index 0000000..a56e676
--- /dev/null
+++ b/board/bds/ec.tasklist
@@ -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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK(n, r, d) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ */
+#define CONFIG_TASK_LIST \
+	TASK(WATCHDOG, watchdog_task, NULL) \
+	TASK(KEYSCAN, keyboard_scan_task, NULL) \
+	TASK(POWERBTN, power_button_task, NULL) \
+	TASK(X86POWER, x86_power_task, NULL) \
+	TASK(CONSOLE, console_task, NULL) \
+	TASK(HOSTCMD, host_command_task, NULL) \
+	TASK(I8042CMD, i8042_command_task, NULL)
diff --git a/board/discovery/board.c b/board/discovery/board.c
new file mode 100644
index 0000000..e6a360d
--- /dev/null
+++ b/board/discovery/board.c
@@ -0,0 +1,67 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* STM32L Discovery board-specific configuration */
+
+#include "board.h"
+#include "common.h"
+#include "gpio.h"
+#include "registers.h"
+#include "util.h"
+
+/* GPIO signal list.  Must match order from enum gpio_signal. */
+const struct gpio_info gpio_list[GPIO_COUNT] = {
+	/* Inputs with interrupt handlers are first for efficiency */
+	{"USER_BUTTON", GPIO_A, (1<<0), GPIO_INT_BOTH, NULL},
+	/* Other inputs */
+	/* Outputs */
+	{"BLUE_LED",    GPIO_B, (1<<6), GPIO_OUT_LOW, NULL},
+	{"GREEN_LED",   GPIO_B, (1<<7), GPIO_OUT_LOW, NULL},
+};
+
+void configure_board(void)
+{
+	/* Enable all GPIOs clocks
+	 * TODO: more fine-grained enabling for power saving
+	 */
+	STM32L_RCC_AHBENR |= 0x3f;
+
+	/* Select Alternate function for USART3 on pins PB10/PB11 */
+	STM32L_GPIO_AFRH(B) = (STM32L_GPIO_AFRH(B) & ~0x0000FF00) |
+				(0x7 << 12) | (0x7 << 8);
+	STM32L_GPIO_MODER(B) = (STM32L_GPIO_MODER(B) & ~0x00F00000) |
+				0x00A00000;
+}
+
+/**
+ * Stubs for non implemented drivers
+ * TODO: implement
+ */
+int jtag_pre_init(void)
+{
+	/* stop TIM2, TIM3 and watchdogs when the JTAG stops the CPU */
+	STM32L_DBGMCU_APB1FZ |= 0x00001803;
+
+	return EC_SUCCESS;
+}
+
+int eeprom_init(void)
+{
+	return EC_SUCCESS;
+}
+
+int i2c_init(void)
+{
+	return EC_SUCCESS;
+}
+
+int power_button_init(void)
+{
+	return EC_SUCCESS;
+}
+
+int adc_init(void)
+{
+	return EC_SUCCESS;
+}
diff --git a/board/discovery/board.h b/board/discovery/board.h
new file mode 100644
index 0000000..72fca46
--- /dev/null
+++ b/board/discovery/board.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* STM32L Discovery board configuration */
+
+#ifndef __BOARD_H
+#define __BOARD_H
+
+/* 16 MHz SYSCLK clock frequency */
+#define CPU_CLOCK 16000000
+
+/* Use USART3 as console serial port */
+#define CONFIG_CONSOLE_UART 1
+
+#define USB_CHARGE_PORT_COUNT 0
+
+/* GPIO signal list */
+enum gpio_signal {
+	/* Inputs with interrupt handlers are first for efficiency */
+	GPIO_USER_BUTTON = 0,     /* Blue user button */
+	/* Other inputs */
+	/* Outputs */
+	GPIO_BLUE_LED,            /* Blue debug LED */
+	GPIO_GREEN_LED,           /* Green debug LED */
+
+	/* Number of GPIOs; not an actual GPIO */
+	GPIO_COUNT
+};
+
+void configure_board(void);
+
+#endif /* __BOARD_H */
diff --git a/board/discovery/build.mk b/board/discovery/build.mk
new file mode 100644
index 0000000..2f0ebe3
--- /dev/null
+++ b/board/discovery/build.mk
@@ -0,0 +1,10 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Board specific files build
+
+# the IC is STmicro STM32L151R8H6
+CHIP:=stm32l
+
+board-y=board.o
diff --git a/board/discovery/ec.tasklist b/board/discovery/ec.tasklist
new file mode 100644
index 0000000..0e5992b
--- /dev/null
+++ b/board/discovery/ec.tasklist
@@ -0,0 +1,18 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK(n, r, d) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ */
+#define CONFIG_TASK_LIST \
+	TASK(WATCHDOG, watchdog_task, NULL) \
+	TASK(CONSOLE, console_task, NULL)
diff --git a/board/link/board.c b/board/link/board.c
new file mode 100644
index 0000000..8304267
--- /dev/null
+++ b/board/link/board.c
@@ -0,0 +1,123 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* EC for Link board configuration */
+
+#include "board.h"
+#include "config.h"
+#include "gpio.h"
+#include "power_button.h"
+#include "registers.h"
+#include "util.h"
+#include "x86_power.h"
+#include "lm4_adc.h"
+#include "adc.h"
+
+#ifndef CONFIG_TASK_X86POWER
+#define x86_power_interrupt NULL
+#endif
+#ifndef CONFIG_TASK_POWERBTN
+#define power_button_interrupt NULL
+#endif
+
+
+/* GPIO signal list.  Must match order from enum gpio_signal. */
+const struct gpio_info gpio_list[GPIO_COUNT] = {
+	/* Inputs with interrupt handlers are first for efficiency */
+	{"POWER_BUTTONn",       LM4_GPIO_K, (1<<7), GPIO_INT_BOTH,
+	 power_button_interrupt},
+	{"LID_SWITCHn",         LM4_GPIO_K, (1<<5), GPIO_INT_BOTH,
+	 power_button_interrupt},
+	/* Other inputs */
+	{"POWER_ONEWIRE",       LM4_GPIO_H, (1<<2), 0, NULL},
+	{"THERMAL_DATA_READYn", LM4_GPIO_B, (1<<4), 0, NULL},
+	{"AC_PRESENT",          LM4_GPIO_H, (1<<3), 0, NULL},
+	{"PCH_BKLTEN",          LM4_GPIO_J, (1<<3), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PCH_SLP_An",          LM4_GPIO_G, (1<<5), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PCH_SLP_ME_CSW_DEVn", LM4_GPIO_G, (1<<4), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PCH_SLP_S3n",         LM4_GPIO_J, (1<<0), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PCH_SLP_S4n",         LM4_GPIO_J, (1<<1), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PCH_SLP_S5n",         LM4_GPIO_J, (1<<2), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PCH_SLP_SUSn",        LM4_GPIO_G, (1<<3), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PCH_SUSWARNn",        LM4_GPIO_G, (1<<2), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PGOOD_1_5V_DDR",      LM4_GPIO_K, (1<<0), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PGOOD_1_5V_PCH",      LM4_GPIO_K, (1<<1), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PGOOD_1_8VS",         LM4_GPIO_K, (1<<3), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PGOOD_5VALW",         LM4_GPIO_H, (1<<0), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PGOOD_CPU_CORE",      LM4_GPIO_M, (1<<3), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PGOOD_VCCP",          LM4_GPIO_K, (1<<2), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PGOOD_VCCSA",         LM4_GPIO_H, (1<<1), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"PGOOD_VGFX_CORE",     LM4_GPIO_D, (1<<2), GPIO_INT_BOTH,
+	 x86_power_interrupt},
+	{"RECOVERYn",           LM4_GPIO_H, (1<<7), 0, NULL},
+	{"USB1_STATUSn",        LM4_GPIO_E, (1<<7), 0, NULL},
+	{"USB2_STATUSn",        LM4_GPIO_E, (1<<1), 0, NULL},
+	{"WRITE_PROTECTn",      LM4_GPIO_J, (1<<4), 0, NULL},
+	/* Outputs; all unasserted by default */
+	{"CPU_PROCHOTn",        LM4_GPIO_F, (1<<2), GPIO_OUT_HIGH, NULL},
+	{"ENABLE_1_5V_DDR",     LM4_GPIO_H, (1<<5), GPIO_OUT_LOW, NULL},
+	{"ENABLE_BACKLIGHT",    LM4_GPIO_H, (1<<4), GPIO_OUT_LOW, NULL},
+	{"ENABLE_VCORE",        LM4_GPIO_F, (1<<7), GPIO_OUT_LOW, NULL},
+	{"ENABLE_VS",           LM4_GPIO_G, (1<<6), GPIO_OUT_LOW, NULL},
+	{"ENTERING_RW",         LM4_GPIO_J, (1<<5), GPIO_OUT_LOW, NULL},
+	{"PCH_A20GATE",         LM4_GPIO_Q, (1<<6), GPIO_OUT_LOW, NULL},
+	{"PCH_DPWROK",          LM4_GPIO_G, (1<<0), GPIO_OUT_LOW, NULL},
+	{"PCH_HDA_SDO",         LM4_GPIO_G, (1<<1), GPIO_OUT_LOW, NULL},
+	{"PCH_LID_SWITCHn",     LM4_GPIO_F, (1<<0), GPIO_OUT_HIGH, NULL},
+	{"PCH_NMIn",            LM4_GPIO_M, (1<<2), GPIO_OUT_HIGH, NULL},
+	{"PCH_PWRBTNn",         LM4_GPIO_G, (1<<7), GPIO_OUT_HIGH, NULL},
+	{"PCH_PWROK",           LM4_GPIO_F, (1<<5), GPIO_OUT_LOW, NULL},
+	{"PCH_RCINn",           LM4_GPIO_Q, (1<<7), GPIO_OUT_HIGH, NULL},
+	/* Exception: RSMRST# is asserted at power-on */
+	{"PCH_RSMRSTn",         LM4_GPIO_F, (1<<1), GPIO_OUT_LOW, NULL},
+	{"PCH_SMIn",            LM4_GPIO_F, (1<<4), GPIO_OUT_HIGH, NULL},
+	{"PCH_SUSACKn",         LM4_GPIO_F, (1<<3), GPIO_OUT_HIGH, NULL},
+	{"SHUNT_1_5V_DDR",      LM4_GPIO_F, (1<<6), GPIO_OUT_HIGH, NULL},
+	{"USB1_CTL1",           LM4_GPIO_E, (1<<2), GPIO_OUT_LOW, NULL},
+	{"USB1_CTL2",           LM4_GPIO_E, (1<<3), GPIO_OUT_LOW, NULL},
+	{"USB1_CTL3",           LM4_GPIO_E, (1<<4), GPIO_OUT_LOW, NULL},
+	{"USB1_ENABLE",         LM4_GPIO_E, (1<<5), GPIO_OUT_LOW, NULL},
+	{"USB1_ILIM_SEL",       LM4_GPIO_E, (1<<6), GPIO_OUT_LOW, NULL},
+	{"USB2_CTL1",           LM4_GPIO_D, (1<<4), GPIO_OUT_LOW, NULL},
+	{"USB2_CTL2",           LM4_GPIO_D, (1<<5), GPIO_OUT_LOW, NULL},
+	{"USB2_CTL3",           LM4_GPIO_D, (1<<6), GPIO_OUT_LOW, NULL},
+	{"USB2_ENABLE",         LM4_GPIO_D, (1<<7), GPIO_OUT_LOW, NULL},
+	{"USB2_ILIM_SEL",       LM4_GPIO_E, (1<<0), GPIO_OUT_LOW, NULL},
+};
+
+/* ADC channels. Must be in the exactly same order as in enum adc_channel. */
+const struct adc_t adc_channels[ADC_CH_COUNT] =
+{
+	/* EC internal temperature is calculated by
+	 * 273 + (295 - 450 * ADC_VALUE / ADC_READ_MAX) / 2
+	 * = -225 * ADC_VALUE / ADC_READ_MAX + 420.5
+	 */
+	{"ECTemp", LM4_ADC_SEQ0, -225, ADC_READ_MAX, 420,
+	 LM4_AIN_NONE, 0x0e /* TS0 | IE0 | END0 */},
+
+	/* Charger current is mapped from 0~4000mA to 0~1.6V.
+	 * And ADC maps 0~3.3V to ADC_READ_MAX.
+	 */
+	{"ChargerCurrent", LM4_ADC_SEQ1, 33 * 4000, ADC_READ_MAX * 16, 0,
+	 LM4_AIN(ADC_IN0), 0x06 /* IE0 | END0 */},
+};
+
+void configure_board(void)
+{
+}
diff --git a/board/link/board.h b/board/link/board.h
new file mode 100644
index 0000000..d199d6c
--- /dev/null
+++ b/board/link/board.h
@@ -0,0 +1,170 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* 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 */
+
+/* TODO: these should really only be used inside lpc.c; once they are, remove
+ * from board header files. */
+/* 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_USER      20  /* User commands - 20=in, 21=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)
+#define LPC_POOL_USER     (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_USER)
+/* LPC COMx I/O address (in x86 I/O address space) */
+#define LPC_COMX_ADDR 0x3f8  /* COM1 */
+
+/* Define the following to print repeated duplicate port 80 writes.  Normally
+ * we only print the port 80 value when it changes, because the kernel spams
+ * port 80 with repeated writes as a delay mechanism. */
+#define CONFIG_PORT80_PRINT_DUPLICATES
+
+/* ADC inputs */
+/* TODO: assign real ADC inputs */
+#define ADC_IN0 11  /* Charger current */
+
+enum adc_channel
+{
+	/* EC internal die temperature in degrees K. */
+	ADC_CH_EC_TEMP = 0,
+	/* Charger current in mA. */
+	ADC_CH_CHARGER_CURRENT,
+
+	ADC_CH_COUNT
+};
+
+/* Charger module */
+#define CONFIG_CHARGER_BQ24725
+#define CONFIG_BQ24725_R_SNS 10 /* 10 mOhm charge sense resistor */
+#define CONFIG_BQ24725_R_AC  20 /* 20 mOhm input current sense resistor */
+
+/* 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? */
+
+/* Keyboard scanner uses an entire GPIO bank for row inputs */
+#define KB_SCAN_ROW_IRQ  LM4_IRQ_GPION
+#define KB_SCAN_ROW_GPIO LM4_GPIO_N
+
+/* USB charge port */
+#define USB_CHARGE_PORT_COUNT 2
+
+/* GPIO signal definitions. */
+enum gpio_signal {
+	/* Inputs with interrupt handlers are first for efficiency */
+	GPIO_POWER_BUTTONn = 0,   /* Power button */
+	GPIO_LID_SWITCHn,         /* Lid switch */
+	GPIO_POWER_ONEWIRE,       /* 1-wire interface to power adapter LEDs */
+	GPIO_THERMAL_DATA_READYn, /* Data ready from I2C thermal sensor */
+	/* Other inputs */
+	GPIO_AC_PRESENT,          /* AC power present */
+	GPIO_PCH_BKLTEN,          /* Backlight enable signal from PCH */
+	GPIO_PCH_SLP_An,          /* SLP_A# signal from PCH */
+	GPIO_PCH_SLP_ME_CSW_DEVn, /* SLP_ME_CSW_DEV# signal from PCH */
+	GPIO_PCH_SLP_S3n,         /* SLP_S3# signal from PCH */
+	GPIO_PCH_SLP_S4n,         /* SLP_S4# signal from PCH */
+	GPIO_PCH_SLP_S5n,         /* SLP_S5# signal from PCH */
+	GPIO_PCH_SLP_SUSn,        /* SLP_SUS# signal from PCH */
+	GPIO_PCH_SUSWARNn,        /* SUSWARN# signal from PCH */
+	GPIO_PGOOD_1_5V_DDR,      /* Power good on +1.5V_DDR */
+	GPIO_PGOOD_1_5V_PCH,      /* Power good on +1.5V_PCH */
+	GPIO_PGOOD_1_8VS,         /* Power good on +1.8VS */
+	GPIO_PGOOD_5VALW,         /* Power good on +5VALW */
+	GPIO_PGOOD_CPU_CORE,      /* Power good on +CPU_CORE */
+	GPIO_PGOOD_VCCP,          /* Power good on +VCCP */
+	GPIO_PGOOD_VCCSA,         /* Power good on +VCCSA */
+	GPIO_PGOOD_VGFX_CORE,     /* Power good on +VGFX_CORE */
+	GPIO_RECOVERYn,           /* Recovery signal from servo */
+	GPIO_USB1_STATUSn,        /* USB charger port 1 status output */
+	GPIO_USB2_STATUSn,        /* USB charger port 2 status output */
+	GPIO_WRITE_PROTECTn,      /* Write protect input */
+	/* Outputs */
+	GPIO_CPU_PROCHOTn,        /* Force CPU to think it's overheated */
+	GPIO_ENABLE_1_5V_DDR,     /* Enable +1.5V_DDR supply */
+	GPIO_ENABLE_BACKLIGHT,    /* Enable backlight power */
+	GPIO_ENABLE_VCORE,        /* Enable +CPU_CORE and +VGFX_CORE */
+	GPIO_ENABLE_VS,           /* Enable VS power supplies */
+	GPIO_ENTERING_RW,         /* Indicate when EC is entering RW code */
+	GPIO_PCH_A20GATE,         /* A20GATE signal to PCH */
+	GPIO_PCH_DPWROK,          /* DPWROK signal to PCH */
+	GPIO_PCH_HDA_SDO,         /* HDA_SDO signal to PCH; when high, ME
+				   * ignores security descriptor */
+	GPIO_PCH_LID_SWITCHn,     /* Lid switch output to PCH */
+	GPIO_PCH_NMIn,            /* Non-maskable interrupt pin to PCH */
+	GPIO_PCH_PWRBTNn,         /* Power button output to PCH */
+	GPIO_PCH_PWROK,           /* PWROK / APWROK signals to PCH */
+	GPIO_PCH_RCINn,           /* RCIN# signal to PCH */
+	GPIO_PCH_RSMRSTn,         /* Reset PCH resume power plane logic */
+	GPIO_PCH_SMIn,            /* System management interrupt to PCH */
+	GPIO_PCH_SUSACKn,         /* Acknowledge PCH SUSWARN# signal */
+	GPIO_SHUNT_1_5V_DDR,      /* Shunt +1.5V_DDR; may also enable +3V_TP
+				   * depending on stuffing. */
+	GPIO_USB1_CTL1,           /* USB charger port 1 CTL1 output */
+	GPIO_USB1_CTL2,           /* USB charger port 1 CTL2 output */
+	GPIO_USB1_CTL3,           /* USB charger port 1 CTL3 output */
+	GPIO_USB1_ENABLE,         /* USB charger port 1 enable */
+	GPIO_USB1_ILIM_SEL,       /* USB charger port 1 ILIM_SEL output */
+	GPIO_USB2_CTL1,           /* USB charger port 2 CTL1 output */
+	GPIO_USB2_CTL2,           /* USB charger port 2 CTL2 output */
+	GPIO_USB2_CTL3,           /* USB charger port 2 CTL3 output */
+	GPIO_USB2_ENABLE,         /* USB charger port 2 enable */
+	GPIO_USB2_ILIM_SEL,       /* USB charger port 2 ILIM_SEL output */
+
+	/* Number of GPIOs; not an actual GPIO */
+	GPIO_COUNT
+};
+
+enum temp_sensor_id {
+	/* I2C die temperature sensor near CPU */
+	TEMP_SENSOR_I2C_DIE_NEAR_CPU = 0,
+	/* PCH temperature sensor */
+	TEMP_SENSOR_I2C_DIE_NEAR_PCH,
+	/* DDR memory temperature sensor */
+	TEMP_SENSOR_I2C_DIE_NEAR_DDR,
+	/* Battery charger temperature sensor */
+	TEMP_SENSOR_I2C_DIE_NEAR_CHARGER,
+	/* EC internal temperature sensor */
+	TEMP_SENSOR_EC_INTERNAL,
+
+	/* TODO: I2C temperature sensors. */
+
+	TEMP_SENSOR_COUNT
+};
+
+void configure_board(void);
+
+#endif /* __BOARD_H */
diff --git a/board/link/board_temp_sensor.c b/board/link/board_temp_sensor.c
new file mode 100644
index 0000000..4a937c9
--- /dev/null
+++ b/board/link/board_temp_sensor.c
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Link-specific temp sensor module for Chrome EC */
+
+#include "temp_sensor.h"
+#include "chip_temp_sensor.h"
+#include "board.h"
+#include "i2c.h"
+
+#define TEMP_CPU_REG_ADDR ((0x40 << 1) | I2C_FLAG_BIG_ENDIAN)
+#define TEMP_PCH_REG_ADDR ((0x41 << 1) | I2C_FLAG_BIG_ENDIAN)
+#define TEMP_DDR_REG_ADDR ((0x43 << 1) | I2C_FLAG_BIG_ENDIAN)
+#define TEMP_CHARGER_REG_ADDR ((0x45 << 1) | I2C_FLAG_BIG_ENDIAN)
+
+#define TEMP_CPU_ADDR TMP006_ADDR(I2C_PORT_THERMAL, TEMP_CPU_REG_ADDR)
+#define TEMP_PCH_ADDR TMP006_ADDR(I2C_PORT_THERMAL, TEMP_PCH_REG_ADDR)
+#define TEMP_DDR_ADDR TMP006_ADDR(I2C_PORT_THERMAL, TEMP_DDR_REG_ADDR)
+#define TEMP_CHARGER_ADDR TMP006_ADDR(I2C_PORT_THERMAL, TEMP_CHARGER_REG_ADDR)
+
+/* Temperature sensors data. Must be in the same order as enum
+ * temp_sensor_id.
+ */
+const struct temp_sensor_t temp_sensors[TEMP_SENSOR_COUNT] = {
+	{"CPU", TEMP_SENSOR_I2C_DIE_NEAR_CPU, TEMP_CPU_ADDR,
+		temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print},
+	{"PCH", TEMP_SENSOR_I2C_DIE_NEAR_PCH, TEMP_PCH_ADDR,
+		temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print},
+	{"DDR", TEMP_SENSOR_I2C_DIE_NEAR_DDR, TEMP_DDR_ADDR,
+		temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print},
+	{"Charger", TEMP_SENSOR_I2C_DIE_NEAR_CHARGER, TEMP_CHARGER_ADDR,
+		temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print},
+	{"ECInternal", TEMP_SENSOR_EC_INTERNAL, TEMP_SENSOR_NO_ADDR,
+		chip_temp_sensor_read, TEMP_SENSOR_NO_PRINT},
+};
diff --git a/board/link/build.mk b/board/link/build.mk
new file mode 100644
index 0000000..b600d69
--- /dev/null
+++ b/board/link/build.mk
@@ -0,0 +1,12 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Board specific files build
+#
+
+# the IC is TI Stellaris LM4
+CHIP:=lm4
+
+board-y=board.o
+board-$(CONFIG_TEMP_SENSOR)+=board_temp_sensor.o
diff --git a/board/link/ec.tasklist b/board/link/ec.tasklist
new file mode 100644
index 0000000..a56e676
--- /dev/null
+++ b/board/link/ec.tasklist
@@ -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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK(n, r, d) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ */
+#define CONFIG_TASK_LIST \
+	TASK(WATCHDOG, watchdog_task, NULL) \
+	TASK(KEYSCAN, keyboard_scan_task, NULL) \
+	TASK(POWERBTN, power_button_task, NULL) \
+	TASK(X86POWER, x86_power_task, NULL) \
+	TASK(CONSOLE, console_task, NULL) \
+	TASK(HOSTCMD, host_command_task, NULL) \
+	TASK(I8042CMD, i8042_command_task, NULL)
diff --git a/chip/lm4/adc.c b/chip/lm4/adc.c
new file mode 100644
index 0000000..29eb73d
--- /dev/null
+++ b/chip/lm4/adc.c
@@ -0,0 +1,183 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* LM4-specific ADC module for Chrome EC */
+
+#include "lm4_adc.h"
+#include "console.h"
+#include "adc.h"
+#include "timer.h"
+#include "registers.h"
+#include "uart.h"
+#include "util.h"
+
+extern const struct adc_t adc_channels[ADC_CH_COUNT];
+
+/* GPIO port and mask for AINs. */
+const uint32_t ain_port[24][2] = {
+	{LM4_GPIO_E, (1<<3)},
+	{LM4_GPIO_E, (1<<2)},
+	{LM4_GPIO_E, (1<<1)},
+	{LM4_GPIO_E, (1<<0)},
+	{LM4_GPIO_D, (1<<7)},
+	{LM4_GPIO_D, (1<<6)},
+	{LM4_GPIO_D, (1<<5)},
+	{LM4_GPIO_D, (1<<4)},
+	{LM4_GPIO_E, (1<<5)},
+	{LM4_GPIO_E, (1<<4)},
+	{LM4_GPIO_B, (1<<4)},
+	{LM4_GPIO_B, (1<<5)},
+	{LM4_GPIO_D, (1<<3)},
+	{LM4_GPIO_D, (1<<2)},
+	{LM4_GPIO_D, (1<<1)},
+	{LM4_GPIO_D, (1<<0)},
+	{LM4_GPIO_K, (1<<0)},
+	{LM4_GPIO_K, (1<<1)},
+	{LM4_GPIO_K, (1<<2)},
+	{LM4_GPIO_K, (1<<3)},
+	{LM4_GPIO_E, (1<<7)},
+	{LM4_GPIO_E, (1<<6)},
+	{LM4_GPIO_N, (1<<1)},
+	{LM4_GPIO_N, (1<<0)},
+};
+
+static void configure_gpio(void)
+{
+	int i;
+	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 AIN */
+	for (i = 0; i < ADC_CH_COUNT; ++i) {
+		int id = adc_channels[i].channel;
+		if (id != LM4_AIN_NONE)
+			gpio_set_alternate_function(ain_port[id][0],
+						    ain_port[id][1],
+						    1);
+	}
+}
+
+int lm4_adc_flush_and_read(enum lm4_adc_sequencer seq)
+{
+	/* 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? */
+	volatile uint32_t scratch  __attribute__((unused));
+
+	/* Empty the FIFO of any previous results */
+	while (!(LM4_ADC_SSFSTAT(seq) & 0x100))
+		scratch = LM4_ADC_SSFIFO(seq);
+
+	/* Clear the interrupt status */
+	LM4_ADC_ADCISC |= 0x01 << seq;
+
+	/* Initiate sample sequence */
+	LM4_ADC_ADCPSSI |= 0x01 << seq;
+
+	/* Wait for interrupt */
+	/* TODO: use a real interrupt */
+	/* TODO: timeout */
+	while (!(LM4_ADC_ADCRIS & (0x01 << seq)));
+
+	/* Read the FIFO and convert to temperature */
+	return LM4_ADC_SSFIFO(seq);
+}
+
+int lm4_adc_configure(enum lm4_adc_sequencer seq,
+		      int ain_id,
+		      int ssctl)
+{
+	volatile uint32_t scratch  __attribute__((unused));
+	/* TODO: set up clock using ADCCC register? */
+	/* Configure sample sequencer */
+	LM4_ADC_ADCACTSS &= ~(0x01 << seq);
+	/* Trigger sequencer by processor request */
+	LM4_ADC_ADCEMUX = (LM4_ADC_ADCEMUX & ~(0xf << (seq * 4))) | 0x00;
+	/* Sample internal temp sensor */
+	if (ain_id != LM4_AIN_NONE) {
+		LM4_ADC_SSMUX(seq) = ain_id & 0xf;
+		LM4_ADC_SSEMUX(seq) = ain_id >> 4;
+	}
+	else {
+		LM4_ADC_SSMUX(seq) = 0x00;
+		LM4_ADC_SSEMUX(seq) = 0x00;
+	}
+	LM4_ADC_SSCTL(seq) = ssctl;
+	/* Enable sample sequencer */
+	LM4_ADC_ADCACTSS |= 0x01 << seq;
+
+	return EC_SUCCESS;
+}
+
+int adc_read_channel(enum adc_channel ch)
+{
+	const struct adc_t *adc = adc_channels + ch;
+	int rv = lm4_adc_flush_and_read(adc->sequencer);
+	return rv * adc->factor_mul / adc->factor_div + adc->shift;
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_ectemp(int argc, char **argv)
+{
+	int t = adc_read_channel(ADC_CH_EC_TEMP);
+	uart_printf("EC temperature is %d K = %d C\n", t, t-273);
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ectemp, command_ectemp);
+
+static int command_adc(int argc, char **argv)
+{
+	int i;
+
+	for (i = 0; i < ADC_CH_COUNT; ++i)
+		uart_printf("ADC channel \"%s\" = %d\n",
+			     adc_channels[i].name,
+			     adc_read_channel(i));
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(adc, command_adc);
+
+/*****************************************************************************/
+/* Initialization */
+
+int adc_init(void)
+{
+	int i;
+	const struct adc_t *adc;
+
+        /* Enable ADC0 module and delay a few clocks */
+	LM4_SYSTEM_RCGCADC |= 0x01;
+	udelay(1);
+
+	/* Configure GPIOs */
+	configure_gpio();
+
+	/* Use external voltage references (VREFA+, VREFA-) instead of
+	 * VDDA and GNDA. */
+	LM4_ADC_ADCCTL = 0x01;
+
+	/* Initialize ADC sequencer */
+	for (i = 0; i < ADC_CH_COUNT; ++i) {
+		adc = adc_channels + i;
+		lm4_adc_configure(adc->sequencer, adc->channel, adc->flag);
+	}
+
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/build.mk b/chip/lm4/build.mk
new file mode 100644
index 0000000..028eeec
--- /dev/null
+++ b/chip/lm4/build.mk
@@ -0,0 +1,18 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# LM4 chip specific files build
+#
+
+# LM4 SoC has a Cortex-M4 ARM core
+CORE:=cortex-m
+
+chip-y=i2c.o adc.o jtag.o
+chip-y+=clock.o gpio.o system.o uart.o power_button.o
+chip-y+=watchdog.o eeprom.o hwtimer.o
+chip-$(CONFIG_FLASH)+=flash.o
+chip-$(CONFIG_LPC)+=lpc.o
+chip-$(CONFIG_PWM)+=pwm.o
+chip-$(CONFIG_TEMP_SENSOR)+=chip_temp_sensor.o
+chip-$(CONFIG_TASK_KEYSCAN)+=keyboard_scan.o
diff --git a/chip/lm4/chip_temp_sensor.c b/chip/lm4/chip_temp_sensor.c
new file mode 100644
index 0000000..d810216
--- /dev/null
+++ b/chip/lm4/chip_temp_sensor.c
@@ -0,0 +1,24 @@
+/* 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 "temp_sensor.h"
+
+int chip_temp_sensor_read(const struct temp_sensor_t* sensor)
+{
+	/* LM4 only has internal temperature sensor */
+	if (sensor->id != TEMP_SENSOR_EC_INTERNAL)
+		return EC_ERROR_INVAL;
+
+	return adc_read_channel(ADC_CH_EC_TEMP);
+}
+
+int chip_temp_sensor_init(void)
+{
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/clock.c b/chip/lm4/clock.c
new file mode 100644
index 0000000..c80f36e
--- /dev/null
+++ b/chip/lm4/clock.c
@@ -0,0 +1,198 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Clocks and power management settings */
+
+#include <stdint.h>
+
+#include "board.h"
+#include "clock.h"
+#include "config.h"
+#include "console.h"
+#include "gpio.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "registers.h"
+#include "util.h"
+
+/**
+ * Idle task
+ * executed when no task are ready to be scheduled
+ */
+void __idle(void)
+{
+	while (1) {
+		/* wait for the irq event */
+		asm("wfi");
+		/* TODO more power management here */
+	}
+}
+
+/* simple busy waiting before clocks are initialized */
+static void wait_cycles(uint32_t cycles)
+{
+	asm("1: subs %0, #1\n"
+	    "   bne 1b\n" :: "r"(cycles));
+}
+
+/**
+ * Function to measure baseline for power consumption.
+ *
+ * Levels :
+ *   0 : CPU running in tight loop
+ *   1 : CPU running in tight loop but peripherals gated
+ *   2 : CPU in sleep mode
+ *   3 : CPU in sleep mode and peripherals gated
+ *   4 : CPU in deep sleep mode
+ *   5 : CPU in deep sleep mode and peripherals gated
+ */
+static int command_sleep(int argc, char **argv)
+{
+	int level = 0;
+	int clock = 0;
+	uint32_t uartibrd = 0;
+	uint32_t uartfbrd = 0;
+
+	if (argc >= 2) {
+		level = strtoi(argv[1], NULL, 10);
+	}
+	if (argc >= 3) {
+		clock = strtoi(argv[2], NULL, 10);
+	}
+
+#ifdef BOARD_bds
+	/* remove LED current sink  */
+	gpio_set_level(GPIO_DEBUG_LED, 0);
+#endif
+
+	uart_printf("Going to sleep : level %d clock %d...\n", level, clock);
+	uart_flush_output();
+
+	/* clock setting */
+	if (clock) {
+		/* Use ROM code function to set the clock */
+		void **func_table = (void **)*(uint32_t *)0x01000044;
+		void (*rom_clock_set)(uint32_t rcc) = func_table[23];
+
+		/* disable interrupts */
+		asm volatile("cpsid i");
+
+		switch (clock) {
+		case 1: /* 16MHz IOSC */
+			uartibrd = 17;
+			uartfbrd = 23;
+			rom_clock_set(0x00000d51);
+			break;
+		case 2: /* 1MHz IOSC */
+			uartibrd = 1;
+			uartfbrd = 5;
+			rom_clock_set(0x07C00d51);
+			break;
+		case 3: /* 30 kHz */
+			uartibrd = 0;
+			uartfbrd = 0;
+			rom_clock_set(0x00000d71);
+			break;
+		}
+
+		/* TODO: move this to the UART module; ugly to have
+		   UARTisms here.  Also note this only fixes UART0,
+		   not UART1. */
+		if (uartfbrd) {
+			/* Disable the port via UARTCTL and add HSE */
+			LM4_UART_CTL(0) = 0x0320;
+			/* Set the baud rate divisor */
+			LM4_UART_IBRD(0) = uartibrd;
+			LM4_UART_FBRD(0) = uartfbrd;
+			/* Poke UARTLCRH to make the new divisor take effect. */
+			LM4_UART_LCRH(0) = LM4_UART_LCRH(0);
+			/* Enable the port */
+			LM4_UART_CTL(0) |= 0x0001;
+		}
+		asm volatile("cpsie i");
+	}
+
+	if (uartfbrd) {
+		uart_printf("We are still alive. RCC=%08x\n", LM4_SYSTEM_RCC);
+		uart_flush_output();
+	}
+
+	asm volatile("cpsid i");
+
+	/* gate peripheral clocks */
+	if (level & 1) {
+		LM4_SYSTEM_RCGCTIMER = 0;
+		LM4_SYSTEM_RCGCGPIO = 0;
+		LM4_SYSTEM_RCGCDMA = 0;
+		LM4_SYSTEM_RCGCUART = 0;
+		LM4_SYSTEM_RCGCLPC = 0;
+		LM4_SYSTEM_RCGCWTIMER = 0;
+	}
+	/* set deep sleep bit */
+	if (level >= 4)
+		LM4_SCB_SYSCTRL |= 0x4;
+	/* go to low power mode (forever ...) */
+	if (level > 1)
+		while (1)
+			asm("wfi");
+	else
+		while(1);
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(sleep, command_sleep);
+
+
+static void clock_init_pll(uint32_t value)
+{
+	/**
+	 * at startup, OSCSRC is PIOSC (precision internal oscillator)
+	 * PLL and PLL2 are in power-down
+	 */
+
+	/* PLL already setup */
+	if (LM4_SYSTEM_PLLSTAT & 1)
+		return;
+
+	/* Put a bypass on the system clock PLLs, no divider */
+	LM4_SYSTEM_RCC = (LM4_SYSTEM_RCC | 0x800) & ~0x400000;
+	LM4_SYSTEM_RCC2 = (LM4_SYSTEM_RCC2 | 0x800) & ~0x80000000;
+
+	/* Enable main and precision internal oscillators */
+	LM4_SYSTEM_RCC &= ~0x3;
+
+	/* Perform an auto calibration of the internal oscillator, using the
+	 * 32.768KHz hibernate clock. */
+	/* TODO: (crosbug.com/p/7693) This is only needed on early chips which
+	 * aren't factory trimmed. */
+	LM4_SYSTEM_PIOSCCAL = 0x80000000;
+	LM4_SYSTEM_PIOSCCAL = 0x80000200;
+
+	/* wait 1 million CPU cycles */
+	wait_cycles(512 * 1024);
+
+	/* clear PLL lock flag (aka PLLLMIS) */
+	LM4_SYSTEM_MISC = 0x40;
+	/* clear powerdown / set XTAL frequency, divider, and source */
+	LM4_SYSTEM_RCC = (LM4_SYSTEM_RCC & ~0x07c027f0) | (value & 0x07c007f0);
+	/* wait 32 CPU cycles */
+	wait_cycles(16);
+	/* wait for PLL to lock */
+	while (!(LM4_SYSTEM_RIS & 0x40));
+
+	/* Remove bypass on PLL */
+	LM4_SYSTEM_RCC = LM4_SYSTEM_RCC & ~0x800;
+}
+
+int clock_init(void)
+{
+	/* CPU clock = PLL/3 = 66.667MHz; System clock = PLL */
+	BUILD_ASSERT(CPU_CLOCK == 66666667);
+	/* Osc source = internal 16MHz oscillator */
+	clock_init_pll(0x01400550);
+
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/config.h b/chip/lm4/config.h
new file mode 100644
index 0000000..f2224f8
--- /dev/null
+++ b/chip/lm4/config.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Memory mapping */
+#define CONFIG_FLASH_BASE       0x00000000
+#define CONFIG_FLASH_SIZE       0x00040000
+#define CONFIG_FLASH_BANK_SIZE  0x2000
+#define CONFIG_RAM_BASE         0x20000000
+#define CONFIG_RAM_SIZE         0x00008000
+
+/* Size of one firmware image in flash */
+#define CONFIG_FW_IMAGE_SIZE    (40 * 1024)
+#define CONFIG_FW_RO_OFF        0
+#define CONFIG_FW_A_OFF         CONFIG_FW_IMAGE_SIZE
+#define CONFIG_FW_B_OFF         (2 * CONFIG_FW_IMAGE_SIZE)
+
+/* Number of IRQ vectors on the NVIC */
+#define CONFIG_IRQ_COUNT 240
+
+/* Debug UART parameters for panic message */
+#define CONFIG_UART_ADDRESS    0x4000c000
+#define CONFIG_UART_DR_OFFSET  0x00
+#define CONFIG_UART_SR_OFFSET  0x18
+#define CONFIG_UART_SR_TXEMPTY 0x80
+
+/* System stack size */
+#define CONFIG_STACK_SIZE 4096
+
+/* build with assertions and debug messages */
+#define CONFIG_DEBUG
+
+/* Optional features */
+#define CONFIG_FLASH
+#define CONFIG_LPC
+#define CONFIG_PWM
+#define CONFIG_TEMP_SENSOR
+#define CONFIG_CHARGER
+
+/* Compile for running from RAM instead of flash */
+/* #define COMPILE_FOR_RAM */
diff --git a/chip/lm4/eeprom.c b/chip/lm4/eeprom.c
new file mode 100644
index 0000000..c605d0e
--- /dev/null
+++ b/chip/lm4/eeprom.c
@@ -0,0 +1,248 @@
+/* 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;
+}
+DECLARE_CONSOLE_COMMAND(eeinfo, command_eeprom_info);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(eeread, command_eeprom_read);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(eewrite, command_eeprom_write);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(eehide, command_eeprom_hide);
+
+
+/*****************************************************************************/
+/* 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;
+
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/flash.c b/chip/lm4/flash.c
new file mode 100644
index 0000000..43f5d59
--- /dev/null
+++ b/chip/lm4/flash.c
@@ -0,0 +1,304 @@
+/* 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"
+
+#define BANK_SHIFT 5 /* bank registers have 32bits each, 2^32 */
+#define BANK_MASK ((1 << BANK_SHIFT) - 1) /* 5 bits */
+#define F_BANK(b) ((b) >> BANK_SHIFT)
+#define F_BIT(b) (1 << ((b) & BANK_MASK))
+
+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: (crosbug.com/p/7473) 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 write_buffer(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 (crosbug.com/p/7478) - 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 = write_buffer();
+			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 = write_buffer();
+		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 (crosbug.com/p/7478) - 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;
+}
+
+/* Get write protect status of single flash block
+ * return value:
+ *   0 - WP
+ *   non-zero - writable
+ */
+static uint32_t get_block_wp(int block)
+{
+	return LM4_FLASH_FMPPE[F_BANK(block)] & F_BIT(block);
+}
+
+static void set_block_wp(int block)
+{
+	LM4_FLASH_FMPPE[F_BANK(block)] &= ~F_BIT(block);
+}
+
+static int find_first_wp_block(void)
+{
+	int block;
+	for (block = 0; block < LM4_FLASH_FSIZE; block++)
+		if (get_block_wp(block) == 0)
+			return block;
+	return -1;
+}
+
+static int find_last_wp_block(void)
+{
+	int block;
+	for (block = LM4_FLASH_FSIZE - 1; block >= 0; block--)
+		if (get_block_wp(block) == 0)
+			return block;
+	return -1;
+}
+
+static int get_wp_range(int *start, int *nblock)
+{
+	int start_blk, end_blk;
+
+	start_blk = find_first_wp_block();
+
+	if (start_blk < 0) {
+		/* Flash is not write protected */
+		*start = 0;
+		*nblock = 0;
+		return EC_SUCCESS;
+	}
+
+	/* TODO: Sanity check the shadow value? */
+
+	end_blk = find_last_wp_block();
+	*nblock = end_blk - start_blk + 1;
+	*start = start_blk;
+	return EC_SUCCESS;
+}
+
+
+static int set_wp_range(int start, int nblock)
+{
+	int end_blk, block;
+
+	if (nblock == 0)
+		return EC_SUCCESS;
+
+	end_blk = (start + nblock - 1);
+
+	for (block = start; block <= end_blk; block++)
+		set_block_wp(block);
+
+	return EC_SUCCESS;
+}
+
+int flash_get_write_protect_range(int *offset, int *size)
+{
+	int start, nblock;
+	int rv;
+
+	rv = get_wp_range(&start, &nblock);
+	if (rv)
+		return rv;
+
+	*size = nblock * FLASH_PROTECT_BYTES;
+	*offset = start * FLASH_PROTECT_BYTES;
+	return EC_SUCCESS;
+}
+
+int flash_set_write_protect_range(int offset, int size)
+{
+	int start, nblock;
+	int rv;
+
+	if ((offset < 0) || (size < 0) || ((offset + size) >
+			(LM4_FLASH_FSIZE * FLASH_PROTECT_BYTES)))
+		return EC_ERROR_UNKNOWN; /* Invalid range */
+
+	rv = flash_get_write_protect_status();
+
+	if (rv & EC_FLASH_WP_RANGE_LOCKED) {
+		if (size == 0) {
+			/* TODO: Clear shadow if system WP is asserted */
+			/* TODO: Reboot EC */
+			return EC_SUCCESS;
+		}
+
+		return EC_ERROR_UNKNOWN; /* Range locked */
+	}
+
+	start = offset / FLASH_PROTECT_BYTES;
+	nblock = ((offset + size - 1) / FLASH_PROTECT_BYTES) - start + 1;
+	rv = set_wp_range(start, nblock);
+	if (rv)
+		return rv;
+
+	return EC_SUCCESS;
+}
+
+
+int flash_get_write_protect_status(void)
+{
+	int start, nblock;
+	int rv;
+
+	rv = get_wp_range(&start, &nblock);
+	if (rv)
+		return rv;
+
+	rv = 0;
+	if (nblock)
+		rv |= EC_FLASH_WP_RANGE_LOCKED;
+	/* TODO: get WP gpio*/
+
+	return rv;
+}
+
+
+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 (crosbug.com/p/7453) - check WP# GPIO.  If it's set and the
+	 * flash protect range is set, write the flash protection registers.
+	 * Probably cleaner to do this in vboot, since we're going to need to
+	 * use the same last block of flash to hold the firmware rollback
+	 * counters. */
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/gpio.c b/chip/lm4/gpio.c
new file mode 100644
index 0000000..4c11e30
--- /dev/null
+++ b/chip/lm4/gpio.c
@@ -0,0 +1,260 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* GPIO module for Chrome EC */
+
+#include "board.h"
+#include "gpio.h"
+#include "power_button.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+
+/* 0-terminated list of GPIO bases */
+const uint32_t gpio_bases[] = {
+	LM4_GPIO_A, LM4_GPIO_B, LM4_GPIO_C, LM4_GPIO_D,
+	LM4_GPIO_E, LM4_GPIO_F, LM4_GPIO_G, LM4_GPIO_H,
+	LM4_GPIO_J, LM4_GPIO_K, LM4_GPIO_L, LM4_GPIO_M,
+	LM4_GPIO_N, LM4_GPIO_P, LM4_GPIO_Q, 0
+};
+
+
+/* Signal information from board.c.  Must match order from enum gpio_signal. */
+extern const struct gpio_info gpio_list[GPIO_COUNT];
+
+
+/* Find the index of a GPIO port base address (LM4_GPIO_[A-Q]); this is used by
+ * the clock gating registers.  Returns the index, or -1 if no match. */
+static int find_gpio_port_index(uint32_t port_base)
+{
+	int i;
+	for (i = 0; gpio_bases[i]; i++) {
+		if (gpio_bases[i] == port_base)
+			return i;
+	}
+	return -1;
+}
+
+
+int gpio_pre_init(void)
+{
+	volatile uint32_t scratch  __attribute__((unused));
+	const struct gpio_info *g = gpio_list;
+	int i;
+
+	/* Enable clocks to all the GPIO blocks (since we use all of them as
+	 * GPIOs) */
+	LM4_SYSTEM_RCGCGPIO |= 0x7fff;
+	scratch = LM4_SYSTEM_RCGCGPIO;  /* Delay a few clocks */
+
+	/* Disable GPIO commit control for PD7 and PF0, since we don't use the
+	 * NMI pin function. */
+	LM4_GPIO_LOCK(LM4_GPIO_D) = LM4_GPIO_LOCK_UNLOCK;
+	LM4_GPIO_CR(LM4_GPIO_D) |= 0x80;
+	LM4_GPIO_LOCK(LM4_GPIO_D) = 0;
+	LM4_GPIO_LOCK(LM4_GPIO_F) = LM4_GPIO_LOCK_UNLOCK;
+	LM4_GPIO_CR(LM4_GPIO_F) |= 0x01;
+	LM4_GPIO_LOCK(LM4_GPIO_F) = 0;
+
+	/* Clear SSI0 alternate function on PA2:5 */
+	LM4_GPIO_AFSEL(LM4_GPIO_A) &= ~0x3c;
+
+	/* Set all GPIOs to defaults */
+	for (i = 0; i < GPIO_COUNT; i++, g++) {
+
+		/* Use as GPIO, not alternate function */
+		gpio_set_alternate_function(g->port, g->mask, 0);
+
+		/* Handle GPIO direction */
+		if (g->flags & GPIO_OUTPUT) {
+			/* Output with default level */
+			LM4_GPIO_DIR(g->port) |= g->mask;
+			/* Must set level after direction; writes to GPIO_DATA
+			 * before direction is output appear to be ignored. */
+			gpio_set_level(i, g->flags & GPIO_HIGH);
+		} else {
+			/* Input */
+			if (g->flags & GPIO_PULL) {
+				/* With pull up/down */
+				if (g->flags & GPIO_HIGH)
+					LM4_GPIO_PUR(g->port) |= g->mask;
+				else
+					LM4_GPIO_PDR(g->port) |= g->mask;
+			}
+		}
+
+		/* Set up interrupts if necessary */
+		if (g->flags & GPIO_INT_LEVEL)
+			LM4_GPIO_IS(g->port) |= g->mask;
+		if (g->flags & (GPIO_INT_RISING | GPIO_INT_HIGH))
+			LM4_GPIO_IEV(g->port) |= g->mask;
+		if (g->flags & GPIO_INT_BOTH)
+			LM4_GPIO_IBE(g->port) |= g->mask;
+		/* Interrupt is enabled by gpio_enable_interrupt() */
+	}
+
+	/* Enable IRQs now that pins are set up */
+	task_enable_irq(LM4_IRQ_GPIOA);
+	task_enable_irq(LM4_IRQ_GPIOB);
+	task_enable_irq(LM4_IRQ_GPIOC);
+	task_enable_irq(LM4_IRQ_GPIOD);
+	task_enable_irq(LM4_IRQ_GPIOE);
+	task_enable_irq(LM4_IRQ_GPIOF);
+	task_enable_irq(LM4_IRQ_GPIOG);
+#if (KB_SCAN_ROW_IRQ != LM4_IRQ_GPIOH)
+	task_enable_irq(LM4_IRQ_GPIOH);
+#endif
+	task_enable_irq(LM4_IRQ_GPIOJ);
+	task_enable_irq(LM4_IRQ_GPIOK);
+	task_enable_irq(LM4_IRQ_GPIOL);
+	task_enable_irq(LM4_IRQ_GPIOM);
+#if (KB_SCAN_ROW_IRQ != LM4_IRQ_GPION)
+	task_enable_irq(LM4_IRQ_GPION);
+#endif
+	task_enable_irq(LM4_IRQ_GPIOP);
+	task_enable_irq(LM4_IRQ_GPIOQ);
+
+	return EC_SUCCESS;
+}
+
+
+void gpio_set_alternate_function(int port, int mask, int func)
+{
+	int port_index = find_gpio_port_index(port);
+	int cgmask;
+
+	if (port_index < 0)
+		return;  /* TODO: assert */
+
+	/* Enable the GPIO port if necessary */
+	cgmask = 1 << port_index;
+	if ((LM4_SYSTEM_RCGCGPIO & cgmask) != cgmask) {
+		volatile uint32_t scratch  __attribute__((unused));
+		LM4_SYSTEM_RCGCGPIO |= cgmask;
+		/* Delay a few clocks before accessing GPIO registers on that
+		 * port. */
+		scratch = LM4_SYSTEM_RCGCGPIO;
+	}
+
+	if (func) {
+		int pctlmask = 0;
+		int i;
+		/* Expand mask from bits to nibbles */
+		for (i = 0; i < 8; i++) {
+			if (mask & (1 << i))
+				pctlmask |= 1 << (4 * i);
+		}
+
+		LM4_GPIO_PCTL(port) =
+			(LM4_GPIO_PCTL(port) & ~(pctlmask * 0xf)) |
+			(pctlmask * func);
+		LM4_GPIO_AFSEL(port) |= mask;
+	} else {
+		LM4_GPIO_AFSEL(port) &= ~mask;
+	}
+	LM4_GPIO_DEN(port) |= mask;
+}
+
+
+int gpio_get_level(enum gpio_signal signal)
+{
+	return LM4_GPIO_DATA(gpio_list[signal].port,
+			     gpio_list[signal].mask) ? 1 : 0;
+}
+
+
+int gpio_set_level(enum gpio_signal signal, int value)
+{
+	/* Ok to write 0xff becuase LM4_GPIO_DATA bit-masks only the bit
+	 * we care about. */
+	LM4_GPIO_DATA(gpio_list[signal].port,
+		      gpio_list[signal].mask) = (value ? 0xff : 0);
+	return EC_SUCCESS;
+}
+
+
+int gpio_enable_interrupt(enum gpio_signal signal)
+{
+	const struct gpio_info *g = gpio_list + signal;
+
+	/* Fail if no interrupt handler */
+	if (!g->irq_handler)
+		return EC_ERROR_UNKNOWN;
+
+	LM4_GPIO_IM(g->port) |= g->mask;
+	return EC_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Interrupt handlers */
+
+static void gpio_interrupt(int port, uint32_t mis)
+{
+	int i = 0;
+	const struct gpio_info *g = gpio_list;
+
+	for (i = 0; i < GPIO_COUNT; i++, g++) {
+		if (port == g->port && (mis & g->mask) && g->irq_handler)
+			g->irq_handler(i);
+	}
+}
+
+/* Handlers for each GPIO port.  These read and clear the interrupt bits for
+ * the port, then call the master handler above. */
+#define GPIO_IRQ_FUNC(irqfunc, gpiobase)		\
+	static void irqfunc(void)			\
+	{						\
+		uint32_t mis = LM4_GPIO_MIS(gpiobase);	\
+		LM4_GPIO_ICR(gpiobase) = mis;		\
+		gpio_interrupt(gpiobase, mis);		\
+	}
+
+GPIO_IRQ_FUNC(__gpio_a_interrupt, LM4_GPIO_A);
+GPIO_IRQ_FUNC(__gpio_b_interrupt, LM4_GPIO_B);
+GPIO_IRQ_FUNC(__gpio_c_interrupt, LM4_GPIO_C);
+GPIO_IRQ_FUNC(__gpio_d_interrupt, LM4_GPIO_D);
+GPIO_IRQ_FUNC(__gpio_e_interrupt, LM4_GPIO_E);
+GPIO_IRQ_FUNC(__gpio_f_interrupt, LM4_GPIO_F);
+GPIO_IRQ_FUNC(__gpio_g_interrupt, LM4_GPIO_G);
+#if (KB_SCAN_ROW_GPIO != LM4_GPIO_H)
+GPIO_IRQ_FUNC(__gpio_h_interrupt, LM4_GPIO_H);
+#endif
+GPIO_IRQ_FUNC(__gpio_j_interrupt, LM4_GPIO_J);
+GPIO_IRQ_FUNC(__gpio_k_interrupt, LM4_GPIO_K);
+GPIO_IRQ_FUNC(__gpio_l_interrupt, LM4_GPIO_L);
+GPIO_IRQ_FUNC(__gpio_m_interrupt, LM4_GPIO_M);
+#if (KB_SCAN_ROW_GPIO != LM4_GPIO_N)
+GPIO_IRQ_FUNC(__gpio_n_interrupt, LM4_GPIO_N);
+#endif
+GPIO_IRQ_FUNC(__gpio_p_interrupt, LM4_GPIO_P);
+GPIO_IRQ_FUNC(__gpio_q_interrupt, LM4_GPIO_Q);
+
+#undef GPIO_IRQ_FUNC
+
+/* Declare IRQs */
+/* TODO: nesting this macro inside the GPIO_IRQ_FUNC macro works poorly because
+ * DECLARE_IRQ() stringizes its inputs. */
+DECLARE_IRQ(LM4_IRQ_GPIOA, __gpio_a_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOB, __gpio_b_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOC, __gpio_c_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOD, __gpio_d_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOE, __gpio_e_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOF, __gpio_f_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOG, __gpio_g_interrupt, 1);
+#if (KB_SCAN_ROW_IRQ != LM4_IRQ_GPIOH)
+DECLARE_IRQ(LM4_IRQ_GPIOH, __gpio_h_interrupt, 1);
+#endif
+DECLARE_IRQ(LM4_IRQ_GPIOJ, __gpio_j_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOK, __gpio_k_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOL, __gpio_l_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOM, __gpio_m_interrupt, 1);
+#if (KB_SCAN_ROW_IRQ != LM4_IRQ_GPION)
+DECLARE_IRQ(LM4_IRQ_GPION, __gpio_n_interrupt, 1);
+#endif
+DECLARE_IRQ(LM4_IRQ_GPIOP, __gpio_p_interrupt, 1);
+DECLARE_IRQ(LM4_IRQ_GPIOQ, __gpio_q_interrupt, 1);
diff --git a/chip/lm4/hwtimer.c b/chip/lm4/hwtimer.c
new file mode 100644
index 0000000..9b22101
--- /dev/null
+++ b/chip/lm4/hwtimer.c
@@ -0,0 +1,91 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Hardware timers driver */
+
+#include <stdint.h>
+
+#include "board.h"
+#include "hwtimer.h"
+#include "registers.h"
+#include "task.h"
+
+#define US_PER_SECOND 1000000
+
+/* Divider to get microsecond for the clock */
+#define CLOCKSOURCE_DIVIDER (CPU_CLOCK/US_PER_SECOND)
+
+void __hw_clock_event_set(uint32_t deadline)
+{
+	/* set the match on the deadline */
+	LM4_TIMER_TAMATCHR(6) = 0xffffffff - deadline;
+	/* Set the match interrupt */
+	LM4_TIMER_IMR(6) |= 0x10;
+}
+
+uint32_t __hw_clock_event_get(void)
+{
+	return 0xffffffff - LM4_TIMER_TAMATCHR(6);
+}
+
+void __hw_clock_event_clear(void)
+{
+	/* Disable the match interrupt */
+	LM4_TIMER_IMR(6) &= ~0x10;
+}
+
+uint32_t __hw_clock_source_read(void)
+{
+	return 0xffffffff - LM4_TIMER_TAV(6);
+}
+
+static void __hw_clock_source_irq(void)
+{
+	uint32_t status = LM4_TIMER_RIS(6);
+
+	/* clear interrupt */
+	LM4_TIMER_ICR(6) = status;
+
+	/*
+	 * Find expired timers and set the new timer deadline
+	 * get from the IRQ status if the free running counter as overflowed
+	 */
+	process_timers(status & 0x01);
+}
+DECLARE_IRQ(LM4_IRQ_TIMERW0A, __hw_clock_source_irq, 1);
+
+
+int __hw_clock_source_init(void)
+{
+	volatile uint32_t scratch __attribute__((unused));
+
+	/* Use WTIMER0 (timer 6) configured as a free running counter with 1 us
+	 * period */
+
+	/* Enable WTIMER0 clock */
+	LM4_SYSTEM_RCGCWTIMER |= 1;
+	/* wait 3 clock cycles before using the module */
+	scratch = LM4_SYSTEM_RCGCWTIMER;
+
+	/* Ensure timer is disabled : TAEN = TBEN = 0 */
+	LM4_TIMER_CTL(6) &= ~0x101;
+	/* Set overflow interrupt */
+	LM4_TIMER_IMR(6) = 0x1;
+	/* 32-bit timer mode */
+	LM4_TIMER_CFG(6) = 4;
+	/* set the prescaler to increment every microsecond */
+	LM4_TIMER_TAPR(6) = CLOCKSOURCE_DIVIDER;
+	/* Periodic mode, counting down */
+	LM4_TIMER_TAMR(6) = 0x22;
+	/* use the full 32-bits of the timer */
+	LM4_TIMER_TAILR(6) = 0xffffffff;
+	/* Starts counting in timer A */
+	LM4_TIMER_CTL(6) |= 0x1;
+
+	/* Enable interrupt */
+	task_enable_irq(LM4_IRQ_TIMERW0A);
+
+	return LM4_IRQ_TIMERW0A;
+}
diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c
new file mode 100644
index 0000000..0f31fc3
--- /dev/null
+++ b/chip/lm4/i2c.c
@@ -0,0 +1,320 @@
+/* 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 port module for Chrome EC */
+
+#include "board.h"
+#include "console.h"
+#include "gpio.h"
+#include "i2c.h"
+#include "task.h"
+#include "timer.h"
+#include "registers.h"
+#include "uart.h"
+#include "util.h"
+
+#define NUM_PORTS 6
+
+static task_id_t task_waiting_on_port[NUM_PORTS];
+
+
+static int wait_idle(int port)
+{
+	int i;
+	int wait_msg;
+
+	i = LM4_I2C_MCS(port);
+	if (i & 0x01) {
+		/* Port is busy, so wait for the interrupt */
+		task_waiting_on_port[port] = task_get_current();
+		LM4_I2C_MIMR(port) = 0x03;
+		wait_msg = task_wait_msg(1000000);
+		LM4_I2C_MIMR(port) = 0x00;
+		task_waiting_on_port[port] = -1;
+		if (wait_msg == 1 << TASK_ID_TIMER)
+			return EC_ERROR_TIMEOUT;
+
+		i = LM4_I2C_MCS(port);
+	}
+
+	/* Check for errors */
+	if (i & 0x02)
+		return EC_ERROR_UNKNOWN;
+
+	return EC_SUCCESS;
+}
+
+
+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);
+}
+
+
+/*****************************************************************************/
+/* Interrupt handlers */
+
+/* Handles an interrupt on the specified port. */
+static void handle_interrupt(int port)
+{
+	int id = task_waiting_on_port[port];
+
+	/* Clear the interrupt status*/
+	LM4_I2C_MICR(port) = LM4_I2C_MMIS(port);
+
+	/* Wake up the task which was waiting on the interrupt, if any */
+	/* TODO: send message based on I2C port number? */
+	if (id != TASK_ID_INVALID)
+		task_send_msg(id, id, 0);
+}
+
+
+static void i2c0_interrupt(void) { handle_interrupt(0); }
+static void i2c1_interrupt(void) { handle_interrupt(1); }
+static void i2c2_interrupt(void) { handle_interrupt(2); }
+static void i2c3_interrupt(void) { handle_interrupt(3); }
+static void i2c4_interrupt(void) { handle_interrupt(4); }
+static void i2c5_interrupt(void) { handle_interrupt(5); }
+
+DECLARE_IRQ(LM4_IRQ_I2C0, i2c0_interrupt, 2);
+DECLARE_IRQ(LM4_IRQ_I2C1, i2c1_interrupt, 2);
+DECLARE_IRQ(LM4_IRQ_I2C2, i2c2_interrupt, 2);
+DECLARE_IRQ(LM4_IRQ_I2C3, i2c3_interrupt, 2);
+DECLARE_IRQ(LM4_IRQ_I2C4, i2c4_interrupt, 2);
+DECLARE_IRQ(LM4_IRQ_I2C5, i2c5_interrupt, 2);
+
+/*****************************************************************************/
+/* Console commands */
+
+
+static void scan_bus(int port, char *desc)
+{
+	int rv;
+	int a;
+
+	uart_printf("Scanning %s I2C bus (%d)...\n", desc, port);
+
+	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 8-bit addr 0x%02x\n", a);
+	}
+	uart_puts("\n");
+}
+
+
+static int command_i2cread(int argc, char **argv)
+{
+	int port, addr, count = 1;
+	char *e;
+	int rv;
+	int d, i;
+
+	if (argc < 3) {
+		uart_puts("Usage: i2cread <port> <addr> [count]\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	port = strtoi(argv[1], &e, 0);
+	if (*e) {
+		uart_puts("Invalid port\n");
+		return EC_ERROR_INVAL;
+	}
+	if (port != I2C_PORT_THERMAL && port != I2C_PORT_BATTERY &&
+	    port != I2C_PORT_CHARGER) {
+		uart_puts("Unsupported port\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	addr = strtoi(argv[2], &e, 0);
+	if (*e || (addr & 0x01)) {
+		uart_puts("Invalid addr; try 'i2cscan' command\n");
+		return EC_ERROR_INVAL;
+	}
+
+	if (argc > 3) {
+		count = strtoi(argv[3], &e, 0);
+		if (*e) {
+			uart_puts("Invalid count\n");
+			return EC_ERROR_INVAL;
+		}
+	}
+
+	uart_printf("Reading %d bytes from I2C device %d:0x%02x...\n",
+		    count, port, addr);
+	LM4_I2C_MSA(port) = addr | 0x01;
+	for (i = 0; i < count; i++) {
+		if (i == 0)
+			LM4_I2C_MCS(port) = (count > 1 ? 0x0b : 0x07);
+		else
+			LM4_I2C_MCS(port) = (i == count - 1 ? 0x05 : 0x09);
+		rv = wait_idle(port);
+		if (rv != EC_SUCCESS)
+			return rv;
+		d = LM4_I2C_MDR(port) & 0xff;
+		uart_printf("0x%02x ", d);
+	}
+	uart_puts("\n");
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(i2cread, command_i2cread);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(i2cscan, command_scan);
+
+
+/*****************************************************************************/
+/* Initialization */
+
+/* Configures GPIOs for the module. */
+static void configure_gpio(void)
+{
+#ifdef BOARD_link
+	/* PA6:7 = I2C1 SCL/SDA; PB2:3 = I2C0 SCL/SDA; PB6:7 = I2C5 SCL/SDA */
+	gpio_set_alternate_function(LM4_GPIO_A, 0xc0, 3);
+	gpio_set_alternate_function(LM4_GPIO_B, 0xcc, 3);
+
+	/* Configure SDA as open-drain.  SCL should not be open-drain,
+	 * since it has an internal pull-up. */
+	LM4_GPIO_ODR(LM4_GPIO_A) |= 0x80;
+	LM4_GPIO_ODR(LM4_GPIO_B) |= 0x88;
+#else
+	/* PG6:7 = I2C5 SCL/SDA */
+	gpio_set_alternate_function(LM4_GPIO_G, 0xc0, 3);
+
+	/* Configure SDA as open-drain.  SCL should not be open-drain,
+	 * since it has an internal pull-up. */
+	LM4_GPIO_ODR(LM4_GPIO_G) |= 0x80;
+#endif
+}
+
+
+int i2c_init(void)
+{
+	volatile uint32_t scratch  __attribute__((unused));
+	int i;
+
+	/* Enable I2C modules 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();
+
+	/* No tasks are waiting on ports */
+	for (i = 0; i < NUM_PORTS; i++)
+		task_waiting_on_port[i] = TASK_ID_INVALID;
+
+	/* Initialize ports as master, with interrupts enabled */
+	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;
+
+	/* Enable irqs */
+	task_enable_irq(LM4_IRQ_I2C0);
+	task_enable_irq(LM4_IRQ_I2C1);
+	task_enable_irq(LM4_IRQ_I2C2);
+	task_enable_irq(LM4_IRQ_I2C3);
+	task_enable_irq(LM4_IRQ_I2C4);
+	task_enable_irq(LM4_IRQ_I2C5);
+
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/jtag.c b/chip/lm4/jtag.c
new file mode 100644
index 0000000..22c9080
--- /dev/null
+++ b/chip/lm4/jtag.c
@@ -0,0 +1,40 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "jtag.h"
+#include "registers.h"
+
+
+int jtag_pre_init(void)
+{
+	/* Enable clocks to GPIO block C */
+	LM4_SYSTEM_RCGCGPIO |= 0x0004;
+
+	/* Ensure PC0:3 are set to JTAG function.  They should be set this way
+	 * on a cold boot, but on a warm reboot a previous misbehaving image
+	 * could have set them differently. */
+	if (((LM4_GPIO_PCTL(LM4_GPIO_C) & 0x0000ffff) == 0x00001111) &&
+	    ((LM4_GPIO_AFSEL(LM4_GPIO_C) & 0x0f) == 0x0f) &&
+	    ((LM4_GPIO_DEN(LM4_GPIO_C) & 0x0f) == 0x0f) &&
+	    ((LM4_GPIO_PUR(LM4_GPIO_C) & 0x0f) == 0x0f))
+		return EC_SUCCESS;  /* Already properly configured */
+
+	/* Unlock commit register for JTAG pins */
+	LM4_GPIO_LOCK(LM4_GPIO_C) = LM4_GPIO_LOCK_UNLOCK;
+	LM4_GPIO_CR(LM4_GPIO_C) |= 0x0f;
+
+	/* Reset JTAG pins */
+	LM4_GPIO_PCTL(LM4_GPIO_C) =
+		(LM4_GPIO_PCTL(LM4_GPIO_C) & 0xffff0000) | 0x00001111;
+	LM4_GPIO_AFSEL(LM4_GPIO_C) |= 0x0f;
+	LM4_GPIO_DEN(LM4_GPIO_C) |= 0x0f;
+	LM4_GPIO_PUR(LM4_GPIO_C) |= 0x0f;
+
+	/* Re-lock commit register */
+	LM4_GPIO_CR(LM4_GPIO_C) &= ~0x0f;
+	LM4_GPIO_LOCK(LM4_GPIO_C) = 0;
+
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/keyboard_scan.c b/chip/lm4/keyboard_scan.c
new file mode 100644
index 0000000..4056f7a
--- /dev/null
+++ b/chip/lm4/keyboard_scan.c
@@ -0,0 +1,326 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Keyboard scanner module for Chrome EC */
+
+#include "board.h"
+#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"
+
+
+/* Notes:
+ *
+ * Link proto0 board:
+ *
+ *   Columns (outputs):
+ *      KSO0 - KSO7  = PP0:7
+ *      KSO8 - KSO12 = PQ0:4
+ *
+ *   Rows (inputs):
+ *      KSI0 - KSI7  = PN0:7
+ *
+ *   Other:
+ *      PWR_BTN#     = PK7 (handled by gpio module)
+ *
+ *
+ * BDS board:
+ *
+ *   Columns (outputs):
+ *      KSO0 - KSO7  = PQ0:7
+ *      KSO8 - KSO11 = PK0:3
+ *      KSO12        = PN2
+ *   Rows (inputs):
+ *      KSI0 - KSI7  = PH0:7
+ *   Other:
+ *      PWR_BTN#     = PC5 (handled by gpio module)
+ */
+
+
+/* used for select_column() */
+enum COLUMN_INDEX {
+	COLUMN_ASSERT_ALL = -2,
+	COLUMN_TRI_STATE_ALL = -1,
+	/* 0 ~ 12 for the corresponding column */
+};
+
+#define POLLING_MODE_TIMEOUT 1000000  /* 1 sec */
+#define SCAN_LOOP_DELAY 10000         /* 10 ms */
+
+#define KB_COLS 13
+
+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: (crosbug.com/p/7485) 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)
+{
+#ifdef BOARD_link
+	if (col == COLUMN_ASSERT_ALL) {
+		LM4_GPIO_DIR(LM4_GPIO_P) = 0xff;
+		LM4_GPIO_DIR(LM4_GPIO_Q) |= 0x1f;
+		LM4_GPIO_DATA(LM4_GPIO_P, 0xff) = 0;
+		LM4_GPIO_DATA(LM4_GPIO_Q, 0x1f) = 0;
+	} else {
+		LM4_GPIO_DIR(LM4_GPIO_P) = 0;
+		LM4_GPIO_DIR(LM4_GPIO_Q) &= ~0x1f;
+		if (col < 8) {
+			LM4_GPIO_DIR(LM4_GPIO_P) |= 1 << col;
+			LM4_GPIO_DATA(LM4_GPIO_P, 1 << col) = 0;
+		} else if (col != COLUMN_TRI_STATE_ALL) {
+			LM4_GPIO_DIR(LM4_GPIO_Q) |= 1 << (col - 8);
+			LM4_GPIO_DATA(LM4_GPIO_Q, 1 << (col - 8)) = 0;
+		}
+	}
+#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 == COLUMN_ASSERT_ALL) {
+		LM4_GPIO_DIR(LM4_GPIO_Q) = 0xff;
+		LM4_GPIO_DIR(LM4_GPIO_K) |= 0x0f;
+		LM4_GPIO_DIR(LM4_GPIO_N) |= 0x04;
+		LM4_GPIO_DATA(LM4_GPIO_Q, 0xff) = 0;
+		LM4_GPIO_DATA(LM4_GPIO_K, 0xff) &= ~0x0f;
+		LM4_GPIO_DATA(LM4_GPIO_N, 0xff) &= ~0x04;
+	} else if (col == COLUMN_TRI_STATE_ALL) {
+		/* All tri-stated */
+		LM4_GPIO_DIR(LM4_GPIO_Q) = 0;
+		LM4_GPIO_DIR(LM4_GPIO_K) &= ~0x0f;
+		LM4_GPIO_DIR(LM4_GPIO_N) &= ~0x04;
+	} else if (col < 8) {
+		LM4_GPIO_DIR(LM4_GPIO_Q) = 1 << col;
+		LM4_GPIO_DIR(LM4_GPIO_K) &= ~0x0f;
+		LM4_GPIO_DIR(LM4_GPIO_N) &= ~0x04;
+		LM4_GPIO_DATA(LM4_GPIO_Q, 0xff) = ~(1 << col);
+	} else if (col < 12) {
+		LM4_GPIO_DIR(LM4_GPIO_Q) = 0;
+		LM4_GPIO_DIR(LM4_GPIO_K) = (LM4_GPIO_DIR(LM4_GPIO_K) & ~0x0f) |
+			(1 << (col - 8));
+		LM4_GPIO_DIR(LM4_GPIO_N) &= ~0x04;
+		LM4_GPIO_DATA(LM4_GPIO_K, 0x0f) = ~(1 << (col - 8));
+	} else {  /* col == 12 */
+		LM4_GPIO_DIR(LM4_GPIO_Q) = 0;
+		LM4_GPIO_DIR(LM4_GPIO_K) &= ~0x0f;
+		LM4_GPIO_DIR(LM4_GPIO_N) |= 0x04;
+		LM4_GPIO_DATA(LM4_GPIO_N, 0x04) = ~0x04;
+	}
+#endif
+}
+
+int keyboard_scan_init(void)
+{
+	volatile uint32_t scratch  __attribute__((unused));
+	int i;
+
+        /* Enable GPIOs */
+#ifdef BOARD_link
+	/* Enable clock to GPIO modules N,P,Q */
+	LM4_SYSTEM_RCGCGPIO |= 0x7000;
+#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 rows */
+#ifdef BOARD_link
+	LM4_GPIO_AFSEL(LM4_GPIO_P) = 0;  /* KSO[7:0] */
+	LM4_GPIO_DEN(LM4_GPIO_P) = 0xff;
+	LM4_GPIO_AFSEL(LM4_GPIO_Q) &= ~0x1f;  /* KSO[12:8] */
+	LM4_GPIO_DEN(LM4_GPIO_Q) |= 0x1f;
+#else
+	LM4_GPIO_AFSEL(LM4_GPIO_K) &= ~0x0f;
+	LM4_GPIO_DEN(LM4_GPIO_K) |= 0x0f;
+	LM4_GPIO_AFSEL(LM4_GPIO_N) &= ~0x04;
+	LM4_GPIO_DEN(LM4_GPIO_N) |= 0x04;
+	LM4_GPIO_AFSEL(LM4_GPIO_Q) = 0;
+	LM4_GPIO_DEN(LM4_GPIO_Q) = 0xff;
+#endif
+
+	/* Set row inputs with pull-up */
+	LM4_GPIO_AFSEL(KB_SCAN_ROW_GPIO) &= 0xff;
+	LM4_GPIO_DEN(KB_SCAN_ROW_GPIO) |= 0xff;
+	LM4_GPIO_DIR(KB_SCAN_ROW_GPIO) = 0;
+	LM4_GPIO_PUR(KB_SCAN_ROW_GPIO) = 0xff;
+
+	/* Tri-state the columns */
+	select_column(COLUMN_TRI_STATE_ALL);
+
+	/* 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];
+
+	/* Enable interrupts, now that we're set up */
+	task_enable_irq(KB_SCAN_ROW_IRQ);
+
+	return EC_SUCCESS;
+}
+
+
+static uint32_t clear_matrix_interrupt_status(void) {
+	uint32_t ris = LM4_GPIO_RIS(KB_SCAN_ROW_GPIO);
+	LM4_GPIO_ICR(KB_SCAN_ROW_GPIO) = ris;
+
+	return ris;
+}
+
+
+void wait_for_interrupt(void)
+{
+	uart_printf("[kbscan %s()]\n", __func__);
+
+	/* Assert all outputs would trigger un-wanted interrupts.
+	 * Clear them before enable interrupt. */
+	select_column(COLUMN_ASSERT_ALL);
+	clear_matrix_interrupt_status();
+
+	LM4_GPIO_IS(KB_SCAN_ROW_GPIO) = 0;      /* 0: edge-sensitive */
+	LM4_GPIO_IBE(KB_SCAN_ROW_GPIO) = 0xff;  /* 1: both edge */
+	LM4_GPIO_IM(KB_SCAN_ROW_GPIO) = 0xff;   /* 1: enable interrupt */
+}
+
+
+void enter_polling_mode(void)
+{
+	uart_printf("[kbscan %s()]\n", __func__);
+	LM4_GPIO_IM(KB_SCAN_ROW_GPIO) = 0;  /* 0: disable interrupt */
+	select_column(COLUMN_TRI_STATE_ALL);
+}
+
+
+/* Returns 1 if any key is still pressed. 0 if no key is pressed. */
+static int check_keys_changed(void)
+{
+	int c;
+	uint8_t r;
+	int change = 0;
+	int num_press = 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 */
+		r = LM4_GPIO_DATA(KB_SCAN_ROW_GPIO, 0xff);
+		/* 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(COLUMN_TRI_STATE_ALL);
+
+	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");
+	}
+
+	/* Count number of key pressed */
+	for (c = 0; c < KB_COLS; c++) {
+		if (raw_state[c]) ++num_press;
+	}
+
+	return num_press ? 1 : 0;
+}
+
+
+void keyboard_scan_task(void)
+{
+	int key_press_timer = 0;
+
+	keyboard_scan_init();
+
+	while (1) {
+		wait_for_interrupt();
+		task_wait_msg(-1);
+
+		enter_polling_mode();
+		/* Busy polling keyboard state. */
+		while (1) {
+			/* sleep for debounce. */
+			usleep(SCAN_LOOP_DELAY);
+			/* Check for keys down */
+			if (check_keys_changed()) {
+				key_press_timer = 0;
+			} else {
+				if (++key_press_timer >=
+				    (POLLING_MODE_TIMEOUT / SCAN_LOOP_DELAY)) {
+					key_press_timer = 0;
+					break;  /* exit the while loop */
+				}
+			}
+		}
+		/* TODO: (crosbug.com/p/7484) A race condition here.
+		 *       If a key state is changed here (before interrupt is
+		 *       enabled), it will be lost.
+		 */
+	}
+}
+
+
+
+static void matrix_interrupt(void)
+{
+	uint32_t ris = clear_matrix_interrupt_status();
+
+	if (ris) {
+		task_send_msg(TASK_ID_KEYSCAN, TASK_ID_KEYSCAN, 0);
+	}
+}
+DECLARE_IRQ(KB_SCAN_ROW_IRQ, matrix_interrupt, 3);
diff --git a/chip/lm4/lm4_adc.h b/chip/lm4/lm4_adc.h
new file mode 100644
index 0000000..98f5293
--- /dev/null
+++ b/chip/lm4/lm4_adc.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* LM4-specific ADC module for Chrome EC */
+
+#ifndef __CROS_EC_LM4_ADC_H
+#define __CROS_EC_LM4_ADC_H
+
+enum lm4_adc_sequencer
+{
+	LM4_ADC_SEQ0 = 0,
+	LM4_ADC_SEQ1,
+	LM4_ADC_SEQ2,
+	LM4_ADC_SEQ3,
+
+	LM4_ADC_SEQ_COUNT
+};
+
+/* Minimum and maximum values returned by lm4_adc_flush_and_read(). */
+#define ADC_READ_MIN 0
+#define ADC_READ_MAX 4095
+
+/* Value returned if the read failed. */
+#define ADC_READ_ERROR -1
+
+/* Just plain id mapping for code readability */
+#define LM4_AIN(x) (x)
+
+/* Dummy value for "channel" in adc_t if we don't have an external channel. */
+#define LM4_AIN_NONE (-1)
+
+/* Flush an ADC sequencer and initiate a read. Return raw ADC value. */
+int lm4_adc_flush_and_read(enum lm4_adc_sequencer);
+
+/* Configure an ADC sequencer to be dedicated for an ADC input "ain_id".
+ * Value in "ssctl" field is passed to sampler sequencer control register.
+ */
+int lm4_adc_configure(enum lm4_adc_sequencer, int ain_id, int ssctl);
+
+#endif /* __CROS_EC_LM4_ADC_H */
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c
new file mode 100644
index 0000000..674d35a
--- /dev/null
+++ b/chip/lm4/lpc.c
@@ -0,0 +1,287 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* LPC module for Chrome EC */
+
+#include "board.h"
+#include "gpio.h"
+#include "host_command.h"
+#include "i8042.h"
+#include "lpc.h"
+#include "lpc_commands.h"
+#include "port80.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+
+
+/* Configures GPIOs for module. */
+static void configure_gpio(void)
+{
+	/* Set digital alternate function 15 for PL0:5, PM0:2, PM4:5 pins. */
+	/* I/O: PL0:3 = command/address/data
+	 * inp: PL4 (frame), PL5 (reset), PM0 (powerdown), PM5 (clock)
+	 * out: PM1 (sci), PM4 (serirq) */
+	gpio_set_alternate_function(LM4_GPIO_L, 0x3f, 0x0f);
+	gpio_set_alternate_function(LM4_GPIO_M, 0x33, 0x0f);
+
+#ifdef BOARD_bds
+	/* Set the drive strength to 8mA for serirq only */
+	/* TODO: (crosbug.com/p/7495) Only necessary on BDS because the cabling
+	 * to the x86 is long and flaky; remove this for Link.  Setting this
+	 * for all I/O lines seems to hang the x86 during boot. */
+	LM4_GPIO_DR8R(LM4_GPIO_M) |= 0x00000010;
+#endif
+}
+
+
+static void wait_send_serirq(uint32_t lpcirqctl) {
+	LM4_LPC_LPCIRQCTL = lpcirqctl;
+
+	/* TODO: udelay() is not graceful. Since the SIRQRIS is almost not
+	 *       cleared in continuous mode and EC has problem to file
+	 *       more than 1 frame in the quiet mode, this is the best way
+	 *       we can do right now. */
+	udelay(4);  /* 4 us is the time of 2 SERIRQ frames, which is long
+	             * enough to guarantee the IRQ has been sent out. */
+}
+
+/* Manually generates an IRQ to host (edge-trigger).
+ *
+ * For SERIRQ quite mode, we need to set LM4_LPC_LPCIRQCTL twice.
+ * The first one is to assert IRQ (pull low), and then the second one is
+ * to de-assert it. This generates a pulse (high-low-high) for an IRQ.
+ *
+ * Note that the irq_num == 0 would set the AH bit (Active High).
+ */
+void lpc_manual_irq(int irq_num) {
+	uint32_t common_bits =
+	    0x00000004 |  /* PULSE */
+	    0x00000002 |  /* ONCHG - for quiet mode */
+	    0x00000001;   /* SND - send immediately */
+
+	/* send out the IRQ first. */
+	wait_send_serirq((1 << (irq_num + 16)) | common_bits);
+
+	/* generate a all-high frame to simulate a rising edge. */
+	wait_send_serirq(common_bits);
+}
+
+
+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;
+
+	LM4_LPC_LPCIM = 0;
+	LM4_LPC_LPCCTL = 0;
+	LM4_LPC_LPCIRQCTL = 0;
+
+	/* 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(LPC_CH_KERNEL) = EC_LPC_ADDR_KERNEL_DATA;
+	LM4_LPC_CTL(LPC_CH_KERNEL) = (LPC_POOL_OFFS_KERNEL << (5 - 1));
+	/* Unmask interrupt for host command writes */
+	LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_KERNEL, 4);
+
+	/* Set LPC channel 1 to I/O address 0x80 (data), single endpoint,
+	 * pool bytes 4(data)/5(cmd). */
+	LM4_LPC_ADR(LPC_CH_PORT80) = 0x80;
+	LM4_LPC_CTL(LPC_CH_PORT80) = (LPC_POOL_OFFS_PORT80 << (5 - 1));
+	/* Unmask interrupt for host data writes */
+	LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_PORT80, 2);
+
+
+	/* 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(LPC_CH_CMD_DATA) = EC_LPC_ADDR_KERNEL_PARAM;
+	LM4_LPC_CTL(LPC_CH_CMD_DATA) = 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(LPC_CH_KEYBOARD) = 0x60;
+	LM4_LPC_CTL(LPC_CH_KEYBOARD) = (1 << 24/* IRQSEL1 */) |
+		(0 << 18/* IRQEN1 */) | (LPC_POOL_OFFS_KEYBOARD << (5 - 1));
+	LM4_LPC_ST(LPC_CH_KEYBOARD) = 0;
+	/* Unmask interrupt for host command/data writes and data reads */
+	LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 7);
+
+	/* Set LPC channel 4 to I/O address 0x200 (data) / 0x204 (command),
+	 * single endpoint, offset 0 for host command/writes and 1 for EC
+	 * data writes, pool bytes 0(data)/1(cmd) */
+	LM4_LPC_ADR(LPC_CH_USER) = EC_LPC_ADDR_USER_DATA;
+	LM4_LPC_CTL(LPC_CH_USER) = (LPC_POOL_OFFS_USER << (5 - 1));
+	/* Unmask interrupt for host command writes */
+	LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_USER, 4);
+
+	/* Set LPC channel 7 to COM port I/O address.  Note that channel 7
+	 * ignores the TYPE bit and is always an 8-byte range. */
+	LM4_LPC_ADR(LPC_CH_COMX) = LPC_COMX_ADDR;
+	/* TODO: could configure IRQSELs and set IRQEN2/CX, and then the host
+	 * can enable IRQs on its own. */
+	LM4_LPC_CTL(LPC_CH_COMX) = 0x0004 | (LPC_POOL_OFFS_COMX << (5 - 1));
+	/* Enable COMx emulation for reads and writes. */
+	LM4_LPC_LPCDMACX = 0x00310000;
+	/* Unmask interrupt for host data writes.  We don't need interrupts for
+	 * reads, because there's no flow control in that direction; LPC is
+	 * much faster than the UART, and the UART doesn't have anywhere
+	 * sensible to buffer input anyway. */
+	LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_COMX, 2);
+
+	/* Enable LPC channels */
+	LM4_LPC_LPCCTL =
+		(1 << LPC_CH_KERNEL) |
+		(1 << LPC_CH_PORT80) |
+		(1 << LPC_CH_CMD_DATA) |
+		(1 << LPC_CH_KEYBOARD) |
+		(1 << LPC_CH_USER) |
+		(1 << LPC_CH_COMX);
+
+	/* Enable LPC interrupt */
+	task_enable_irq(LM4_IRQ_LPC);
+
+	return EC_SUCCESS;
+}
+
+
+uint8_t *lpc_get_host_range(int slot)
+{
+	return (uint8_t *)LPC_POOL_CMD_DATA + 256 * slot;
+}
+
+
+void lpc_send_host_response(int slot, int status)
+{
+	int ch = slot ? LPC_CH_USER : LPC_CH_KERNEL;
+
+	/* Set status nibble (bits 7:4 from host side) and clear the busy
+	 * bit (0x1000) (bit 2 from host side) */
+	LM4_LPC_ST(ch) = (LM4_LPC_ST(ch) & 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: (crosbug.com/p/7496) or it would, if we actually set up host
+	 * IRQs */
+	if (slot)
+		LPC_POOL_USER[1] = 0;
+	else
+		LPC_POOL_KERNEL[1] = 0;
+}
+
+
+/* Return true if the TOH is still set */
+int lpc_keyboard_has_char(void) {
+	return (LM4_LPC_ST(LPC_CH_KEYBOARD) & (1 << 0 /* TOH */)) ? 1 : 0;
+}
+
+
+void lpc_keyboard_put_char(uint8_t chr, int send_irq) {
+	LPC_POOL_KEYBOARD[1] = chr;
+	if (send_irq) {
+		lpc_manual_irq(1);  /* IRQ#1 */
+	}
+}
+
+
+int lpc_comx_has_char(void)
+{
+	return LM4_LPC_ST(LPC_CH_COMX) & 0x02;
+}
+
+
+int lpc_comx_get_char(void)
+{
+	return LPC_POOL_COMX[0];
+}
+
+
+void lpc_comx_put_char(int c)
+{
+	LPC_POOL_COMX[1] = c;
+	/* TODO: manually trigger IRQ, like we do for keyboard? */
+}
+
+
+
+/* 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;
+
+#ifdef CONFIG_TASK_HOSTCMD
+	/* Handle host kernel/user command writes */
+	if (mis & LM4_LPC_INT_MASK(LPC_CH_KERNEL, 4)) {
+		/* Set the busy bit and clear the status */
+		LM4_LPC_ST(LPC_CH_KERNEL) = (LM4_LPC_ST(LPC_CH_KERNEL) &
+					     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(0, LPC_POOL_KERNEL[0]);
+	}
+	if (mis & LM4_LPC_INT_MASK(LPC_CH_USER, 4)) {
+		/* Set the busy bit and clear the status */
+		LM4_LPC_ST(LPC_CH_USER) = (LM4_LPC_ST(LPC_CH_USER) &
+					   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(1, LPC_POOL_USER[0]);
+	}
+#endif
+
+	/* Handle port 80 writes (CH0MIS1) */
+	if (mis & LM4_LPC_INT_MASK(LPC_CH_PORT80, 2))
+		port_80_write(LPC_POOL_PORT80[0]);
+
+#ifdef CONFIG_TASK_I8042CMD
+	/* Handle port 60 command (CH3MIS2) and data (CH3MIS1) */
+	if (mis & LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 2)) {
+		/* 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 & LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 4)) {
+		/* 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 & LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 1)) {
+		/* Host picks up the data, try to send remaining bytes */
+		task_send_msg(TASK_ID_I8042CMD, TASK_ID_I8042CMD, 0);
+	}
+#endif
+
+	/* Handle COMx */
+	if (mis & LM4_LPC_INT_MASK(LPC_CH_COMX, 2)) {
+		/* Handle host writes */
+		if (lpc_comx_has_char()) {
+			/* Copy a character to the UART if there's space */
+			if (uart_comx_putc_ok())
+				uart_comx_putc(lpc_comx_get_char());
+		}
+	}
+}
+
+DECLARE_IRQ(LM4_IRQ_LPC, lpc_interrupt, 2);
diff --git a/chip/lm4/openocd/lm4x.cfg b/chip/lm4/openocd/lm4x.cfg
new file mode 100644
index 0000000..3619320
--- /dev/null
+++ b/chip/lm4/openocd/lm4x.cfg
@@ -0,0 +1,34 @@
+# Script for TI/Luminary Stellaris LM4F232H (LM4F232H5BB)
+
+if { [info exists CHIPNAME] } {
+   set  _CHIPNAME $CHIPNAME
+} else {
+   set  _CHIPNAME lm4f232h
+}
+
+if { [info exists CPUTAPID ] } {
+   set _CPUTAPID $CPUTAPID
+} else {
+   set _CPUTAPID 0x4ba00477
+}
+
+#jtag scan chain
+jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 1 -irmask 0xf -expected-id $_CPUTAPID
+
+# the luminary variant causes a software reset rather than asserting SRST
+# this stops the debug registers from being cleared
+# this will be fixed in later revisions of silicon
+set _TARGETNAME $_CHIPNAME.cpu
+# TODO(rspangler): variant lm4x?
+target create $_TARGETNAME cortex_m3 -chain-position $_CHIPNAME.cpu -variant lm3s
+
+# 8k working area at base of ram, not backed up
+#$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 0x2000
+# 12k working area at base of ram, not backed up
+$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 0x3000
+
+#flash configuration
+flash bank stellaris 0 0 0 0 $_TARGETNAME
+
+# useful command definitions for software loading
+source [find lm4x_cmds.tcl]
diff --git a/chip/lm4/openocd/lm4x_cmds.tcl b/chip/lm4/openocd/lm4x_cmds.tcl
new file mode 100644
index 0000000..d117db7
--- /dev/null
+++ b/chip/lm4/openocd/lm4x_cmds.tcl
@@ -0,0 +1,42 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Command automation for Blizzard LM4F chip
+
+# Program internal flash
+
+proc flash_lm4 {path size} {
+	set lastsect [expr {$size / 1024 - 1}];
+	reset halt;
+	flash erase_sector 0 0 $lastsect;
+	flash write_image $path 0;
+	reset
+}
+
+# Link proto0 has 128KB flash; proto1+ will likely have 256KB, in which
+# case this'll need changing.
+proc flash_link { } {
+	flash_lm4 ../../../build/link/ec.bin 131072
+}
+
+proc flash_bds { } {
+	flash_lm4 ../../../build/bds/ec.bin 262144
+}
+
+# Boot a software using internal RAM only
+
+proc ramboot_lm4 {path} {
+	reset halt
+	load_image $path 0x20000000 bin
+	reg 15 0x20000400
+	resume
+}
+
+proc ramboot_link { } {
+	ramboot_lm4 ../../../build/link/ec.RO.flat
+}
+
+proc ramboot_bds { } {
+	ramboot_lm4 ../../../build/bds/ec.RO.flat
+}
diff --git a/chip/lm4/openocd/openocd.cfg b/chip/lm4/openocd/openocd.cfg
new file mode 100644
index 0000000..dd80e12
--- /dev/null
+++ b/chip/lm4/openocd/openocd.cfg
@@ -0,0 +1,7 @@
+telnet_port 4444
+gdb_port 3333
+gdb_memory_map enable
+gdb_flash_program enable
+
+source [find interface/luminary-icdi.cfg]
+source [find lm4x.cfg]
diff --git a/chip/lm4/openocd/servo_v2.cfg b/chip/lm4/openocd/servo_v2.cfg
new file mode 100644
index 0000000..caf178e
--- /dev/null
+++ b/chip/lm4/openocd/servo_v2.cfg
@@ -0,0 +1,10 @@
+telnet_port 4444
+gdb_port 3333
+gdb_memory_map enable
+gdb_flash_program enable
+
+interface ft2232
+ft2232_layout jtagkey
+ft2232_vid_pid 0x18d1 0x5002
+jtag_khz 6000
+source [find lm4x.cfg]
diff --git a/chip/lm4/power_button.c b/chip/lm4/power_button.c
new file mode 100644
index 0000000..79e7c58
--- /dev/null
+++ b/chip/lm4/power_button.c
@@ -0,0 +1,214 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Power button and lid switch module for Chrome EC */
+
+#include "console.h"
+#include "gpio.h"
+#include "power_button.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+enum debounce_isr_id {
+	DEBOUNCE_LID,
+	DEBOUNCE_PWRBTN,
+	DEBOUNCE_ISR_ID_MAX
+};
+
+struct debounce_isr_t {
+	/* TODO: Add a carry bit to indicate timestamp overflow */
+	timestamp_t tstamp;
+	int started;
+	void (*callback)(void);
+};
+
+struct debounce_isr_t debounce_isr[DEBOUNCE_ISR_ID_MAX];
+
+enum power_button_state {
+	PWRBTN_STATE_STOPPED = 0,
+	PWRBTN_STATE_START,
+	PWRBTN_STATE_T0,
+	PWRBTN_STATE_T1,
+	PWRBTN_STATE_HELD_DOWN,
+	PWRBTN_STATE_STOPPING,
+};
+static enum power_button_state pwrbtn_state = PWRBTN_STATE_STOPPED;
+/* The next timestamp to move onto next state if power button is still pressed.
+ */
+static timestamp_t pwrbtn_next_ts = {0};
+
+#define PWRBTN_DELAY_T0 32000  /* 32ms */
+#define PWRBTN_DELAY_T1 (4000000 - PWRBTN_DELAY_T0)  /* 4 secs - t0 */
+
+
+static void lid_switch_isr(void)
+{
+	/* TODO: Currently we pass through the LID_SW# pin to R_EC_LID_OUT#
+	 * directly. Modify this if we need to consider more conditions. */
+	gpio_set_level(GPIO_PCH_LID_SWITCHn,
+		       gpio_get_level(GPIO_LID_SWITCHn));
+}
+
+
+/* Power button state machine.
+ *
+ *   PWRBTN#   ---                      ----
+ *     to EC     |______________________|
+ *
+ *
+ *   PWRBTN#   ---  ---------           ----
+ *    to PCH     |__|       |___________|
+ *                t0    t1    held down
+ */
+static void set_pwrbtn_to_pch(int high)
+{
+	uart_printf("[%d] set_pwrbtn_to_pch(%s)\n",
+		    get_time().le.lo, high ? "HIGH" : "LOW");
+	gpio_set_level(GPIO_PCH_PWRBTNn, high);
+}
+
+
+static void pwrbtn_sm_start(void)
+{
+	pwrbtn_state = PWRBTN_STATE_START;
+	pwrbtn_next_ts = get_time();  /* execute action now! */
+}
+
+
+static void pwrbtn_sm_stop(void)
+{
+	pwrbtn_state = PWRBTN_STATE_STOPPING;
+	pwrbtn_next_ts = get_time();  /* execute action now ! */
+}
+
+
+static void pwrbtn_sm_handle(timestamp_t current)
+{
+	/* Not the time to move onto next state */
+	if (current.val < pwrbtn_next_ts.val)
+		return;
+
+	switch (pwrbtn_state) {
+	case PWRBTN_STATE_START:
+		pwrbtn_next_ts.val = current.val + PWRBTN_DELAY_T0;
+		pwrbtn_state = PWRBTN_STATE_T0;
+		set_pwrbtn_to_pch(0);
+		break;
+	case PWRBTN_STATE_T0:
+		pwrbtn_next_ts.val = current.val + PWRBTN_DELAY_T1;
+		pwrbtn_state = PWRBTN_STATE_T1;
+		set_pwrbtn_to_pch(1);
+		break;
+	case PWRBTN_STATE_T1:
+		pwrbtn_state = PWRBTN_STATE_HELD_DOWN;
+		set_pwrbtn_to_pch(0);
+		break;
+	case PWRBTN_STATE_STOPPING:
+		set_pwrbtn_to_pch(1);
+		pwrbtn_state = PWRBTN_STATE_STOPPED;
+		break;
+	case PWRBTN_STATE_STOPPED:
+	case PWRBTN_STATE_HELD_DOWN:
+		/* Do nothing */
+		break;
+	}
+}
+
+
+static void power_button_isr(void)
+{
+	if (!gpio_get_level(GPIO_POWER_BUTTONn)) {
+		/* pressed */
+		pwrbtn_sm_start();
+		/* TODO: implement after chip/lm4/x86_power.c is completed. */
+		/* if system is in S5, power_on_system()
+		 * elif system is in S3, resume_system()
+		 * else S0 i8042_send_host(make_code); */
+	} else {
+		/* released */
+		pwrbtn_sm_stop();
+		/* TODO: implement after chip/lm4/x86_power.c is completed. */
+		/* if system in S0, i8042_send_host(break_code); */
+	}
+}
+
+
+void power_button_interrupt(enum gpio_signal signal)
+{
+	timestamp_t timelimit;
+	int d = (signal == GPIO_LID_SWITCHn ? DEBOUNCE_LID : DEBOUNCE_PWRBTN);
+
+	/* Set 30 ms debounce timelimit */
+	timelimit = get_time();
+	timelimit.val += 30000;
+
+	/* Handle lid switch and power button debounce */
+	debounce_isr[d].tstamp = timelimit;
+	debounce_isr[d].started = 1;
+}
+
+
+int power_button_init(void)
+{
+	debounce_isr[DEBOUNCE_LID].started = 0;
+	debounce_isr[DEBOUNCE_LID].callback = lid_switch_isr;
+	debounce_isr[DEBOUNCE_PWRBTN].started = 0;
+	debounce_isr[DEBOUNCE_PWRBTN].callback = power_button_isr;
+
+	/* Enable interrupts, now that we've initialized */
+	gpio_enable_interrupt(GPIO_POWER_BUTTONn);
+	gpio_enable_interrupt(GPIO_LID_SWITCHn);
+
+	return EC_SUCCESS;
+}
+
+
+void power_button_task(void)
+{
+	int i;
+	timestamp_t ts;
+
+	while (1) {
+		usleep(1000);
+		ts = get_time();
+		for (i = 0; i < DEBOUNCE_ISR_ID_MAX; ++i) {
+			if (debounce_isr[i].started &&
+				ts.val >= debounce_isr[i].tstamp.val) {
+				debounce_isr[i].started = 0;
+				debounce_isr[i].callback();
+			}
+		}
+
+		pwrbtn_sm_handle(ts);
+	}
+}
+
+
+/*****************************************************************************/
+/* Console commnands */
+
+static int command_powerbtn(int argc, char **argv)
+{
+	int ms = 100;  /* Press duration in ms */
+	char *e;
+
+	if (argc > 1) {
+		ms = strtoi(argv[1], &e, 0);
+		if (*e) {
+			uart_puts("Invalid duration.\n"
+				  "Usage: powerbtn [duration_ms]\n");
+			return EC_ERROR_INVAL;
+		}
+	}
+
+	uart_printf("Simulating %d ms power button press.\n", ms);
+	set_pwrbtn_to_pch(0);
+	usleep(ms * 1000);
+	set_pwrbtn_to_pch(1);
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(powerbtn, command_powerbtn);
diff --git a/chip/lm4/pwm.c b/chip/lm4/pwm.c
new file mode 100644
index 0000000..d877e72
--- /dev/null
+++ b/chip/lm4/pwm.c
@@ -0,0 +1,241 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* PWM control module for Chrome EC */
+
+#include "board.h"
+#include "console.h"
+#include "gpio.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
+/* Scaling factor for requested/actual RPM for CPU fan.  We need this because
+ * the fan controller on Blizzard filters tach pulses that are less than 64
+ * 15625Hz ticks apart, which works out to ~7000rpm on an unscaled fan.  By
+ * telling the controller we actually have twice as many edges per revolution,
+ * the controller can handle fans that actually go twice as fast.  See
+ * crosbug.com/p/7718. */
+#define CPU_FAN_SCALE 2
+
+
+/* Configures the GPIOs for the fan module. */
+static void configure_gpios(void)
+{
+#ifdef BOARD_link
+	/* PK6 alternate function 1 = channel 1 PWM */
+	gpio_set_alternate_function(LM4_GPIO_K, 0x40, 1);
+	/* PM6:7 alternate function 1 = channel 0 PWM/tach */
+	gpio_set_alternate_function(LM4_GPIO_M, 0xc0, 1);
+#else
+	/* PK6 alternate function 1 = channel 1 PWM */
+	gpio_set_alternate_function(LM4_GPIO_K, 0x40, 1);
+	/* PN6:7 alternate function 1 = channel 4 PWM/tach */
+	gpio_set_alternate_function(LM4_GPIO_N, 0xc0, 1);
+#endif
+}
+
+
+int pwm_get_fan_rpm(void)
+{
+	return (LM4_FAN_FANCST(FAN_CH_CPU) & MAX_RPM) * CPU_FAN_SCALE;
+}
+
+
+int pwm_set_fan_target_rpm(int rpm)
+{
+	/* Apply fan scaling */
+	if (rpm > 0)
+		rpm /= CPU_FAN_SCALE;
+
+	/* 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;
+}
+
+
+/*****************************************************************************/
+/* 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) * CPU_FAN_SCALE);
+	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;
+}
+DECLARE_CONSOLE_COMMAND(faninfo, command_fan_info);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(fanset, command_fan_set);
+
+
+/* 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;
+}
+DECLARE_CONSOLE_COMMAND(fanduty, command_fan_duty);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(kblight, command_kblight);
+
+static const struct console_command console_commands[] = {
+	{"fanduty", command_fan_duty},
+	{"faninfo", command_fan_info},
+	{"fanset", command_fan_set},
+	{"kblight", command_kblight},
+};
+
+
+/*****************************************************************************/
+/* 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
+	 * 0x000c = bits 3:2   = 8 pulses per revolution
+	 *                       (see note at top of file)
+	 * 0x0000 = bit 0      = automatic control */
+	LM4_FAN_FANCH(FAN_CH_CPU) = 0x802c;
+
+	/* 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;
+
+	/* Set initial fan speed to maximum, backlight off */
+	pwm_set_fan_target_rpm(-1);
+	pwm_set_keyboard_backlight(0);
+
+	/* Enable CPU fan and keyboard backlight */
+	LM4_FAN_FANCTL |= (1 << FAN_CH_CPU) | (1 << FAN_CH_KBLIGHT);
+
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/registers.h b/chip/lm4/registers.h
new file mode 100644
index 0000000..a379be1
--- /dev/null
+++ b/chip/lm4/registers.h
@@ -0,0 +1,461 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * 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 LM4_UART_CH_SEP        0x00001000
+static inline int lm4_uart_addr(int ch, int offset)
+{
+	return offset + LM4_UART_CH0_BASE + LM4_UART_CH_SEP * ch;
+}
+#define LM4UARTREG(ch, offset) LM4REG(lm4_uart_addr(ch, 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 LM4_ADC_SS_SEP         0x00000020
+static inline int lm4_adc_addr(int ss, int offset)
+{
+	return offset + LM4_ADC_SS0_BASE + LM4_ADC_SS_SEP * ss;
+}
+#define LM4ADCREG(ss, offset)  LM4REG(lm4_adc_addr(ss, 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_INT_MASK(ch, bits) ((bits) << (4 * (ch)))
+#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 LM4_LPC_CH_SEP         0x00000010
+static inline int lm4_lpc_addr(int ch, int offset)
+{
+	return offset + LM4_LPC_CH0_BASE + LM4_LPC_CH_SEP * ch;
+}
+#define LM4LPCREG(ch, offset)  LM4REG(lm4_lpc_addr(ch, 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 LM4_FAN_CH_SEP         0x00000010
+static inline int lm4_fan_addr(int ch, int offset)
+{
+	return offset + LM4_FAN_CH0_BASE + LM4_FAN_CH_SEP * ch;
+}
+#define LM4FANREG(ch, offset)  LM4REG(lm4_fan_addr(ch, 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_FMPPE        ((volatile uint32_t*)0x400fe400)
+#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_PIOSCCAL    LM4REG(0x400fe150)
+#define LM4_SYSTEM_PIOSCSTAT   LM4REG(0x400fe154)
+#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)
+
+/* IRQ numbers */
+#define LM4_IRQ_GPIOA            0
+#define LM4_IRQ_GPIOB            1
+#define LM4_IRQ_GPIOC            2
+#define LM4_IRQ_GPIOD            3
+#define LM4_IRQ_GPIOE            4
+#define LM4_IRQ_UART0            5
+#define LM4_IRQ_UART1            6
+#define LM4_IRQ_SSI0             7
+#define LM4_IRQ_I2C0             8
+/* 9 - 13 reserved */
+#define LM4_IRQ_ADC0_SS0        14
+#define LM4_IRQ_ADC0_SS1        15
+#define LM4_IRQ_ADC0_SS2        16
+#define LM4_IRQ_ADC0_SS3        17
+#define LM4_IRQ_WATCHDOG        18
+#define LM4_IRQ_TIMER0A         19
+#define LM4_IRQ_TIMER0B         20
+#define LM4_IRQ_TIMER1A         21
+#define LM4_IRQ_TIMER1B         22
+#define LM4_IRQ_TIMER2A         23
+#define LM4_IRQ_TIMER2B         24
+#define LM4_IRQ_ACMP0           25
+#define LM4_IRQ_ACMP1           26
+#define LM4_IRQ_ACMP2           27
+#define LM4_IRQ_SYSCTRL         28
+#define LM4_IRQ_EEPROM          29
+#define LM4_IRQ_GPIOF           30
+#define LM4_IRQ_GPIOG           31
+#define LM4_IRQ_GPIOH           32
+#define LM4_IRQ_UART2           33
+#define LM4_IRQ_SSI1            34
+#define LM4_IRQ_TIMER3A         35
+#define LM4_IRQ_TIMER3B         36
+#define LM4_IRQ_I2C1            37
+/* 38 - 42 reserved */
+#define LM4_IRQ_HIBERNATE       43
+/* 44 - 45 reserved */
+#define LM4_IRQ_UDMA_SOFTWARE   46
+#define LM4_IRQ_UDMA_ERROR      47
+#define LM4_IRQ_ADC1_SS0        48
+#define LM4_IRQ_ADC1_SS1        49
+#define LM4_IRQ_ADC1_SS2        50
+#define LM4_IRQ_ADC1_SS3        51
+/* 52 - 53 reserved */
+#define LM4_IRQ_GPIOJ           54
+#define LM4_IRQ_GPIOK           55
+#define LM4_IRQ_GPIOL           56
+#define LM4_IRQ_SSI2            57
+#define LM4_IRQ_SSI3            58
+#define LM4_IRQ_UART3           59
+#define LM4_IRQ_UART4           60
+#define LM4_IRQ_UART5           61
+#define LM4_IRQ_UART6           62
+#define LM4_IRQ_UART7           63
+/* 64 - 67 reserved */
+#define LM4_IRQ_I2C2            68
+#define LM4_IRQ_I2C3            69
+#define LM4_IRQ_TIMER4A         70
+#define LM4_IRQ_TIMER4B         71
+/* 72 - 91 reserved */
+#define LM4_IRQ_TIMER5A         92
+#define LM4_IRQ_TIMER5B         93
+#define LM4_IRQ_TIMERW0A        94
+#define LM4_IRQ_TIMERW0B        95
+#define LM4_IRQ_TIMERW1A        96
+#define LM4_IRQ_TIMERW1B        97
+#define LM4_IRQ_TIMERW2A        98
+#define LM4_IRQ_TIMERW2B        99
+#define LM4_IRQ_TIMERW3A       100
+#define LM4_IRQ_TIMERW3B       101
+#define LM4_IRQ_TIMERW4A       102
+#define LM4_IRQ_TIMERW4B       103
+#define LM4_IRQ_TIMERW5A       104
+#define LM4_IRQ_TIMERW5B       105
+#define LM4_IRQ_SYS_EXCEPTION  106
+#define LM4_IRQ_SYS_PECI       107
+#define LM4_IRQ_LPC            108
+#define LM4_IRQ_I2C4           109
+#define LM4_IRQ_I2C5           110
+#define LM4_IRQ_GPIOM          111
+#define LM4_IRQ_GPION          112
+/* 113 reserved */
+#define LM4_IRQ_FAN            114
+/* 115 reserved */
+#define LM4_IRQ_GPIOP          116
+#define LM4_IRQ_GPIOP1         117
+#define LM4_IRQ_GPIOP2         118
+#define LM4_IRQ_GPIOP3         119
+#define LM4_IRQ_GPIOP4         120
+#define LM4_IRQ_GPIOP5         121
+#define LM4_IRQ_GPIOP6         122
+#define LM4_IRQ_GPIOP7         123
+#define LM4_IRQ_GPIOQ          124
+#define LM4_IRQ_GPIOQ1         125
+#define LM4_IRQ_GPIOQ2         126
+#define LM4_IRQ_GPIOQ3         127
+#define LM4_IRQ_GPIOQ4         128
+#define LM4_IRQ_GPIOQ5         129
+#define LM4_IRQ_GPIOQ6         130
+#define LM4_IRQ_GPIOQ7         131
+/* 132 - 138 reserved */
+
+#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_PORTJ_BASE         0x4003d000
+#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
+/* Ports for passing to LM4GPIOREG(); abstracted from base addresses above so
+ * that we can switch to/from AHB. */
+#define LM4_GPIO_A LM4_GPIO_PORTA_BASE
+#define LM4_GPIO_B LM4_GPIO_PORTB_BASE
+#define LM4_GPIO_C LM4_GPIO_PORTC_BASE
+#define LM4_GPIO_D LM4_GPIO_PORTD_BASE
+#define LM4_GPIO_E LM4_GPIO_PORTE_BASE
+#define LM4_GPIO_F LM4_GPIO_PORTF_BASE
+#define LM4_GPIO_G LM4_GPIO_PORTG_BASE
+#define LM4_GPIO_H LM4_GPIO_PORTH_BASE
+#define LM4_GPIO_J LM4_GPIO_PORTJ_BASE
+#define LM4_GPIO_K LM4_GPIO_PORTK_BASE
+#define LM4_GPIO_L LM4_GPIO_PORTL_BASE
+#define LM4_GPIO_M LM4_GPIO_PORTM_BASE
+#define LM4_GPIO_N LM4_GPIO_PORTN_BASE
+#define LM4_GPIO_P LM4_GPIO_PORTP_BASE
+#define LM4_GPIO_Q LM4_GPIO_PORTQ_BASE
+#define LM4GPIOREG(port, offset)      LM4REG((port) + (offset))
+#define LM4_GPIO_DATA(port, mask)     LM4GPIOREG(port, ((mask) << 2))
+#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_IEV(port)            LM4GPIOREG(port, 0x40c)
+#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)
+
+/* Value to write to LM4_GPIO_LOCK to unlock writes */
+#define LM4_GPIO_LOCK_UNLOCK          0x4c4f434b
+
+/* 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 */
+/* Timers 0-5 are 16/32 bit */
+#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
+/* Timers 6-11 are 32/64 bit */
+#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_SEP                   0x00001000
+static inline int lm4_timer_addr(int timer, int offset)
+{
+	if (timer < 8)
+		return offset + LM4_TIMER0_BASE + LM4_TIMER_SEP * timer;
+	else
+		return offset + LM4_TIMERW2_BASE + LM4_TIMER_SEP * (timer - 8);
+}
+#define LM4TIMERREG(timer, offset)      LM4REG(lm4_timer_addr(timer, offset))
+#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 LM4_WATCHDOG0_BASE              0x40000000
+#define LM4_WATCHDOG1_BASE              0x40001000
+static inline int lm4_watchdog_addr(int num, int offset)
+{
+	return offset + (num ? LM4_WATCHDOG1_BASE : LM4_WATCHDOG0_BASE);
+}
+#define LM4WDTREG(num, offset) LM4REG(lm4_watchdog_addr(num, offset))
+#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 0000000..fa17ecc
--- /dev/null
+++ b/chip/lm4/system.c
@@ -0,0 +1,178 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* System module for Chrome EC : hardware specific implementation */
+
+#include "cpu.h"
+#include "registers.h"
+#include "system.h"
+
+
+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;
+	enum system_reset_cause_t reset_cause = SYSTEM_RESET_UNKNOWN;
+	uint32_t raw_reset_cause;
+
+	/* 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;
+	}
+	system_set_reset_cause(reset_cause);
+}
+
+
+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;
+
+	return EC_SUCCESS;
+}
+
+
+int system_reset(int is_cold)
+{
+	/* TODO: (crosbug.com/p/7470) support cold boot; this is a
+	   warm boot. */
+	CPU_NVIC_APINT = 0x05fa0004;
+
+	/* Spin and wait for reboot; should never return */
+	/* TODO: (crosbug.com/p/7471) 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: (crosbug.com/p/7472) might be more elegant to have a
+	 * write_hibernate_reg() method which takes an address and
+	 * data and does the delays.  Then we could move the hibernate
+	 * register accesses to a separate module. */
+	LM4_HIBERNATE_HIBDATA = value;
+
+	/* Wait for write-complete */
+	return wait_for_hibctl_wc();
+}
+
+
+uint32_t system_get_scratchpad(void)
+{
+	return LM4_HIBERNATE_HIBDATA;
+}
diff --git a/chip/lm4/temp_sensor.c b/chip/lm4/temp_sensor.c
new file mode 100644
index 0000000..a9095bf
--- /dev/null
+++ b/chip/lm4/temp_sensor.c
@@ -0,0 +1,202 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* 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 temp sensors in system */
+#define TEMP0_ADDR ((0x40 << 1) | I2C_FLAG_BIG_ENDIAN)
+#ifdef BOARD_link
+#define TEMP1_ADDR ((0x41 << 1) | I2C_FLAG_BIG_ENDIAN)
+#define TEMP2_ADDR ((0x43 << 1) | I2C_FLAG_BIG_ENDIAN)
+#define TEMP3_ADDR ((0x45 << 1) | I2C_FLAG_BIG_ENDIAN)
+#endif
+
+/* Address of battery charger */
+#define CHARGER_ADDR 0x12
+
+/* Address of battery */
+#define BATTERY_ADDR 0x16
+
+
+static const int i2c_addrs[] = {
+	TEMP0_ADDR,
+#ifdef BOARD_link
+	TEMP1_ADDR, TEMP2_ADDR, TEMP3_ADDR,
+#endif
+};
+
+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_channel(ADC_CH_EC_TEMP);
+	default:
+		return -1;
+	}
+	/* 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;
+	int i;
+
+	uart_puts("Reading temperature sensors...\n");
+
+	for (i = 0; i < ARRAY_SIZE(i2c_addrs); i++) {
+		int a = i2c_addrs[i];
+		uart_printf("Sensor at 0x%02x:\n", a);
+
+		rv = i2c_read16(I2C_PORT_THERMAL, a, 0xfe, &d);
+		if (rv)
+			return rv;
+		uart_printf("  Manufacturer ID: 0x%04x\n", d);
+
+		rv = i2c_read16(I2C_PORT_THERMAL, a, 0xff, &d);
+		uart_printf("  Device ID:       0x%04x\n", d);
+
+		rv = i2c_read16(I2C_PORT_THERMAL, a, 0x02, &d);
+		uart_printf("  Config:          0x%04x\n", d);
+
+		rv = i2c_read16(I2C_PORT_THERMAL, a, 0x01, &traw);
+		t = ((int)(int16_t)traw * 100) / 128;
+		uart_printf("  Die Temperature: 0x%04x = %d.%02d C\n",
+			    traw, t / 100, t > 0 ? t % 100 : 100 - (t % 100));
+
+		rv = i2c_read16(I2C_PORT_THERMAL, a, 0x00, &vraw);
+		v = ((int)(int16_t)vraw * 15625) / 100;
+		uart_printf("  Voltage:         0x%04x = %d nV\n", vraw, v);
+		/* TODO: calculate remote temperature from voltage offset */
+
+		uart_flush_output();
+	}
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(temps, command_temps);
+
+
+/* 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;
+}
+DECLARE_CONSOLE_COMMAND(charger, command_charger);
+
+
+/* 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;
+}
+DECLARE_CONSOLE_COMMAND(battery, command_battery);
+
+
+/*****************************************************************************/
+/* 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
+
+	return EC_SUCCESS;
+}
diff --git a/chip/lm4/uart.c b/chip/lm4/uart.c
new file mode 100644
index 0000000..84916a0
--- /dev/null
+++ b/chip/lm4/uart.c
@@ -0,0 +1,216 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* UART module for Chrome EC */
+
+#include <stdarg.h>
+
+#include "board.h"
+#include "console.h"
+#include "gpio.h"
+#include "lpc.h"
+#include "registers.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+
+/* Baud rate for UARTs */
+#define BAUD_RATE 115200
+
+void uart_tx_start(void)
+{
+	/* 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;
+	task_trigger_irq(LM4_IRQ_UART0);
+}
+
+void uart_tx_stop(void)
+{
+	LM4_UART_IM(0) &= ~0x20;
+}
+
+int uart_tx_stopped(void)
+{
+	return !(LM4_UART_IM(0) & 0x20);
+}
+
+void uart_tx_flush(void)
+{
+	/* Wait for transmit FIFO empty */
+	while (!(LM4_UART_FR(0) & 0x80))
+		;
+}
+
+int uart_tx_ready(void)
+{
+	return !(LM4_UART_FR(0) & 0x20);
+}
+
+int uart_rx_available(void)
+{
+	return !(LM4_UART_FR(0) & 0x10);
+}
+
+void uart_write_char(char c)
+{
+	/* Wait for space in transmit FIFO. */
+	while (!uart_tx_ready())
+		;
+
+	LM4_UART_DR(0) = c;
+}
+
+int uart_read_char(void)
+{
+	return LM4_UART_DR(0);
+}
+
+void uart_disable_interrupt(void)
+{
+	task_disable_irq(LM4_IRQ_UART0);
+}
+
+void uart_enable_interrupt(void)
+{
+	task_enable_irq(LM4_IRQ_UART0);
+}
+
+/* Interrupt handler for UART0 */
+static void uart_0_interrupt(void)
+{
+	/* Clear transmit and receive interrupt status */
+	LM4_UART_ICR(0) = 0x70;
+
+
+	/* Read input FIFO until empty, then fill output FIFO */
+	uart_process();
+}
+DECLARE_IRQ(LM4_IRQ_UART0, 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: (crosbug.com/p/7488) handle input */
+
+	/* If we have space in our FIFO and a character is pending in LPC,
+	 * handle that character. */
+	if (!(LM4_UART_FR(1) & 0x20) && lpc_comx_has_char()) {
+		/* Copy the next byte then disable transmit interrupt */
+		LM4_UART_DR(1) = lpc_comx_get_char();
+		LM4_UART_IM(1) &= ~0x20;
+	}
+
+	/* Handle received character.  There is no flow control on input;
+	 * received characters are blindly forwarded to LPC.  This is ok
+	 * because LPC is much faster than UART, and we don't have flow control
+	 * on the UART receive-side either. */
+	if (!(LM4_UART_FR(1) & 0x10))
+		lpc_comx_put_char(LM4_UART_DR(1));
+}
+/* Must be same prio as LPC interrupt handler so they don't preempt */
+DECLARE_IRQ(LM4_IRQ_UART1, uart_1_interrupt, 2);
+
+
+/* Configure GPIOs for the UART module. */
+static void configure_gpio(void)
+{
+#ifdef BOARD_link
+	/* UART0 RX and TX are GPIO PA0:1 alternate function 1 */
+	gpio_set_alternate_function(LM4_GPIO_A, 0x03, 1);
+	/* UART1 RX and TX are GPIO PC4:5 alternate function 2 */
+	gpio_set_alternate_function(LM4_GPIO_C, 0x30, 2);
+#else
+	/* UART0 RX and TX are GPIO PA0:1 alternate function 1 */
+	gpio_set_alternate_function(LM4_GPIO_A, 0x03, 1);
+	/* UART1 RX and TX are GPIO PB0:1 alternate function 1*/
+	gpio_set_alternate_function(LM4_GPIO_B, 0x03, 1);
+#endif
+}
+
+
+int uart_init(void)
+{
+	volatile uint32_t scratch  __attribute__((unused));
+	int ch;
+
+	/*
+	 * Check that the UART parameters used for panic/watchdog are matching
+	 * the UART0 parameters.
+	*/
+	BUILD_ASSERT(LM4_UART_CH0_BASE == CONFIG_UART_ADDRESS);
+
+	/* Enable UART0 and UART1 and delay a few clocks */
+	LM4_SYSTEM_RCGCUART |= 0x03;
+	scratch = LM4_SYSTEM_RCGCUART;
+
+	/* Configure GPIOs */
+	configure_gpio();
+
+	/* Configure UART0 and UART1 (identically) */
+	for (ch = 0; ch < 2; ch++) {
+		/* Disable the port */
+		LM4_UART_CTL(ch) = 0x0300;
+		/* Set the baud rate divisor */
+		LM4_UART_IBRD(ch) = (CPU_CLOCK / 16) / BAUD_RATE;
+		LM4_UART_FBRD(ch) =
+			(((CPU_CLOCK / 16) % BAUD_RATE) * 64 + BAUD_RATE / 2) /
+			BAUD_RATE;
+		/* 8-N-1, FIFO enabled.  Must be done after setting
+		 * the divisor for the new divisor to take effect. */
+		LM4_UART_LCRH(ch) = 0x70;
+		/* Interrupt when RX fifo at minimum (>= 1/8 full), and TX fifo
+		 * when <= 1/4 full */
+		LM4_UART_IFLS(ch) = 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(ch) = 0x50;
+		/* Enable the port */
+		LM4_UART_CTL(ch) |= 0x0001;
+	}
+
+	/* Enable interrupts */
+	task_enable_irq(LM4_IRQ_UART0);
+	task_enable_irq(LM4_IRQ_UART1);
+
+	/* Print hello on UART1 for debugging */
+	/* TODO: remove in production */
+	{
+		const char *c = "Hello on UART1\r\n";
+		while (*c)
+			uart_comx_putc(*c++);
+	}
+
+	return EC_SUCCESS;
+}
+
+/*****************************************************************************/
+/* COMx functions */
+
+int uart_comx_putc_ok(void)
+{
+	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;
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
+
+void uart_comx_putc(int c)
+{
+	LM4_UART_DR(1) = c;
+}
diff --git a/chip/lm4/watchdog.c b/chip/lm4/watchdog.c
new file mode 100644
index 0000000..ccc6e63
--- /dev/null
+++ b/chip/lm4/watchdog.c
@@ -0,0 +1,156 @@
+/* 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.
+ */
+
+/* Watchdog driver */
+
+#include <stdint.h>
+
+#include "board.h"
+#include "common.h"
+#include "config.h"
+#include "registers.h"
+#include "gpio.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+/*
+ * We use watchdog 0 which is clocked on the system clock
+ * to avoid the penalty cycles on each write access
+ */
+
+/* magic value to unlock the watchdog registers */
+#define LM4_WATCHDOG_MAGIC_WORD  0x1ACCE551
+
+/* watchdog counter initial value */
+static uint32_t watchdog_period;
+
+/* console debug command prototypes */
+int command_task_info(int argc, char **argv);
+int command_timer_info(int argc, char **argv);
+
+/**
+ * watchdog debug trace.
+ *
+ * It is triggered if the watchdog has not been reloaded after 1x the timeout
+ * period, after 2x the period an hardware reset is triggering.
+ */
+void watchdog_trace(uint32_t excep_lr, uint32_t excep_sp)
+{
+	uint32_t psp;
+	uint32_t *stack;
+
+	/* we do NOT reset the watchdog interrupt here, it will be done in
+	 * watchdog_reload() or fire the reset
+	 * instead de-activate the interrupt in the NVIC :
+	 * so, we will get the trace only once
+	 */
+	task_disable_irq(LM4_IRQ_WATCHDOG);
+
+	asm("mrs %0, psp":"=r"(psp));
+	if ((excep_lr & 0xf) == 1) {
+		/* we were already in exception context */
+		stack = (uint32_t *)excep_sp;
+	} else {
+		/* we were in task context */
+		stack = (uint32_t *)psp;
+	}
+
+	uart_printf("### WATCHDOG PC=%08x / LR=%08x / pSP=%08x ###\n",
+	            stack[6], stack[5], psp);
+	/* ensure this debug message is always flushed to the UART */
+	uart_emergency_flush();
+	/* if we are blocked in a high priority IT handler, the following
+	 * debug messages might not appear but they are useless in that
+	 * situation.
+	 */
+	command_task_info(0, NULL);
+	command_timer_info(0, NULL);
+}
+
+void irq_LM4_IRQ_WATCHDOG_handler(void) __attribute__((naked));
+void irq_LM4_IRQ_WATCHDOG_handler(void)
+{
+	asm volatile("mov r0, lr\n"
+	             "mov r1, sp\n"
+	             "push {lr}\n"
+	             "bl watchdog_trace\n"
+	             "pop {lr}\n"
+	             "mov r0, lr\n"
+		     "b task_resched_if_needed\n");
+}
+const struct irq_priority prio_LM4_IRQ_WATCHDOG
+	__attribute__((section(".rodata.irqprio")))
+		= {LM4_IRQ_WATCHDOG, 0}; /* put the watchdog at the highest
+					    priority */
+
+void watchdog_reload(void)
+{
+	uint32_t status = LM4_WATCHDOG_RIS(0);
+
+	/* unlock watchdog registers */
+	LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD;
+
+	/* As we reboot only on the second time-out,
+	 * if we have already reached 1 time-out
+	 * we need to reset the interrupt bit.
+	 */
+	if (status)
+		LM4_WATCHDOG_ICR(0) = status;
+
+	/* reload the watchdog counter */
+	LM4_WATCHDOG_LOAD(0) = watchdog_period;
+
+	/* re-lock watchdog registers */
+	LM4_WATCHDOG_LOCK(0) = 0xdeaddead;
+}
+
+int watchdog_init(int period_ms)
+{
+	volatile uint32_t scratch  __attribute__((unused));
+
+	/* Enable watchdog 0 clock */
+	LM4_SYSTEM_RCGCWD |= 0x1;
+	/* wait 3 clock cycles before using the module */
+	scratch = LM4_SYSTEM_RCGCWD;
+
+	/* set the time-out period */
+	watchdog_period = period_ms * (CPU_CLOCK / 1000);
+	LM4_WATCHDOG_LOAD(0) = watchdog_period;
+
+	/* de-activate the watchdog when the JTAG stops the CPU */
+	LM4_WATCHDOG_TEST(0) |= 1 << 8;
+
+	/* reset after 2 time-out,
+	 * activate the watchdog and lock the control register
+	 */
+	LM4_WATCHDOG_CTL(0) = 0x3;
+
+	/* lock watchdog registers against unintended accesses */
+	LM4_WATCHDOG_LOCK(0) = 0xdeaddead;
+
+	/* Enable watchdog interrupt */
+	task_enable_irq(LM4_IRQ_WATCHDOG);
+
+	return EC_SUCCESS;
+}
+
+/* Low priority task to reload the watchdog */
+void watchdog_task(void)
+{
+	while (1) {
+#ifdef BOARD_bds
+		gpio_set_level(GPIO_DEBUG_LED, 1);
+#endif
+		usleep(500000);
+		watchdog_reload();
+#ifdef BOARD_bds
+		gpio_set_level(GPIO_DEBUG_LED, 0);
+#endif
+		usleep(500000);
+		watchdog_reload();
+	}
+}
diff --git a/chip/stm32l/build.mk b/chip/stm32l/build.mk
new file mode 100644
index 0000000..6f7c8bc
--- /dev/null
+++ b/chip/stm32l/build.mk
@@ -0,0 +1,12 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# STM32L chip specific files build
+#
+
+# STM32L15xx SoC family has a Cortex-M3 ARM core
+CORE:=cortex-m
+
+chip-y=uart.o clock.o hwtimer.o system.o gpio.o
+chip-$(CONFIG_TASK_WATCHDOG)+=watchdog.o
diff --git a/chip/stm32l/clock.c b/chip/stm32l/clock.c
new file mode 100644
index 0000000..9b4c01a
--- /dev/null
+++ b/chip/stm32l/clock.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Clocks and power management settings */
+
+#include <stdint.h>
+
+#include "board.h"
+#include "clock.h"
+#include "common.h"
+#include "registers.h"
+#include "util.h"
+
+/**
+ * Idle task
+ * executed when no task are ready to be scheduled
+ */
+void __idle(void)
+{
+	while (1) {
+		/* wait for the irq event */
+		asm("wfi");
+		/* TODO more power management here */
+	}
+}
+
+int clock_init(void)
+{
+	uint32_t tmp_acr;
+
+	/*
+	 * The initial state :
+	 *  SYSCLK from HSI (=16MHz), no divider on AHB, APB1, APB2
+	 *  PLL unlocked, RTC enabled on LSE
+	 */
+
+	/* Ensure that HSI is ON */
+	if (!(STM32L_RCC_CR & (1 << 1))) {
+		/* Enable HSI */
+		STM32L_RCC_CR |= 1 << 0;
+		/* Wait for HSI to be ready */
+		while (!(STM32L_RCC_CR & (1 << 1)))
+			;
+	}
+
+	/* Set the recommended flash settings for 16MHz clock.
+	 *
+	 * The 3 bits must be programmed strictly sequentially,
+	 * but it is faster not to read-back the value of the ACR register
+	 * in the middle of the sequence so let's use a temporary variable.
+	 */
+	tmp_acr = STM32L_FLASH_ACR;
+	/* Enable 64-bit access */
+	tmp_acr |= (1 << 2);
+	STM32L_FLASH_ACR = tmp_acr;
+	/* Enable Prefetch Buffer */
+	tmp_acr |= (1 << 1);
+	STM32L_FLASH_ACR = tmp_acr;
+	/* Flash 1 wait state */
+	tmp_acr |= (1 << 0);
+	STM32L_FLASH_ACR = tmp_acr;
+
+	/*
+	 * stays on HSI, no prescaler, PLLSRC = HSI, PLLMUL = x3, PLLDIV = /3,
+	 * no MCO                      => PLLVCO = 48 MHz and PLLCLK = 16 Mhz
+	 */
+	BUILD_ASSERT(CPU_CLOCK == 16000000);
+	STM32L_RCC_CFGR = 0x00800001;
+	/* Enable the PLL */
+	STM32L_RCC_CR |= 1 << 24;
+	/* Wait for the PLL to lock */
+	while (!(STM32L_RCC_CR & (1 << 25)))
+		;
+	/* switch to SYSCLK to the PLL */
+	STM32L_RCC_CFGR = 0x00800003;
+	/* wait until the PLL is the clock source */
+	while ((STM32L_RCC_CFGR & 0xc) != 0xc)
+		;
+
+	return EC_SUCCESS;
+}
diff --git a/chip/stm32l/config.h b/chip/stm32l/config.h
new file mode 100644
index 0000000..9309fc5
--- /dev/null
+++ b/chip/stm32l/config.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Memory mapping */
+#define CONFIG_FLASH_BASE       0x08000000
+#define CONFIG_FLASH_SIZE       0x00020000
+#define CONFIG_FLASH_BANK_SIZE  0x1000
+#define CONFIG_RAM_BASE         0x20000000
+#define CONFIG_RAM_SIZE         0x00004000
+
+/* Size of one firmware image in flash */
+#define CONFIG_FW_IMAGE_SIZE    (32 * 1024)
+#define CONFIG_FW_RO_OFF        0
+#define CONFIG_FW_A_OFF         CONFIG_FW_IMAGE_SIZE
+#define CONFIG_FW_B_OFF         (2 * CONFIG_FW_IMAGE_SIZE)
+
+/* Number of IRQ vectors on the NVIC */
+#define CONFIG_IRQ_COUNT 45
+
+/* Debug UART parameters for panic message */
+#define CONFIG_UART_ADDRESS    0x40013800
+#define CONFIG_UART_DR_OFFSET  0x04
+#define CONFIG_UART_SR_OFFSET  0x00
+#define CONFIG_UART_SR_TXEMPTY 0x80
+
+/* System stack size */
+#define CONFIG_STACK_SIZE 1024
+
+/* build with assertions and debug messages */
+#define CONFIG_DEBUG
+
+/* Compile for running from RAM instead of flash */
+/* #define COMPILE_FOR_RAM */
diff --git a/chip/stm32l/gpio.c b/chip/stm32l/gpio.c
new file mode 100644
index 0000000..56d22ab
--- /dev/null
+++ b/chip/stm32l/gpio.c
@@ -0,0 +1,69 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* GPIO module for Chrome EC */
+
+#include "board.h"
+#include "gpio.h"
+#include "registers.h"
+#include "task.h"
+
+/* Signal information from board.c.  Must match order from enum gpio_signal. */
+extern const struct gpio_info gpio_list[GPIO_COUNT];
+
+
+int gpio_pre_init(void)
+{
+	const struct gpio_info *g = gpio_list;
+	int i;
+
+	/* Enable all GPIOs clocks
+	 * TODO: more fine-grained enabling for power saving
+	 */
+	STM32L_RCC_AHBENR |= 0x3f;
+
+	/* Set all GPIOs to defaults */
+	for (i = 0; i < GPIO_COUNT; i++, g++) {
+		/* bitmask for registers with 2 bits per GPIO pin */
+		uint32_t mask2 = (g->mask * g->mask) | (g->mask * g->mask * 2);
+
+		if (g->flags & GPIO_OUTPUT) {
+			/* Set pin level */
+			gpio_set_level(i, g->flags & GPIO_HIGH);
+			/* General purpose output : MODE = 01 */
+			STM32L_GPIO_MODER_OFF(g->port) |= 0x55555555 & mask2;
+		} else {
+			/* Input */
+			STM32L_GPIO_MODER_OFF(g->port) &= ~mask2;
+			if (g->flags & GPIO_PULL) {
+				/* With pull up/down */
+				if (g->flags & GPIO_HIGH) /* Pull Up = 01 */
+					STM32L_GPIO_PUPDR_OFF(g->port) |=
+							0x55555555 & mask2;
+				else /* Pull Down = 10 */
+					STM32L_GPIO_PUPDR_OFF(g->port) |=
+							0xaaaaaaaa & mask2;
+			}
+		}
+	}
+
+	return EC_SUCCESS;
+}
+
+
+int gpio_get_level(enum gpio_signal signal)
+{
+	return !!(STM32L_GPIO_IDR_OFF(gpio_list[signal].port) &
+		  gpio_list[signal].mask);
+}
+
+
+int gpio_set_level(enum gpio_signal signal, int value)
+{
+	STM32L_GPIO_BSRR_OFF(gpio_list[signal].port) =
+			gpio_list[signal].mask << (value ? 0 : 16);
+
+	return EC_SUCCESS;
+}
diff --git a/chip/stm32l/hwtimer.c b/chip/stm32l/hwtimer.c
new file mode 100644
index 0000000..9f5a7c4
--- /dev/null
+++ b/chip/stm32l/hwtimer.c
@@ -0,0 +1,142 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Hardware timers driver */
+
+#include <stdint.h>
+
+#include "board.h"
+#include "common.h"
+#include "hwtimer.h"
+#include "registers.h"
+#include "task.h"
+
+#define US_PER_SECOND 1000000
+
+/* Divider to get microsecond for the clock */
+#define CLOCKSOURCE_DIVIDER (CPU_CLOCK/US_PER_SECOND)
+
+static uint32_t last_deadline;
+
+void __hw_clock_event_set(uint32_t deadline)
+{
+	last_deadline = deadline;
+
+	if ((deadline >> 16) == STM32L_TIM_CNT(2)) {
+		/* we can set a match on the LSB only */
+		STM32L_TIM_CCR1(3) = deadline & 0xffff;
+		/* disable MSB match */
+		STM32L_TIM_DIER(2) &= ~2;
+		/* Clear the match flags */
+		STM32L_TIM_SR(2) = ~2;
+		STM32L_TIM_SR(3) = ~2;
+		/* Set the match interrupt */
+		STM32L_TIM_DIER(3) |= 2;
+	} else if ((deadline >> 16) > STM32L_TIM_CNT(2)) {
+		/* first set a match on the MSB */
+		STM32L_TIM_CCR1(2) = deadline >> 16;
+		/* disable LSB match */
+		STM32L_TIM_DIER(3) &= ~2;
+		/* Clear the match flags */
+		STM32L_TIM_SR(2) = ~2;
+		STM32L_TIM_SR(3) = ~2;
+		/* Set the match interrupt */
+		STM32L_TIM_DIER(2) |= 2;
+	}
+}
+
+uint32_t __hw_clock_event_get(void)
+{
+	return last_deadline;
+}
+
+void __hw_clock_event_clear(void)
+{
+	/* Disable the match interrupts */
+	STM32L_TIM_DIER(3) &= ~2;
+	STM32L_TIM_DIER(2) &= ~2;
+}
+
+uint32_t __hw_clock_source_read(void)
+{
+	uint32_t hi;
+	uint32_t lo;
+
+	/* ensure the two half-words are coherent */
+	do {
+		hi = STM32L_TIM_CNT(2);
+		lo = STM32L_TIM_CNT(3);
+	} while (hi != STM32L_TIM_CNT(2));
+
+	return (hi << 16) | lo;
+}
+
+static void __hw_clock_source_irq(void)
+{
+	uint32_t stat_tim2 = STM32L_TIM_SR(2);
+
+	/* clear status */
+	STM32L_TIM_SR(3) = 0;
+	STM32L_TIM_SR(2) = 0;
+
+	/*
+	 * Find expired timers and set the new timer deadline
+	 * signal overflow if the 16-bit MSB counter has overflowed.
+	 */
+	process_timers(stat_tim2 & 0x01);
+}
+DECLARE_IRQ(STM32L_IRQ_TIM2, __hw_clock_source_irq, 1);
+DECLARE_IRQ(STM32L_IRQ_TIM3, __hw_clock_source_irq, 1);
+
+int __hw_clock_source_init(void)
+{
+	/*
+	 * we use 2 chained 16-bit counters to emulate a 32-bit one :
+	 * TIM2 is the MSB (Slave)
+	 * TIM3 is the LSB (Master)
+	 */
+
+	/* Enable TIM2 and TIM3 clocks */
+	STM32L_RCC_APB1ENR |= 0x3;
+
+	/*
+	 * Timer configuration : Upcounter, counter disabled, update event only
+	 * on overflow.
+	 */
+	STM32L_TIM_CR1(2) = 0x0004;
+	STM32L_TIM_CR1(3) = 0x0004;
+	/* TIM3 (master mode) generates a periodic trigger signal on each UEV */
+	STM32L_TIM_CR2(2) = 0x0000;
+	STM32L_TIM_CR2(3) = 0x0020;
+	/* TIM2 (slave mode) uses ITR2 as internal trigger */
+	STM32L_TIM_SMCR(2) = 0x0027;
+	STM32L_TIM_SMCR(3) = 0x0000;
+	/* Auto-reload value : 16-bit free-running counters */
+	STM32L_TIM_ARR(2) = 0xffff;
+	STM32L_TIM_ARR(3) = 0xffff;
+	/* Pre-scaler value :
+	 * TIM3 is counting microseconds, TIM2 is counting every TIM3 overflow.
+	 */
+	STM32L_TIM_PSC(2) = 0;
+	STM32L_TIM_PSC(3) = CLOCKSOURCE_DIVIDER - 1;
+
+	/* Reload the pre-scaler */
+	STM32L_TIM_EGR(2) = 0x0000;
+	STM32L_TIM_EGR(3) = 0x0000;
+
+	/* setup the overflow interrupt on TIM2 */
+	STM32L_TIM_DIER(2) = 0x0001;
+	STM32L_TIM_DIER(3) = 0x0000;
+
+	/* Start counting */
+	STM32L_TIM_CR1(2) |= 1;
+	STM32L_TIM_CR1(3) |= 1;
+
+	/* Enable timer interrupts */
+	task_enable_irq(STM32L_IRQ_TIM2);
+	task_enable_irq(STM32L_IRQ_TIM3);
+
+	return STM32L_IRQ_TIM3;
+}
diff --git a/chip/stm32l/registers.h b/chip/stm32l/registers.h
new file mode 100644
index 0000000..23bad85
--- /dev/null
+++ b/chip/stm32l/registers.h
@@ -0,0 +1,270 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Register map for STM32L processor
+ */
+
+#ifndef __STM32L_REGISTERS
+#define __STM32L_REGISTERS
+
+#include <stdint.h>
+
+/* concatenation helper */
+#define STM32L_CAT(prefix, n, suffix) prefix ## n ## suffix
+
+/* Macros to access registers */
+#define REG32(addr) (*(volatile uint32_t*)(addr))
+#define REG16(addr) (*(volatile uint16_t*)(addr))
+
+/* IRQ numbers */
+#define STM32L_IRQ_WWDG             0
+#define STM32L_IRQ_PVD              1
+#define STM32L_IRQ_TAMPER_STAMP     2
+#define STM32L_IRQ_RTC_WAKEUP       3
+#define STM32L_IRQ_FLASH            4
+#define STM32L_IRQ_RCC              5
+#define STM32L_IRQ_EXTI0            6
+#define STM32L_IRQ_EXTI1            7
+#define STM32L_IRQ_EXTI2            8
+#define STM32L_IRQ_EXTI3            9
+#define STM32L_IRQ_EXTI4           10
+#define STM32L_IRQ_DMA_CHANNEL_1   11
+#define STM32L_IRQ_DMA_CHANNEL_2   12
+#define STM32L_IRQ_DMA_CHANNEL_3   13
+#define STM32L_IRQ_DMA_CHANNEL_4   14
+#define STM32L_IRQ_DMA_CHANNEL_5   15
+#define STM32L_IRQ_DMA_CHANNEL_6   16
+#define STM32L_IRQ_DMA_CHANNEL_7   17
+#define STM32L_IRQ_ADC_1           18
+#define STM32L_IRQ_USB_HP          19
+#define STM32L_IRQ_USB_LP          20
+#define STM32L_IRQ_DAC             21
+#define STM32L_IRQ_COMP            22
+#define STM32L_IRQ_EXTI9_5         23
+#define STM32L_IRQ_LCD             24
+#define STM32L_IRQ_TIM9            25
+#define STM32L_IRQ_TIM10           26
+#define STM32L_IRQ_TIM11           27
+#define STM32L_IRQ_TIM2            28
+#define STM32L_IRQ_TIM3            29
+#define STM32L_IRQ_TIM4            30
+#define STM32L_IRQ_I2C1_EV         31
+#define STM32L_IRQ_I2C1_ER         32
+#define STM32L_IRQ_I2C2_EV         33
+#define STM32L_IRQ_I2C2_ER         34
+#define STM32L_IRQ_SPI1            35
+#define STM32L_IRQ_SPI2            36
+#define STM32L_IRQ_USART1          37
+#define STM32L_IRQ_USART2          38
+#define STM32L_IRQ_USART3          39
+#define STM32L_IRQ_EXTI15_10       40
+#define STM32L_IRQ_RTC_ALARM       41
+#define STM32L_IRQ_USB_FS_WAKEUP   42
+#define STM32L_IRQ_TIM6            43
+#define STM32L_IRQ_TIM7            44
+
+/* --- USART --- */
+#define STM32L_USART1_BASE          0x40013800
+#define STM32L_USART2_BASE          0x40004400
+#define STM32L_USART3_BASE          0x40004800
+
+#define STM32L_USART_BASE(n)        STM32L_CAT(STM32L_USART, n, _BASE)
+
+#define STM32L_USART_REG(n, offset) \
+		REG16(STM32L_CAT(STM32L_USART, n, _BASE) + (offset))
+
+#define STM32L_USART_SR(n)          STM32L_USART_REG(n, 0x00)
+#define STM32L_USART_DR(n)          STM32L_USART_REG(n, 0x04)
+#define STM32L_USART_BRR(n)         STM32L_USART_REG(n, 0x08)
+#define STM32L_USART_CR1(n)         STM32L_USART_REG(n, 0x0C)
+#define STM32L_USART_CR2(n)         STM32L_USART_REG(n, 0x10)
+#define STM32L_USART_CR3(n)         STM32L_USART_REG(n, 0x14)
+#define STM32L_USART_GTPR(n)        STM32L_USART_REG(n, 0x18)
+
+#define STM32L_IRQ_USART(n)         STM32L_CAT(STM32L_IRQ_USART, n, )
+
+/* --- TIMERS --- */
+#define STM32L_TIM2_BASE            0x40000000
+#define STM32L_TIM3_BASE            0x40000400
+#define STM32L_TIM4_BASE            0x40000800
+#define STM32L_TIM6_BASE            0x40001000
+#define STM32L_TIM7_BASE            0x40001400
+#define STM32L_TIM9_BASE            0x40010800
+#define STM32L_TIM10_BASE           0x40010C00
+#define STM32L_TIM11_BASE           0x40011000
+
+#define STM32L_TIM_REG(n, offset) \
+		REG16(STM32L_CAT(STM32L_TIM, n, _BASE) + (offset))
+
+#define STM32L_TIM_CR1(n)           STM32L_TIM_REG(n, 0x00)
+#define STM32L_TIM_CR2(n)           STM32L_TIM_REG(n, 0x04)
+#define STM32L_TIM_SMCR(n)          STM32L_TIM_REG(n, 0x08)
+#define STM32L_TIM_DIER(n)          STM32L_TIM_REG(n, 0x0C)
+#define STM32L_TIM_SR(n)            STM32L_TIM_REG(n, 0x10)
+#define STM32L_TIM_EGR(n)           STM32L_TIM_REG(n, 0x14)
+#define STM32L_TIM_CCMR1(n)         STM32L_TIM_REG(n, 0x18)
+#define STM32L_TIM_CCMR2(n)         STM32L_TIM_REG(n, 0x1C)
+#define STM32L_TIM_CCER(n)          STM32L_TIM_REG(n, 0x20)
+#define STM32L_TIM_CNT(n)           STM32L_TIM_REG(n, 0x24)
+#define STM32L_TIM_PSC(n)           STM32L_TIM_REG(n, 0x28)
+#define STM32L_TIM_ARR(n)           STM32L_TIM_REG(n, 0x2C)
+#define STM32L_TIM_CCR1(n)          STM32L_TIM_REG(n, 0x34)
+#define STM32L_TIM_CCR2(n)          STM32L_TIM_REG(n, 0x38)
+#define STM32L_TIM_CCR3(n)          STM32L_TIM_REG(n, 0x3C)
+#define STM32L_TIM_CCR4(n)          STM32L_TIM_REG(n, 0x40)
+#define STM32L_TIM_DCR(n)           STM32L_TIM_REG(n, 0x48)
+#define STM32L_TIM_DMAR(n)          STM32L_TIM_REG(n, 0x4C)
+#define STM32L_TIM_OR(n)            STM32L_TIM_REG(n, 0x50)
+
+/* --- GPIO --- */
+#define STM32L_GPIOA_BASE            0x40020000
+#define STM32L_GPIOB_BASE            0x40020400
+#define STM32L_GPIOC_BASE            0x40020800
+#define STM32L_GPIOD_BASE            0x40020C00
+#define STM32L_GPIOE_BASE            0x40021000
+#define STM32L_GPIOH_BASE            0x40021400
+
+#define GPIO_A                       STM32L_GPIOA_BASE
+#define GPIO_B                       STM32L_GPIOB_BASE
+#define GPIO_C                       STM32L_GPIOC_BASE
+#define GPIO_D                       STM32L_GPIOD_BASE
+#define GPIO_E                       STM32L_GPIOE_BASE
+#define GPIO_H                       STM32L_GPIOH_BASE
+
+#define STM32L_GPIO_REG32(l, offset) \
+		REG32(STM32L_CAT(STM32L_GPIO, l, _BASE) + (offset))
+#define STM32L_GPIO_REG16(l, offset) \
+		REG16(STM32L_CAT(STM32L_GPIO, l, _BASE) + (offset))
+
+#define STM32L_GPIO_MODER(l)         STM32L_GPIO_REG32(l, 0x00)
+#define STM32L_GPIO_OTYPER(l)        STM32L_GPIO_REG16(l, 0x04)
+#define STM32L_GPIO_OSPEEDR(l)       STM32L_GPIO_REG32(l, 0x08)
+#define STM32L_GPIO_PUPDR(l)         STM32L_GPIO_REG32(l, 0x0C)
+#define STM32L_GPIO_IDR(l)           STM32L_GPIO_REG16(l, 0x10)
+#define STM32L_GPIO_ODR(l)           STM32L_GPIO_REG16(l, 0x14)
+#define STM32L_GPIO_BSRR(l)          STM32L_GPIO_REG32(l, 0x18)
+#define STM32L_GPIO_LCKR(l)          STM32L_GPIO_REG32(l, 0x1C)
+#define STM32L_GPIO_AFRL(l)          STM32L_GPIO_REG32(l, 0x20)
+#define STM32L_GPIO_AFRH(l)          STM32L_GPIO_REG32(l, 0x24)
+
+#define STM32L_GPIO_MODER_OFF(b)     REG32((b) + 0x00)
+#define STM32L_GPIO_OTYPER_OFF(b)    REG16((b) + 0x04)
+#define STM32L_GPIO_OSPEEDR_OFF(b)   REG32((b) + 0x08)
+#define STM32L_GPIO_PUPDR_OFF(b)     REG32((b) + 0x0C)
+#define STM32L_GPIO_IDR_OFF(b)       REG16((b) + 0x10)
+#define STM32L_GPIO_ODR_OFF(b)       REG16((b) + 0x14)
+#define STM32L_GPIO_BSRR_OFF(b)      REG32((b) + 0x18)
+#define STM32L_GPIO_LCKR_OFF(b)      REG32((b) + 0x1C)
+#define STM32L_GPIO_AFRL_OFF(b)      REG32((b) + 0x20)
+#define STM32L_GPIO_AFRH_OFF(b)      REG32((b) + 0x24)
+
+/* --- I2C --- */
+#define STM32L_I2C1_BASE             0x40005400
+#define STM32L_I2C2_BASE             0x40005800
+
+#define STM32L_I2C_REG(n, offset) \
+		REG16(STM32L_CAT(STM32L_I2C, n, _BASE) + (offset))
+
+#define STM32L_I2C_CR1(n)            STM32L_I2C_REG(n, 0x00)
+#define STM32L_I2C_CR2(n)            STM32L_I2C_REG(n, 0x04)
+#define STM32L_I2C_OAR1(n)           STM32L_I2C_REG(n, 0x08)
+#define STM32L_I2C_OAR2(n)           STM32L_I2C_REG(n, 0x0C)
+#define STM32L_I2C_DR(n)             STM32L_I2C_REG(n, 0x10)
+#define STM32L_I2C_SR1(n)            STM32L_I2C_REG(n, 0x14)
+#define STM32L_I2C_SR2(n)            STM32L_I2C_REG(n, 0x18)
+#define STM32L_I2C_CCR(n)            STM32L_I2C_REG(n, 0x1C)
+#define STM32L_I2C_TRISE(n)          STM32L_I2C_REG(n, 0x20)
+
+/* --- Power / Reset / Clocks --- */
+#define STM32L_PWR_BASE              0x40007000
+
+#define STM32L_PWR_CR                REG32(STM32L_PWR_BASE + 0x00)
+#define STM32L_PWR_CSR               REG32(STM32L_PWR_BASE + 0x04)
+
+#define STM32L_RCC_BASE              0x40023800
+
+#define STM32L_RCC_CR                REG32(STM32L_RCC_BASE + 0x00)
+#define STM32L_RCC_ICSR              REG32(STM32L_RCC_BASE + 0x04)
+#define STM32L_RCC_CFGR              REG32(STM32L_RCC_BASE + 0x08)
+#define STM32L_RCC_CIR               REG32(STM32L_RCC_BASE + 0x0C)
+#define STM32L_RCC_AHBRSTR           REG32(STM32L_RCC_BASE + 0x10)
+#define STM32L_RCC_APB2RSTR          REG32(STM32L_RCC_BASE + 0x14)
+#define STM32L_RCC_APB1RSTR          REG32(STM32L_RCC_BASE + 0x18)
+#define STM32L_RCC_AHBENR            REG32(STM32L_RCC_BASE + 0x1C)
+#define STM32L_RCC_APB2ENR           REG32(STM32L_RCC_BASE + 0x20)
+#define STM32L_RCC_APB1ENR           REG32(STM32L_RCC_BASE + 0x24)
+#define STM32L_RCC_AHBLPENR          REG32(STM32L_RCC_BASE + 0x28)
+#define STM32L_RCC_APB2LPENR         REG32(STM32L_RCC_BASE + 0x2C)
+#define STM32L_RCC_APB1LPENR         REG32(STM32L_RCC_BASE + 0x30)
+#define STM32L_RCC_CSR               REG32(STM32L_RCC_BASE + 0x34)
+
+#define STM32L_SYSCFG_BASE           0x40010000
+
+#define STM32L_SYSCFG_MEMRMP         REG32(STM32L_SYSCFG_BASE + 0x00)
+#define STM32L_SYSCFG_PMC            REG32(STM32L_SYSCFG_BASE + 0x04)
+#define STM32L_SYSCFG_EXTICR(n)      REG32(STM32L_SYSCFG_BASE + 8 + 4 * (n))
+
+/* --- Watchdogs --- */
+
+#define STM32L_WWDG_BASE             0x40002C00
+
+#define STM32L_WWDG_CR               REG32(STM32L_WWDG_BASE + 0x00)
+#define STM32L_WWDG_CFR              REG32(STM32L_WWDG_BASE + 0x04)
+#define STM32L_WWDG_SR               REG32(STM32L_WWDG_BASE + 0x08)
+
+#define STM32L_IWDG_BASE             0x40003000
+
+#define STM32L_IWDG_KR               REG32(STM32L_IWDG_BASE + 0x00)
+#define STM32L_IWDG_PR               REG32(STM32L_IWDG_BASE + 0x04)
+#define STM32L_IWDG_RLR              REG32(STM32L_IWDG_BASE + 0x08)
+#define STM32L_IWDG_SR               REG32(STM32L_IWDG_BASE + 0x0C)
+
+/* --- Real-Time Clock --- */
+
+#define STM32L_RTC_BASE              0x40002800
+
+#define STM32L_RTC_TR                REG32(STM32L_RTC_BASE + 0x00)
+#define STM32L_RTC_DR                REG32(STM32L_RTC_BASE + 0x04)
+#define STM32L_RTC_CR                REG32(STM32L_RTC_BASE + 0x08)
+#define STM32L_RTC_ISR               REG32(STM32L_RTC_BASE + 0x0C)
+#define STM32L_RTC_PRER              REG32(STM32L_RTC_BASE + 0x10)
+#define STM32L_RTC_WUTR              REG32(STM32L_RTC_BASE + 0x14)
+#define STM32L_RTC_CALIBR            REG32(STM32L_RTC_BASE + 0x18)
+#define STM32L_RTC_ALRMAR            REG32(STM32L_RTC_BASE + 0x1C)
+#define STM32L_RTC_ALRMBR            REG32(STM32L_RTC_BASE + 0x20)
+#define STM32L_RTC_WPR               REG32(STM32L_RTC_BASE + 0x24)
+#define STM32L_RTC_TSTR              REG32(STM32L_RTC_BASE + 0x30)
+#define STM32L_RTC_TSDR              REG32(STM32L_RTC_BASE + 0x34)
+#define STM32L_RTC_TAFCR             REG32(STM32L_RTC_BASE + 0x40)
+#define STM32L_RTC_BACKUP(n)         REG32(STM32L_RTC_BASE + 0x50 + 4 * (n))
+
+/* --- Debug --- */
+
+#define STM32L_DBGMCU_BASE           0xE0042000
+
+#define STM32L_DBGMCU_IDCODE         REG32(STM32L_DBGMCU_BASE + 0x00)
+#define STM32L_DBGMCU_CR             REG32(STM32L_DBGMCU_BASE + 0x04)
+#define STM32L_DBGMCU_APB1FZ         REG32(STM32L_DBGMCU_BASE + 0x08)
+#define STM32L_DBGMCU_APB2FZ         REG32(STM32L_DBGMCU_BASE + 0x0C)
+
+/* --- Flash --- */
+
+#define STM32L_FLASH_REGS_BASE       0x40023c00
+
+#define STM32L_FLASH_ACR             REG32(STM32L_FLASH_REGS_BASE + 0x00)
+
+/* --- MISC --- */
+
+#define STM32L_RI_BASE               0x40007C04
+#define STM32L_EXTI_BASE             0x40010400
+#define STM32L_ADC1_BASE             0x40012400
+#define STM32L_ADC_BASE              0x40012700
+#define STM32L_COMP_BASE             0x40007C00
+#define STM32L_DAC_BASE              0x40007400
+#define STM32L_SPI1_BASE             0x40013000
+#define STM32L_SPI2_BASE             0x40003800
+#define STM32L_CRC_BASE              0x40023000
+#define STM32L_LCD_BASE              0x40002400
+
+#endif /* __STM32L_REGISTERS */
diff --git a/chip/stm32l/system.c b/chip/stm32l/system.c
new file mode 100644
index 0000000..649950a
--- /dev/null
+++ b/chip/stm32l/system.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* System module for Chrome EC : hardware specific implementation */
+
+#include "cpu.h"
+#include "registers.h"
+#include "system.h"
+
+
+static void check_reset_cause(void)
+{
+	enum system_image_copy_t copy = system_get_image_copy();
+	enum system_reset_cause_t reset_cause = SYSTEM_RESET_UNKNOWN;
+	uint32_t raw_cause = STM32L_RCC_CSR;
+
+	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_cause & 0x60000000) {
+		/* IWDG pr WWDG */
+		reset_cause = SYSTEM_RESET_WATCHDOG;
+	} else if (raw_cause & 0x10000000) {
+		reset_cause = SYSTEM_RESET_SOFT_COLD;
+	} else if (raw_cause & 0x08000000) {
+		reset_cause = SYSTEM_RESET_POWER_ON;
+	} else if (raw_cause & 0x04000000) {
+		reset_cause = SYSTEM_RESET_RESET_PIN;
+	} else if (raw_cause & 0xFE000000) {
+		reset_cause = SYSTEM_RESET_OTHER;
+	} else {
+		reset_cause = SYSTEM_RESET_UNKNOWN;
+	}
+	system_set_reset_cause(reset_cause);
+}
+
+
+void system_hibernate(uint32_t seconds, uint32_t microseconds)
+{
+	/* we are going to hibernate ... */
+	while (1)
+		/* NOT IMPLEMENTED */;
+}
+
+
+int system_pre_init(void)
+{
+	/* enable clock on Power module */
+	STM32L_RCC_APB1ENR |= 1 << 28;
+	/* Enable access to RCC CSR register and RTC backup registers */
+	STM32L_PWR_CR |= 1 << 8;
+
+	/* switch on LSI */
+	STM32L_RCC_CSR |= 1 << 0;
+	/* Wait for LSI to be ready */
+	while (!(STM32L_RCC_CSR & (1 << 1)))
+		;
+	/* re-configure RTC if needed */
+	if ((STM32L_RCC_CSR & 0x00C30000) != 0x00420000) {
+		/* the RTC settings are bad, we need to reset it */
+		STM32L_RCC_CSR |= 0x00800000;
+		/* Enable RTC and use LSI as clock source */
+		STM32L_RCC_CSR = (STM32L_RCC_CSR & ~0x00C30000) | 0x00420000;
+	}
+
+	check_reset_cause();
+
+	return EC_SUCCESS;
+}
+
+
+int system_init(void)
+{
+	/* Clear the hardware reset cause by setting the RMVF bit,
+	 * now that we've committed to running this image.
+	 */
+	STM32L_RCC_CSR |= 1 << 24;
+
+	return EC_SUCCESS;
+}
+
+
+int system_reset(int is_cold)
+{
+	/* TODO: (crosbug.com/p/7470) support cold boot; this is a
+	   warm boot. */
+	CPU_NVIC_APINT = 0x05fa0004;
+
+	/* Spin and wait for reboot; should never return */
+	/* TODO: (crosbug.com/p/7471) should disable task swaps while
+	   waiting */
+	while (1)
+		;
+
+	return EC_ERROR_UNKNOWN;
+}
+
+
+int system_set_scratchpad(uint32_t value)
+{
+	STM32L_RTC_BACKUP(0) = value;
+
+	return EC_SUCCESS;
+}
+
+
+uint32_t system_get_scratchpad(void)
+{
+	return STM32L_RTC_BACKUP(0);
+}
diff --git a/chip/stm32l/uart.c b/chip/stm32l/uart.c
new file mode 100644
index 0000000..0127c7b
--- /dev/null
+++ b/chip/stm32l/uart.c
@@ -0,0 +1,137 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* USART driver for Chrome EC */
+
+#include <stdarg.h>
+
+#include "board.h"
+#include "config.h"
+#include "registers.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+
+/* Baud rate for UARTs */
+#define BAUD_RATE 115200
+
+/* Console USART index */
+#define UARTN CONFIG_CONSOLE_UART
+
+/* record last TX control action */
+static int should_stop;
+
+void uart_tx_start(void)
+{
+	STM32L_USART_CR1(UARTN) |= 0x80;
+	should_stop = 0;
+	task_trigger_irq(STM32L_IRQ_USART(UARTN));
+}
+
+void uart_tx_stop(void)
+{
+	STM32L_USART_CR1(UARTN) &= ~0x80;
+	should_stop = 1;
+}
+
+int uart_tx_stopped(void)
+{
+	return !(STM32L_USART_CR1(UARTN) & 0x80);
+}
+
+void uart_tx_flush(void)
+{
+	while (!(STM32L_USART_SR(UARTN) & 0x80))
+		;
+}
+
+int uart_tx_ready(void)
+{
+	return STM32L_USART_SR(UARTN) & 0x80;
+}
+
+int uart_rx_available(void)
+{
+	return STM32L_USART_SR(UARTN) & 0x20;
+}
+
+void uart_write_char(char c)
+{
+	STM32L_USART_DR(UARTN) = c;
+}
+
+int uart_read_char(void)
+{
+	return STM32L_USART_DR(UARTN);
+}
+
+void uart_disable_interrupt(void)
+{
+	task_disable_irq(STM32L_IRQ_USART(UARTN));
+}
+
+void uart_enable_interrupt(void)
+{
+	task_enable_irq(STM32L_IRQ_USART(UARTN));
+}
+
+/* Interrupt handler for console USART */
+static void uart_interrupt(void)
+{
+	/*
+	 * Disable the TX empty interrupt before filling the TX buffer since it
+	 * needs an actual write to DR to be cleared.
+	 */
+	STM32L_USART_CR1(UARTN) &= ~0x80;
+
+	/* Read input FIFO until empty, then fill output FIFO */
+	uart_process();
+
+	/*
+	 * Re-enable TX empty interrupt only if it was not disabled by
+	 * uart_process.
+	 */
+	if (!should_stop)
+		STM32L_USART_CR1(UARTN) |= 0x80;
+}
+DECLARE_IRQ(STM32L_IRQ_USART(UARTN), uart_interrupt, 1);
+
+int uart_init(void)
+{
+	/*
+	 * Check that the UART parameters used for panic/watchdog are matching
+	 * the console USART parameters.
+	*/
+	BUILD_ASSERT(STM32L_USART_BASE(UARTN) == CONFIG_UART_ADDRESS);
+
+	/* Enable USART clock */
+	if (UARTN == 1)
+		STM32L_RCC_APB2ENR |= 1 << 14; /* USART1 */
+	else if (UARTN == 2)
+		STM32L_RCC_APB1ENR |= 1 << 17; /* USART2 */
+	else if (UARTN == 3)
+		STM32L_RCC_APB1ENR |= 1 << 18; /* USART3 */
+
+	/* UART enabled, 8 Data bits, oversampling x16, no parity,
+	 * RXNE interrupt, TX and RX enabled.
+	 */
+	STM32L_USART_CR1(UARTN) = 0x202C;
+
+	/* 1 stop bit, no fancy stuff */
+	STM32L_USART_CR2(UARTN) = 0x0000;
+
+	/* DMA disabled, special modes disabled, error interrupt disabled */
+	STM32L_USART_CR3(UARTN) = 0x0000;
+
+	/* Select the baud rate
+	 * using x16 oversampling (OVER8 == 0)
+	 */
+	STM32L_USART_BRR(UARTN) = DIV_ROUND_NEAREST(CPU_CLOCK, BAUD_RATE);
+
+	/* Enable interrupts */
+	task_enable_irq(STM32L_IRQ_USART(UARTN));
+
+	return EC_SUCCESS;
+}
diff --git a/chip/stm32l/watchdog.c b/chip/stm32l/watchdog.c
new file mode 100644
index 0000000..4a1cd7f
--- /dev/null
+++ b/chip/stm32l/watchdog.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Watchdog driver */
+
+#include <stdint.h>
+
+#include "board.h"
+#include "common.h"
+#include "config.h"
+#include "registers.h"
+#include "gpio.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+/* LSI oscillator frequency is typically 38 kHz
+ * but might vary from 28 to 56kHz.
+ * So let's pick 56kHz to ensure we reload
+ * early enough.
+ */
+#define LSI_CLOCK 56000
+
+/* Prescaler divider = /256 */
+#define IWDG_PRESCALER 6
+#define IWDG_PRESCALER_DIV (1 << ((IWDG_PRESCALER) + 2))
+
+void watchdog_reload(void)
+{
+	/* Reload the watchdog */
+	STM32L_IWDG_KR = 0xaaaa;
+}
+
+int watchdog_init(int period_ms)
+{
+	uint32_t watchdog_period;
+
+	/* set the time-out period */
+	watchdog_period = period_ms * (LSI_CLOCK / IWDG_PRESCALER_DIV) / 1000;
+
+	/* Unlock watchdog registers */
+	STM32L_IWDG_KR = 0x5555;
+
+	/* Set the prescaler between the LSI clock and the watchdog counter */
+	STM32L_IWDG_PR = IWDG_PRESCALER & 7;
+	/* Set the reload value of the watchdog counter */
+	STM32L_IWDG_RLR = watchdog_period & 0x7FF ;
+
+	/* Start the watchdog (and re-lock registers) */
+	STM32L_IWDG_KR = 0xcccc;
+
+	return EC_SUCCESS;
+}
+
+/* Low priority task to reload the watchdog */
+void watchdog_task(void)
+{
+	while (1) {
+#ifdef BOARD_discovery
+		gpio_set_level(GPIO_GREEN_LED, 1);
+#endif
+		usleep(500000);
+		watchdog_reload();
+#ifdef BOARD_discovery
+		gpio_set_level(GPIO_GREEN_LED, 0);
+#endif
+		usleep(500000);
+		watchdog_reload();
+	}
+}
diff --git a/common/build.mk b/common/build.mk
new file mode 100644
index 0000000..564cfe8
--- /dev/null
+++ b/common/build.mk
@@ -0,0 +1,20 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Common files build
+#
+
+common-y=main.o util.o console.o vboot.o uart_buffering.o
+common-y+=memory_commands.o shared_mem.o system.o usb_charge.o
+common-y+=gpio_commands.o
+common-$(CONFIG_LPC)+=port80.o
+common-$(CONFIG_TASK_HOSTCMD)+=host_command.o
+common-$(CONFIG_TASK_I8042CMD)+=i8042.o keyboard.o
+common-$(CONFIG_TASK_X86POWER)+=x86_power.o
+common-$(CONFIG_FLASH)+=flash_commands.o
+common-$(CONFIG_PWM)+=pwm_commands.o
+common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o temp_sensor_commands.o
+
+# Board driver modules
+common-$(CONFIG_CHARGER_BQ24725)+=charger_bq24725.o
diff --git a/common/charger_bq24725.c b/common/charger_bq24725.c
new file mode 100644
index 0000000..5b70d0d
--- /dev/null
+++ b/common/charger_bq24725.c
@@ -0,0 +1,273 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * TI bq24725 battery charger driver.
+ */
+
+#include "board.h"
+#include "charger.h"
+#include "charger_bq24725.h"
+#include "console.h"
+#include "common.h"
+#include "i2c.h"
+#include "smart_battery.h"
+#include "uart.h"
+#include "util.h"
+
+/* Sense resistor configurations and macros */
+#define DEFAULT_SENSE_RESISTOR 10
+#define R_SNS CONFIG_BQ24725_R_SNS
+#define R_AC  CONFIG_BQ24725_R_AC
+#define REG_TO_CURRENT(REG, RS) ((REG) * DEFAULT_SENSE_RESISTOR / (RS))
+#define CURRENT_TO_REG(CUR, RS) ((CUR) * (RS) / DEFAULT_SENSE_RESISTOR)
+
+/* Charger infomation
+ * charge voltage bitmask: 0111 1111 1111 0000
+ * charge current bitmask: 0001 1111 1000 0000
+ * input current bitmask : 0000 0000 1000 0000
+ */
+static const struct charger_info bq24725_charger_info = {
+	.name         = "bq24725",
+	.voltage_max  = 19200,
+	.voltage_min  = 1024,
+	.voltage_step = 16,
+	.current_max  = REG_TO_CURRENT(8128, R_SNS),
+	.current_min  = REG_TO_CURRENT(128, R_SNS),
+	.current_step = REG_TO_CURRENT(128, R_SNS),
+	.input_current_max  = REG_TO_CURRENT(8064, R_AC),
+	.input_current_min  = REG_TO_CURRENT(128, R_AC),
+	.input_current_step = REG_TO_CURRENT(128, R_AC),
+};
+
+/* bq24725 specific interfaces */
+
+static int charger_set_input_current(int input_current)
+{
+	return i2c_write16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		BQ24725_INPUT_CURRENT, CURRENT_TO_REG(input_current, R_AC));
+}
+
+static int charger_get_input_current(int *input_current)
+{
+	int rv;
+	int reg;
+
+	rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		BQ24725_INPUT_CURRENT, &reg);
+	if (rv)
+		return rv;
+
+	*input_current = REG_TO_CURRENT(reg, R_AC);
+
+	return EC_SUCCESS;
+}
+
+static int charger_manufacturer_id(int *id)
+{
+	return i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		BQ24725_MANUFACTURE_ID, id);
+}
+
+static int charger_device_id(int *id)
+{
+	return i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		BQ24725_DEVICE_ID, id);
+}
+
+static int charger_get_option(int *option)
+{
+	return i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		BQ24725_CHARGE_OPTION, option);
+}
+
+static int charger_set_option(int option)
+{
+	return i2c_write16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		BQ24725_CHARGE_OPTION, option);
+}
+
+/* charger interfaces */
+const struct charger_info *charger_get_info(void)
+{
+	return &bq24725_charger_info;
+}
+
+int charger_get_status(int *status)
+{
+	int rv;
+	int option;
+
+	rv = charger_get_option(&option);
+	if (rv)
+		return rv;
+
+	/* Default status */
+	*status = CHARGER_LEVEL_2;
+
+	if (option & OPTION_CHARGE_INHIBIT)
+		*status |= CHARGER_CHARGE_INHIBITED;
+
+	return EC_SUCCESS;
+}
+
+int charger_set_mode(int mode)
+{
+	int rv;
+	int option;
+
+	rv = charger_get_option(&option);
+	if (rv)
+		return rv;
+
+	if (mode & CHARGE_FLAG_INHIBIT_CHARGE)
+		option |= OPTION_CHARGE_INHIBIT;
+	else
+		option &= ~OPTION_CHARGE_INHIBIT;
+	return charger_set_option(option);
+}
+
+int charger_get_current(int *current)
+{
+	int rv;
+	int reg;
+
+	rv = i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		SB_CHARGING_CURRENT, &reg);
+	if (rv)
+		return rv;
+
+	*current = REG_TO_CURRENT(reg, R_SNS);
+	return EC_SUCCESS;
+}
+
+int charger_set_current(int current)
+{
+	return i2c_write16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		SB_CHARGING_CURRENT, CURRENT_TO_REG(current, R_SNS));
+}
+
+int charger_get_voltage(int *voltage)
+{
+	return i2c_read16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		SB_CHARGING_VOLTAGE, voltage);
+}
+
+int charger_set_voltage(int voltage)
+{
+	return i2c_write16(I2C_PORT_CHARGER, CHARGER_ADDR,
+		SB_CHARGING_VOLTAGE, voltage);
+}
+
+/* Initialization */
+int charger_init(void)
+{
+	/* bq24725 power on reset state:
+	 * charger watch dog timer = 175sec
+	 * charger input current limit = 4096 * 10 / RS_AC
+	 */
+	return EC_SUCCESS;
+}
+
+
+/* Console commands */
+
+static void print_usage(void)
+{
+	uart_puts("Usage: charger [set_command value]\n");
+	uart_puts("    charger input   input_current_in_mA\n");
+	uart_puts("    charger voltage voltage_limit_in_mV\n");
+	uart_puts("    charger current current_limit_in_mA\n\n");
+}
+
+static int print_info(void)
+{
+	int rv;
+	int d;
+	const struct charger_info *info;
+
+	uart_puts("Charger properties : now (max, min, step)\n");
+
+	/* info */
+	info = charger_get_info();
+	uart_printf("  name           : %s\n", info->name);
+
+	/* manufacturer id */
+	rv = charger_manufacturer_id(&d);
+	if (rv)
+		return rv;
+	uart_printf("  manufacturer id: 0x%04x\n", d);
+
+	/* device id */
+	rv = charger_device_id(&d);
+	if (rv)
+		return rv;
+	uart_printf("  device id      : 0x%04x\n", d);
+
+	/* charge voltage limit */
+	rv = charger_get_voltage(&d);
+	if (rv)
+		return rv;
+	uart_printf("  voltage        : %5d (%5d, %4d, %3d)\n", d,
+		info->voltage_max, info->voltage_min, info->voltage_step);
+
+	/* charge current limit */
+	rv = charger_get_current(&d);
+	if (rv)
+		return rv;
+	uart_printf("  current        : %5d (%5d, %4d, %3d)\n", d,
+		info->current_max, info->current_min, info->current_step);
+
+	/* input current limit */
+	rv = charger_get_input_current(&d);
+	if (rv)
+		return rv;
+
+	uart_printf("  input current  : %5d (%5d, %4d, %3d)\n", d,
+		info->input_current_max, info->input_current_min,
+		info->input_current_step);
+
+	return EC_SUCCESS;
+}
+
+static int command_charger(int argc, char **argv)
+{
+	int d;
+	char *endptr;
+
+	if (argc != 3) {
+		if (argc != 1)
+			print_usage();
+		return print_info();
+	}
+
+	if (strcasecmp(argv[1], "input") == 0) {
+		d = strtoi(argv[2], &endptr, 0);
+		if (*endptr) {
+			print_usage();
+			return EC_ERROR_UNKNOWN;
+		}
+		return charger_set_input_current(d);
+	} else if (strcasecmp(argv[1], "current") == 0) {
+		d = strtoi(argv[2], &endptr, 0);
+		if (*endptr) {
+			print_usage();
+			return EC_ERROR_UNKNOWN;
+		}
+		return charger_set_current(d);
+	} else if (strcasecmp(argv[1], "voltage") == 0) {
+		d = strtoi(argv[2], &endptr, 0);
+		if (*endptr) {
+			print_usage();
+			return EC_ERROR_UNKNOWN;
+		}
+		return charger_set_voltage(d);
+	} else {
+		print_usage();
+		return print_info();
+	}
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(charger, command_charger);
+
diff --git a/common/charger_bq24725.h b/common/charger_bq24725.h
new file mode 100644
index 0000000..ab0a432
--- /dev/null
+++ b/common/charger_bq24725.h
@@ -0,0 +1,53 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * TI bq24725 battery charger driver.
+ */
+
+#ifndef __CROS_EC_CHARGER_BQ24725_H
+#define __CROS_EC_CHARGER_BQ24725_H
+
+/* I2C address */
+#define CHARGER_ADDR 0x12
+
+/* Chip specific commands */
+#define BQ24725_CHARGE_OPTION           0x12
+#define BQ24725_INPUT_CURRENT           0x3f
+#define BQ24725_MANUFACTURE_ID          0xfe
+#define BQ24725_DEVICE_ID               0xff
+
+/* ChargeOption 0x12 */
+#define OPTION_CHARGE_INHIBIT           (1 << 0)
+#define OPTION_ACOC_THRESHOLD           (3 << 1)
+#define OPTION_IOUT_SELECTION           (1 << 5)
+#define OPTION_LEARN_ENABLE             (1 << 6)
+#define OPTION_IFAULT_HI_THRESHOLD      (3 << 7)
+#define OPTION_EMI_FREQ_ENABLE          (1 << 9)
+#define OPTION_EMI_FREQ_ADJ             (1 << 10)
+#define OPTION_BAT_DEPLETION_THRESHOLD  (3 << 11)
+#define OPTION_WATCHDOG_TIMER           (3 << 13)
+#define OPTION_AOC_DELITCH_TIME         (1 << 15)
+/* OPTION_ACOC_THRESHOLD */
+#define ACOC_THRESHOLD_DISABLE          (0 << 1)
+#define ACOC_THRESHOLD_133X             (1 << 1)
+#define ACOC_THRESHOLD_166X_DEFAULT     (2 << 1)
+#define ACOC_THRESHOLD_222X             (3 << 1)
+/* OPTION_IFAULT_HI_THRESHOLD */
+#define IFAULT_THRESHOLD_300MV          (0 << 7)
+#define IFAULT_THRESHOLD_500MV          (1 << 7)
+#define IFAULT_THRESHOLD_700MV_DEFAULT  (2 << 7)
+#define IFAULT_THRESHOLD_900MV          (3 << 7)
+/* OPTION_BAT_DEPLETION_THRESHOLD */
+#define FALLING_THRESHOLD_5919          (0 << 11)
+#define FALLING_THRESHOLD_6265          (1 << 11)
+#define FALLING_THRESHOLD_6655          (2 << 11)
+#define FALLING_THRESHOLD_7097_DEFAULT  (3 << 11)
+/* OPTION_WATCHDOG_TIMER */
+#define CHARGE_WATCHDOG_DISABLE         (0 << 13)
+#define CHARGE_WATCHDOG_44SEC           (1 << 13)
+#define CHARGE_WATCHDOG_88SEC           (2 << 13)
+#define CHARGE_WATCHDOG_175SEC_DEFAULT  (3 << 13)
+
+#endif /* __CROS_EC_CHARGER_BQ24725_H */
+
diff --git a/common/console.c b/common/console.c
new file mode 100644
index 0000000..130b7e4
--- /dev/null
+++ b/common/console.c
@@ -0,0 +1,189 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Console module for Chrome EC */
+
+#include "console.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+
+#define MAX_ARGS_PER_COMMAND 10
+
+#define PROMPT "> "
+
+/* Console commands are described in a special section */
+extern const struct console_command __cmds[];
+extern const struct console_command __cmds_end[];
+
+void console_has_input(void)
+{
+	/* Wake up the console task */
+	task_send_msg(TASK_ID_CONSOLE, TASK_ID_CONSOLE, 0);
+}
+
+
+/* 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;
+
+	for (cmd = __cmds; cmd < __cmds_end; 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 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));
+
+		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;
+	const int ncmds = ((uint32_t)__cmds_end - (uint32_t)__cmds) /
+		sizeof(struct console_command);
+	const char *prev = " ";
+	int i;
+
+	uart_puts("Known commands:");
+
+	/* Sort the commands by name */
+	for (i = 0; i < ncmds; i++) {
+		const char *next = "zzzz";
+
+		if (!(i % 5))
+			uart_puts("\n  ");
+
+		/* Find the next command */
+		for (cmd = __cmds; cmd < __cmds_end; cmd++) {
+			if (strcasecmp(prev, cmd->name) < 0 &&
+			    strcasecmp(cmd->name, next) < 0)
+				next = cmd->name;
+		}
+
+		uart_printf("%-15s", next);
+		/* Generates enough output to overflow the buffer */
+		uart_flush_output();
+
+		prev = next;
+	}
+
+	uart_puts("\n");
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(help, command_help);
+
+/*****************************************************************************/
+/* Initialization */
+
+int console_init(void)
+{
+	*input_buf = '\0';
+	uart_set_console_mode(1);
+	uart_printf("Console is enabled; type HELP for help.\n");
+	uart_puts(PROMPT);
+
+	return EC_SUCCESS;
+}
diff --git a/common/firmware_image.S b/common/firmware_image.S
new file mode 100644
index 0000000..78207f9
--- /dev/null
+++ b/common/firmware_image.S
@@ -0,0 +1,25 @@
+/* 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.
+ *
+ * Build the full image with 3 copies (Read only, A, B) of the program
+ */
+
+#include "config.h"
+
+#define FW_FILE(builddir,proj,sect) builddir##/##proj##.##sect##.flat
+#define STRINGIFY0(name)  #name
+#define STRINGIFY(name)  STRINGIFY0(name)
+#define FW_IMAGE(sect) STRINGIFY(FW_FILE(OUTDIR,PROJECT,sect))
+
+/* Read Only firmware */
+.section .image.RO, "ax"
+.incbin FW_IMAGE(RO)
+
+/* Read Write firmware copy A */
+.section .image.A, "ax"
+.incbin FW_IMAGE(A)
+
+/* Read Write firmware copy B */
+.section .image.B, "ax"
+.incbin FW_IMAGE(B)
diff --git a/common/firmware_image.lds.S b/common/firmware_image.lds.S
new file mode 100644
index 0000000..c1c7d59
--- /dev/null
+++ b/common/firmware_image.lds.S
@@ -0,0 +1,23 @@
+#include "config.h"
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+MEMORY
+{
+    FLASH (rx) : ORIGIN = CONFIG_FLASH_BASE, LENGTH = CONFIG_FLASH_SIZE
+}
+SECTIONS
+{
+    . = ALIGN(CONFIG_FLASH_BANK_SIZE);
+    .image.RO : AT(CONFIG_FW_RO_OFF) {
+        *(.image.RO)
+    } > FLASH
+    . = ALIGN(CONFIG_FLASH_BANK_SIZE);
+    .image.A : AT(CONFIG_FW_A_OFF) {
+        *(.image.A)
+    } > FLASH
+    . = ALIGN(CONFIG_FLASH_BANK_SIZE);
+    .image.B : AT(CONFIG_FW_B_OFF) {
+        *(.image.B)
+    } > FLASH
+}
diff --git a/common/flash_commands.c b/common/flash_commands.c
new file mode 100644
index 0000000..000be73
--- /dev/null
+++ b/common/flash_commands.c
@@ -0,0 +1,333 @@
+/* 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 B\n", flash_get_size());
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(flashinfo, command_flash_info);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(flasherase, command_flash_erase);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(flashwrite, command_flash_write);
+
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(flashwp, command_flash_wp);
+
+static int command_flash_wp_range(int argc, char **argv)
+{
+	int offset, size;
+	char *endptr;
+	int rv;
+
+	if (argc < 3) {
+		uart_puts("Usage: flashwprange [offset size]\n");
+		rv = flash_get_write_protect_range(&offset, &size);
+		if (rv)
+			uart_puts("flash_get_write_protect_range failed\n");
+		else
+			uart_printf("Current range : offset(%d) size(%d)\n",
+					offset, size);
+		uart_printf("FMPPEs : %08x %08x %08x %08x\n",
+				LM4_FLASH_FMPPE0, LM4_FLASH_FMPPE1,
+				LM4_FLASH_FMPPE2, LM4_FLASH_FMPPE3);
+	} else {
+		offset = strtoi(argv[1], &endptr, 0);
+		if (*endptr) {
+			uart_printf("Invalid offset \"%s\"\n", argv[1]);
+			return EC_ERROR_UNKNOWN;
+		}
+		size = strtoi(argv[2], &endptr, 0);
+		if (*endptr) {
+			uart_printf("Invalid size \"%s\"\n", argv[2]);
+			return EC_ERROR_UNKNOWN;
+		}
+
+		rv = flash_set_write_protect_range(offset, size);
+		if (rv) {
+			uart_puts("flash_set_write_protect_range failed\n");
+			return rv;
+		}
+	}
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(flashwprange, command_flash_wp_range);
+
+
+/*****************************************************************************/
+/* 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;
+}
+
+
+#ifdef SUPPORT_CHECKSUM
+enum lpc_status flash_command_checksum(uint8_t *data)
+{
+	struct lpc_params_flash_checksum *p =
+	                (struct lpc_params_flash_checksum *)data;
+	struct lpc_response_flash_checksum *r =
+	                (struct lpc_response_flash_checksum *)data;
+	uint8_t cs, byte;
+	int j;
+
+	for (cs = 0, j = 0; j < p->size; ++j) {
+		if (flash_read(p->offset + j, 1, &byte)) {
+			uart_printf("flash_read() error at 0x%02x.\n",
+			            p->offset + j);
+			return EC_LPC_STATUS_ERROR;
+		}
+		BYTE_IN(cs, byte);
+	}
+
+	r->checksum = cs;
+
+	return EC_LPC_STATUS_SUCCESS;
+}
+#endif
+
+
+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;
+}
+
+/* TODO: use shadow range in EEPROM */
+static int shadow_wp_offset;
+static int shadow_wp_size;
+
+enum lpc_status flash_command_wp_enable(uint8_t *data)
+{
+	struct lpc_params_flash_wp_enable *p =
+			(struct lpc_params_flash_wp_enable *)data;
+	int offset, size;
+
+	if (p->enable_wp) {
+		offset = shadow_wp_offset;
+		size   = shadow_wp_size;
+	} else {
+		offset = 0;
+		size   = 0;
+	}
+	if (flash_set_write_protect_range(offset, size))
+		return EC_LPC_STATUS_ERROR;
+
+	return EC_LPC_STATUS_SUCCESS;
+}
+
+enum lpc_status flash_command_wp_get_state(uint8_t *data)
+{
+	struct lpc_response_flash_wp_enable *p =
+			(struct lpc_response_flash_wp_enable *)data;
+
+	if (flash_get_write_protect_status() & EC_FLASH_WP_RANGE_LOCKED)
+		p->enable_wp = 1;
+	else
+		p->enable_wp = 0;
+
+	return EC_LPC_STATUS_SUCCESS;
+}
+
+enum lpc_status flash_command_wp_set_range(uint8_t *data)
+{
+	struct lpc_params_flash_wp_range *p =
+			(struct lpc_params_flash_wp_range *)data;
+
+	if (flash_set_write_protect_range(p->offset, p->size))
+		return EC_LPC_STATUS_ERROR;
+
+	return EC_LPC_STATUS_SUCCESS;
+}
+
+enum lpc_status flash_command_wp_get_range(uint8_t *data)
+{
+	struct lpc_response_flash_wp_range *p =
+			(struct lpc_response_flash_wp_range *)data;
+
+	if (flash_get_write_protect_range(&p->offset, &p->size))
+		return EC_LPC_STATUS_ERROR;
+
+	return EC_LPC_STATUS_SUCCESS;
+}
diff --git a/common/gpio_commands.c b/common/gpio_commands.c
new file mode 100644
index 0000000..66bebb4
--- /dev/null
+++ b/common/gpio_commands.c
@@ -0,0 +1,129 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* GPIO console commands for Chrome EC */
+
+#include "board.h"
+#include "console.h"
+#include "gpio.h"
+#include "uart.h"
+#include "util.h"
+
+
+/* Signal information from board.c.  Must match order from enum gpio_signal. */
+extern const struct gpio_info gpio_list[GPIO_COUNT];
+
+
+/* Find a GPIO signal by name.  Returns the signal index, or GPIO_COUNT if
+ * no match. */
+static enum gpio_signal find_signal_by_name(const char *name)
+{
+	const struct gpio_info *g = gpio_list;
+	int i;
+
+	if (!name || !*name)
+		return GPIO_COUNT;
+
+	for (i = 0; i < GPIO_COUNT; i++, g++) {
+		if (!strcasecmp(name, g->name))
+			return i;
+	}
+
+	return GPIO_COUNT;
+}
+
+
+static uint8_t last_val[(GPIO_COUNT + 7) / 8];
+
+/* If v is different from the last value for index i, updates the last value
+ * and returns 1; else returns 0. */
+static int last_val_changed(int i, int v)
+{
+	if (v && !(last_val[i / 8] & (1 << (i % 8)))) {
+		last_val[i / 8] |= 1 << (i % 8);
+		return 1;
+	} else if (!v && last_val[i / 8] & (1 << (i % 8))) {
+		last_val[i / 8] &= ~(1 << (i % 8));
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+static int command_gpio_get(int argc, char **argv)
+{
+	const struct gpio_info *g = gpio_list;
+	int changed, v, i;
+
+	/* If a signal is specified, print only that one */
+	if (argc == 2) {
+		i = find_signal_by_name(argv[1]);
+		if (i == GPIO_COUNT) {
+			uart_puts("Unknown signal name.\n");
+			return EC_ERROR_UNKNOWN;
+		}
+		g = gpio_list + i;
+		v = gpio_get_level(i);
+		changed = last_val_changed(i, v);
+		uart_printf("  %d%c %s\n", v, (changed ? '*' : ' '), g->name);
+
+		return EC_SUCCESS;
+	}
+
+	/* Otherwise print them all */
+	uart_puts("Current GPIO levels:\n");
+	for (i = 0; i < GPIO_COUNT; i++, g++) {
+		if (!g->mask)
+			continue;  /* Skip unsupported signals */
+
+		v = gpio_get_level(i);
+		changed = last_val_changed(i, v);
+		uart_printf("  %d%c %s\n", v, (changed ? '*' : ' '), g->name);
+
+		/* We have enough GPIOs that we'll overflow the output buffer
+		 * without flushing */
+		uart_flush_output();
+	}
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(gpioget, command_gpio_get);
+
+
+static int command_gpio_set(int argc, char **argv)
+{
+	const struct gpio_info *g;
+	char *e;
+	int v, i;
+
+	if (argc < 3) {
+		uart_puts("Usage: gpioset <signal_name> <0|1>\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	i = find_signal_by_name(argv[1]);
+	if (i == GPIO_COUNT) {
+		uart_puts("Unknown signal name.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+	g = gpio_list + i;
+
+	if (!g->mask) {
+		uart_puts("Signal is not implemented.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+	if (!(g->flags & GPIO_OUTPUT)) {
+		uart_puts("Signal is not an output.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	v = strtoi(argv[2], &e, 0);
+	if (*e) {
+		uart_puts("Invalid signal value.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	return gpio_set_level(i, v);
+}
+DECLARE_CONSOLE_COMMAND(gpioset, command_gpio_set);
diff --git a/common/host_command.c b/common/host_command.c
new file mode 100644
index 0000000..5cebed2
--- /dev/null
+++ b/common/host_command.c
@@ -0,0 +1,232 @@
+/* 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 "temp_sensor_commands.h"
+#include "pwm_commands.h"
+#include "lpc.h"
+#include "lpc_commands.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "registers.h"
+#include "util.h"
+
+static int host_command[2];
+
+/*****************************************************************************/
+/* Host commands */
+
+void host_command_received(int slot, int command)
+{
+	/* TODO: should warn if we already think we're in a command */
+
+	/* If this is the reboot command, reboot immediately.  This gives
+	 * the host processor a way to unwedge the EC even if it's busy with
+	 * some other command. */
+	if (command == EC_LPC_COMMAND_REBOOT) {
+		system_reset(1);
+		/* Reset should never return; if it does, post an error */
+		lpc_send_host_response(slot, EC_LPC_STATUS_ERROR);
+		return;
+	}
+
+	/* Save the command */
+	host_command[slot] = command;
+
+	/* Wake up the task to handle the command.  Use the slot as
+	 * the task ID. */
+	task_send_msg(TASK_ID_HOSTCMD, slot, 0);
+}
+
+
+static enum lpc_status host_command_hello(uint8_t *data)
+{
+	struct lpc_params_hello *p = (struct lpc_params_hello *)data;
+	struct lpc_response_hello *r = (struct lpc_response_hello *)data;
+	uint32_t d = p->in_data;
+
+	uart_printf("[LPC Hello 0x%08x]\n", d);
+
+#ifdef DELAY_HELLO_RESPONSE
+	/* Pretend command takes a long time, so we can see the busy
+	 * bit set on the host side. */
+	/* TODO: remove in production.  Or maybe hello should take a
+	 * param with how long the delay should be; that'd be more
+	 * useful. */
+	usleep(1000000);
+#endif
+
+	uart_puts("[LPC sending hello back]\n");
+
+	r->out_data = d + 0x01020304;
+	return EC_LPC_STATUS_SUCCESS;
+}
+
+
+static enum lpc_status host_command_get_version(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 host_command_read_test(uint8_t *data)
+{
+	struct lpc_params_read_test *p = (struct lpc_params_read_test *)data;
+	struct lpc_response_read_test *r =
+			(struct lpc_response_read_test *)data;
+
+	int offset = p->offset;
+	int size = p->size / sizeof(uint32_t);
+	int i;
+
+	if (size > ARRAY_SIZE(r->data))
+		return EC_LPC_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(int slot)
+{
+	int command = host_command[slot];
+	uint8_t *data = lpc_get_host_range(slot);
+
+	uart_printf("[hostcmd%d 0x%02x]\n", slot, command);
+
+	/* TODO: might be smaller to make this a table, once we get a bunch
+	 * of commands. */
+	switch (command) {
+	case EC_LPC_COMMAND_HELLO:
+		lpc_send_host_response(slot, host_command_hello(data));
+		return;
+	case EC_LPC_COMMAND_GET_VERSION:
+		lpc_send_host_response(slot, host_command_get_version(data));
+		return;
+	case EC_LPC_COMMAND_READ_TEST:
+		lpc_send_host_response(slot, host_command_read_test(data));
+		return;
+	case EC_LPC_COMMAND_FLASH_INFO:
+		lpc_send_host_response(slot, flash_command_get_info(data));
+		return;
+	case EC_LPC_COMMAND_FLASH_READ:
+		lpc_send_host_response(slot, flash_command_read(data));
+		return;
+	case EC_LPC_COMMAND_FLASH_WRITE:
+		lpc_send_host_response(slot, flash_command_write(data));
+		return;
+	case EC_LPC_COMMAND_FLASH_ERASE:
+		lpc_send_host_response(slot, flash_command_erase(data));
+		return;
+	case EC_LPC_COMMAND_FLASH_WP_ENABLE:
+		lpc_send_host_response(slot, flash_command_wp_enable(data));
+		return;
+	case EC_LPC_COMMAND_FLASH_WP_GET_STATE:
+		lpc_send_host_response(slot, flash_command_wp_get_state(data));
+		return;
+	case EC_LPC_COMMAND_FLASH_WP_SET_RANGE:
+		lpc_send_host_response(slot, flash_command_wp_set_range(data));
+		return;
+	case EC_LPC_COMMAND_FLASH_WP_GET_RANGE:
+		lpc_send_host_response(slot, flash_command_wp_get_range(data));
+		return;
+#ifdef SUPPORT_CHECKSUM
+	case EC_LPC_COMMAND_FLASH_CHECKSUM:
+		lpc_send_host_response(slot, flash_command_checksum(data));
+		return;
+#endif
+	case EC_LPC_COMMAND_TEMP_SENSOR_GET_READINGS:
+		lpc_send_host_response(slot, temp_sensor_command_get_readings(data));
+		return;
+	case EC_LPC_COMMAND_PWM_GET_FAN_RPM:
+		lpc_send_host_response(slot, pwm_command_get_fan_rpm(data));
+	        return;
+	case EC_LPC_COMMAND_PWM_SET_FAN_TARGET_RPM:
+	        lpc_send_host_response(slot, pwm_command_set_fan_target_rpm(data));
+	        return;
+	default:
+		lpc_send_host_response(slot, 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;
+}
+DECLARE_CONSOLE_COMMAND(version, command_version);
+
+/*****************************************************************************/
+/* Initialization / task */
+
+static int host_command_init(void)
+{
+	host_command[0] = host_command[1] = -1;
+
+	return EC_SUCCESS;
+}
+
+
+void host_command_task(void)
+{
+	host_command_init();
+
+	while (1) {
+		/* wait for the next command message */
+		int m = task_wait_msg(-1);
+		/* process it */
+		/* TODO: use message flags to determine which slots */
+		if (m & 0x01)
+			command_process(0);
+		if (m & 0x02)
+			command_process(1);
+	}
+}
diff --git a/common/i8042.c b/common/i8042.c
new file mode 100644
index 0000000..785e6a7
--- /dev/null
+++ b/common/i8042.c
@@ -0,0 +1,154 @@
+/* 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"
+#include "lpc.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];
+
+static int i8042_irq_enabled = 0;
+
+
+/* Reset all i8042 buffer */
+void i8042_init()
+{
+	head_to_buffer = tail_to_buffer = 0;
+}
+
+
+/* Called by the chip-specific code when host sedns a byte to port 0x60. */
+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);
+}
+
+
+/* Called by the chip-specific code when host sedns a byte to port 0x64. */
+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);
+}
+
+
+/* Called by EC common code to send bytes to host via port 0x60. */
+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 */
+}
+
+
+/* Called by common/keyboard.c when the host wants to receive keyboard IRQ
+ * (or not).
+ */
+void i8042_enable_keyboard_irq(void) {
+	i8042_irq_enabled = 1;
+}
+
+void i8042_disable_keyboard_irq(void) {
+	i8042_irq_enabled = 0;
+}
+
+
+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 (lpc_keyboard_has_char()) {
+#if I8042_DEBUG >= 5
+				uart_printf("[%d] i8042_command_task() "
+					    "cannot send to host due to host "
+					    "havn't taken away.\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. */
+			lpc_keyboard_put_char(chr, i8042_irq_enabled);
+#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/keyboard.c b/common/keyboard.c
new file mode 100644
index 0000000..0c90458
--- /dev/null
+++ b/common/keyboard.c
@@ -0,0 +1,582 @@
+/* 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 keyboard common code.
+ */
+
+#include "common.h"
+#include "console.h"
+#include "keyboard.h"
+#include "i8042.h"
+#include "registers.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+#define KEYBOARD_DEBUG 1
+
+#undef ASSERT
+#define ASSERT(expr) do { \
+      if (!(expr)) { \
+        uart_printf("ASSERT(%s) failed at %s:%d.\n", #expr, __FUNCTION__, __LINE__); \
+        while (1) usleep(1000000); \
+      } \
+    } while (0)
+
+/*
+ * i8042 global settings.
+ */
+static int keyboard_enabled = 0;  /* default the keyboard is disabled. */
+static uint8_t resend_command[MAX_SCAN_CODE_LEN];
+static uint8_t resend_command_len = 0;
+static uint8_t controller_ram_address;
+static uint8_t controller_ram[0x20] = {
+  /* the so called "command byte" */
+  I8042_XLATE | I8042_AUX_DIS | I8042_KBD_DIS,
+  /* 0x01 - 0x1f are controller RAM */
+};
+
+/*
+ * Scancode settings
+ */
+static enum scancode_set_list scancode_set = SCANCODE_SET_2;
+
+/*
+ * Typematic delay, rate and counter variables.
+ *
+ *    7     6     5     4     3     2     1     0
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * |un-  |   delay   |     B     |        D        |
+ * | used|  0     1  |  0     1  |  0     1     1  |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * Formula:
+ *   the inter-char delay = (2 ** B) * (D + 8) / 240 (sec)
+ * Default: 500ms delay, 10.9 chars/sec.
+ */
+#define DEFAULT_TYPEMATIC_VALUE ((1 << 5) || (1 << 3) || (3 << 0))
+#define DEFAULT_FIRST_DELAY 500
+#define DEFAULT_INTER_DELAY 91
+static uint8_t typematic_value_from_host = DEFAULT_TYPEMATIC_VALUE;
+static int refill_first_delay = DEFAULT_FIRST_DELAY;  /* unit: ms */
+static int counter_first_delay;
+static int refill_inter_delay = DEFAULT_INTER_DELAY;  /* unit: ms */
+static int counter_inter_delay;
+
+
+/* The standard Chrome OS keyboard matrix table. */
+#define CROS_ROW_NUM 8  /* TODO: +1 for power button. */
+#define CROS_COL_NUM 13
+static uint16_t scancode_set1[CROS_ROW_NUM][CROS_COL_NUM] = {
+  {0x0000, 0xe05b, 0x003b, 0x0030, 0x0044, 0x0073, 0x0031, 0x0000, 0x000d,
+                                           0x0000, 0xe038, 0x0000, 0x0000},
+  {0x0000, 0x0001, 0x003e, 0x0022, 0x0041, 0x0000, 0x0023, 0x0000, 0x0028,
+                                           0x0043, 0x0000, 0x000e, 0x0078},
+  {0x001d, 0x000f, 0x003d, 0x0014, 0x0040, 0x001b, 0x0015, 0x0056, 0x001a,
+                                           0x0042, 0x0073, 0x0000, 0x0000},
+  {0x0000, 0x0029, 0x003c, 0x0006, 0x003f, 0x0000, 0x0007, 0x0000, 0x000c,
+                                           0x0000, 0x0000, 0x002b, 0x0079},
+  {0xe01d, 0x001e, 0x0020, 0x0021, 0x001f, 0x0025, 0x0024, 0x0000, 0x0027,
+                                           0x0026, 0x002b, 0x001c, 0x0000},
+  {0x0000, 0x002c, 0x002e, 0x002f, 0x002d, 0x0033, 0x0032, 0x002a, 0x0035,
+                                           0x0034, 0x0000, 0x0039, 0x0000},
+  {0x0000, 0x0002, 0x0004, 0x0005, 0x0003, 0x0009, 0x0008, 0x0000, 0x000b,
+                                           0x000a, 0x0038, 0xe050, 0xe04d},
+  {0x0000, 0x0010, 0x0012, 0x0013, 0x0011, 0x0017, 0x0016, 0x0036, 0x0019,
+                                           0x0018, 0x0000, 0xe048, 0xe04b},
+};
+
+static uint16_t scancode_set2[CROS_ROW_NUM][CROS_COL_NUM] = {
+  {0x0000, 0xe01f, 0x0005, 0x0032, 0x0009, 0x0051, 0x0031, 0x0000, 0x0055,
+                                           0x0000, 0xe011, 0x0000, 0x0000},
+  {0x0000, 0x0076, 0x000c, 0x0034, 0x0083, 0x0000, 0x0033, 0x0000, 0x0052,
+                                           0x0001, 0x0000, 0x0066, 0x0067},
+  {0x0014, 0x000d, 0x0004, 0x002c, 0x000b, 0x005b, 0x0035, 0x0061, 0x0054,
+                                           0x000a, 0x0051, 0x0000, 0x0000},
+  {0x0000, 0x000e, 0x0006, 0x002e, 0x0003, 0x0000, 0x0036, 0x0000, 0x004e,
+                                           0x0000, 0x0000, 0x005d, 0x0064},
+  {0xe014, 0x001c, 0x0023, 0x002b, 0x001b, 0x0042, 0x003b, 0x0000, 0x004c,
+                                           0x004b, 0x005d, 0x005a, 0x0000},
+  {0x0000, 0x001a, 0x0021, 0x002a, 0x0022, 0x0041, 0x003a, 0x0012, 0x004a,
+                                           0x0049, 0x0000, 0x0029, 0x0000},
+  {0x0000, 0x0016, 0x0026, 0x0025, 0x001e, 0x003e, 0x003d, 0x0000, 0x0045,
+                                           0x0046, 0x0011, 0xe072, 0xe074},
+  {0x0000, 0x0015, 0x0024, 0x002d, 0x001d, 0x0043, 0x003c, 0x0059, 0x004d,
+                                           0x0044, 0x0000, 0xe075, 0xe06b},
+};
+
+
+static enum ec_error_list matrix_callback(
+    int8_t row, int8_t col, int8_t pressed,
+    enum scancode_set_list code_set, uint8_t *scan_code, int32_t* len) {
+
+  uint16_t make_code;
+
+  ASSERT(scan_code);
+  ASSERT(len);
+
+  if (row > CROS_ROW_NUM ||
+      col > CROS_COL_NUM) {
+    return EC_ERROR_INVAL;
+  }
+
+  *len = 0;
+
+  if (controller_ram[0] & I8042_XLATE) {
+    /* If the keyboard translation is enabled,
+     * then always generates set 1. */
+    code_set = SCANCODE_SET_1;
+  }
+
+  switch (code_set) {
+  case SCANCODE_SET_1:
+    make_code = scancode_set1[row][col];
+    break;
+
+  case SCANCODE_SET_2:
+    make_code = scancode_set2[row][col];
+    break;
+
+  default:
+#if KEYBOARD_DEBUG >= 1
+    uart_printf("Not supported scan code set: %d\n", code_set);
+#endif
+    return EC_ERROR_UNIMPLEMENTED;
+  }
+  if (!make_code) {
+#if KEYBOARD_DEBUG >= 1
+    uart_printf("No scancode for [row:col]=[%d:%d].\n", row, col);
+#endif
+    return EC_ERROR_UNIMPLEMENTED;
+  }
+
+  /* Output the make code (from table) */
+  if (make_code >= 0x0100) {
+    *len += 2;
+    scan_code[0] = make_code >> 8;
+    scan_code[1] = make_code & 0xff;
+  } else {
+    *len += 1;
+    scan_code[0] = make_code & 0xff;
+  }
+
+  switch (code_set) {
+  case SCANCODE_SET_1:
+    /* OR 0x80 for the last byte. */
+    if (!pressed) {
+      ASSERT(*len >= 1);
+      scan_code[*len - 1] |= 0x80;
+    }
+    break;
+
+  case SCANCODE_SET_2:
+    /* insert the break byte, move back the last byte and insert a 0xf0 byte
+     * before that. */
+    if (!pressed) {
+      ASSERT(*len >= 1);
+      scan_code[*len] = scan_code[*len - 1];
+      scan_code[*len - 1] = 0xF0;
+      *len += 1;
+    }
+    break;
+  default:
+    break;
+  }
+
+  return EC_SUCCESS;
+}
+
+
+static void reset_rate_and_delay(void) {
+  typematic_value_from_host = DEFAULT_TYPEMATIC_VALUE;
+  refill_first_delay = DEFAULT_FIRST_DELAY;
+  refill_inter_delay = DEFAULT_INTER_DELAY;
+}
+
+
+static void clean_underlying_buffer(void) {
+  i8042_init();
+}
+
+
+void keyboard_state_changed(int row, int col, int is_pressed) {
+  uint8_t scan_code[MAX_SCAN_CODE_LEN];
+  int32_t len;
+  enum ec_error_list ret;
+
+#if KEYBOARD_DEBUG >= 5
+  uart_printf("File %s:%s(): row=%d col=%d is_pressed=%d\n",
+      __FILE__, __FUNCTION__, row, col, is_pressed);
+#endif
+
+  ret = matrix_callback(row, col, is_pressed, scancode_set, scan_code, &len);
+  if (ret == EC_SUCCESS) {
+    ASSERT(len > 0);
+
+    i8042_send_to_host(len, scan_code);
+  } else {
+    /* FIXME: long-term solution is to ignore this key. However, keep
+     *        assertion in the debug stage. */
+    ASSERT(ret == EC_SUCCESS);
+  }
+}
+
+
+void keyboard_enable(int enable) {
+  if (!keyboard_enabled && enable) {
+    /* enable */
+  } else if (keyboard_enabled && !enable) {
+    /* disable */
+    reset_rate_and_delay();
+    clean_underlying_buffer();
+  }
+  keyboard_enabled = enable;
+}
+
+
+uint8_t read_ctl_ram(uint8_t addr) {
+  ASSERT(addr < 0x20);  // Controller RAM is only 32 bytes.
+
+  return controller_ram[addr];
+}
+
+
+/* Manipulates the controller_ram[]. Some bits change may trigger internal
+ * state change.
+ */
+void update_ctl_ram(uint8_t addr, uint8_t data) {
+  uint8_t orig;
+
+  ASSERT(addr < 0x20);  // Controller RAM is only 32 bytes.
+  orig = controller_ram[addr];
+  controller_ram[addr] = data;
+#if KEYBOARD_DEBUG >= 5
+  uart_printf("Set CTR_RAM[0x%02x]=0x%02x (old:0x%02x)\n",
+              addr, data, orig);
+#endif
+
+  if (addr == 0x00) {  /* the controller RAM */
+    /* Handle the I8042_KBD_DIS bit */
+    keyboard_enable(!(data & I8042_KBD_DIS));
+
+    /* Handle the I8042_ENIRQ1 bit */
+    if (!(orig & I8042_ENIRQ1) && (data & I8042_ENIRQ1)) {
+      i8042_enable_keyboard_irq();
+    } else if ((orig & I8042_ENIRQ1) && !(data & I8042_ENIRQ1)) {
+      i8042_disable_keyboard_irq();
+    }
+  }
+}
+
+
+enum {
+  STATE_NORMAL = 0,
+  STATE_SCANCODE,
+  STATE_SETLEDS,
+  STATE_WRITE_CMD_BYTE,
+  STATE_ECHO_MOUSE,
+  STATE_SEND_TO_MOUSE,
+} data_port_state = STATE_NORMAL;
+
+
+int handle_keyboard_data(uint8_t data, uint8_t *output) {
+  int out_len = 0;
+  int save_for_resend = 1;
+  int i;
+
+#if KEYBOARD_DEBUG >= 5
+  uart_printf("[%d] Recv data:[0x%02x]\n", get_time().le.lo, data);
+#endif
+
+  switch (data_port_state) {
+  case STATE_SCANCODE:
+#if KEYBOARD_DEBUG >= 5
+    uart_printf("Eaten by STATE_SCANCODE: 0x%02x\n", data);
+#endif
+    if (data == SCANCODE_GET_SET) {
+      output[out_len++] = I8042_RET_ACK;
+      output[out_len++] = scancode_set;
+    } else {
+      scancode_set = data;
+#if KEYBOARD_DEBUG >= 1
+      uart_printf("Scancode set to %d\n", scancode_set);
+#endif
+      output[out_len++] = I8042_RET_ACK;
+    }
+    data_port_state = STATE_NORMAL;
+    break;
+
+  case STATE_SETLEDS:
+#if KEYBOARD_DEBUG >= 5
+    uart_printf("Eaten by STATE_SETLEDS\n");
+#endif
+    output[out_len++] = I8042_RET_ACK;
+    data_port_state = STATE_NORMAL;
+    break;
+
+  case STATE_WRITE_CMD_BYTE:
+#if KEYBOARD_DEBUG >= 5
+    uart_printf("Eaten by STATE_WRITE_CMD_BYTE: 0x%02x\n", data);
+#endif
+    update_ctl_ram(controller_ram_address, data);
+    output[out_len++] = I8042_RET_ACK;
+    data_port_state = STATE_NORMAL;
+    break;
+
+  case STATE_ECHO_MOUSE:
+#if KEYBOARD_DEBUG >= 5
+    uart_printf("Eaten by STATE_ECHO_MOUSE: 0x%02x\n", data);
+#endif
+    output[out_len++] = I8042_RET_ACK;
+    output[out_len++] = data;
+    data_port_state = STATE_NORMAL;
+    break;
+
+  case STATE_SEND_TO_MOUSE:
+#if KEYBOARD_DEBUG >= 5
+    uart_printf("Eaten by STATE_SEND_TO_MOUSE: 0x%02x\n", data);
+#endif
+    data_port_state = STATE_NORMAL;
+    break;
+
+  default:  /* STATE_NORMAL */
+    switch (data) {
+      case I8042_CMD_GSCANSET:  /* also I8042_CMD_SSCANSET */
+        output[out_len++] = I8042_RET_ACK;
+        data_port_state = STATE_SCANCODE;
+        break;
+
+      case I8042_CMD_SETLEDS:  /* fall-thru */
+      case I8042_CMD_EX_SETLEDS:
+        /* We use screen indicator. Do thing in keyboard controller. */
+        output[out_len++] = I8042_RET_ACK;
+        data_port_state = STATE_SETLEDS;
+        break;
+
+      case I8042_CMD_DIAG_ECHO:
+        output[out_len++] = I8042_RET_ACK;
+        output[out_len++] = I8042_CMD_DIAG_ECHO;
+        break;
+
+      case I8042_CMD_GETID:    /* fall-thru */
+      case I8042_CMD_OK_GETID:
+        output[out_len++] = I8042_RET_ACK;
+        output[out_len++] = 0xab;  /* Regular keyboards */
+        output[out_len++] = 0x83;
+        break;
+
+      case I8042_CMD_SETREP:
+        output[out_len++] = I8042_RET_ACK;
+        typematic_value_from_host = data;
+        refill_first_delay = counter_first_delay + counter_inter_delay;
+        refill_first_delay = ((typematic_value_from_host & 0x60) >> 5) * 250;
+        refill_inter_delay = 1000 *  /* ms */
+                             (1 << ((typematic_value_from_host & 0x18) >> 3)) *
+                             ((typematic_value_from_host & 0x7) + 8) /
+                             240;
+        break;
+
+      case I8042_CMD_ENABLE:
+        output[out_len++] = I8042_RET_ACK;
+        keyboard_enable(1);
+        break;
+
+      case I8042_CMD_RESET_DIS:
+        output[out_len++] = I8042_RET_ACK;
+        keyboard_enable(0);
+        reset_rate_and_delay();
+        clean_underlying_buffer();
+        break;
+
+      case I8042_CMD_RESET_DEF:
+        output[out_len++] = I8042_RET_ACK;
+        reset_rate_and_delay();
+        clean_underlying_buffer();
+        break;
+
+      case I8042_CMD_RESET_BAT:
+        output[out_len++] = I8042_RET_ACK;
+        keyboard_enable(0);
+        output[out_len++] = I8042_RET_BAT;
+        output[out_len++] = I8042_RET_BAT;
+        break;
+
+      case I8042_CMD_RESEND:
+        output[out_len++] = I8042_RET_ACK;
+        save_for_resend = 0;
+        for (i = 0; i < resend_command_len; ++i) {
+          output[out_len++] = resend_command[i];
+        }
+        break;
+
+      /* u-boot hack */
+      case 0x60:  /* see CONFIG_USE_CPCIDVI in */
+      case 0x45:  /* third_party/u-boot/files/drivers/input/i8042.c */
+        /* just ignore, don't reply anything. */
+        break;
+
+      case I8042_CMD_SETALL_MB:  /* fall-thru below */
+      case I8042_CMD_SETALL_MBR:
+      case I8042_CMD_EX_ENABLE:
+      default:
+        output[out_len++] = I8042_RET_NAK;
+#if KEYBOARD_DEBUG >= 1
+        uart_printf("Unsupported i8042 data 0x%02x.\n", data);
+#endif
+        break;
+    }
+  }
+
+  /* For resend, keep output before leaving. */
+  if (out_len && save_for_resend) {
+    ASSERT(out_len <= MAX_SCAN_CODE_LEN);
+    for (i = 0; i < out_len; ++i) {
+      resend_command[i] = output[i];
+    }
+    resend_command_len = out_len;
+  }
+
+  ASSERT(out_len <= MAX_SCAN_CODE_LEN);
+  return out_len;
+}
+
+
+int handle_keyboard_command(uint8_t command, uint8_t *output) {
+  int out_len = 0;
+
+#if KEYBOARD_DEBUG >= 5
+  uart_printf("[%d] Recv cmd:[0x%02x]\n", get_time().le.lo, command);
+#endif
+  switch (command) {
+  case I8042_READ_CMD_BYTE:
+    output[out_len++] = read_ctl_ram(0);
+    break;
+
+  case I8042_WRITE_CMD_BYTE:
+    data_port_state = STATE_WRITE_CMD_BYTE;
+    controller_ram_address = command - 0x60;
+    break;
+
+  case I8042_DIS_KB:
+    keyboard_enable(0);
+    break;
+
+  case I8042_ENA_KB:
+    keyboard_enable(1);
+    break;
+
+  case I8042_RESET_SELF_TEST:
+    output[out_len++] = 0x55;  // Self test success.
+    break;
+
+  case I8042_DIS_MOUSE:
+    update_ctl_ram(0, read_ctl_ram(0) | I8042_AUX_DIS);
+    break;
+
+  case I8042_ENA_MOUSE:
+    update_ctl_ram(0, read_ctl_ram(0) & ~I8042_AUX_DIS);
+    break;
+
+  case I8042_TEST_MOUSE:
+    output[out_len++] = 0;  // no error detected
+    break;
+
+  case I8042_ECHO_MOUSE:
+    data_port_state = STATE_ECHO_MOUSE;
+    break;
+
+  case I8042_SEND_TO_MOUSE:
+    data_port_state = STATE_SEND_TO_MOUSE;
+    break;
+
+  default:
+    if (command >= I8042_READ_CTL_RAM &&
+        command <= I8042_READ_CTL_RAM_END) {
+      output[out_len++] = read_ctl_ram(command - 0x20);
+    } else if (command >= I8042_WRITE_CTL_RAM &&
+               command <= I8042_WRITE_CTL_RAM_END) {
+      data_port_state = STATE_WRITE_CMD_BYTE;
+      controller_ram_address = command - 0x60;
+    } else if (command >= I8042_PULSE_START &&
+               command <= I8042_PULSE_END) {
+      /* Pulse Output Bit. Not implemented. Ignore it. */
+    } else {
+#if KEYBOARD_DEBUG >= 1
+      uart_printf("Unsupported cmd:[0x%02x]\n", command);
+#endif
+      reset_rate_and_delay();
+      clean_underlying_buffer();
+      output[out_len++] = I8042_RET_NAK;
+      data_port_state = STATE_NORMAL;
+    }
+    break;
+  }
+
+  return out_len;
+}
+
+
+static int command_codeset(int argc, char **argv)
+{
+	int set;
+
+	if (argc == 1) {
+		uart_printf("Current scancode set: %d\n", scancode_set);
+		uart_printf("I8042_XLATE: %d\n",
+		            controller_ram[0] & I8042_XLATE ? 1 : 0);
+	} else if (argc == 2) {
+		set = strtoi(argv[1], NULL, 0);
+		switch (set) {
+		case SCANCODE_SET_1:  /* fall-thru */
+		case SCANCODE_SET_2:  /* fall-thru */
+			scancode_set = set;
+			uart_printf("Set scancode set to %d\n", scancode_set);
+			break;
+		default:
+			uart_printf("Scancode %d is NOT supported.\n", set);
+			return EC_ERROR_UNKNOWN;
+			break;
+		}
+	} else {
+		uart_puts("Usage: codeset [<set>]\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	uart_flush_output();
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(codeset, command_codeset);
+
+
+static int command_controller_ram(int argc, char **argv)
+{
+	int index;
+
+	if (argc >= 2) {
+		index = strtoi(argv[1], NULL, 0);
+		uart_printf("Controller RAM index = %d\n", index);
+		if (index >= 0x20) {
+			uart_printf("Index is out of range (0x00-0x1f).\n");
+			return EC_ERROR_UNKNOWN;
+		}
+
+		if (argc >= 3) {
+			update_ctl_ram(index, strtoi(argv[2], NULL, 0));
+			uart_printf("Write ctlram[%d] as 0x%02x.\n",
+			            index, controller_ram[index]);
+		} else {
+			uart_printf("ctlram[%d] is 0x%02x.\n",
+			            index, controller_ram[index]);
+		}
+	} else {
+		uart_puts("Usage: ctrlram <index> [<write_value>]\n");
+		uart_puts("\nGet/set controller RAM.\n\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	uart_flush_output();
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ctrlram, command_controller_ram);
diff --git a/common/main.c b/common/main.c
new file mode 100644
index 0000000..79533b9
--- /dev/null
+++ b/common/main.c
@@ -0,0 +1,99 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Main routine for Chrome EC
+ */
+
+#include "adc.h"
+#include "config.h"
+#include "clock.h"
+#include "console.h"
+#include "eeprom.h"
+#include "flash.h"
+#include "flash_commands.h"
+#include "gpio.h"
+#include "i2c.h"
+#include "jtag.h"
+#include "keyboard.h"
+#include "lpc.h"
+#include "memory_commands.h"
+#include "port80.h"
+#include "power_button.h"
+#include "powerdemo.h"
+#include "pwm.h"
+#include "pwm_commands.h"
+#include "system.h"
+#include "task.h"
+#ifdef CONFIG_TEMP_SENSOR
+#include "temp_sensor.h"
+#endif
+#include "timer.h"
+#include "uart.h"
+#include "vboot.h"
+#include "watchdog.h"
+#include "usb_charge.h"
+#include "chip_temp_sensor.h"
+#include "charger.h"
+
+int main(void)
+{
+	/* Configure the pin multiplexers */
+	configure_board();
+	jtag_pre_init();
+
+	/* Initialize the system module.  This enables the hibernate clock
+	 * source we need to calibrate the internal oscillator. */
+	system_pre_init();
+
+	/* 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. */
+	gpio_pre_init();
+	vboot_pre_init();
+
+	task_init();
+
+#ifdef CONFIG_TASK_WATCHDOG
+	watchdog_init(1100);
+#endif
+	timer_init();
+	uart_init();
+	system_init();
+#ifdef CONFIG_FLASH
+	flash_init();
+#endif
+	eeprom_init();
+#ifdef CONFIG_LPC
+	port_80_init();
+	lpc_init();
+#endif
+#ifdef CONFIG_PWM
+	pwm_init();
+#endif
+	i2c_init();
+#ifdef CONFIG_TEMP_SENSOR
+	temp_sensor_init();
+	chip_temp_sensor_init();
+#endif
+	power_button_init();
+	adc_init();
+	usb_charge_init();
+#ifdef CONFIG_CHARGER
+	charger_init();
+#endif
+
+	/* 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/memory_commands.c b/common/memory_commands.c
new file mode 100644
index 0000000..476aa92
--- /dev/null
+++ b/common/memory_commands.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.
+ */
+
+/* System module for Chrome EC */
+
+#include "console.h"
+#include "uart.h"
+#include "util.h"
+
+
+static int command_write_word(int argc, char **argv)
+{
+	volatile uint32_t *address;
+	uint32_t value;
+
+	if (argc != 3) {
+		uart_puts("Usage: ww <address> <value>\n");
+		return EC_ERROR_UNKNOWN;
+	}
+	address = (uint32_t*)strtoi(argv[1], NULL, 0);
+	value = strtoi(argv[2], NULL, 0);
+
+	uart_printf("write word 0x%p = 0x%08x\n", address, value);
+	uart_flush_output();
+
+	*address = value;
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ww, command_write_word);
+DECLARE_CONSOLE_COMMAND(writeword, command_write_word);
+
+
+static int command_read_word(int argc, char **argv)
+{
+	volatile uint32_t *address;
+	uint32_t value;
+
+	if (argc != 2) {
+		uart_puts("Usage: rw <address>\n");
+		return EC_ERROR_UNKNOWN;
+	}
+	address = (uint32_t*)strtoi(argv[1], NULL, 0);
+	value = *address;
+
+	uart_printf("read word 0x%p = 0x%08x\n", address, value);
+	uart_flush_output();
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(rw, command_read_word);
+DECLARE_CONSOLE_COMMAND(readword, command_read_word);
diff --git a/common/port80.c b/common/port80.c
new file mode 100644
index 0000000..114f1cf
--- /dev/null
+++ b/common/port80.c
@@ -0,0 +1,69 @@
+/* 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 "board.h"
+#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)
+{
+#ifndef CONFIG_PORT80_PRINT_DUPLICATES
+	/* Ignore duplicate writes, since the linux kernel writes to port 80
+	 * as a delay mechanism during boot. */
+	if (data == last_data)
+		return;
+#endif
+
+	/* 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;
+}
+DECLARE_CONSOLE_COMMAND(port80, command_port80);
+
+/*****************************************************************************/
+/* Initialization */
+
+int port_80_init(void)
+{
+	memset(history, 0, sizeof(history));
+	return EC_SUCCESS;
+}
diff --git a/common/pwm_commands.c b/common/pwm_commands.c
new file mode 100644
index 0000000..7581fe5
--- /dev/null
+++ b/common/pwm_commands.c
@@ -0,0 +1,32 @@
+/* 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 */
+
+#include "pwm.h"
+#include "pwm_commands.h"
+#include "lpc_commands.h"
+
+
+/*****************************************************************************/
+/* Host commands */
+
+enum lpc_status pwm_command_get_fan_rpm(uint8_t *data)
+{
+	struct lpc_response_pwm_get_fan_rpm *r =
+			(struct lpc_response_pwm_get_fan_rpm *)data;
+
+	r->rpm = pwm_get_fan_rpm();
+	return EC_LPC_STATUS_SUCCESS;
+}
+
+enum lpc_status pwm_command_set_fan_target_rpm(uint8_t *data)
+{
+	struct lpc_params_pwm_set_fan_target_rpm *p =
+			(struct lpc_params_pwm_set_fan_target_rpm *)data;
+
+	pwm_set_fan_target_rpm(p->rpm);
+	return EC_LPC_STATUS_SUCCESS;
+}
diff --git a/common/shared_mem.c b/common/shared_mem.c
new file mode 100644
index 0000000..bff3ec6
--- /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/smart_battery.h b/common/smart_battery.h
new file mode 100644
index 0000000..0aa91f6
--- /dev/null
+++ b/common/smart_battery.h
@@ -0,0 +1,58 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Smart battery charger 1.1
+ */
+#ifndef __CROS_EC_SMART_BATTERY_H
+#define __CROS_EC_SMART_BATTERY_H
+
+/* Smart battery charger functions */
+#define SB_CHARGER_SPEC_INFO		0x11
+#define SB_CHARGE_MODE			0x12
+#define SB_CHARGER_STATUS		0x13
+#define SB_CHARGING_CURRENT		0x14
+#define SB_CHARGING_VOLTAGE		0x15
+#define SB_ALARM_WARNING		0x16
+
+/* SB_ALARM_WARNING */
+#define ALARM_OVER_CHARGE		0x8000
+#define ALARM_TERMINATE_CHARG		0x4000
+#define ALARM_RESERVED_2000		0x2000
+#define ALARM_OVER_TEMP			0x1000
+#define ALARM_TERMINATE_DISCHARGE	0x0800
+#define ALARM_RESERVED_0400		0x0400
+#define ALARM_REMAINING_CAPACITY	0x0200
+#define ALARM_REMAINING_TIME		0x0100
+#define ALARM_STATUS_INITIALIZE		0x0080
+#define ALARM_STATUS_DISCHARGING	0x0040
+#define ALARM_STATUS_FULLY_CHARGED	0x0020
+#define ALARM_STATUS_FULLY_DISCHARG	0x0010
+/* SB_CHARGE_MODE */
+#define CHARGE_FLAG_INHIBIT_CHARGE	(1 << 0)
+#define CHARGE_FLAG_ENABLE_POLLING	(1 << 1)
+#define CHARGE_FLAG_POR_RESET		(1 << 2)
+#define CHARGE_FLAG_RESET_TO_ZERO	(1 << 3)
+/* SB_CHARGER_STATUS */
+#define CHARGER_CHARGE_INHIBITED	(1 << 0)
+#define CHARGER_POLLING_ENABLED		(1 << 1)
+#define CHARGER_VOLTAGE_NOTREG		(1 << 2)
+#define CHARGER_CURRENT_NOTREG		(1 << 3)
+#define CHARGER_LEVEL_2			(1 << 4)
+#define CHARGER_LEVEL_3			(1 << 5)
+#define CHARGER_CURRENT_OR		(1 << 6)
+#define CHARGER_VOLTAGE_OR		(1 << 7)
+#define CHARGER_RES_OR			(1 << 8)
+#define CHARGER_RES_COLD		(1 << 9)
+#define CHARGER_RES_HOT			(1 << 10)
+#define CHARGER_RES_UR			(1 << 11)
+#define CHARGER_ALARM_INHIBITED		(1 << 12)
+#define CHARGER_POWER_FAIL		(1 << 13)
+#define CHARGER_BATTERY_PRESENT		(1 << 14)
+#define CHARGER_AC_PRESENT		(1 << 15)
+/* SB_CHARGER_SPEC_INFO */
+#define INFO_CHARGER_SPEC(INFO)		((INFO) & 0xf)
+#define INFO_SELECTOR_SUPPORT(INFO)	(((INFO) >> 4) & 1)
+
+#endif /* __CROS_EC_SMART_BATTERY_H */
+
diff --git a/common/system.c b/common/system.c
new file mode 100644
index 0000000..0bfe3aa
--- /dev/null
+++ b/common/system.c
@@ -0,0 +1,208 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* System module for Chrome EC : common functions */
+
+#include "console.h"
+#include "system.h"
+#include "uart.h"
+#include "util.h"
+#include "version.h"
+
+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 enum system_reset_cause_t reset_cause = SYSTEM_RESET_UNKNOWN;
+
+enum system_reset_cause_t system_get_reset_cause(void)
+{
+	return reset_cause;
+}
+
+
+void system_set_reset_cause(enum system_reset_cause_t cause)
+{
+	reset_cause = 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_FLASH_BASE) /
+		   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;
+
+	/* 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 checks (crosbug.com/p/7468)
+	 *
+	 * Fail if called outside of pre-init.
+	 *
+	 * Fail if reboot reason is not soft reboot.  Power-on
+	 * reset cause must run RO firmware; if it wants to move to RW
+	 * firmware, it must go through a soft reboot first
+	 *
+	 * 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;
+}
+
+
+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: (crosbug.com/p/7469) 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("Scratchpad: 0x%08x\n", system_get_scratchpad());
+	uart_printf("Firmware copy: %s\n", system_get_image_copy_string());
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(sysinfo, command_sysinfo);
+
+
+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);
+}
+DECLARE_CONSOLE_COMMAND(setscratchpad, command_set_scratchpad);
+
+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;
+}
+DECLARE_CONSOLE_COMMAND(hibernate, command_hibernate);
diff --git a/common/temp_sensor.c b/common/temp_sensor.c
new file mode 100644
index 0000000..d428565
--- /dev/null
+++ b/common/temp_sensor.c
@@ -0,0 +1,252 @@
+/* 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 "i2c.h"
+#include "temp_sensor.h"
+#include "uart.h"
+#include "util.h"
+#include "console.h"
+#include "board.h"
+
+/* Defined in board_temp_sensor.c. Must be in the same order as
+ * in enum temp_sensor_id.
+ */
+extern const struct temp_sensor_t temp_sensors[TEMP_SENSOR_COUNT];
+
+int temp_sensor_read(enum temp_sensor_id id)
+{
+	const struct temp_sensor_t *sensor;
+
+	if (id < 0 || id >= TEMP_SENSOR_COUNT)
+		return -1;
+	sensor = temp_sensors + id;
+	return sensor->read(sensor);
+}
+
+int temp_sensor_tmp006_read_die_temp(const struct temp_sensor_t* sensor)
+{
+	int traw, t;
+	int rv;
+	int addr = sensor->addr;
+
+	rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
+	if (rv)
+		return -1;
+	t = (int)(int16_t)traw / 128;
+	return t + 273;
+}
+
+/* Calculate the remote object temperature.
+ * Parameters:
+ *     Tdie: Die temperature in 1/100 K.
+ *     Vobj: Voltage read from register 0. In nV.
+ *     S0:   Sensitivity factor in 1/1000.
+ * Return:
+ *     Object temperature in 1/100 K.
+ */
+int temp_sensor_tmp006_calculate_object_temp(int Tdie, int Vobj, int S0)
+{
+	int32_t Tx, S19, Vos, Vx, fv9, ub, lb;
+
+	/* Calculate according to TMP006 users guide.
+	 * Division is delayed when possible to preserve precision, but should
+	 * not cause overflow.
+	 * Assuming Tdie is between 200K and 400K, and S0 between 3e-14 and
+	 * 9e-14, the maximum value during the calculation should be less than
+	 * (1 << 30), which fits in int32_t.
+	 */
+	Tx = Tdie - 29815;
+	/* S19 is the sensitivity multipled by 1e19 */
+	S19 = S0 * (100000 + 175 * Tx / 100 -
+		1678 * Tx / 100 * Tx / 100000) / 1000;
+	/* Vos is the offset voltage in nV */
+	Vos = -29400 - 570 * Tx / 100 + 463 * Tx / 100 * Tx / 10000;
+	Vx = Vobj - Vos;
+	/* fv9 is Seebeck coefficient f(Vobj) multipled by 1e9 */
+	fv9 = Vx + 134 * Vx / 100000 * Vx / 100000;
+
+	/* The last step in the calculation involves square root, so we use
+	 * binary search.
+	 * Assuming the object temperature is between 200K and 400K, the search
+	 * should take at most 14 iterations.
+	 */
+	ub = 40000;
+	lb = 20000;
+	while (lb != ub) {
+		int32_t t, rhs, lhs;
+
+		t = (ub + lb) / 2;
+		lhs = t / 100 * t / 10000 * t / 10000 * (S19/100) / 1000 * t;
+		rhs = Tdie / 100 * Tdie / 10000 * Tdie / 10000 * (S19/100) / 1000 *
+			Tdie + fv9 * 1000;
+		if (lhs > rhs)
+			ub = t;
+		else
+			lb = t + 1;
+	}
+
+	return ub;
+}
+
+int temp_sensor_tmp006_read_object_temp(const struct temp_sensor_t* sensor)
+{
+	int traw, t;
+	int vraw, v;
+	int rv;
+	int addr = sensor->addr;
+
+	rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
+	if (rv)
+		return -1;
+	t = (int)(int16_t)traw / 128 + 273;
+
+	rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
+	if (rv)
+		return -1;
+	v = ((int)(int16_t)vraw * 15625) / 100;
+
+	return temp_sensor_tmp006_calculate_object_temp(t * 100, v, 6400);
+}
+
+void temp_sensor_tmp006_config(const struct temp_sensor_t* sensor)
+{
+	int addr = sensor->addr;
+
+	/* 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(TMP006_PORT(addr), TMP006_REG(addr), 0x02, 0x7500);
+}
+
+int temp_sensor_tmp006_print(const struct temp_sensor_t* sensor)
+{
+	int vraw, v;
+	int traw, t;
+	int rv;
+	int d;
+	int addr = sensor->addr;
+
+	uart_printf("Debug data from %s:\n", sensor->name);
+	rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0xfe, &d);
+	if (rv)
+		return rv;
+	uart_printf("  Manufacturer ID: 0x%04x\n", d);
+
+	rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0xff, &d);
+	uart_printf("  Device ID:       0x%04x\n", d);
+
+	rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x02, &d);
+	uart_printf("  Config:          0x%04x\n", d);
+
+	rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
+	v = ((int)(int16_t)vraw * 15625) / 100;
+	uart_printf("  Voltage:         0x%04x = %d nV\n", vraw, v);
+
+	rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(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));
+
+	return EC_SUCCESS;
+}
+/*****************************************************************************/
+/* Console commands */
+
+static int command_temps(int argc, char **argv)
+{
+	int i;
+	int rv = 0;
+	int t;
+
+	uart_puts("Reading temperature sensors...\n");
+
+	for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {
+		uart_printf("  Temp from %s:  ", temp_sensors[i].name);
+		t = temp_sensor_read(temp_sensors[i].id);
+		if (t < 0) {
+			uart_printf("Error.\n\n");
+			rv = -1;
+		}
+		else
+			uart_printf("%d K\n\n", t);
+	}
+
+	if (rv == -1)
+		return EC_ERROR_UNKNOWN;
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(temps, command_temps);
+
+static int command_sensor_info(int argc, char ** argv)
+{
+	int i;
+	int rv;
+	const struct temp_sensor_t* sensor;
+
+	for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {
+		sensor = temp_sensors + i;
+		if (sensor->print == TEMP_SENSOR_NO_PRINT)
+			continue;
+		rv = sensor->print(sensor);
+		if (rv != EC_SUCCESS)
+			return rv;
+	}
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(tempsinfo, command_sensor_info);
+
+/* TMP006 object temperature calculation command.
+ * TODO: This command is only for debugging. Remove it when temporal correciton
+ *       is done.
+ */
+static int command_sensor_remote(int argc, char **argv)
+{
+	char *e;
+	int32_t Td2, Vobj9, Sm03;
+
+	if (argc != 4) {
+		uart_puts("Usage: tempcorrect <Tdie*100> <Vobj*10^9> <S0*10^11>\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	Td2 = strtoi(argv[1], &e, 0);
+	if (e && *e) {
+		uart_puts("Bad Tdie.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	Vobj9 = strtoi(argv[2], &e, 0);
+	if (e && *e) {
+		uart_puts("Bad Vobj.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	Sm03 = strtoi(argv[3], &e, 0);
+	if (e && *e) {
+		uart_puts("Bad S0.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	uart_printf("%d\n",
+		temp_sensor_tmp006_calculate_object_temp(Td2, Vobj9, Sm03));
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(tempremote, command_sensor_remote);
+
+/*****************************************************************************/
+/* Initialization */
+
+int temp_sensor_init(void)
+{
+	return EC_SUCCESS;
+}
diff --git a/common/temp_sensor_commands.c b/common/temp_sensor_commands.c
new file mode 100644
index 0000000..a532144
--- /dev/null
+++ b/common/temp_sensor_commands.c
@@ -0,0 +1,33 @@
+/* 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 "console.h"
+#include "temp_sensor.h"
+#include "temp_sensor_commands.h"
+#include "lpc_commands.h"
+#include "uart.h"
+#include "util.h"
+
+
+/*****************************************************************************/
+/* Host commands */
+
+enum lpc_status temp_sensor_command_get_readings(uint8_t *data)
+{
+	struct lpc_params_temp_sensor_get_readings *p =
+			(struct lpc_params_temp_sensor_get_readings *)data;
+	struct lpc_response_temp_sensor_get_readings *r =
+			(struct lpc_response_temp_sensor_get_readings *)data;
+
+	int rv;
+	rv = temp_sensor_read(p->temp_sensor_id);
+	if (rv == -1)
+		return EC_LPC_STATUS_ERROR;
+	r->value = rv;
+
+	return EC_LPC_STATUS_SUCCESS;
+}
diff --git a/common/uart_buffering.c b/common/uart_buffering.c
new file mode 100644
index 0000000..4706ce8
--- /dev/null
+++ b/common/uart_buffering.c
@@ -0,0 +1,665 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Common code to do UART buffering and printing */
+
+#include <stdarg.h>
+
+#include "console.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+
+/* 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. */
+#define HISTORY_SIZE 8
+
+/* The size limit of single command */
+#define RX_LINE_SIZE 80
+
+/* Macros to advance in the circular 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))
+#define CMD_HIST_NEXT(i) (((i) + 1) & (HISTORY_SIZE - 1))
+#define CMD_HIST_PREV(i) (((i) - 1) & (HISTORY_SIZE - 1))
+
+/* Macro to calculate difference of pointers in the circular receive buffer. */
+#define RX_BUF_DIFF(i, j) (((i) - (j)) & (RX_BUF_SIZE - 1))
+
+/* Transmit and receive buffers */
+static volatile char tx_buf[TX_BUF_SIZE];
+static volatile int tx_buf_head;
+static volatile int tx_buf_tail;
+static volatile char rx_buf[RX_BUF_SIZE];
+static volatile int rx_buf_head;
+static volatile int rx_buf_tail;
+static volatile char rx_cur_buf[RX_LINE_SIZE];
+static volatile int rx_cur_buf_tail;
+static volatile int rx_cur_buf_head;
+static volatile int rx_cur_buf_ptr;
+static int last_rx_was_cr;
+static int in_escape;
+static char esc_seq_char;
+
+/* Command history */
+struct cmd_history_t {
+	volatile int head;
+	volatile int tail;
+};
+static struct cmd_history_t cmd_history[HISTORY_SIZE];
+static volatile int cmd_history_head;
+static volatile int cmd_history_tail;
+static volatile int cmd_history_ptr;
+
+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;
+}
+
+static void move_rx_ptr_fwd(void)
+{
+	if (rx_cur_buf_ptr != rx_cur_buf_head) {
+		++rx_cur_buf_ptr;
+		uart_write_char(0x1B);
+		uart_write_char('[');
+		uart_write_char('1');
+		uart_write_char('C');
+	}
+}
+
+static void move_rx_ptr_bwd(void)
+{
+	if (rx_cur_buf_ptr != 0) {
+		--rx_cur_buf_ptr;
+		uart_write_char(0x1B);
+		uart_write_char('[');
+		uart_write_char('1');
+		uart_write_char('D');
+	}
+}
+
+static void repeat_char(char c, int cnt)
+{
+	while (cnt--)
+		uart_write_char(c);
+}
+
+static void handle_backspace(void)
+{
+	if (rx_cur_buf_ptr != 0) {
+		/* Move texts after cursor and also update rx buffer. */
+		int ptr;
+		for (ptr = rx_cur_buf_ptr; ptr < rx_cur_buf_head; ++ptr) {
+			uart_write_char(rx_cur_buf[ptr]);
+			rx_cur_buf[ptr - 1] = rx_cur_buf[ptr];
+		}
+
+		/* Space over last character and move cursor back to correct
+		 * position.
+		 */
+		uart_write_char(' ');
+		repeat_char('\b', ptr - rx_cur_buf_ptr + 1);
+
+		--rx_cur_buf_head;
+		--rx_cur_buf_ptr;
+	}
+	else
+		/* Cursor moves pass the first character. Move it back. */
+		uart_write_char(' ');
+}
+
+static void insert_char(char c)
+{
+	int ptr;
+
+	/* On overflow, discard input */
+	if (rx_cur_buf_head == RX_LINE_SIZE)
+		return;
+
+	/* Move buffer ptr to the end if 'c' is new line */
+	if (c == '\n')
+		rx_cur_buf_ptr = rx_cur_buf_head;
+
+	/* Move text after cursor. */
+	for (ptr = rx_cur_buf_ptr; ptr < rx_cur_buf_head; ++ptr)
+		uart_write_char(rx_cur_buf[ptr]);
+
+	/* Insert character to rx buffer and move cursor to correct
+	 * position.
+	 */
+	repeat_char('\b', ptr - rx_cur_buf_ptr);
+	for (ptr = rx_cur_buf_head; ptr > rx_cur_buf_ptr; --ptr)
+		rx_cur_buf[ptr] = rx_cur_buf[ptr - 1];
+	rx_cur_buf[rx_cur_buf_ptr] = c;
+	++rx_cur_buf_head;
+	++rx_cur_buf_ptr;
+
+	/* Insert character directly into rx_buf if not in console mode. */
+	if (!console_mode) {
+		rx_buf[rx_buf_head] = c;
+		rx_buf_head = RX_BUF_NEXT(rx_buf_head);
+		if (rx_buf_tail == rx_buf_head)
+			rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
+	}
+}
+
+static int rx_buf_space_available(void)
+{
+	if (cmd_history_head == cmd_history_tail)
+		return RX_BUF_SIZE;
+	return RX_BUF_DIFF(cmd_history[cmd_history_tail].tail,
+			   cmd_history[CMD_HIST_PREV(cmd_history_head)].head);
+}
+
+static void history_save(void)
+{
+	int ptr;
+	int tail, head;
+	int hist_id;
+
+	/* If there is not enough space in rx buffer, discard the oldest
+	 * history. */
+	while (rx_buf_space_available() < rx_cur_buf_head)
+		cmd_history_tail = CMD_HIST_NEXT(cmd_history_tail);
+
+	/* If history buffer is full, discard the oldest one */
+	hist_id = cmd_history_head;
+	cmd_history_head = CMD_HIST_NEXT(cmd_history_head);
+	if (cmd_history_head == cmd_history_tail)
+		cmd_history_tail = CMD_HIST_NEXT(cmd_history_tail);
+
+	/* Copy the current command, but we do not save the '\n' */
+	if (hist_id == cmd_history_tail)
+		tail = 0;
+	else
+		tail = cmd_history[CMD_HIST_PREV(hist_id)].head + 1;
+	head = tail;
+	for (ptr = 0; ptr < rx_cur_buf_head; ++ptr, head = RX_BUF_NEXT(head))
+		rx_buf[head] = rx_cur_buf[ptr];
+	if (rx_buf[RX_BUF_PREV(head)] == '\n') {
+		head = RX_BUF_PREV(head);
+		rx_buf[head] = '\0';
+	}
+
+	cmd_history[hist_id].head = head;
+	cmd_history[hist_id].tail = tail;
+}
+
+static void history_load(int id)
+{
+	int head = cmd_history[id].head;
+	int tail = cmd_history[id].tail;
+	int ptr;
+
+	cmd_history_ptr = id;
+
+	/* Move cursor back to begin of the line. */
+	repeat_char('\b', rx_cur_buf_ptr);
+
+	/* Load command and print it. */
+	for (ptr = tail, rx_cur_buf_ptr = 0; ptr != head;
+			ptr = RX_BUF_NEXT(ptr), ++rx_cur_buf_ptr) {
+		rx_cur_buf[rx_cur_buf_ptr] = rx_buf[ptr];
+		uart_write_char(rx_buf[ptr]);
+	}
+
+	/* If needed, space over the remaining text. */
+	if (rx_cur_buf_ptr < rx_cur_buf_head) {
+		repeat_char(' ', rx_cur_buf_head - rx_cur_buf_ptr);
+		repeat_char('\b', rx_cur_buf_head - rx_cur_buf_ptr);
+	}
+
+	rx_cur_buf_head = rx_cur_buf_ptr;
+}
+
+static void history_prev(void)
+{
+	if (cmd_history_ptr == cmd_history_tail)
+		return;
+
+	/* Stash the current command if we are not currently using history.
+	 * Prevent loading history if there is no space to stash current
+	 * command. */
+	if (cmd_history_ptr == cmd_history_head) {
+		int last_id = CMD_HIST_PREV(cmd_history_head);
+		int last_len = RX_BUF_DIFF(cmd_history[last_id].head,
+					   cmd_history[last_id].tail);
+		if (last_len + rx_cur_buf_head > RX_BUF_SIZE)
+			return;
+
+		history_save();
+	}
+
+	cmd_history_ptr = CMD_HIST_PREV(cmd_history_ptr);
+	history_load(cmd_history_ptr);
+}
+
+static void history_next(void)
+{
+	if (cmd_history_ptr == cmd_history_head)
+		return;
+
+	cmd_history_ptr = CMD_HIST_NEXT(cmd_history_ptr);
+	history_load(cmd_history_ptr);
+
+	/* Remove the stashed command if we just loaded it. */
+	if (cmd_history_ptr == CMD_HIST_PREV(cmd_history_head))
+		cmd_history_head = cmd_history_ptr;
+}
+
+/* Helper for UART processing */
+void uart_process(void)
+{
+	/* Copy input from buffer until RX fifo empty */
+	while (uart_rx_available()) {
+		int c = uart_read_char();
+
+		/* 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;
+			}
+
+			/* Handle left and right key, and eat other terminal
+			 * escape sequences (ESC [ ...).
+			 * Would be really cool if we used arrow keys to edit
+			 * command history, but for now it's sufficient just to
+			 * keep them from causing problems. */
+			if (c == 0x1B) {
+				in_escape = 1;
+				esc_seq_char = c;
+				continue;
+			} else if (in_escape) {
+				if (esc_seq_char == 0x1B && c == '[')
+					esc_seq_char = '[';
+				else if (esc_seq_char == '[') {
+					if (c == 'A') /* Up key */
+						history_prev();
+					else if (c == 'B') /* Down key */
+						history_next();
+					else if (c == 'C') /* Right key */
+						move_rx_ptr_fwd();
+					else if (c == 'D') /* Left key */
+						move_rx_ptr_bwd();
+					esc_seq_char = 0;
+				}
+				else
+					esc_seq_char = 0;
+
+				if (isalpha(c) || c == '~') {
+					esc_seq_char = 0;
+					in_escape = 0;
+				}
+				continue;
+			}
+
+			/* Echo characters directly to the transmit FIFO so we
+			 * don't interfere with the transmit buffer. */
+			if (c == '\n')
+				uart_write_char('\r');
+			uart_write_char(c);
+
+			/* Handle backspace if we can */
+			if (c == '\b') {
+				handle_backspace();
+				continue;
+			}
+		}
+
+		insert_char(c);
+
+		/* 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 (uart_tx_ready() && (tx_buf_head != tx_buf_tail)) {
+		uart_write_char(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)
+		uart_tx_stop();
+}
+
+void uart_set_console_mode(int enable)
+{
+	console_mode = enable;
+
+	if (!enable)
+		rx_cur_buf_ptr = rx_cur_buf_head;
+}
+
+
+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)
+		uart_tx_start();
+
+	/* 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: (crosbug.com/p/7490) 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)
+		uart_tx_start();
+
+	/* 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 (uart_tx_stopped())
+			uart_tx_start();
+	}
+
+	/* Wait for transmit FIFO empty */
+	uart_tx_flush();
+}
+
+void uart_emergency_flush(void)
+{
+	do {
+		/* Copy output from buffer until TX fifo full
+		 * or output buffer empty
+		 */
+		while (uart_tx_ready() &&
+		       (tx_buf_head != tx_buf_tail)) {
+			uart_write_char(tx_buf[tx_buf_tail]);
+			tx_buf_tail = TX_BUF_NEXT(tx_buf_tail);
+		}
+		/* Wait for transmit FIFO empty */
+		uart_tx_flush();
+	} while (tx_buf_head != tx_buf_tail);
+}
+
+
+void uart_flush_input(void)
+{
+	/* Disable interrupts */
+	uart_disable_interrupt();
+
+	/* Empty the hardware FIFO */
+	uart_process();
+
+	/* Clear the input buffer */
+	rx_cur_buf_head = 0;
+	rx_buf_tail = rx_buf_head;
+
+	/* Re-enable interrupts */
+	uart_enable_interrupt();
+}
+
+
+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. */
+	uart_disable_interrupt();
+
+	/* 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_process();
+
+	for (i = 0; i < rx_cur_buf_head; ++i) {
+		if (rx_cur_buf[i] == c) {
+			index = i;
+			break;
+		}
+	}
+
+	/* Re-enable interrupts */
+	uart_enable_interrupt();
+
+	return index;
+}
+
+
+int uart_getc(void)
+{
+	int c;
+
+	/* Disable interrupts */
+	uart_disable_interrupt();
+
+	/* Call interrupt handler to empty the hardware FIFO */
+	uart_process();
+
+	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 */
+	uart_enable_interrupt();
+
+	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. */
+	uart_disable_interrupt();
+
+	/* Call interrupt handler to empty the hardware FIFO */
+	uart_process();
+
+	/* Remove the stashed command if any. */
+	if (cmd_history_ptr != cmd_history_head)
+		cmd_history_head = CMD_HIST_PREV(cmd_history_head);
+
+	/* Record last command. */
+	if (!(rx_cur_buf_head == 1 && rx_cur_buf[0] == '\n'))
+		history_save();
+	cmd_history_ptr = cmd_history_head;
+
+	/* Read characters */
+	while (got < size - 1 && got < rx_cur_buf_head) {
+		c = rx_cur_buf[got];
+		dest[got++] = c;
+		if (c == '\n')
+			break;  /* Stop on newline */
+	}
+	rx_cur_buf_ptr = 0;
+	rx_cur_buf_head = 0;
+	rx_cur_buf_tail = rx_cur_buf_head;
+
+	/* Re-enable interrupts */
+	uart_enable_interrupt();
+
+	/* Null-terminate */
+	dest[got] = '\0';
+
+	/* Return the length we got */
+	return got;
+}
diff --git a/common/usb_charge.c b/common/usb_charge.c
new file mode 100644
index 0000000..28c98e4
--- /dev/null
+++ b/common/usb_charge.c
@@ -0,0 +1,136 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* USB charging control module for Chrome EC */
+
+#include "usb_charge.h"
+#include "board.h"
+#include "gpio.h"
+#include "uart.h"
+#include "console.h"
+#include "util.h"
+
+static void usb_charge_set_control_mode(int port_id, int mode)
+{
+#ifdef BOARD_link
+	if (port_id == 0) {
+		gpio_set_level(GPIO_USB1_CTL1, (mode & 0x4) >> 2);
+		gpio_set_level(GPIO_USB1_CTL2, (mode & 0x2) >> 1);
+		gpio_set_level(GPIO_USB1_CTL3, mode & 0x1);
+	}
+	else if (port_id == 1) {
+		gpio_set_level(GPIO_USB2_CTL1, (mode & 0x4) >> 2);
+		gpio_set_level(GPIO_USB2_CTL2, (mode & 0x2) >> 1);
+		gpio_set_level(GPIO_USB2_CTL3, mode & 0x1);
+	}
+#endif
+}
+
+static void usb_charge_set_enabled(int port_id, int en)
+{
+#ifdef BOARD_link
+	if (port_id == 0)
+		gpio_set_level(GPIO_USB1_ENABLE, en);
+	else
+		gpio_set_level(GPIO_USB2_ENABLE, en);
+#endif
+}
+
+static void usb_charge_set_ilim(int port_id, int sel)
+{
+#ifdef BOARD_link
+	if (port_id == 0)
+		gpio_set_level(GPIO_USB1_ILIM_SEL, sel);
+	else
+		gpio_set_level(GPIO_USB2_ILIM_SEL, sel);
+#endif
+}
+
+int usb_charge_set_mode(int port_id, enum usb_charge_mode mode)
+{
+
+	if (port_id >= USB_CHARGE_PORT_COUNT)
+		return EC_ERROR_INVAL;
+
+	if (mode == USB_CHARGE_MODE_DISABLED) {
+		usb_charge_set_enabled(port_id, 0);
+		return EC_SUCCESS;
+	}
+	else
+		usb_charge_set_enabled(port_id, 1);
+
+	switch (mode) {
+		case USB_CHARGE_MODE_CHARGE_AUTO:
+			usb_charge_set_control_mode(port_id, 1);
+			usb_charge_set_ilim(port_id, 1);
+			break;
+		case USB_CHARGE_MODE_CHARGE_BC12:
+			usb_charge_set_control_mode(port_id, 4);
+			break;
+		case USB_CHARGE_MODE_DOWNSTREAM_500MA:
+			usb_charge_set_control_mode(port_id, 2);
+			usb_charge_set_ilim(port_id, 0);
+			break;
+		case USB_CHARGE_MODE_DOWNSTREAM_1500MA:
+			usb_charge_set_control_mode(port_id, 2);
+			usb_charge_set_ilim(port_id, 1);
+			break;
+		default:
+			return EC_ERROR_UNKNOWN;
+	}
+
+	return EC_SUCCESS;
+}
+
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_set_mode(int argc, char **argv)
+{
+	int port_id = -1;
+	int mode = -1;
+	char* endptr;
+
+	if (argc != 3) {
+		uart_puts("Usage: usbchargemode <port_id> <mode>\n");
+		uart_puts("Modes: 0=Disabled.\n"
+			  "       1=Dedicated charging. Auto select.\n"
+			  "       2=Dedicated charging. BC 1.2.\n"
+			  "       3=Downstream. Max 500mA.\n"
+			  "       4=Downstream. Max 1.5A.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	port_id = strtoi(argv[1], &endptr, 0);
+	if (*endptr || port_id < 0 || port_id >= USB_CHARGE_PORT_COUNT) {
+		uart_puts("Invalid port ID.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	mode = strtoi(argv[2], &endptr, 0);
+	if (*endptr || mode < 0 || mode >= USB_CHARGE_MODE_COUNT) {
+		uart_puts("Invalid mode.\n");
+		return EC_ERROR_UNKNOWN;
+	}
+
+	uart_printf("Setting USB mode...\n");
+	return usb_charge_set_mode(port_id, mode);
+}
+DECLARE_CONSOLE_COMMAND(usbchargemode, command_set_mode);
+
+
+/*****************************************************************************/
+/* Initialization */
+
+int usb_charge_init(void)
+{
+	int i;
+
+	for (i = 0; i < USB_CHARGE_PORT_COUNT; ++i)
+		usb_charge_set_mode(i, USB_CHARGE_MODE_DOWNSTREAM_500MA);
+
+	return EC_SUCCESS;
+}
diff --git a/common/util.c b/common/util.c
new file mode 100644
index 0000000..885d830
--- /dev/null
+++ b/common/util.c
@@ -0,0 +1,157 @@
+/* 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 isalpha(int c)
+{
+	return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+
+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 0000000..4f8934b
--- /dev/null
+++ b/common/vboot.c
@@ -0,0 +1,98 @@
+/* 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;
+}
+DECLARE_CONSOLE_COMMAND(reboot, command_reboot);
+
+/*****************************************************************************/
+/* Initialization */
+
+int vboot_pre_init(void)
+{
+	/* Jump to a different image if necessary; this may not return */
+	jump_to_other_image();
+	return EC_SUCCESS;
+}
diff --git a/common/x86_power.c b/common/x86_power.c
new file mode 100644
index 0000000..a911ae4
--- /dev/null
+++ b/common/x86_power.c
@@ -0,0 +1,354 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* X86 chipset power control module for Chrome EC */
+
+#include "board.h"
+#include "clock.h"
+#include "console.h"
+#include "gpio.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+#include "x86_power.h"
+
+/* Default timeout in us; if we've been waiting this long for an input
+ * transition, just jump to the next state. */
+#define DEFAULT_TIMEOUT 1000000
+
+enum x86_state {
+	X86_G3 = 0,                 /* Initial state */
+	X86_S5,                     /* System is off */
+	X86_S3,                     /* RAM is on; processor is asleep */
+	X86_S0,                     /* System is on */
+
+	/* Transitions */
+	X86_G3S5,                   /* G3 -> S5 (at system init time) */
+	X86_S5S3,                   /* S5 -> S3 */
+	X86_S3S0,                   /* S3 -> S0 */
+	X86_S0S3,                   /* S0 -> S3 */
+	X86_S3S5,                   /* S3 -> S5 */
+
+	/* TODO: S3 state, S0S5, S0S3, S3S0 */
+};
+
+static const char * const state_names[] = {
+	"G3",
+	"S5",
+	"S3",
+	"S0",
+	"G3->S5",
+	"S5->S3",
+	"S3->S0",
+	"S0->S3",
+	"S3->S5",
+};
+
+/* Input state flags */
+#define IN_PGOOD_5VALW             0x0001
+#define IN_PGOOD_1_5V_DDR          0x0002
+#define IN_PGOOD_1_5V_PCH          0x0004
+#define IN_PGOOD_1_8VS             0x0008
+#define IN_PGOOD_VCCP              0x0010
+#define IN_PGOOD_VCCSA             0x0020
+#define IN_PGOOD_CPU_CORE          0x0040
+#define IN_PGOOD_VGFX_CORE         0x0080
+#define IN_PCH_SLP_S3n_DEASSERTED  0x0100
+#define IN_PCH_SLP_S4n_DEASSERTED  0x0200
+#define IN_PCH_SLP_S5n_DEASSERTED  0x0400
+#define IN_PCH_SLP_An_DEASSERTED   0x0800
+#define IN_PCH_SLP_SUSn_DEASSERTED 0x1000
+#define IN_PCH_SLP_MEn_DEASSERTED  0x2000
+#define IN_PCH_SUSWARNn_DEASSERTED 0x4000
+#define IN_PCH_BKLTEN_ASSERTED     0x8000
+/* All always-on supplies */
+#define IN_PGOOD_ALWAYS_ON   (IN_PGOOD_5VALW)
+/* All non-core power rails */
+#define IN_PGOOD_ALL_NONCORE (IN_PGOOD_1_5V_DDR | IN_PGOOD_1_5V_PCH |	\
+			      IN_PGOOD_1_8VS | IN_PGOOD_VCCP | IN_PGOOD_VCCSA)
+/* All core power rails */
+#define IN_PGOOD_ALL_CORE    (IN_PGOOD_CPU_CORE | IN_PGOOD_VGFX_CORE)
+/* All PM_SLP signals from PCH deasserted */
+#define IN_ALL_PM_SLP_DEASSERTED (IN_PCH_SLP_S3n_DEASSERTED |		\
+				  IN_PCH_SLP_S4n_DEASSERTED |		\
+				  IN_PCH_SLP_S5n_DEASSERTED |		\
+				  IN_PCH_SLP_An_DEASSERTED)
+
+
+static enum x86_state state;  /* Current state */
+static uint32_t in_signals;   /* Current input signal states (IN_PGOOD_*) */
+static uint32_t in_want;      /* Input signal state we're waiting for */
+
+
+/* Update input signal state */
+static void update_in_signals(void)
+{
+	uint32_t inew = 0;
+	int v;
+
+	if (gpio_get_level(GPIO_PGOOD_5VALW))
+		inew |= IN_PGOOD_5VALW;
+
+	if (gpio_get_level(GPIO_PGOOD_1_5V_DDR))
+		inew |= IN_PGOOD_1_5V_DDR;
+	if (gpio_get_level(GPIO_PGOOD_1_5V_PCH))
+		inew |= IN_PGOOD_1_5V_PCH;
+	if (gpio_get_level(GPIO_PGOOD_1_8VS))
+		inew |= IN_PGOOD_1_8VS;
+	if (gpio_get_level(GPIO_PGOOD_VCCP))
+		inew |= IN_PGOOD_VCCP;
+	if (gpio_get_level(GPIO_PGOOD_VCCSA))
+		inew |= IN_PGOOD_VCCSA;
+
+	if (gpio_get_level(GPIO_PGOOD_CPU_CORE))
+		inew |= IN_PGOOD_CPU_CORE;
+	if (gpio_get_level(GPIO_PGOOD_VGFX_CORE))
+		inew |= IN_PGOOD_VGFX_CORE;
+
+	if (gpio_get_level(GPIO_PCH_SLP_An))
+		inew |= IN_PCH_SLP_An_DEASSERTED;
+	if (gpio_get_level(GPIO_PCH_SLP_S3n))
+		inew |= IN_PCH_SLP_S3n_DEASSERTED;
+	if (gpio_get_level(GPIO_PCH_SLP_S4n))
+		inew |= IN_PCH_SLP_S4n_DEASSERTED;
+	if (gpio_get_level(GPIO_PCH_SLP_S5n))
+		inew |= IN_PCH_SLP_S5n_DEASSERTED;
+
+	if (gpio_get_level(GPIO_PCH_SLP_SUSn))
+		inew |= IN_PCH_SLP_SUSn_DEASSERTED;
+	if (gpio_get_level(GPIO_PCH_SLP_ME_CSW_DEVn))
+		inew |= IN_PCH_SLP_MEn_DEASSERTED;
+
+	v = gpio_get_level(GPIO_PCH_SUSWARNn);
+	if (v)
+		inew |= IN_PCH_SUSWARNn_DEASSERTED;
+	/* Copy SUSWARN# signal from PCH to SUSACK# */
+	gpio_set_level(GPIO_PCH_SUSACKn, v);
+
+	v = gpio_get_level(GPIO_PCH_BKLTEN);
+	if (v)
+		inew |= IN_PCH_BKLTEN_ASSERTED;
+	/* Copy backlight enable signal from PCH to BKLTEN */
+	gpio_set_level(GPIO_ENABLE_BACKLIGHT, v);
+
+	in_signals = inew;
+}
+
+
+/* Wait for all the inputs in <want> to be present.  Returns EC_ERROR_TIMEOUT
+ * if timeout before reaching the desired state. */
+static int wait_in_signals(uint32_t want)
+{
+	in_want = want;
+
+	while ((in_signals & in_want) != in_want) {
+		if (task_wait_msg(DEFAULT_TIMEOUT) == (1 << TASK_ID_TIMER)) {
+			update_in_signals();
+			uart_printf("[x86 power timeout on input; "
+				    "wanted 0x%04x, got 0x%04x]\n",
+				    in_want, in_signals & in_want);
+			return EC_ERROR_TIMEOUT;
+		}
+		/* TODO: should really shrink the remaining timeout if we woke
+		 * up but didn't have all the signals we wanted.  Also need to
+		 * handle aborts if we're no longer in the same state we were
+		 * when we started waiting. */
+	}
+	return EC_SUCCESS;
+}
+
+
+/*****************************************************************************/
+/* Interrupts */
+
+void x86_power_interrupt(enum gpio_signal signal)
+{
+	/* Shadow signals and compare with our desired signal state. */
+	update_in_signals();
+
+	/* Wake up the task */
+	task_send_msg(TASK_ID_X86POWER, TASK_ID_X86POWER, 0);
+}
+
+/*****************************************************************************/
+/* Initialization */
+
+int x86_power_init(void)
+{
+	state = X86_G3;
+
+	/* Update input state */
+	update_in_signals();
+	in_want = 0;
+
+	/* Enable interrupts for our GPIOs */
+	gpio_enable_interrupt(GPIO_PCH_BKLTEN);
+	gpio_enable_interrupt(GPIO_PCH_SLP_An);
+	gpio_enable_interrupt(GPIO_PCH_SLP_ME_CSW_DEVn);
+	gpio_enable_interrupt(GPIO_PCH_SLP_S3n);
+	gpio_enable_interrupt(GPIO_PCH_SLP_S4n);
+	gpio_enable_interrupt(GPIO_PCH_SLP_S5n);
+	gpio_enable_interrupt(GPIO_PCH_SLP_SUSn);
+	gpio_enable_interrupt(GPIO_PCH_SUSWARNn);
+	gpio_enable_interrupt(GPIO_PGOOD_1_5V_DDR);
+	gpio_enable_interrupt(GPIO_PGOOD_1_5V_PCH);
+	gpio_enable_interrupt(GPIO_PGOOD_1_8VS);
+	gpio_enable_interrupt(GPIO_PGOOD_5VALW);
+	gpio_enable_interrupt(GPIO_PGOOD_CPU_CORE);
+	gpio_enable_interrupt(GPIO_PGOOD_VCCP);
+	gpio_enable_interrupt(GPIO_PGOOD_VCCSA);
+	gpio_enable_interrupt(GPIO_PGOOD_VGFX_CORE);
+
+	return EC_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Task function */
+
+void x86_power_task(void)
+{
+	x86_power_init();
+
+	while (1) {
+		uart_printf("[x86 power state %d = %s, in 0x%04x]\n",
+			    state, state_names[state], in_signals);
+
+		switch (state) {
+		case X86_G3:
+			/* Move to S5 state on boot */
+			state = X86_G3S5;
+			break;
+
+		case X86_G3S5:
+			/* Wait for the always-on rails to be good */
+			wait_in_signals(IN_PGOOD_ALWAYS_ON);
+
+			/* Wait 10ms after +5VALW good */
+			usleep(10000);
+
+			/* Assert DPWROK, deassert RSMRST# */
+			gpio_set_level(GPIO_PCH_DPWROK, 1);
+			gpio_set_level(GPIO_PCH_RSMRSTn, 1);
+
+			/* Wait 5ms for SUSCLK to stabilize */
+			usleep(5000);
+
+			state = X86_S5;
+			break;
+
+
+		case X86_S5S3:
+			/* Turn on power to RAM */
+			gpio_set_level(GPIO_SHUNT_1_5V_DDR, 0);
+			gpio_set_level(GPIO_ENABLE_1_5V_DDR, 1);
+
+			state = X86_S3;
+			break;
+
+		case X86_S3S0:
+			/* Turn on power rails */
+			gpio_set_level(GPIO_ENABLE_VS, 1);
+
+			/* Wait for non-core power rails good */
+			wait_in_signals(IN_PGOOD_ALL_NONCORE);
+
+			/* Enable +CPU_CORE and +VGFX_CORE */
+			gpio_set_level(GPIO_ENABLE_VCORE, 1);
+
+			/* Wait for all supplies good */
+			wait_in_signals(IN_PGOOD_ALL_NONCORE |
+					IN_PGOOD_ALL_CORE);
+
+			/* Wait 99ms after all voltages good */
+			usleep(99000);
+
+			/* Set PCH_PWROK */
+			gpio_set_level(GPIO_PCH_PWROK, 1);
+
+			state = X86_S0;
+			break;
+
+		case X86_S0S3:
+			/* Clear PCH_PWROK */
+			gpio_set_level(GPIO_PCH_PWROK, 0);
+
+			/* Wait 40ns */
+			udelay(1);
+
+			/* Disable +CPU_CORE and +VGFX_CORE */
+			gpio_set_level(GPIO_ENABLE_VCORE, 0);
+
+			/* Turn off power rails */
+			gpio_set_level(GPIO_ENABLE_VS, 0);
+
+			state = X86_S3;
+			break;
+
+		case X86_S3S5:
+			/* Turn off power to RAM */
+			gpio_set_level(GPIO_ENABLE_1_5V_DDR, 0);
+			gpio_set_level(GPIO_SHUNT_1_5V_DDR, 1);
+
+			state = X86_S5;
+			break;
+
+		case X86_S5:
+			if (gpio_get_level(GPIO_PCH_SLP_S5n) == 1) {
+				/* Power up to next state */
+				state = X86_S5S3;
+				break;
+			}
+
+			/* Otherwise, steady state; wait for a message */
+			in_want = 0;
+			task_wait_msg(-1);
+			break;
+
+		case X86_S3:
+			if (gpio_get_level(GPIO_PCH_SLP_S3n) == 1) {
+				/* Power up to next state */
+				state = X86_S3S0;
+				break;
+			} else if (gpio_get_level(GPIO_PCH_SLP_S5n) == 0) {
+				/* Power down to next state */
+				state = X86_S3S5;
+				break;
+			}
+
+			/* Otherwise, steady state; wait for a message */
+			in_want = 0;
+			task_wait_msg(-1);
+			break;
+
+		case X86_S0:
+			if (gpio_get_level(GPIO_PCH_SLP_S3n) == 0) {
+				/* Power down to next state */
+				state = X86_S0S3;
+				break;
+			}
+
+			/* Otherwise, steady state; wait for a message */
+			in_want = 0;
+			task_wait_msg(-1);
+		}
+	}
+}
+
+/*****************************************************************************/
+/* Console commnands */
+
+static int command_x86power(int argc, char **argv)
+{
+	/* Print current state */
+	uart_printf("Current X86 state: %d (%s)\n", state, state_names[state]);
+
+	/* Forcing a power state from EC is deprecated */
+	if (argc > 1)
+		uart_puts("Use 'powerbtn' instead of 'x86power s0'.\n");
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(x86power, command_x86power);
diff --git a/core/cortex-m/atomic.h b/core/cortex-m/atomic.h
new file mode 100644
index 0000000..a6fff4b
--- /dev/null
+++ b/core/cortex-m/atomic.h
@@ -0,0 +1,65 @@
+/* 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.
+ */
+
+/* Atomic operations for ARMv7 */
+
+#ifndef __ATOMIC_H
+#define __ATOMIC_H
+
+/**
+ * Implements atomic arithmetic operations on 32-bit integers.
+ *
+ * It used load/store exclusive.
+ * If you write directly the integer used as an atomic variable,
+ * you must either clear explicitly the exclusive monitor (using clrex)
+ * or do it in exception context (which clears the monitor).
+ */
+#define ATOMIC_OP(asm_op,a,v) do {                              \
+	uint32_t reg0, reg1;                                    \
+	                                                        \
+	__asm__ __volatile__("1: ldrex   %0, [%2]\n"            \
+	                        #asm_op" %0, %0, %3\n"          \
+	                     "   strex   %1, %0, [%2]\n"        \
+	                     "   teq     %1, #0\n"              \
+	                     "   bne     1b"                    \
+	                     : "=&r" (reg0), "=&r" (reg1)       \
+	                     : "r" (a), "r" (v) : "cc");        \
+} while (0);
+
+static inline void atomic_clear(uint32_t *addr, uint32_t bits)
+{
+	ATOMIC_OP(bic, addr, bits);
+}
+
+static inline void atomic_or(uint32_t *addr, uint32_t bits)
+{
+	ATOMIC_OP(orr, addr, bits);
+}
+
+static inline void atomic_add(uint32_t *addr, uint32_t value)
+{
+	ATOMIC_OP(add, addr, value);
+}
+
+static inline void atomic_sub(uint32_t *addr, uint32_t value)
+{
+	ATOMIC_OP(sub, addr, value);
+}
+
+static inline uint32_t atomic_read_clear(uint32_t *addr)
+{
+	uint32_t ret, tmp;
+
+	__asm__ __volatile__("   mov     %3, #0\n"
+	                     "1: ldrex   %0, [%2]\n"
+	                     "   strex   %1, %3, [%2]\n"
+	                     "   teq     %1, #0\n"
+	                     "   bne     1b"
+	                     : "=&r" (ret), "=&r" (tmp)
+	                     : "r" (addr), "r" (0) : "cc");
+
+	return ret;
+}
+#endif  /* __ATOMIC_H */
diff --git a/core/cortex-m/build.mk b/core/cortex-m/build.mk
new file mode 100644
index 0000000..8da6792
--- /dev/null
+++ b/core/cortex-m/build.mk
@@ -0,0 +1,11 @@
+# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Cortex-M4 core OS files build
+#
+
+# CPU specific compilation flags
+CFLAGS_CPU=-mcpu=cortex-m4 -mthumb -Os -mno-sched-prolog
+
+core-y=init.o panic.o switch.o task.o timer.o
diff --git a/core/cortex-m/cpu.h b/core/cortex-m/cpu.h
new file mode 100644
index 0000000..1ec9544
--- /dev/null
+++ b/core/cortex-m/cpu.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Registers map and defintions for Cortex-MLM4x processor
+ */
+
+#ifndef __CPU_H
+#define __CPU_H
+
+#include <stdint.h>
+
+/* Macro to access 32-bit registers */
+#define CPUREG(addr) (*(volatile uint32_t*)(addr))
+
+/* Nested Vectored Interrupt Controller */
+#define CPU_NVIC_EN(x)         CPUREG(0xe000e100 + 4 * (x))
+#define CPU_NVIC_DIS(x)        CPUREG(0xe000e180 + 4 * (x))
+#define CPU_NVIC_PRI(x)        CPUREG(0xe000e400 + 4 * (x))
+#define CPU_NVIC_APINT         CPUREG(0xe000ed0c)
+#define CPU_NVIC_SWTRIG        CPUREG(0xe000ef00)
+
+#endif /* __CPU_H */
diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S
new file mode 100644
index 0000000..35006ae
--- /dev/null
+++ b/core/cortex-m/ec.lds.S
@@ -0,0 +1,63 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "config.h"
+
+#define CONFIG_FW_SECT_OFF(section) CONFIG_FW_##section##_OFF
+#define CONFIG_FW_BASE(section) (CONFIG_FLASH_BASE + CONFIG_FW_SECT_OFF(section))
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(reset)
+MEMORY
+{
+    FLASH (rx) : ORIGIN = CONFIG_FW_BASE(SECTION), LENGTH = CONFIG_FW_IMAGE_SIZE
+    IRAM (rw)  : ORIGIN = CONFIG_RAM_BASE, LENGTH = CONFIG_RAM_SIZE
+}
+SECTIONS
+{
+    .text : {
+        OUTDIR/core/CORE/init.o (.text)
+        *(.text*)
+#ifdef COMPILE_FOR_RAM
+    } > IRAM
+#else
+    } > FLASH
+#endif
+    . = ALIGN(4);
+    .rodata : {
+        __irqprio = .;
+        *(.rodata.irqprio)
+        __irqprio_end = .;
+        . = ALIGN(4);
+        __cmds = .;
+        *(.rodata.cmds)
+        __cmds_end = .;
+        *(.rodata*)
+        . = ALIGN(4);
+#ifdef COMPILE_FOR_RAM
+    } > IRAM
+    __ro_end = . ;
+    .data : {
+#else
+    } > FLASH
+    __ro_end = . ;
+    .data : AT(ADDR(.rodata) + SIZEOF(.rodata)) {
+#endif
+        . = ALIGN(4);
+        __data_start = .;
+        *(.data.tasks)
+        *(.data)
+        . = ALIGN(4);
+        __data_end = .;
+    } > IRAM
+    .bss : {
+        . = ALIGN(4);
+        __bss_start = .;
+        *(.bss)
+        . = ALIGN(4);
+        __bss_end = .;
+    } > IRAM
+    /DISCARD/ : { *(.ARM.*) }
+}
diff --git a/core/cortex-m/init.S b/core/cortex-m/init.S
new file mode 100644
index 0000000..6e3f348
--- /dev/null
+++ b/core/cortex-m/init.S
@@ -0,0 +1,378 @@
+/* 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.
+ *
+ * Cortex-M CPU initialization
+ */
+
+#include "config.h"
+
+.text
+
+.syntax unified
+.code 16
+
+.macro vector name
+.long \name\()_handler
+.weak \name\()_handler
+.set \name\()_handler, default_handler
+.endm
+
+.macro vector_irq number
+.if \number < CONFIG_IRQ_COUNT
+vector irq_\()\number
+.endif
+.endm
+
+/* Exceptions vector */
+vectors:
+.long stack_end       		  @ initial stack pointer
+.long reset           		  @ reset handler
+vector nmi            		  @ NMI handler
+vector hard_fault     		  @ HardFault handler
+vector mpu_fault      		  @ MPU fault handler
+vector bus_fault      		  @ Bus fault handler
+vector usage_fault    		  @ Usage fault handler
+.long 0               		  @ reserved
+.long 0               		  @ reserved
+.long 0               		  @ reserved
+.long 0               		  @ reserved
+vector svc            		  @ SWI
+vector debug          		  @ Debug handler
+.long 0               		  @ reserved
+vector pendsv         		  @ PendSV handler
+vector sys_tick       		  @ SysTick handler
+vector_irq 0                      @ IRQ   0 handler
+vector_irq 1                      @ IRQ   1 handler
+vector_irq 2                      @ IRQ   2 handler
+vector_irq 3                      @ IRQ   3 handler
+vector_irq 4                      @ IRQ   4 handler
+vector_irq 5                      @ IRQ   5 handler
+vector_irq 6                      @ IRQ   6 handler
+vector_irq 7                      @ IRQ   7 handler
+vector_irq 8                      @ IRQ   8 handler
+vector_irq 9                      @ IRQ   9 handler
+vector_irq 10                     @ IRQ  10 handler
+vector_irq 11                     @ IRQ  11 handler
+vector_irq 12                     @ IRQ  12 handler
+vector_irq 13                     @ IRQ  13 handler
+vector_irq 14                     @ IRQ  14 handler
+vector_irq 15                     @ IRQ  15 handler
+vector_irq 16                     @ IRQ  16 handler
+vector_irq 17                     @ IRQ  17 handler
+vector_irq 18                     @ IRQ  18 handler
+vector_irq 19                     @ IRQ  19 handler
+vector_irq 20                     @ IRQ  20 handler
+vector_irq 21                     @ IRQ  21 handler
+vector_irq 22                     @ IRQ  22 handler
+vector_irq 23                     @ IRQ  23 handler
+vector_irq 24                     @ IRQ  24 handler
+vector_irq 25                     @ IRQ  25 handler
+vector_irq 26                     @ IRQ  26 handler
+vector_irq 27                     @ IRQ  27 handler
+vector_irq 28                     @ IRQ  28 handler
+vector_irq 29                     @ IRQ  29 handler
+vector_irq 30                     @ IRQ  30 handler
+vector_irq 31                     @ IRQ  31 handler
+vector_irq 32                     @ IRQ  32 handler
+vector_irq 33                     @ IRQ  33 handler
+vector_irq 34                     @ IRQ  34 handler
+vector_irq 35                     @ IRQ  35 handler
+vector_irq 36                     @ IRQ  36 handler
+vector_irq 37                     @ IRQ  37 handler
+vector_irq 38                     @ IRQ  38 handler
+vector_irq 39                     @ IRQ  39 handler
+vector_irq 40                     @ IRQ  40 handler
+vector_irq 41                     @ IRQ  41 handler
+vector_irq 42                     @ IRQ  42 handler
+vector_irq 43                     @ IRQ  43 handler
+vector_irq 44                     @ IRQ  44 handler
+vector_irq 45                     @ IRQ  45 handler
+vector_irq 46                     @ IRQ  46 handler
+vector_irq 47                     @ IRQ  47 handler
+vector_irq 48                     @ IRQ  48 handler
+vector_irq 49                     @ IRQ  49 handler
+vector_irq 50                     @ IRQ  50 handler
+vector_irq 51                     @ IRQ  51 handler
+vector_irq 52                     @ IRQ  52 handler
+vector_irq 53                     @ IRQ  53 handler
+vector_irq 54                     @ IRQ  54 handler
+vector_irq 55                     @ IRQ  55 handler
+vector_irq 56                     @ IRQ  56 handler
+vector_irq 57                     @ IRQ  57 handler
+vector_irq 58                     @ IRQ  58 handler
+vector_irq 59                     @ IRQ  59 handler
+vector_irq 60                     @ IRQ  60 handler
+vector_irq 61                     @ IRQ  61 handler
+vector_irq 62                     @ IRQ  62 handler
+vector_irq 63                     @ IRQ  63 handler
+vector_irq 64                     @ IRQ  64 handler
+vector_irq 65                     @ IRQ  65 handler
+vector_irq 66                     @ IRQ  66 handler
+vector_irq 67                     @ IRQ  67 handler
+vector_irq 68                     @ IRQ  68 handler
+vector_irq 69                     @ IRQ  69 handler
+vector_irq 70                     @ IRQ  70 handler
+vector_irq 71                     @ IRQ  71 handler
+vector_irq 72                     @ IRQ  72 handler
+vector_irq 73                     @ IRQ  73 handler
+vector_irq 74                     @ IRQ  74 handler
+vector_irq 75                     @ IRQ  75 handler
+vector_irq 76                     @ IRQ  76 handler
+vector_irq 77                     @ IRQ  77 handler
+vector_irq 78                     @ IRQ  78 handler
+vector_irq 79                     @ IRQ  79 handler
+vector_irq 80                     @ IRQ  80 handler
+vector_irq 81                     @ IRQ  81 handler
+vector_irq 82                     @ IRQ  82 handler
+vector_irq 83                     @ IRQ  83 handler
+vector_irq 84                     @ IRQ  84 handler
+vector_irq 85                     @ IRQ  85 handler
+vector_irq 86                     @ IRQ  86 handler
+vector_irq 87                     @ IRQ  87 handler
+vector_irq 88                     @ IRQ  88 handler
+vector_irq 89                     @ IRQ  89 handler
+vector_irq 90                     @ IRQ  90 handler
+vector_irq 91                     @ IRQ  91 handler
+vector_irq 92                     @ IRQ  92 handler
+vector_irq 93                     @ IRQ  93 handler
+vector_irq 94                     @ IRQ  94 handler
+vector_irq 95                     @ IRQ  95 handler
+vector_irq 96                     @ IRQ  96 handler
+vector_irq 97                     @ IRQ  97 handler
+vector_irq 98                     @ IRQ  98 handler
+vector_irq 99                     @ IRQ  99 handler
+vector_irq 100                    @ IRQ 100 handler
+vector_irq 101                    @ IRQ 101 handler
+vector_irq 102                    @ IRQ 102 handler
+vector_irq 103                    @ IRQ 103 handler
+vector_irq 104                    @ IRQ 104 handler
+vector_irq 105                    @ IRQ 105 handler
+vector_irq 106                    @ IRQ 106 handler
+vector_irq 107                    @ IRQ 107 handler
+vector_irq 108                    @ IRQ 108 handler
+vector_irq 109                    @ IRQ 109 handler
+vector_irq 110                    @ IRQ 110 handler
+vector_irq 111                    @ IRQ 111 handler
+vector_irq 112                    @ IRQ 112 handler
+vector_irq 113                    @ IRQ 113 handler
+vector_irq 114                    @ IRQ 114 handler
+vector_irq 115                    @ IRQ 115 handler
+vector_irq 116                    @ IRQ 116 handler
+vector_irq 117                    @ IRQ 117 handler
+vector_irq 118                    @ IRQ 118 handler
+vector_irq 119                    @ IRQ 119 handler
+vector_irq 120                    @ IRQ 120 handler
+vector_irq 121                    @ IRQ 121 handler
+vector_irq 122                    @ IRQ 122 handler
+vector_irq 123                    @ IRQ 123 handler
+vector_irq 124                    @ IRQ 124 handler
+vector_irq 125                    @ IRQ 125 handler
+vector_irq 126                    @ IRQ 126 handler
+vector_irq 127                    @ IRQ 127 handler
+vector_irq 128                    @ IRQ 128 handler
+vector_irq 129                    @ IRQ 129 handler
+vector_irq 130                    @ IRQ 130 handler
+vector_irq 131                    @ IRQ 131 handler
+vector_irq 132                    @ IRQ 132 handler
+vector_irq 133                    @ IRQ 133 handler
+vector_irq 134                    @ IRQ 134 handler
+vector_irq 135                    @ IRQ 135 handler
+vector_irq 136                    @ IRQ 136 handler
+vector_irq 137                    @ IRQ 137 handler
+vector_irq 138                    @ IRQ 138 handler
+vector_irq 139                    @ IRQ 139 handler
+vector_irq 140                    @ IRQ 140 handler
+vector_irq 141                    @ IRQ 141 handler
+vector_irq 142                    @ IRQ 142 handler
+vector_irq 143                    @ IRQ 143 handler
+vector_irq 144                    @ IRQ 144 handler
+vector_irq 145                    @ IRQ 145 handler
+vector_irq 146                    @ IRQ 146 handler
+vector_irq 147                    @ IRQ 147 handler
+vector_irq 148                    @ IRQ 148 handler
+vector_irq 149                    @ IRQ 149 handler
+vector_irq 150                    @ IRQ 150 handler
+vector_irq 151                    @ IRQ 151 handler
+vector_irq 152                    @ IRQ 152 handler
+vector_irq 153                    @ IRQ 153 handler
+vector_irq 154                    @ IRQ 154 handler
+vector_irq 155                    @ IRQ 155 handler
+vector_irq 156                    @ IRQ 156 handler
+vector_irq 157                    @ IRQ 157 handler
+vector_irq 158                    @ IRQ 158 handler
+vector_irq 159                    @ IRQ 159 handler
+vector_irq 160                    @ IRQ 160 handler
+vector_irq 161                    @ IRQ 161 handler
+vector_irq 162                    @ IRQ 162 handler
+vector_irq 163                    @ IRQ 163 handler
+vector_irq 164                    @ IRQ 164 handler
+vector_irq 165                    @ IRQ 165 handler
+vector_irq 166                    @ IRQ 166 handler
+vector_irq 167                    @ IRQ 167 handler
+vector_irq 168                    @ IRQ 168 handler
+vector_irq 169                    @ IRQ 169 handler
+vector_irq 170                    @ IRQ 170 handler
+vector_irq 171                    @ IRQ 171 handler
+vector_irq 172                    @ IRQ 172 handler
+vector_irq 173                    @ IRQ 173 handler
+vector_irq 174                    @ IRQ 174 handler
+vector_irq 175                    @ IRQ 175 handler
+vector_irq 176                    @ IRQ 176 handler
+vector_irq 177                    @ IRQ 177 handler
+vector_irq 178                    @ IRQ 178 handler
+vector_irq 179                    @ IRQ 179 handler
+vector_irq 180                    @ IRQ 180 handler
+vector_irq 181                    @ IRQ 181 handler
+vector_irq 182                    @ IRQ 182 handler
+vector_irq 183                    @ IRQ 183 handler
+vector_irq 184                    @ IRQ 184 handler
+vector_irq 185                    @ IRQ 185 handler
+vector_irq 186                    @ IRQ 186 handler
+vector_irq 187                    @ IRQ 187 handler
+vector_irq 188                    @ IRQ 188 handler
+vector_irq 189                    @ IRQ 189 handler
+vector_irq 190                    @ IRQ 190 handler
+vector_irq 191                    @ IRQ 191 handler
+vector_irq 192                    @ IRQ 192 handler
+vector_irq 193                    @ IRQ 193 handler
+vector_irq 194                    @ IRQ 194 handler
+vector_irq 195                    @ IRQ 195 handler
+vector_irq 196                    @ IRQ 196 handler
+vector_irq 197                    @ IRQ 197 handler
+vector_irq 198                    @ IRQ 198 handler
+vector_irq 199                    @ IRQ 199 handler
+vector_irq 200                    @ IRQ 200 handler
+vector_irq 201                    @ IRQ 201 handler
+vector_irq 202                    @ IRQ 202 handler
+vector_irq 203                    @ IRQ 203 handler
+vector_irq 204                    @ IRQ 204 handler
+vector_irq 205                    @ IRQ 205 handler
+vector_irq 206                    @ IRQ 206 handler
+vector_irq 207                    @ IRQ 207 handler
+vector_irq 208                    @ IRQ 208 handler
+vector_irq 209                    @ IRQ 209 handler
+vector_irq 210                    @ IRQ 210 handler
+vector_irq 211                    @ IRQ 211 handler
+vector_irq 212                    @ IRQ 212 handler
+vector_irq 213                    @ IRQ 213 handler
+vector_irq 214                    @ IRQ 214 handler
+vector_irq 215                    @ IRQ 215 handler
+vector_irq 216                    @ IRQ 216 handler
+vector_irq 217                    @ IRQ 217 handler
+vector_irq 218                    @ IRQ 218 handler
+vector_irq 219                    @ IRQ 219 handler
+vector_irq 220                    @ IRQ 220 handler
+vector_irq 221                    @ IRQ 221 handler
+vector_irq 222                    @ IRQ 222 handler
+vector_irq 223                    @ IRQ 223 handler
+vector_irq 224                    @ IRQ 224 handler
+vector_irq 225                    @ IRQ 225 handler
+vector_irq 226                    @ IRQ 226 handler
+vector_irq 227                    @ IRQ 227 handler
+vector_irq 228                    @ IRQ 228 handler
+vector_irq 229                    @ IRQ 229 handler
+vector_irq 230                    @ IRQ 230 handler
+vector_irq 231                    @ IRQ 231 handler
+vector_irq 232                    @ IRQ 232 handler
+vector_irq 233                    @ IRQ 233 handler
+vector_irq 234                    @ IRQ 234 handler
+vector_irq 235                    @ IRQ 235 handler
+vector_irq 236                    @ IRQ 236 handler
+vector_irq 237                    @ IRQ 237 handler
+vector_irq 238                    @ IRQ 238 handler
+vector_irq 239                    @ IRQ 239 handler
+vector_irq 240                    @ IRQ 240 handler
+vector_irq 241                    @ IRQ 241 handler
+vector_irq 242                    @ IRQ 242 handler
+vector_irq 243                    @ IRQ 243 handler
+vector_irq 244                    @ IRQ 244 handler
+vector_irq 245                    @ IRQ 245 handler
+vector_irq 246                    @ IRQ 246 handler
+vector_irq 247                    @ IRQ 247 handler
+vector_irq 248                    @ IRQ 248 handler
+vector_irq 249                    @ IRQ 249 handler
+vector_irq 250                    @ IRQ 250 handler
+vector_irq 251                    @ IRQ 251 handler
+vector_irq 252                    @ IRQ 252 handler
+vector_irq 253                    @ IRQ 253 handler
+vector_irq 254                    @ IRQ 254 handler
+
+.global reset
+.thumb_func
+reset:
+    /* set the vector table on our current code */
+    adr r1, vectors
+    ldr r2, =0xE000ED08   /* VTABLE register in SCB*/
+    str r1, [r2]
+    /* Clear BSS */
+    mov r0, #0
+    ldr r1,_bss_start
+    ldr r2,_bss_end
+bss_loop:
+    cmp r1, r2
+    it lt
+    strlt r0, [r1], #4
+    blt bss_loop
+
+#ifndef COMPILE_FOR_RAM
+    /* Copy initialized data to Internal RAM */
+    ldr r0,_ro_end
+    ldr r1,_data_start
+    ldr r2,_data_end
+data_loop:
+    ldr r3, [r0], #4
+    cmp r1, r2
+    it lt
+    strlt r3, [r1], #4
+    blt data_loop
+#endif
+
+    /**
+     * Set stack pointer
+     * already done my Cortex-M hardware but this allows software to
+     * jump directly to reset function or to run on other ARM
+     */
+    ldr r0, =stack_end
+    mov sp, r0
+
+    /* jump to C code */
+    bl main
+    /* we should not return here */
+    /* TODO check error code ? */
+fini_loop:
+    b fini_loop
+
+/* default exception handler */
+.thumb_func
+default_handler:
+    b panic
+
+_bss_start:
+    .long __bss_start
+_bss_end:
+    .long __bss_end
+_data_start:
+    .long __data_start
+_data_end:
+    .long __data_end
+_ro_end:
+    .long __ro_end
+
+/* Dummy functions to avoid linker complaints */
+.global __aeabi_unwind_cpp_pr0
+.global __aeabi_unwind_cpp_pr1
+.global __aeabi_unwind_cpp_pr2
+__aeabi_unwind_cpp_pr0:
+__aeabi_unwind_cpp_pr1:
+__aeabi_unwind_cpp_pr2:
+    bx lr
+
+.section .bss
+
+/* Reserve space for system stack */
+stack_start:
+  .space CONFIG_STACK_SIZE, 0
+stack_end:
+  .globl stack_end
+
diff --git a/core/cortex-m/panic.S b/core/cortex-m/panic.S
new file mode 100644
index 0000000..766af75
--- /dev/null
+++ b/core/cortex-m/panic.S
@@ -0,0 +1,108 @@
+/* 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.
+ *
+ * Fatal exception handling and debug tracing
+ */
+
+#include "config.h"
+
+.text
+
+.syntax unified
+.code 16
+
+.macro hex_reg r, offset                      @ prepare to build
+    add r1, r3, #\offset                      @ .. hexadecimal string
+    mov r0, \r                                @ .. from the reg value
+    bl buildhex
+.endm
+
+/* fatal exception handler */
+.global panic
+.thumb_func
+panic:
+#ifndef CONFIG_DEBUG
+    b EcSystemReset                           @ Reboot the system
+#else /* CONFIG_DEBUG */
+    /* check that the exception stack pointer is valid */
+    ldr r0, =CONFIG_RAM_BASE                  @ start of RAM
+    ldr r1, =CONFIG_RAM_BASE+CONFIG_RAM_SIZE  @ end of RAM
+    mrs r12, psp                              @ process stack pointer
+    cmp r12, r0                               @ is sp >= RAM start ?
+    it  ge
+    cmpge r1, r12                             @ is sp < RAM end ?
+    blt panic_print                           @ no => no values to read
+    /* output registers value */
+    ldr r3, =msg_excep                        @ pointer to the text buffer
+    mrs r2, ipsr                              @ get exception num from IPSR
+    bfc r2, #9, #23                           @ the exception is the 3 LSB
+    hex_reg r2, 18                            @ prepare hexa for excep number
+    hex_reg r4, 119                           @ prepare hexa for R4
+    hex_reg r5, 132                           @ prepare hexa for R5
+    hex_reg r6, 145                           @ prepare hexa for R6
+    hex_reg r7, 156                           @ prepare hexa for R7
+    hex_reg r8, 171                           @ prepare hexa for R8
+    hex_reg r9, 184                           @ prepare hexa for R9
+    hex_reg r10, 197                          @ prepare hexa for R10
+    hex_reg r11, 210                          @ prepare hexa for R11
+    ldmia r12!, {r4-r11}                      @ load saved r0-r3,r12,lr,pc,psr
+    hex_reg r4, 66                            @ prepare hexa for R0
+    hex_reg r5, 79                            @ prepare hexa for R1
+    hex_reg r6, 92                            @ prepare hexa for R2
+    hex_reg r7, 105                           @ prepare hexa for R3
+    hex_reg r8, 225                           @ prepare hexa for R12
+    hex_reg r12, 238                          @ prepare hexa for SP
+    hex_reg r9, 251                           @ prepare hexa for LR
+    hex_reg r10, 264                          @ prepare hexa for PC
+    hex_reg r11, 40                           @ prepare hexa for xPSR
+    /* print exception trace */
+panic_print:
+    ldr r0, =msg_excep                        @ pointer to the text buffer
+    bl emergency_puts                         @ print the banner
+    b system_reset                            @ Reboot the system
+
+/* Helpers for exception trace */
+/* print a string on the UART
+ * r0: asciiZ string pointer
+ */
+emergency_puts:
+    ldr r1, =CONFIG_UART_ADDRESS              @ UART base address
+1:
+    ldrb r3, [r0], #1                         @ read one character
+    cmp r3, #0                                @ end of the string ?
+    beq 3f                                    @ if yes, return
+2:  /* putchar */
+    ldr r2, [r1, #CONFIG_UART_SR_OFFSET]      @ read UART status
+    tst r2, #CONFIG_UART_SR_TXEMPTY           @ free space on TX ?
+    beq 2b                                    @ if no, wait
+    str r3, [r1, #CONFIG_UART_DR_OFFSET]      @ send character to UART DR
+    b 1b                                      @ goto next character
+3:  /* return */
+    bx lr
+
+/* write a number in hexadecimal in a text buffer
+ * r0: number to print
+ * r1: pointer to *end* of the buffer (filled with '0')
+ */
+buildhex:
+    cmp r0, #0
+    it  eq
+    bxeq lr
+    and r2, r0, #0xf
+    cmp r2, #10
+    ite  lt
+    addlt r2, #'0'
+    addge r2, #'A'-10
+    strb r2, [r1],#-1
+    lsr r0, #4
+    b buildhex
+
+.data
+msg_excep: .ascii "\r\n=== EXCEPTION: 00 ====== xPSR: 00000000 ===========\r\n"
+msg_reg0:  .ascii "R0 :00000000 R1 :00000000 R2 :00000000 R3 :00000000\r\n"
+msg_reg1:  .ascii "R4 :00000000 R5 :00000000 R6 :00000000 R7 :00000000\r\n"
+msg_reg2:  .ascii "R8 :00000000 R9 :00000000 R10:00000000 R11:00000000\r\n"
+msg_reg3:  .ascii "R12:00000000 SP :00000000 LR :00000000 PC :00000000\r\n\0"
+.align 4
+#endif /* CONFIG_DEBUG */
diff --git a/core/cortex-m/switch.S b/core/cortex-m/switch.S
new file mode 100644
index 0000000..4d97425
--- /dev/null
+++ b/core/cortex-m/switch.S
@@ -0,0 +1,63 @@
+/* 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.
+ *
+ * Context swtching
+ */
+
+#include "config.h"
+
+.text
+
+.syntax unified
+.code 16
+
+/**
+ * Task context switching
+ *
+ * Change the task scheduled after returning from the exception.
+ *
+ * Save the registers of the current task below the exception context on
+ * its task, then restore the live registers of the next task and set the
+ * process stack pointer to the new stack.
+ *
+ * r0: pointer to the task to switch from
+ * r1: pointer to the task to switch to
+ *
+ * must be called from interrupt context
+ *
+ * the structure of tje saved context on the stack is :
+ *     r0, r1, r2, r3, r12, lr, pc, psr, r4, r5, r6, r7, r8, r9, r10, r11
+ *       exception frame              <|> additional registers
+ */
+.global __switchto
+.thumb_func
+__switchto:
+  mrs r3, psp            @ get the task stack where the context has been saved
+  ldr r2, [r1]           @ get the new scheduled task stack pointer
+  stmdb r3!, {r4-r11}    @ save additional r4-r7 in the task stack
+  ldmia r2!, {r4-r11}    @ restore r4-r7 for the next task context
+  str r3, [r0]           @ save the task stack pointer in its context
+  msr psp, r2            @ set the process stack pointer to exception context
+  bx lr                  @ return from exception
+
+/**
+ * Start the task scheduling
+ */
+.global task_start
+.thumb_func
+task_start:
+  ldr r2,=scratchpad     @ area used as dummy thread stack for the first switch
+  mov r3, #2
+  mov r0, #0             @ __Schedule parameter : de-schedule nothing
+  mov r1, #0             @ __Schedule parameter : re-schedule nothing
+  add r2, #17*4          @ put the pointer at the top of the stack
+  msr psp, r2            @ setup a thread stack up to the first context switch
+  isb                    @ ensure the write is done
+  msr control, r3        @ use : priv. mode / thread stack / no floating point
+  isb                    @ ensure the write is done
+  bl __schedule          @ execute the task with the highest priority
+  /* we should never return here */
+  mov r0, #1            @ set to EC_ERROR_UNKNOWN
+  bx lr
+
diff --git a/core/cortex-m/task.c b/core/cortex-m/task.c
new file mode 100644
index 0000000..65e86f4
--- /dev/null
+++ b/core/cortex-m/task.c
@@ -0,0 +1,394 @@
+/* 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.
+ */
+
+/* Task scheduling / events module for Chrome EC operating system */
+
+#include <stdint.h>
+
+#include "config.h"
+#include "atomic.h"
+#include "console.h"
+#include "cpu.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "util.h"
+
+/**
+ * Global memory size for a task : 512 bytes
+ * including its contexts and its stack
+ */
+#define TASK_SIZE_LOG2 9
+#define TASK_SIZE      (1<<TASK_SIZE_LOG2)
+
+typedef union {
+	struct {
+		uint32_t sp;       /* saved stack pointer for context switch */
+		uint32_t events;   /* bitmaps of received events */
+		uint8_t stack[0];  /* task stack */
+	};
+	uint32_t context[TASK_SIZE/4];
+} task_;
+
+/* declare task routine prototypes */
+#define TASK(n, r, d) int r(void *);
+#include TASK_LIST
+void __idle(void);
+CONFIG_TASK_LIST
+#undef TASK
+
+
+extern void __switchto(task_ *from, task_ *to);
+
+
+/* declare and fill the contexts for all the tasks */
+#define TASK(n, r, d)  {						\
+	.context[0] = (uint32_t)(tasks + TASK_ID_##n + 1) - 64,	        \
+	.context[TASK_SIZE/4 - 8/*r0*/] = (uint32_t)d,                  \
+	/* TODO set a LR to a trap */		                        \
+	.context[TASK_SIZE/4 - 2/*pc*/] = (uint32_t)r,                  \
+	.context[TASK_SIZE/4 - 1/*psr*/] = 0x01000000 },
+#include TASK_LIST
+static task_ tasks[] __attribute__((section(".data.tasks")))
+		__attribute__((aligned(TASK_SIZE))) = {
+	TASK(IDLE, __idle, 0)
+	CONFIG_TASK_LIST
+};
+#undef TASK
+/* reserve space to discard context on first context switch */
+uint32_t scratchpad[17] __attribute__((section(".data.tasks")));
+
+/* context switch at the next exception exit if needed */
+/* TODO: who sets this back to 0 after it's set to 1? */
+static int need_resched = 0;
+
+/**
+ * bitmap of all tasks ready to be run
+ *
+ * Currently all tasks are enabled at startup.
+ */
+static unsigned tasks_ready = (1<<TASK_ID_COUNT) - 1;
+
+
+static task_ *__get_current(void)
+{
+	unsigned sp;
+
+	asm("mov %0, sp":"=r"(sp));
+	return (task_ *)((sp - 4) & ~(TASK_SIZE-1));
+}
+
+
+/**
+ * Return a pointer to the task preempted by the current exception
+ *
+ * designed to be called from interrupt context.
+ */
+static task_ *__get_task_scheduled(void)
+{
+	unsigned sp;
+
+	asm("mrs %0, psp":"=r"(sp));
+	return (task_ *)((sp - 16) & ~(TASK_SIZE-1));
+}
+
+
+static inline task_ *__task_id_to_ptr(task_id_t id)
+{
+	return tasks + id;
+}
+
+
+inline int in_interrupt_context(void)
+{
+	int ret;
+	asm("mrs %0, ipsr \n"             /* read exception number */
+	    "lsl %0, #23  \n":"=r"(ret)); /* exception bits are the 9 LSB */
+	return ret;
+}
+
+
+task_id_t task_get_current(void)
+{
+	task_id_t id = __get_current() - tasks;
+	if (id >= TASK_ID_COUNT)
+		id = TASK_ID_INVALID;
+
+	return id;
+}
+
+
+uint32_t *task_get_event_bitmap(task_id_t tskid)
+{
+	task_ *tsk = __task_id_to_ptr(tskid);
+	return &tsk->events;
+}
+
+
+/**
+ * scheduling system call
+ */
+void svc_handler(int desched, task_id_t resched)
+{
+	task_ *current, *next;
+	uint32_t reg;
+
+	/* push the priority to -1 until the return, to avoid being
+	 * interrupted */
+	asm volatile("mov %0, #1\n"
+	             "msr faultmask, %0" :"=r"(reg));
+	current = __get_task_scheduled();
+	if (desched && !current->events) {
+		/* Remove our own ready bit */
+		tasks_ready &= ~(1 << (current-tasks));
+	}
+	tasks_ready |= 1 << resched;
+
+	ASSERT(tasks_ready);
+	next = __task_id_to_ptr(31 - __builtin_clz(tasks_ready));
+
+	/* Nothing to do */
+	if (next == current)
+		return;
+
+	__switchto(current, next);
+}
+
+
+void __schedule(int desched, int resched)
+{
+	register int p0 asm("r0") = desched;
+	register int p1 asm("r1") = resched;
+	/* TODO remove hardcoded opcode
+	 * SWI not compiled properly for ARMv7-M on our current chroot toolchain
+	 */
+	asm(".hword 0xdf00 @swi 0"::"r"(p0),"r"(p1));
+}
+
+
+/**
+ * Change the task scheduled after returning from the exception.
+ *
+ * If task_send_msg has been called and has set need_resched flag,
+ * we re-compute which task is running and eventually swap the context
+ * saved on the process stack to restore the new one at exception exit.
+ *
+ * it must be called from interrupt context !
+ */
+void task_resched_if_needed(void *excep_return)
+{
+	/**
+	 * continue iff a rescheduling event happened and
+	 * we are not called from another exception
+	 */
+	if (!need_resched || (((uint32_t)excep_return & 0xf) == 1))
+		return;
+
+	svc_handler(0, 0);
+}
+
+
+static uint32_t __wait_msg(int timeout_us, task_id_t resched)
+{
+	task_ *tsk = __get_current();
+	task_id_t me = tsk - tasks;
+	uint32_t evt;
+	int ret;
+
+	ASSERT(!in_interrupt_context());
+
+	if (timeout_us > 0) {
+		timestamp_t deadline = get_time();
+		deadline.val += timeout_us;
+		ret = timer_arm(deadline, me);
+		ASSERT(ret == EC_SUCCESS);
+	}
+	while (!(evt = atomic_read_clear(&tsk->events)))
+	{
+		/* Remove ourself and get the next task in the scheduler */
+		__schedule(1, resched);
+		resched = TASK_ID_IDLE;
+	}
+	if (timeout_us > 0)
+		timer_cancel(me);
+	return evt;
+}
+
+
+uint32_t task_send_msg(task_id_t tskid, task_id_t from, int wait)
+{
+	task_ *receiver = __task_id_to_ptr(tskid);
+	ASSERT(receiver);
+
+	if (from == TASK_ID_CURRENT) {
+		from = task_get_current();
+	}
+
+	/* set the event bit in the receiver message bitmap */
+	atomic_or(&receiver->events, 1 << from);
+
+	/* Re-schedule if priorities have changed */
+	if (in_interrupt_context()) {
+		/* the receiver might run again */
+		tasks_ready |= 1 << tskid;
+		need_resched = 1;
+	} else {
+		if (wait)
+			return __wait_msg(-1, tskid);
+		else
+			__schedule(0, tskid);
+	}
+
+	return 0;
+}
+
+
+uint32_t task_wait_msg(int timeout_us)
+{
+	return __wait_msg(timeout_us, TASK_ID_IDLE);
+}
+
+
+void task_enable_irq(int irq)
+{
+	CPU_NVIC_EN(irq / 32) = 1 << (irq % 32);
+}
+
+
+void task_disable_irq(int irq)
+{
+	CPU_NVIC_DIS(irq / 32) = 1 << (irq % 32);
+}
+
+
+void task_trigger_irq(int irq)
+{
+	CPU_NVIC_SWTRIG = irq;
+}
+
+
+/**
+ * Enable all used IRQ in the NVIC and set their priorities
+ * as defined by the DECLARE_IRQ statements
+ */
+static void __nvic_init_irqs(void)
+{
+	/* get the IRQ priorities section from the linker */
+	extern struct irq_priority __irqprio[];
+	extern struct irq_priority __irqprio_end[];
+	int irq_count = __irqprio_end - __irqprio;
+	int i;
+
+	for (i = 0; i < irq_count; i++) {
+		uint8_t irq = __irqprio[i].irq;
+		uint8_t prio = __irqprio[i].priority;
+		uint32_t prio_shift = irq % 4 * 8 + 5;
+		CPU_NVIC_PRI(irq / 4) =
+				(CPU_NVIC_PRI(irq / 4) &
+				 ~(0x7 << prio_shift)) |
+				(prio << prio_shift);
+	}
+}
+
+
+void mutex_lock(struct mutex *mtx)
+{
+	uint32_t value;
+	uint32_t id = 1 << task_get_current();
+
+	ASSERT(id != TASK_ID_INVALID);
+	atomic_or(&mtx->waiters, id);
+
+	do {
+		/* try to get the lock (set 1 into the lock field) */
+		__asm__ __volatile__("   ldrex   %0, [%1]\n"
+				     "   teq     %0, #0\n"
+				     "   it eq\n"
+				     "   strexeq %0, %2, [%1]\n"
+				     : "=&r" (value)
+				     : "r" (&mtx->lock), "r" (1) : "cc");
+		if (value) {
+			/* contention on the mutex */
+			task_wait_msg(0);
+		}
+	} while (value);
+
+	atomic_clear(&mtx->waiters, id);
+}
+
+void mutex_unlock(struct mutex *mtx)
+{
+	uint32_t waiters;
+	task_ *tsk = __get_current();
+
+	__asm__ __volatile__("   ldr     %0, [%2]\n"
+			     "   str     %3, [%1]\n"
+			     : "=&r" (waiters)
+			     : "r" (&mtx->lock), "r" (&mtx->waiters), "r" (0)
+			     : "cc");
+	while (waiters) {
+		task_id_t id = 31 - __builtin_clz(waiters);
+		/* somebody is waiting on the mutex */
+		task_send_msg(id, TASK_ID_MUTEX, 0);
+		waiters &= ~(1 << id);
+	}
+	/* Ensure no event is remaining from mutex wake-up */
+	atomic_clear(&tsk->events, 1 << TASK_ID_MUTEX);
+}
+
+
+#ifdef CONFIG_DEBUG
+
+/* store the task names for easier debugging */
+#define TASK(n, r, d)  #n,
+#include TASK_LIST
+static const char * const task_names[] = {
+	"<< idle >>",
+	CONFIG_TASK_LIST
+};
+#undef TASK
+
+
+int command_task_info(int argc, char **argv)
+{
+	int i;
+
+	for (i = 0; i < TASK_ID_COUNT; i++) {
+		char is_ready = (tasks_ready & (1<<i)) ? 'R' : ' ';
+		uart_printf("%2d %c %-16s events %08x\n", i, is_ready,
+		            task_names[i], tasks[i].events);
+	}
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(taskinfo, command_task_info);
+
+
+static int command_task_ready(int argc, char **argv)
+{
+	if (argc < 2) {
+		uart_printf("tasks_ready: 0x%08x\n", tasks_ready);
+	} else {
+		tasks_ready = strtoi(argv[1], NULL, 16);
+		uart_printf("Setting tasks_ready to 0x%08x\n", tasks_ready);
+		__schedule(0, 0);
+	}
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(taskready, command_task_ready);
+#endif
+
+
+int task_init(void)
+{
+	/* sanity checks about static task invariants */
+	BUILD_ASSERT(TASK_ID_COUNT <= sizeof(unsigned) * 8);
+	BUILD_ASSERT(TASK_ID_COUNT < (1 << (sizeof(task_id_t) * 8)));
+
+	/* Initialize IRQs */
+	__nvic_init_irqs();
+
+	return EC_SUCCESS;
+}
diff --git a/core/cortex-m/timer.c b/core/cortex-m/timer.c
new file mode 100644
index 0000000..9a32d6f
--- /dev/null
+++ b/core/cortex-m/timer.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Timer module for Chrome EC operating system */
+
+#include <stdint.h>
+
+#include "task.h"
+#include "timer.h"
+#include "hwtimer.h"
+#include "atomic.h"
+#include "console.h"
+#include "uart.h"
+#include "util.h"
+
+/* high word of the 64-bit timestamp counter  */
+static volatile uint32_t clksrc_high;
+
+/* bitmap of currently running timers */
+static uint32_t timer_running = 0;
+
+/* deadlines of all timers */
+static timestamp_t timer_deadline[TASK_ID_COUNT];
+
+static uint32_t next_deadline = 0xffffffff;
+
+/* Hardware timer routine IRQ number */
+static int timer_irq;
+
+static void expire_timer(task_id_t tskid)
+{
+	/* we are done with this timer */
+	atomic_clear(&timer_running, 1<<tskid);
+	/* wake up the taks waiting for this timer */
+	task_send_msg(tskid, TASK_ID_TIMER, 0);
+}
+
+/**
+ * Search the next deadline and program it in the timer hardware
+ *
+ * overflow: if true, the 32-bit counter as overflowed since the last call.
+ */
+void process_timers(int overflow)
+{
+	uint32_t check_timer, running_t0;
+	timestamp_t next;
+	timestamp_t now;
+
+	if (overflow)
+		clksrc_high++;
+
+reprocess_timers:
+	next.val = 0xffffffffffffffff;
+	now = get_time();
+	do {
+		/* read atomically the current state of timer running */
+		check_timer = running_t0 = timer_running;
+		while (check_timer) {
+			int tskid = 31 - __builtin_clz(check_timer);
+
+			/* timer has expired ? */
+			if (timer_deadline[tskid].val < now.val)
+				expire_timer(tskid);
+			else if ((timer_deadline[tskid].le.hi == now.le.hi) &&
+			         (timer_deadline[tskid].le.lo < next.le.lo))
+				next.val = timer_deadline[tskid].val;
+
+			check_timer &= ~(1 << tskid);
+		}
+	/* if there is a new timer, let's retry */
+	} while (timer_running & ~running_t0);
+
+	if (next.le.hi == 0xffffffff) {
+		/* no deadline to set */
+		__hw_clock_event_clear();
+		next_deadline = 0xffffffff;
+		return;
+	}
+
+	if (next.val <= get_time().val)
+		goto reprocess_timers;
+	__hw_clock_event_set(next.le.lo);
+	next_deadline = next.le.lo;
+	//TODO narrow race: deadline might have been reached before
+}
+
+void udelay(unsigned us)
+{
+	timestamp_t deadline = get_time();
+
+	deadline.val += us;
+	while (get_time().val < deadline.val) {}
+}
+
+int timer_arm(timestamp_t tstamp, task_id_t tskid)
+{
+	ASSERT(tskid < TASK_ID_COUNT);
+
+	if (timer_running & (1<<tskid))
+		return EC_ERROR_BUSY;
+
+	timer_deadline[tskid] = tstamp;
+	atomic_or(&timer_running, 1<<tskid);
+
+	/* modify the next event if needed */
+	if ((tstamp.le.hi < clksrc_high) ||
+	    ((tstamp.le.hi == clksrc_high) && (tstamp.le.lo <= next_deadline)))
+		task_trigger_irq(timer_irq);
+
+	return EC_SUCCESS;
+}
+
+int timer_cancel(task_id_t tskid)
+{
+	ASSERT(tskid < TASK_ID_COUNT);
+
+	atomic_clear(&timer_running, 1<<tskid);
+	/* don't bother about canceling the interrupt:
+	 * it would be slow, just do it on the next IT
+	 */
+
+	return EC_SUCCESS;
+}
+
+
+void usleep(unsigned us)
+{
+	uint32_t evt = 0;
+	ASSERT(us);
+	do {
+		evt |= task_wait_msg(us);
+	} while (!(evt & (1 << TASK_ID_TIMER)));
+	/* re-queue other events which happened in the meanwhile */
+	if (evt)
+		atomic_or(task_get_event_bitmap(task_get_current()),
+		          evt & ~(1 << TASK_ID_TIMER));
+}
+
+
+timestamp_t get_time(void)
+{
+	timestamp_t ts;
+	ts.le.hi = clksrc_high;
+	ts.le.lo = __hw_clock_source_read();
+	if (ts.le.hi != clksrc_high) {
+		ts.le.hi = clksrc_high;
+		ts.le.lo = __hw_clock_source_read();
+	}
+	return ts;
+}
+
+
+static int command_wait(int argc, char **argv)
+{
+	if (argc < 2)
+		return EC_ERROR_INVAL;
+
+	udelay(atoi(argv[1]) * 1000);
+
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(waitms, command_wait);
+
+
+static int command_get_time(int argc, char **argv)
+{
+	timestamp_t ts = get_time();
+	uart_printf("Time: 0x%08x%08x us\n", ts.le.hi, ts.le.lo);
+	return EC_SUCCESS;
+
+}
+DECLARE_CONSOLE_COMMAND(gettime, command_get_time);
+
+
+int command_timer_info(int argc, char **argv)
+{
+	timestamp_t ts = get_time();
+	int tskid;
+
+	uart_printf("Time:     0x%08x%08x us\n"
+	            "Deadline: 0x%08x%08x us\n"
+	            "Active timers:\n",
+	            ts.le.hi, ts.le.lo, clksrc_high,
+		    __hw_clock_event_get());
+	for (tskid = 0; tskid < TASK_ID_COUNT; tskid++) {
+		if (timer_running & (1<<tskid))
+			uart_printf("Tsk %d tmr 0x%08x%08x\n", tskid,
+			            timer_deadline[tskid].le.hi,
+			            timer_deadline[tskid].le.lo);
+	}
+	return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(timerinfo, command_timer_info);
+
+
+int timer_init(void)
+{
+	BUILD_ASSERT(TASK_ID_COUNT < sizeof(timer_running) * 8);
+
+	timer_irq = __hw_clock_source_init();
+
+	return EC_SUCCESS;
+}
diff --git a/include/adc.h b/include/adc.h
new file mode 100644
index 0000000..3288e90
--- /dev/null
+++ b/include/adc.h
@@ -0,0 +1,36 @@
+/* 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"
+#include "board.h"
+#include "gpio.h"
+
+/* forward declaration */
+enum adc_channel;
+
+/* Data structure to define ADC channels. */
+struct adc_t
+{
+	const char* name;
+	int sequencer;
+	int factor_mul;
+	int factor_div;
+	int shift;
+	int channel;
+	int flag;
+};
+
+/* Initializes the module. */
+int adc_init(void);
+
+/* Read ADC channel. */
+int adc_read_channel(enum adc_channel ch);
+
+#endif  /* __CROS_EC_ADC_H */
diff --git a/include/charger.h b/include/charger.h
new file mode 100644
index 0000000..19184c5
--- /dev/null
+++ b/include/charger.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Charger/battery debug command module for Chrome EC */
+
+#ifndef __CROS_EC_CHARGER_H
+#define __CROS_EC_CHARGER_H
+
+#include "common.h"
+
+/* Charger infomation
+ * voltage unit: mV
+ * current unit: mA
+ */
+struct charger_info {
+	const char *name;
+	uint16_t voltage_max;
+	uint16_t voltage_min;
+	uint16_t voltage_step;
+	uint16_t current_max;
+	uint16_t current_min;
+	uint16_t current_step;
+	uint16_t input_current_max;
+	uint16_t input_current_min;
+	uint16_t input_current_step;
+};
+
+/* Initializes the charger, with AC input on and battery
+ * charging off. */
+int charger_init(void);
+
+/* Get charger information. */
+const struct charger_info *charger_get_info(void);
+
+/* Get smart battery charger status. Supported flags:
+ *     CHARGER_CHARGE_INHIBITED
+ *     CHARGER_LEVEL_2
+ */
+int charger_get_status(int *status);
+
+/* Set smart battery charger mode. Supported mode(s):
+ *     CHARGER_FLAG_INHIBIT_CHARGE
+ */
+int charger_set_mode(int mode);
+
+/* Get/set charge current limit in mA */
+int charger_get_current(int *current);
+int charger_set_current(int current);
+
+/* Get/set charge voltage limit in mV */
+int charger_get_voltage(int *voltage);
+int charger_set_voltage(int voltage);
+
+#endif /* __CROS_EC_CHARGER_H */
+
diff --git a/include/chip_temp_sensor.h b/include/chip_temp_sensor.h
new file mode 100644
index 0000000..22c3587
--- /dev/null
+++ b/include/chip_temp_sensor.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Temperature sensor module for LM4 chip */
+
+#ifndef __CHIP_TEMP_SENSOR_H
+#define __CHIP_TEMP_SENSOR_H
+
+struct temp_sensor_t;
+
+/* Temperature reading function. Input pointer to a sensor in temp_sensors.
+ * Return temperature in K.
+ */
+int chip_temp_sensor_read(const struct temp_sensor_t* sensor);
+
+int chip_temp_sensor_init(void);
+
+#endif /* __CHIP_TEMP_SENSOR_H */
diff --git a/include/clock.h b/include/clock.h
new file mode 100644
index 0000000..c400867
--- /dev/null
+++ b/include/clock.h
@@ -0,0 +1,14 @@
+/* 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.
+ */
+
+/*  Clocks and power management settings */
+
+#ifndef __CLOCK_H
+#define __CLOCK_H
+
+/* set the CPU clocks and PLLs */
+int clock_init(void);
+
+#endif  /* __CLOCK_H */
diff --git a/include/common.h b/include/common.h
new file mode 100644
index 0000000..ceb2bc8
--- /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 0000000..4aeca1c
--- /dev/null
+++ b/include/console.h
@@ -0,0 +1,36 @@
+/* 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);
+};
+
+
+/* Initializes the console module. */
+int console_init(void);
+
+
+/* Called by UART when a line of input is pending. */
+void console_has_input(void);
+
+/* Register a console command handler */
+#define DECLARE_CONSOLE_COMMAND(name, routine)			\
+	static const char __con_cmd_label_##name[] = #name;	\
+	const struct console_command __con_cmd_##name		\
+		__attribute__((section(".rodata.cmds")))	\
+		= {__con_cmd_label_##name, routine}
+
+#endif  /* __CROS_EC_CONSOLE_H */
diff --git a/include/eeprom.h b/include/eeprom.h
new file mode 100644
index 0000000..70290e1
--- /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 0000000..38944e4
--- /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 0000000..5efc7e2
--- /dev/null
+++ b/include/flash_commands.h
@@ -0,0 +1,31 @@
+/* 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);
+enum lpc_status flash_command_wp_enable(uint8_t *data);
+enum lpc_status flash_command_wp_get_state(uint8_t *data);
+enum lpc_status flash_command_wp_set_range(uint8_t *data);
+enum lpc_status flash_command_wp_get_range(uint8_t *data);
+#ifdef SUPPORT_CHECKSUM
+enum lpc_status flash_command_checksum(uint8_t *data);
+#endif
+
+
+#endif  /* __CROS_EC_FLASH_COMMANDS_H */
diff --git a/include/gpio.h b/include/gpio.h
new file mode 100644
index 0000000..4a35134
--- /dev/null
+++ b/include/gpio.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* GPIO module for Chrome EC */
+
+#ifndef __CROS_EC_GPIO_H
+#define __CROS_EC_GPIO_H
+
+#include "board.h"  /* For board-dependent enum gpio_signal list */
+#include "common.h"
+
+
+/* Flag definitions for gpio_info. */
+#define GPIO_OUTPUT      0x0001  /* Output */
+#define GPIO_PULL        0x0002  /* Input with on-chip pullup/pulldown */
+#define GPIO_HIGH        0x0004  /* If GPIO_OUTPUT, default high; if GPIO_PULL,
+				  * pull up (otherwise default low / pull
+				  * down) */
+#define GPIO_INT_RISING  0x0010  /* Interrupt on rising edge */
+#define GPIO_INT_FALLING 0x0020  /* Interrupt on falling edge */
+#define GPIO_INT_BOTH    0x0040  /* Interrupt on both edges */
+#define GPIO_INT_LOW     0x0080  /* Interrupt on low level */
+#define GPIO_INT_HIGH    0x0100  /* Interrupt on high level */
+/* Common flag combinations */
+#define GPIO_OUT_LOW     GPIO_OUTPUT
+#define GPIO_OUT_HIGH    (GPIO_OUTPUT | GPIO_HIGH)
+#define GPIO_PULL_DOWN   GPIO_PULL
+#define GPIO_PULL_UP     (GPIO_PULL | GPIO_HIGH)
+#define GPIO_INT_EDGE    (GPIO_INT_RISING | GPIO_INT_FALLING | GPIO_INT_BOTH)
+#define GPIO_INT_LEVEL   (GPIO_INT_LOW | GPIO_INT_HIGH)
+#define GPIO_INT_ANY     (GPIO_INT_EDGE | GPIO_INT_LEVEL)
+/* Note that if no flags are present, the signal is a high-Z input */
+
+/* GPIO signal definition structure, for use by board.c */
+struct gpio_info {
+	const char *name;
+	int port;         /* Port (LM4_GPIO_*) */
+	int mask;         /* Bitmask on that port (0x01 - 0x80; 0x00 =
+			   * signal not implemented) */
+	uint32_t flags;   /* Flags (GPIO_*) */
+	/* Interrupt handler.  If non-NULL, and the signal's interrupt is
+	 * enabled, this will be called in the context of the GPIO interrupt
+	 * handler. */
+	void (*irq_handler)(enum gpio_signal signal);
+};
+
+/* Macro for signals which don't exist */
+#define GPIO_SIGNAL_NOT_IMPLEMENTED(name) {name, LM4_GPIO_A, 0, 0, NULL}
+
+
+/* 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);
+
+/* Gets the current value of a signal (0=low, 1=hi). */
+int gpio_get_level(enum gpio_signal signal);
+
+/* Sets the current value of a signal.  Returns error if the signal is
+ * not supported or is an input signal. */
+int gpio_set_level(enum gpio_signal signal, int value);
+
+/* Enables interrupts for the signal.  The signal must have been defined with
+ * an interrupt handler.  Normally called by the module which handles the
+ * interrupt, once it's ready to start processing interrupts. */
+int gpio_enable_interrupt(enum gpio_signal signal);
+
+/* Set alternate function <func> for GPIO <port> (LM4_GPIO_*) and <mask>.  If
+ * func==0, configures the specified GPIOs for normal GPIO operation.
+ *
+ * This is intended for use by other modules' configure_gpio() functions. */
+void gpio_set_alternate_function(int port, int mask, int func);
+
+#endif  /* __CROS_EC_GPIO_H */
diff --git a/include/host_command.h b/include/host_command.h
new file mode 100644
index 0000000..9c2dda2
--- /dev/null
+++ b/include/host_command.h
@@ -0,0 +1,17 @@
+/* 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"
+
+/* Called by LPC module when a command is written to one of the
+   command slots (0=kernel, 1=user). */
+void host_command_received(int slot, int command);
+
+#endif  /* __CROS_EC_HOST_COMMAND_H */
diff --git a/include/hwtimer.h b/include/hwtimer.h
new file mode 100644
index 0000000..384e361
--- /dev/null
+++ b/include/hwtimer.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Hardware timer driver API */
+
+#ifndef __EC_HWTIMER_H
+#define __EC_HWTIMER_H
+
+/**
+ * Programs when the next timer should fire an interrupt.
+ * deadline: timestamp of the event.
+ */
+void __hw_clock_event_set(uint32_t deadline);
+
+/* Returns the timestamp of the next programed event */
+uint32_t __hw_clock_event_get(void);
+
+/* Cancel the next event programed by __hw_clock_event_set */
+void __hw_clock_event_clear(void);
+
+/* Returns the value of the free-running counter used as clock. */
+uint32_t __hw_clock_source_read(void);
+
+/**
+ * Initializes the hardware timer used to provide clock services.
+ *
+ * It returns the IRQ number of the timer routine.
+ */
+int __hw_clock_source_init(void);
+
+/**
+ * Searches the next deadline and program it in the timer hardware.
+ *
+ * overflow: if true, the 32-bit counter as overflowed since the last call.
+ *
+ * This function is exported from the common timers code as an helper for the
+ * hardware timer interrupt routine.
+ */
+void process_timers(int overflow);
+
+#endif  /* __EC_HWTIMER_H */
diff --git a/include/i2c.h b/include/i2c.h
new file mode 100644
index 0000000..34076b1
--- /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/i8042.h b/include/i8042.h
new file mode 100644
index 0000000..c3d858b
--- /dev/null
+++ b/include/i8042.h
@@ -0,0 +1,125 @@
+/* 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.
+ *
+ * i8042.h -- defines the interface between EC core and the EC lib, which
+ * talks to the LPC driver (on the EC side) peering to the keyboard driver
+ * (on the host side).
+ *
+ * The EC lib implements this interface.
+ */
+
+#ifndef __INTERFACE_I8042_H
+#define __INTERFACE_I8042_H
+
+#include "common.h"
+
+
+/* Keyboard command definition. Modified from Linux kernel atkbd.c file. */
+/* port 0x60 */
+#define I8042_CMD_MOUSE_1_1     0xe6
+#define I8042_CMD_MOUSE_2_1     0xe7
+#define I8042_CMD_MOUSE_RES     0xe8  /* dup to I8042_CMD_OK_GETID */
+#define I8042_CMD_GET_MOUSE     0xe9
+#define I8042_CMD_SETLEDS       0xed
+#define I8042_CMD_DIAG_ECHO     0xee
+#define I8042_CMD_GSCANSET      0xf0
+#define I8042_CMD_SSCANSET      0xf0
+#define I8042_CMD_GETID         0xf2
+#define I8042_CMD_SETREP        0xf3
+#define I8042_CMD_ENABLE        0xf4
+#define I8042_CMD_RESET_DIS     0xf5
+#define I8042_CMD_RESET_DEF     0xf6
+#define I8042_CMD_ALL_TYPEM     0xf7
+#define I8042_CMD_SETALL_MB     0xf8
+#define I8042_CMD_SETALL_MBR    0xfa
+#define I8042_CMD_SET_A_KEY_T   0xfb
+#define I8042_CMD_SET_A_KEY_MR  0xfc
+#define I8042_CMD_SET_A_KEY_M   0xfd
+#define I8042_CMD_RESET_BAT     0xff
+#define I8042_CMD_RESEND        0xfe
+#define I8042_CMD_EX_ENABLE     0xea
+#define I8042_CMD_EX_SETLEDS    0xeb
+#define I8042_CMD_OK_GETID      0xe8
+
+/* port 0x64 */
+#define I8042_READ_CMD_BYTE     0x20
+#define I8042_READ_CTL_RAM      0x21
+#define I8042_READ_CTL_RAM_END  0x3f
+#define I8042_WRITE_CMD_BYTE    0x60  /* expect a byte on port 0x60 */
+#define I8042_WRITE_CTL_RAM     0x61
+#define I8042_WRITE_CTL_RAM_END 0x7f
+#define I8042_ROUTE_AUX0        0x90
+#define I8042_ROUTE_AUX1        0x91
+#define I8042_ROUTE_AUX2        0x92
+#define I8042_ROUTE_AUX3        0x93
+#define I8042_ENA_PASSWORD      0xa6
+#define I8042_DIS_MOUSE         0xa7
+#define I8042_ENA_MOUSE         0xa8
+#define I8042_TEST_MOUSE        0xa9
+#define I8042_RESET_SELF_TEST   0xaa
+#define I8042_TEST_KB_PORT      0xab
+#define I8042_DIS_KB            0xad
+#define I8042_ENA_KB            0xae
+#define I8042_ECHO_MOUSE        0xd3  /* expect a byte on port 0x60 */
+#define I8042_SEND_TO_MOUSE     0xd4  /* expect a byte on port 0x60 */
+#define I8042_PULSE_START       0xf0
+#define I8042_PULSE_END         0xfd
+#define I8042_SYSTEM_RESET      0xfe
+
+/* port 0x60 return value */
+#define I8042_RET_BAT           0xaa
+#define I8042_RET_EMUL0         0xe0
+#define I8042_RET_EMUL1         0xe1
+#define I8042_RET_ECHO          0xee
+#define I8042_RET_RELEASE       0xf0
+#define I8042_RET_HANJA         0xf1
+#define I8042_RET_HANGEUL       0xf2
+#define I8042_RET_ACK           0xfa
+#define I8042_RET_TEST_FAIL     0xfc
+#define I8042_RET_INTERNAL_FAIL 0xfd
+#define I8042_RET_NAK           0xfe
+#define I8042_RET_ERR           0xff
+
+/* port 64 - command byte bits */
+#define I8042_XLATE             (1 << 6)
+#define I8042_AUX_DIS           (1 << 5)
+#define I8042_KBD_DIS           (1 << 4)
+#define I8042_SYS_FLAG          (1 << 2)
+#define I8042_ENIRQ12           (1 << 1)
+#define I8042_ENIRQ1            (1 << 0)
+
+
+void i8042_init(void);
+
+
+/* common/i8042.c implements this function, which is called by lpc.c
+ * when an i8042 command/data from host side appears.
+ *
+ * Actually the a pair of data/command write would trigger 2 LPc interrupts.
+ * So, we will queue the data byte first, then call keyboard routine after
+ * receiving the command byte.
+ */
+void i8042_receives_data(int data);
+void i8042_receives_command(int cmd);
+
+
+/* Called by common/keyboard.c when the host doesn't want to receive
+ * keyboard IRQ.
+ */
+void i8042_enable_keyboard_irq(void);
+void i8042_disable_keyboard_irq(void);
+
+
+/* Send the scan code to the host. The EC lib will push the scan code bytes
+ * to host via port 0x60 and assert the IBF flag to trigger an interrupt.
+ * The EC lib must queue them if the host cannot read the previous byte away
+ * in time.
+ *
+ * Return:
+ *   EC_ERROR_BUFFER_FULL -- the queue to host is full. Try again?
+ */
+enum ec_error_list i8042_send_to_host(int len, uint8_t *to_host);
+
+
+#endif  /* __INTERFACE_I8042_H */
diff --git a/include/jtag.h b/include/jtag.h
new file mode 100644
index 0000000..6dd83a9
--- /dev/null
+++ b/include/jtag.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* JTAG interface for Chrome EC */
+
+#ifndef __CROS_EC_JTAG_H
+#define __CROS_EC_JTAG_H
+
+#include "common.h"
+
+/* Pre-initializes the module. */
+int jtag_pre_init(void);
+
+#endif  /* __CROS_EC_JTAG_H */
diff --git a/include/keyboard.h b/include/keyboard.h
new file mode 100644
index 0000000..98394e6
--- /dev/null
+++ b/include/keyboard.h
@@ -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.
+ *
+ * The functions implemented by keyboard component of EC core.
+ */
+
+#ifndef __INCLUDE_KEYBOARD_H
+#define __INCLUDE_KEYBOARD_H
+
+#include "common.h"
+
+/***************************************************************************/
+/* Functions exported by common/keyboard.c.
+ */
+
+#define MAX_SCAN_CODE_LEN 4
+
+enum scancode_set_list {
+  SCANCODE_GET_SET = 0,
+  SCANCODE_SET_1,
+  SCANCODE_SET_2,
+  SCANCODE_SET_3,
+  SCANCODE_MAX = SCANCODE_SET_3,
+};
+
+
+/* The initialize code of keyboard lib. Called by core main. */
+enum ec_error_list keyboard_init(void);
+
+
+/* Called by keyboard scan code once any key state change (after de-bounce),
+ *
+ * This function will look up matrix table and convert scancode host.
+ */
+void keyboard_state_changed(int row, int col, int is_pressed);
+
+
+/* Handle the port 0x60 writes from host.
+ *
+ * This functions returns the number of bytes stored in *output buffer.
+ */
+int handle_keyboard_data(uint8_t data, uint8_t *output);
+
+/* Handle the port 0x64 writes from host.
+ *
+ * This functions returns the number of bytes stored in *output buffer.
+ * BUT theose bytes will appear at port 0x60.
+ */
+int handle_keyboard_command(uint8_t command, uint8_t *output);
+
+
+/* Register the board-specific keyboard matrix translation function.
+ * The callback function accepts col/row and returns the scan code.
+ *
+ * Note that *scan_code must be at least 4 bytes long to store maximum
+ * possible sequence.
+ */
+typedef enum ec_error_list (*keyboard_matrix_callback)(
+    int8_t row, int8_t col, int8_t pressed,
+    enum scancode_set_list code_set, uint8_t *scan_code, int32_t* len);
+
+enum ec_error_list keyboard_matrix_register_callback(
+    int8_t row_num, int8_t col_num,
+    keyboard_matrix_callback callback);
+
+
+/***************************************************************************/
+/* Below is the interface with the underlying chip-dependent code.
+ */
+#define MAX_KEYBOARD_MATRIX_ROWS 8
+#define MAX_KEYBOARD_MATRIX_COLS 16
+
+typedef void (*keyboard_callback)(int row, int col, int is_pressed);
+
+/* Registers a callback function to underlayer EC lib. So that any key state
+ * change would notify the upper EC main code.
+ *
+ * Note that passing NULL removes any previously registered callback.
+ */
+enum ec_error_list keyboard_register_callback(keyboard_callback cb);
+
+/* Asks the underlayer EC lib what keys are pressed right now.
+ *
+ * Sets bit_array to a debounced array of which keys are currently pressed,
+ * where a 1-bit means the key is pressed. For example, if only row=2 col=3
+ * is pressed, it would set bit_array to {0, 0, 0x08, 0, ...}
+ *
+ * bit_array must be at least MAX_KEYBOARD_MATRIX_COLS bytes long.
+ */
+enum ec_error_list keyboard_get_state(uint8_t *bit_array);
+
+
+#endif  /* __INCLUDE_KEYBOARD_H */
diff --git a/include/keyboard_scan.h b/include/keyboard_scan.h
new file mode 100644
index 0000000..8bc10c2
--- /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 0000000..d87d9ba
--- /dev/null
+++ b/include/lpc.h
@@ -0,0 +1,50 @@
+/* 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"
+
+
+/* Manually generates an IRQ to host.
+ * Note that the irq_num == 0 would set the AH bit (Active High).
+ */
+void lpc_manual_irq(int irq_num);
+
+
+/* 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().  <slot> is 0 for kernel-originated
+ * commands, 1 for usermode-originated commands. */
+uint8_t *lpc_get_host_range(int slot);
+
+/* Sends a response to a host command.  The bottom 4 bits of <status>
+ * are sent in the status byte.  <slot> is 0 for kernel-originated
+ * commands, 1 for usermode-originated commands. */
+void lpc_send_host_response(int slot, int status);
+
+/* Return true if the TOH is still set */
+int lpc_keyboard_has_char(void);
+
+/* Send a byte to host via port 0x60 and asserts IRQ if specified. */
+void lpc_keyboard_put_char(uint8_t chr, int send_irq);
+
+/* Returns non-zero if the COMx interface has received a character. */
+int lpc_comx_has_char(void);
+
+/* Returns the next character pending on the COMx interface. */
+int lpc_comx_get_char(void);
+
+/* Puts a character to the COMx LPC interface. */
+void lpc_comx_put_char(int c);
+
+#endif  /* __CROS_EC_LPC_H */
diff --git a/include/lpc_commands.h b/include/lpc_commands.h
new file mode 100644
index 0000000..151256c
--- /dev/null
+++ b/include/lpc_commands.h
@@ -0,0 +1,235 @@
+/* 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>
+
+
+/* During the development stage, the LPC bus has high error bit rate.
+ * Using checksum can detect the error and trigger re-transmit.
+ * FIXME: remove this after mass production.
+ */
+#define SUPPORT_CHECKSUM
+
+
+/* I/O addresses for LPC commands */
+#define EC_LPC_ADDR_KERNEL_DATA   0x62
+#define EC_LPC_ADDR_KERNEL_CMD    0x66
+#define EC_LPC_ADDR_KERNEL_PARAM 0x800
+#define EC_LPC_ADDR_USER_DATA    0x200
+#define EC_LPC_ADDR_USER_CMD     0x204
+#define EC_LPC_ADDR_USER_PARAM   0x900
+#define EC_LPC_PARAM_SIZE          256  /* Size of param areas in bytes */
+
+/* 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));
+
+/* Flashmap offset */
+#define EC_LPC_COMMAND_FLASH_GET_FLASHMAP 0x14
+struct lpc_response_flash_flashmap {
+	uint32_t offset;   /* Flashmap offset */
+} __attribute__ ((packed));
+
+/* Enable/disable flash write protect */
+#define EC_LPC_COMMAND_FLASH_WP_ENABLE 0x15
+struct lpc_params_flash_wp_enable {
+	uint32_t enable_wp;
+} __attribute__ ((packed));
+
+/* Get flash write protection commit state */
+#define EC_LPC_COMMAND_FLASH_WP_GET_STATE 0x16
+struct lpc_response_flash_wp_enable {
+	uint32_t enable_wp;
+} __attribute__ ((packed));
+
+/* Set/get flash write protection range */
+#define EC_LPC_COMMAND_FLASH_WP_SET_RANGE 0x17
+struct lpc_params_flash_wp_range {
+	/* Byte offset aligned to info.protect_block_size */
+	uint32_t offset;
+	/* Size should be multiply of info.protect_block_size */
+	uint32_t size;
+} __attribute__ ((packed));
+
+#define EC_LPC_COMMAND_FLASH_WP_GET_RANGE 0x18
+struct lpc_response_flash_wp_range {
+	uint32_t offset;
+	uint32_t size;
+} __attribute__ ((packed));
+
+/* Read flash write protection GPIO pin */
+#define EC_LPC_COMMAND_FLASH_WP_GET_GPIO 0x19
+struct lpc_params_flash_wp_gpio {
+	uint32_t pin_no;
+} __attribute__ ((packed));
+struct lpc_response_flash_wp_gpio {
+	uint32_t value;
+} __attribute__ ((packed));
+
+#ifdef SUPPORT_CHECKSUM
+/* Checksum a range of flash datq */
+#define EC_LPC_COMMAND_FLASH_CHECKSUM 0x1f
+struct lpc_params_flash_checksum {
+	uint32_t offset;   /* Byte offset to read */
+	uint32_t size;     /* Size to read in bytes */
+} __attribute__ ((packed));
+struct lpc_response_flash_checksum {
+	uint8_t checksum;
+} __attribute__ ((packed));
+#define BYTE_IN(sum, byte) do {  \
+		sum = (sum << 1) | (sum >> 7);  \
+		sum ^= (byte ^ 0x53);  \
+	} while (0)
+#endif  /* SUPPORT_CHECKSUM */
+
+/*****************************************************************************/
+/* PWM commands */
+
+/* Get fan RPM */
+#define EC_LPC_COMMAND_PWM_GET_FAN_RPM 0x20
+struct lpc_response_pwm_get_fan_rpm {
+	uint32_t rpm;
+} __attribute__ ((packed));
+
+/* Set target fan RPM */
+#define EC_LPC_COMMAND_PWM_SET_FAN_TARGET_RPM 0x21
+struct lpc_params_pwm_set_fan_target_rpm {
+	uint32_t rpm;
+} __attribute__ ((packed));
+
+
+/*****************************************************************************/
+/* Temperature sensor commands */
+
+/* Get temperature readings */
+#define EC_LPC_COMMAND_TEMP_SENSOR_GET_READINGS 0x30
+struct lpc_params_temp_sensor_get_readings {
+	uint8_t temp_sensor_id;
+} __attribute__ ((packed));
+struct lpc_response_temp_sensor_get_readings {
+	uint32_t value;
+} __attribute__ ((packed));
+
+#endif  /* __CROS_EC_LPC_COMMANDS_H */
diff --git a/include/memory_commands.h b/include/memory_commands.h
new file mode 100644
index 0000000..7967a63
--- /dev/null
+++ b/include/memory_commands.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.
+ */
+
+/* Memory commands for Chrome EC */
+
+#ifndef __CROS_EC_MEMORY_COMMANDS_H
+#define __CROS_EC_MEMORY_COMMANDS_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int memory_commands_init(void);
+
+#endif  /* __CROS_EC_MEMORY_COMMANDS_H */
diff --git a/include/port80.h b/include/port80.h
new file mode 100644
index 0000000..060ee7b
--- /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/power_button.h b/include/power_button.h
new file mode 100644
index 0000000..c6cf4f4
--- /dev/null
+++ b/include/power_button.h
@@ -0,0 +1,24 @@
+/* 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 button module for Chrome EC */
+
+#ifndef __CROS_EC_POWER_BUTTON_H
+#define __CROS_EC_POWER_BUTTON_H
+
+#include "common.h"
+#include "gpio.h"
+
+/* Initializes the module. */
+int power_button_init(void);
+
+/* Interrupt handler for the power button and lid switch.  Passed the signal
+ * which triggered the interrupt. */
+void power_button_interrupt(enum gpio_signal signal);
+
+/* Power button task */
+void power_button_task(void);
+
+#endif  /* __CROS_EC_POWER_BUTTON_H */
diff --git a/include/powerdemo.h b/include/powerdemo.h
new file mode 100644
index 0000000..78acfc0
--- /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 0000000..50534f2
--- /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/pwm_commands.h b/include/pwm_commands.h
new file mode 100644
index 0000000..31f397c
--- /dev/null
+++ b/include/pwm_commands.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.
+ */
+
+/* PWM commands for Chrome EC */
+
+#ifndef __CROS_EC_PWM_COMMANDS_H
+#define __CROS_EC_PWM_COMMANDS_H
+
+#include "common.h"
+#include "lpc_commands.h"
+
+/* Host command handlers. */
+enum lpc_status pwm_command_get_fan_rpm(uint8_t *data);
+enum lpc_status pwm_command_set_fan_target_rpm(uint8_t *data);
+
+
+#endif  /* __CROS_EC_PWM_COMMANDS_H */
diff --git a/include/shared_mem.h b/include/shared_mem.h
new file mode 100644
index 0000000..14b6613
--- /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 0000000..23607cf
--- /dev/null
+++ b/include/system.h
@@ -0,0 +1,103 @@
+/* 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);
+
+/* Record the cause of the last reset. */
+void system_set_reset_cause(enum system_reset_cause_t cause);
+
+/* 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/task.h b/include/task.h
new file mode 100644
index 0000000..17a83de
--- /dev/null
+++ b/include/task.h
@@ -0,0 +1,124 @@
+/* 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.
+ */
+
+/* Task scheduling / events module for Chrome EC operating system */
+
+#ifndef __EC_TASK_H
+#define __EC_TASK_H
+
+#include "common.h"
+#include "task_id.h"
+
+/**
+ * Return true if we are in interrupt context
+ */
+inline int in_interrupt_context(void);
+
+/**
+ * Send a message to a task and wake it up if it is higher priority than us
+ *
+ * tskid : identifier of the receiver task
+ * from : identifier of the sender of the message
+ * wait : after sending, de-schedule the calling task to wait for the answer
+ *
+ * returns the bitmap of events which have occured.
+ *
+ * Can be called both in interrupt context and task context.
+ */
+uint32_t task_send_msg(task_id_t tskid, task_id_t from, int wait);
+
+/**
+ * Return the identifier of the task currently running
+ *
+ * when called in interrupt context, returns TASK_ID_INVALID
+ */
+task_id_t task_get_current(void);
+
+/**
+ * Return a pointer to the bitmap of received events of the task.
+ */
+uint32_t *task_get_event_bitmap(task_id_t tsk);
+
+/**
+ * Waits for the incoming next message.
+ *
+ * if an event is already pending, it returns it immediatly, else it
+ * de-schedules the calling task and wake up the next one in the priority order
+ *
+ * if timeout_us > 0, it also sets a timer to produce an event after the
+ * specified micro-second duration.
+ *
+ * returns the bitmap of received events (and clear it atomically).
+ */
+uint32_t task_wait_msg(int timeout_us);
+
+/**
+ * Changes the task scheduled after returning from the exception.
+ *
+ * If task_send_msg has been called and has set need_resched flag,
+ * we re-compute which task is running and eventually swap the context
+ * saved on the process stack to restore the new one at exception exit.
+ *
+ * it must be called from interrupt context !
+ * and it is designed to be the last call of the interrupt handler.
+ */
+void task_resched_if_needed(void *excep_return);
+
+/* Initializes tasks and interrupt controller. */
+int task_init(void);
+
+/* Starts task scheduling. */
+int task_start(void);
+
+/* Enables an interrupt. */
+void task_enable_irq(int irq);
+
+/* Disables an interrupt. */
+void task_disable_irq(int irq);
+
+/* Software-triggers an interrupt. */
+void task_trigger_irq(int irq);
+
+struct mutex {
+	uint32_t lock;
+	uint32_t waiters;
+};
+
+/**
+ * try to lock the mutex mtx
+ * and de-schedule the task if it is already locked by another task.
+ *
+ * Should not be used in interrupt context !
+ */
+void mutex_lock(struct mutex *mtx);
+
+/* Release a mutex previously locked by the same task. */
+void mutex_unlock(struct mutex *mtx);
+
+struct irq_priority {
+	uint8_t irq;
+	uint8_t priority;
+};
+
+/* Helper macros to build the IRQ handler name */
+#define IRQ_BUILD_NAME(prefix, irqnum, postfix) prefix ## irqnum ## postfix
+#define IRQ_HANDLER(irqname)  IRQ_BUILD_NAME(irq_,irqname,_handler)
+
+/**
+ * Connects the interrupt handler "routine" to the irq number "irq" and
+ * ensures it is enabled in the interrupt controller with the right priority.
+ */
+#define DECLARE_IRQ(irq, routine, priority)                     \
+	void IRQ_HANDLER(irq)(void)				\
+	{							\
+		void *ret = __builtin_return_address(0);	\
+		routine();					\
+		task_resched_if_needed(ret);			\
+	}							\
+	const struct irq_priority IRQ_BUILD_NAME(prio_, irq, )  \
+	__attribute__((section(".rodata.irqprio")))		\
+			= {irq, priority}
+
+#endif  /* __EC_TASK_H */
diff --git a/include/task_id.h b/include/task_id.h
new file mode 100644
index 0000000..a2fb17c
--- /dev/null
+++ b/include/task_id.h
@@ -0,0 +1,42 @@
+/* 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.
+ */
+
+/* define the task identifier of all compiled tasks */
+
+#ifndef __TASK_ID_H
+#define __TASK_ID_H
+
+/* define the name of the header containing the list of tasks */
+#define STRINGIFY0(name)  #name
+#define STRINGIFY(name)  STRINGIFY0(name)
+#define TASK_LIST STRINGIFY(TASKFILE)
+
+/* Task identifier (8 bits) */
+typedef uint8_t task_id_t;
+
+/**
+ * enumerate all tasks in the priority order
+ *
+ * the identifier of a task can be retrieved using the following constant:
+ * TASK_ID_<taskname> where <taskname> is the first parameter passed to the
+ * TASK macro in the TASK_LIST file.
+ */
+#define TASK(n, r, d) TASK_ID_##n,
+#include TASK_LIST
+enum {
+	TASK_ID_IDLE,
+	/* CONFIG_TASK_LIST is a macro coming from the TASK_LIST file */
+	CONFIG_TASK_LIST
+	/* Number of tasks */
+	TASK_ID_COUNT,
+	/* Special task identifiers */
+	TASK_ID_MUTEX   = 0x1e, /* signal mutex unlocking */
+	TASK_ID_TIMER   = 0x1f, /* message from an expired timer */
+	TASK_ID_CURRENT = 0xfe, /* the currently running task */
+	TASK_ID_INVALID = 0xff  /* unable to find the task */
+};
+#undef TASK
+
+#endif  /* __TASK_ID_H */
diff --git a/include/temp_sensor.h b/include/temp_sensor.h
new file mode 100644
index 0000000..a89dbe7
--- /dev/null
+++ b/include/temp_sensor.h
@@ -0,0 +1,60 @@
+/* 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"
+#include "board.h"
+
+/* "enum temp_sensor_id" must be defined for each board in board.h. */
+struct temp_sensor_t {
+	const char* name;
+	enum temp_sensor_id id;
+	/* Sensor address. Used by read and print functions. */
+	int addr;
+	/* Read sensor value and return temperature in K. */
+	int (*read)(const struct temp_sensor_t* self);
+	/* Print debug info on console. */
+	int (*print)(const struct temp_sensor_t* self);
+};
+
+/* Dummy value to put in "addr" field in temp_sensor_t if we don't need to
+ * specify address.
+ */
+#define TEMP_SENSOR_NO_ADDR 0
+
+/* Dummy value to put in "print" field in temp_sensor_t if we don't have debug
+ * function for a sensor.
+ */
+#define TEMP_SENSOR_NO_PRINT 0
+
+/* 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);
+
+
+#define TMP006_ADDR(PORT,REG) ((PORT << 16) + REG)
+#define TMP006_PORT(ADDR) (ADDR >> 16)
+#define TMP006_REG(ADDR) (ADDR & 0xffff)
+
+/* Read TI TMP006 die temperature sensor. Return temperature in K. */
+int temp_sensor_tmp006_read_die_temp(const struct temp_sensor_t* sensor);
+
+/* Read TI TMP006 object temperature sensor. Return temperature in K. */
+int temp_sensor_tmp006_read_object_temp(const struct temp_sensor_t* sensor);
+
+/* Configure TMP006 DRDY pin. */
+void temp_sensor_tmp006_config(const struct temp_sensor_t* sensor);
+
+/* Print debug messages for TMP006. */
+int temp_sensor_tmp006_print(const struct temp_sensor_t* sensor);
+
+#endif  /* __CROS_EC_TEMP_SENSOR_H */
diff --git a/include/temp_sensor_commands.h b/include/temp_sensor_commands.h
new file mode 100644
index 0000000..6971804
--- /dev/null
+++ b/include/temp_sensor_commands.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.
+ */
+
+/* Temperature sensor commands for Chrome EC */
+
+#ifndef __CROS_EC_TEMP_SENSOR_COMMANDS_H
+#define __CROS_EC_TEMP_SENSOR_COMMANDS_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int temp_sensor_commands_init(void);
+
+/* Host command handlers. */
+enum lpc_status temp_sensor_command_get_readings(uint8_t *data);
+
+#endif  /* __CROS_EC_TEMP_SENSOR_COMMANDS_H */
diff --git a/include/timer.h b/include/timer.h
new file mode 100644
index 0000000..b73ac29
--- /dev/null
+++ b/include/timer.h
@@ -0,0 +1,60 @@
+/* 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.
+ */
+
+/* Timer module for Chrome EC operating system */
+
+#ifndef __EC_TIMER_H
+#define __EC_TIMER_H
+
+#include "common.h"
+#include "task_id.h"
+
+/* Micro-second timestamp. */
+typedef union {
+	uint64_t val;
+	struct {
+		uint32_t lo;
+		uint32_t hi;
+	} le /* little endian words */;
+} timestamp_t;
+
+/* Initializes the Timer module. */
+int timer_init(void);
+
+/**
+ * Launches a one-shot timer.
+ *
+ * tstamp : timestamp in micro-seconds when the timer expires
+ * tskid : identifier of the task owning the timer
+ */
+int timer_arm(timestamp_t tstamp, task_id_t tskid);
+
+/**
+ * Cancels a running timer.
+ *
+ * tskid : identifier of the task owning the timer
+ */
+int timer_cancel(task_id_t tskid);
+
+/**
+ * Busy wait the selected number of micro-seconds
+ */
+void udelay(unsigned us);
+
+/**
+ * Sleep during the selected number of micro-seconds
+ *
+ * The current task will be de-scheduled until the delay expired
+ *
+ * Note: if an event happens before the end of sleep, the function will return.
+ */
+void usleep(unsigned us);
+
+/**
+ * Get the current timestamp from the system timer
+ */
+timestamp_t get_time(void);
+
+#endif  /* __EC_TIMER_H */
diff --git a/include/uart.h b/include/uart.h
new file mode 100644
index 0000000..334ee62
--- /dev/null
+++ b/include/uart.h
@@ -0,0 +1,182 @@
+/* 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? */
+
+/*****************************************************************************/
+/* Hardware UART driver functions */
+
+/* Flushes the transmit FIFO. */
+void uart_tx_flush(void);
+
+/* Returns true if there is room to transmit a character immediatly. */
+int uart_tx_ready(void);
+
+/* Returns true if the UART has character available. */
+int uart_rx_available(void);
+
+/**
+ * Sends a character to the UART data register.
+ * If the transmit FIFO is full, this function blocks until there is space.
+ *
+ * c : byte to send.
+ */
+void uart_write_char(char c);
+
+/**
+ * Reads and returns one char from the UART data register.
+ *
+ * Called when uart_rx_available once returns true.
+ */
+int uart_read_char(void);
+
+/**
+ * Disables all UART related IRQs.
+ *
+ * To avoid concurrent accesses on UART management variables.
+ */
+void uart_disable_interrupt(void);
+
+/* Re-enables UART IRQs. */
+void uart_enable_interrupt(void);
+
+/**
+ * Re-enables the UART transmit interrupt.
+ *
+ * It also forces triggering an interrupt if the hardware doesn't automatically
+ * trigger it when the transmit buffer was filled beforehand.
+ */
+void uart_tx_start(void);
+
+/* Disables the UART transmit interrupt. */
+void uart_tx_stop(void);
+
+/* Returns true if the UART transmit interrupt is disabled */
+int uart_tx_stopped(void);
+
+/**
+ * Helper for UART processing.
+ * Read the input FIFO until empty, then fill the output FIFO until the transmit
+ * buffer is empty or the FIFO full.
+ *
+ * Designed to be called from the driver interrupt handler.
+ */
+void uart_process(void);
+
+
+/*****************************************************************************/
+/* COMx functions */
+
+/* Returns non-zero if ok to put a character via uart_comx_putc(). */
+int uart_comx_putc_ok(void);
+
+/* Puts a character to the COMx UART interface. */
+void uart_comx_putc(int c);
+
+#endif  /* __CROS_EC_UART_H */
diff --git a/include/usb_charge.h b/include/usb_charge.h
new file mode 100644
index 0000000..0b3b7a4
--- /dev/null
+++ b/include/usb_charge.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* USB charging control module for Chrome EC */
+
+#ifndef __CROS_EC_USB_CHARGE_H
+#define __CROS_EC_USB_CHARGE_H
+
+#include "board.h"
+
+enum usb_charge_mode {
+	/* Disable USB port. */
+	USB_CHARGE_MODE_DISABLED,
+	/* Set USB port to be dedicated charging port, auto selecting charging
+	 * schemes. */
+	USB_CHARGE_MODE_CHARGE_AUTO,
+	/* Set USB port to be dedicated charging port following USB Battery
+	 * Charging Specification 1.2. */
+	USB_CHARGE_MODE_CHARGE_BC12,
+	/* Set USB port to be standard downstream port, with current limit set
+	 * to 500mA or 1500mA. */
+	USB_CHARGE_MODE_DOWNSTREAM_500MA,
+	USB_CHARGE_MODE_DOWNSTREAM_1500MA,
+
+	USB_CHARGE_MODE_COUNT
+};
+
+int usb_charge_set_mode(int usb_port_id, enum usb_charge_mode);
+
+int usb_charge_init(void);
+
+#endif  /* __CROS_EC_USB_CHARGE_H */
diff --git a/include/util.h b/include/util.h
new file mode 100644
index 0000000..5a76a66
--- /dev/null
+++ b/include/util.h
@@ -0,0 +1,61 @@
+/* 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.
+ */
+
+/* Various utility functions and macros */
+
+#ifndef __UTIL_H
+#define __UTIL_H
+
+#include <stdint.h>
+
+#include "config.h"
+
+/**
+ * Trigger a compilation failure if the condition
+ * is not verified at build time.
+ */
+#define BUILD_ASSERT(cond) ((void)sizeof(char[1 - 2*!(cond)]))
+
+/**
+ * Trigger a debug exception if the condition
+ * is not verified at runtime.
+ */
+#ifdef CONFIG_DEBUG
+#define ASSERT(cond) do {			\
+		if (!(cond))			\
+			__asm("bkpt");		\
+	} while (0);
+#else
+#define ASSERT(cond)
+#endif
+
+
+/* Standard macros / definitions */
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define NULL ((void *)0)
+
+/**
+ * macros for integer division with various rounding variants
+ * default integer division rounds down.
+ */
+#define DIV_ROUND_UP(x, y) (((x) + ((y) - 1)) / (y))
+#define DIV_ROUND_NEAREST(x, y) (((x) + ((y) / 2)) / (y))
+
+/* Standard library functions */
+int atoi(const char *nptr);
+int isdigit(int c);
+int isspace(int c);
+int isalpha(int c);
+void *memcpy(void *dest, const void *src, int len);
+void *memset(void *dest, int c, int len);
+int strcasecmp(const char *s1, const char *s2);
+int strlen(const char *s);
+int strtoi(const char *nptr, char **endptr, int base);
+char *strzcpy(char *dest, const char *src, int len);
+int tolower(int c);
+
+#endif  /* __UTIL_H */
diff --git a/include/vboot.h b/include/vboot.h
new file mode 100644
index 0000000..430c617
--- /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 0000000..0031339
--- /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/watchdog.h b/include/watchdog.h
new file mode 100644
index 0000000..75a5e0b
--- /dev/null
+++ b/include/watchdog.h
@@ -0,0 +1,21 @@
+/* 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.
+ */
+
+/* Watchdog driver */
+
+#ifndef _WATCHDOG_H
+#define _WATCHDOG_H
+
+/* Reload the watchdog counter */
+void watchdog_reload(void);
+
+/**
+ * Initialize the watchdog
+ * with a reloading period of <period_ms> milliseconds.
+ * It reboots the CPU if the counter has not been reloaded for twice the period.
+ */
+int watchdog_init(int period_ms);
+
+#endif /* _WATCHDOG_H */
diff --git a/include/x86_power.h b/include/x86_power.h
new file mode 100644
index 0000000..ce28e1b
--- /dev/null
+++ b/include/x86_power.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* x86 power module for Chrome EC */
+
+#ifndef __CROS_EC_X86_POWER_H
+#define __CROS_EC_X86_POWER_H
+
+#include "common.h"
+#include "gpio.h"
+
+/* Initializes the module. */
+int x86_power_init(void);
+
+/* Interrupt handler for input GPIOs */
+void x86_power_interrupt(enum gpio_signal signal);
+
+#endif  /* __CROS_EC_X86_POWER_H */
diff --git a/test/build.mk b/test/build.mk
new file mode 100644
index 0000000..7bc5731
--- /dev/null
+++ b/test/build.mk
@@ -0,0 +1,12 @@
+#
+# on-board test binaries build
+#
+
+test-list=hello pingpong timer_calib timer_dos mutex
+#disable: powerdemo
+
+pingpong-y=pingpong.o
+powerdemo-y=powerdemo.o
+timer_calib-y=timer_calib.o
+timer_dos-y=timer_dos.o
+mutex-y=mutex.o
diff --git a/test/hello.py b/test/hello.py
new file mode 100644
index 0000000..a717185
--- /dev/null
+++ b/test/hello.py
@@ -0,0 +1,15 @@
+# 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.
+#
+# Simple test as an example
+#
+
+def test(helper):
+      helper.wait_output("--- Chrome EC initialized! ---")
+      helper.ec_command("version")
+      ro = helper.wait_output("RO version:\s*(?P<ro>\S+)", use_re=True)["ro"]
+      wa = helper.wait_output("RW-A version:\s*(?P<a>\S+)", use_re=True)["a"]
+      wb = helper.wait_output("RW-B version:\s*(?P<b>\S+)", use_re=True)["b"]
+      helper.trace("Version (RO/A/B) %s / %s / %s\n" % (ro, wa, wb))
+      return True # PASS !
diff --git a/test/hello.tasklist b/test/hello.tasklist
new file mode 100644
index 0000000..d0c0002
--- /dev/null
+++ b/test/hello.tasklist
@@ -0,0 +1,18 @@
+/* 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK(n, r, d) where :
+ * 'n' in the name of the task
+ * 'r' in the main routine of the task
+ * 'd' in an opaque parameter passed to the routine at startup
+ */
+#define CONFIG_TASK_LIST \
+	TASK(WATCHDOG, watchdog_task, NULL) \
+	TASK(CONSOLE, console_task, NULL)
diff --git a/test/mutex.c b/test/mutex.c
new file mode 100644
index 0000000..a5b7244
--- /dev/null
+++ b/test/mutex.c
@@ -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.
+ * Copyright 2011 Google Inc.
+ *
+ * Tasks for mutexes basic tests.
+ */
+
+#include "common.h"
+#include "uart.h"
+#include "task.h"
+#include "timer.h"
+
+static struct mutex mtx;
+
+/* Linear congruential pseudo random number generator*/
+static uint32_t prng(uint32_t x)
+{
+	return 22695477 * x + 1;
+}
+
+/* period between 50us and 12.8ms */
+#define PERIOD_US(num) (((num % 256) + 1) * 50)
+/* one of the 3 MTX3x tasks */
+#define RANDOM_TASK(num) (TASK_ID_MTX3C + (num % 3))
+
+int mutex_random_task(void *unused)
+{
+	char letter = 'A'+(TASK_ID_MTX3A - task_get_current());
+	/* wait to be activated */
+
+	while (1) {
+		task_wait_msg(0);
+		uart_printf("%c+\n", letter);
+		mutex_lock(&mtx);
+		uart_printf("%c=\n", letter);
+		task_wait_msg(0);
+		uart_printf("%c-\n", letter);
+		mutex_unlock(&mtx);
+	}
+
+	task_wait_msg(0);
+
+	return EC_SUCCESS;
+}
+
+int mutex_second_task(void *unused)
+{
+	task_id_t id = task_get_current();
+
+	uart_printf("\n[Mutex second task %d]\n", id);
+
+	task_wait_msg(0);
+	uart_printf("MTX2: locking...");
+	mutex_lock(&mtx);
+	uart_printf("done\n");
+	task_send_msg(TASK_ID_MTX1, 1, 0);
+	uart_printf("MTX2: unlocking...\n");
+	mutex_unlock(&mtx);
+
+	task_wait_msg(0);
+
+	return EC_SUCCESS;
+}
+
+int mutex_main_task(void *unused)
+{
+	task_id_t id = task_get_current();
+	uint32_t rdelay = (uint32_t)0x0bad1dea;
+	uint32_t rtask = (uint32_t)0x1a4e1dea;
+	int i;
+
+	uart_printf("\n[Mutex main task %d]\n", id);
+
+	/* --- Lock/Unlock without contention --- */
+	uart_printf("No contention :");
+	mutex_lock(&mtx);
+	mutex_unlock(&mtx);
+	mutex_lock(&mtx);
+	mutex_unlock(&mtx);
+	mutex_lock(&mtx);
+	mutex_unlock(&mtx);
+	uart_printf("done.\n");
+
+	/* --- Serialization to test simple contention --- */
+	uart_printf("Simple contention :\n");
+	/* lock the mutex from the other task */
+	task_send_msg(TASK_ID_MTX2, 1, 1);
+	/* block on the mutex */
+	uart_printf("MTX1: blocking...\n");
+	mutex_lock(&mtx);
+	uart_printf("MTX1: get lock\n");
+	mutex_unlock(&mtx);
+
+	/* --- mass lock-unlocking from several tasks --- */
+	uart_printf("Massive locking/unlocking :\n");
+	for (i = 0; i < 500; i++) {
+		/* Wake up a random task */
+		task_send_msg(RANDOM_TASK(rtask), 1, 0);
+		/* next pseudo random delay */
+		rtask = prng(rtask);
+		/* Wait for a "random" period */
+		task_wait_msg(PERIOD_US(rdelay));
+		/* next pseudo random delay */
+		rdelay = prng(rdelay);
+	}
+
+	uart_printf("Test done.\n");
+	task_wait_msg(0);
+
+	return EC_SUCCESS;
+}
diff --git a/test/mutex.py b/test/mutex.py
new file mode 100644
index 0000000..afd3429
--- /dev/null
+++ b/test/mutex.py
@@ -0,0 +1,25 @@
+# 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.
+#
+# Mutexes test
+#
+
+def test(helper):
+      helper.wait_output("[Mutex main task")
+
+      # 3 locking in a row without contention
+      helper.wait_output("No contention :done.")
+
+      # serialization (simple contention)
+      helper.wait_output("Simple contention :")
+      helper.wait_output("MTX2: locking...done")
+      helper.wait_output("MTX1: blocking...")
+      helper.wait_output("MTX1: get lock")
+
+      # multiple contention
+      helper.wait_output("Massive locking/unlocking :")
+      #TODO check sequence
+      helper.wait_output("Test done.")
+
+      return True # PASS !
diff --git a/test/mutex.tasklist b/test/mutex.tasklist
new file mode 100644
index 0000000..92b2ae7
--- /dev/null
+++ b/test/mutex.tasklist
@@ -0,0 +1,14 @@
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ */
+#define CONFIG_TASK_LIST \
+  TASK(WATCHDOG, watchdog_task, NULL) \
+  TASK(CONSOLE, console_task, NULL) \
+  TASK(MTX3C, mutex_random_task, NULL) \
+  TASK(MTX3B, mutex_random_task, NULL) \
+  TASK(MTX3A, mutex_random_task, NULL) \
+  TASK(MTX2, mutex_second_task, NULL) \
+  TASK(MTX1, mutex_main_task, NULL)
diff --git a/test/pingpong.c b/test/pingpong.c
new file mode 100644
index 0000000..1811506
--- /dev/null
+++ b/test/pingpong.c
@@ -0,0 +1,43 @@
+/* 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.
+ *
+ * Tasks for scheduling test.
+ */
+
+#include "common.h"
+#include "uart.h"
+#include "task.h"
+#include "timer.h"
+
+int TaskAbc(void *data)
+{
+	char letter = (char)(unsigned)data;
+	char string[2] = {letter, '\0' };
+	task_id_t next = task_get_current() + 1;
+	if (next > TASK_ID_TESTC)
+		next = TASK_ID_TESTA;
+
+	uart_printf("\n[starting Task %c]\n", letter);
+
+	while (1) {
+		uart_puts(string);
+		uart_flush_output();
+		task_send_msg(next, TASK_ID_CURRENT, 1);
+	}
+
+	return EC_SUCCESS;
+}
+
+int TaskTick(void *data)
+{
+	uart_set_console_mode(1);
+	uart_printf("\n[starting Task T]\n");
+	/* Print T every tick */
+	while (1) {
+		/* Wait for timer interrupt message */
+		usleep(3000);
+		uart_puts("T\n");
+	}
+}
diff --git a/test/pingpong.py b/test/pingpong.py
new file mode 100644
index 0000000..df7d4b3
--- /dev/null
+++ b/test/pingpong.py
@@ -0,0 +1,27 @@
+# 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.
+#
+# Task scheduling test
+#
+
+import time
+
+# Test during 5s
+DURATION=5
+
+def test(helper):
+      helper.wait_output("[starting Task T]")
+      helper.wait_output("[starting Task C]")
+      helper.wait_output("[starting Task B]")
+      helper.wait_output("[starting Task A]")
+      deadline = time.time() + DURATION
+      count = []
+      while time.time() < deadline:
+          sched = helper.wait_output("(?P<a>(?:ABC){3,200})T", use_re=True,
+                                     timeout=1)["a"]
+          count.append(len(sched) / 3)
+
+      helper.trace("IRQ count %d, cycles count min %d  max %d\n" %
+                   (len(count), min(count), max(count)))
+      return True # PASS !
diff --git a/test/pingpong.tasklist b/test/pingpong.tasklist
new file mode 100644
index 0000000..dd95534
--- /dev/null
+++ b/test/pingpong.tasklist
@@ -0,0 +1,13 @@
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ */
+#define CONFIG_TASK_LIST \
+  TASK(WATCHDOG, watchdog_task, NULL) \
+  TASK(CONSOLE, console_task, NULL) \
+  TASK(TESTA, TaskAbc, (void *)'A') \
+  TASK(TESTB, TaskAbc, (void *)'B') \
+  TASK(TESTC, TaskAbc, (void *)'C') \
+  TASK(TESTT, TaskTick, (void *)'T')
diff --git a/test/powerdemo.c b/test/powerdemo.c
new file mode 100644
index 0000000..c809c1e
--- /dev/null
+++ b/test/powerdemo.c
@@ -0,0 +1,186 @@
+/* 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 */
+
+#include "board.h"
+#include "powerdemo.h"
+#include "task.h"
+#include "timer.h"
+#include "uart.h"
+#include "registers.h"
+
+
+#define US_PER_SECOND 1000000
+/* Divider to get microsecond for the clock */
+#define CLOCKSOURCE_DIVIDER (CPU_CLOCK/US_PER_SECOND)
+
+static volatile enum {
+	POWER_STATE_IDLE = 0,    /* Idle */
+	POWER_STATE_DOWN1,       /* Assert output for 1ms */
+	POWER_STATE_UP1,         /* Deassert output for 1ms */
+	POWER_STATE_DOWN10,      /* Assert output for 10ms */
+	POWER_STATE_UP5,         /* Deassert output for 5ms */
+	POWER_STATE_DOWN15,      /* Assert output for 15ms */
+	POWER_STATE_WAIT,        /* Wait for button to be released */
+	POWER_STATE_DOWN2        /* Assert output for 2ms */
+} state = POWER_STATE_IDLE;
+
+
+/* Stops the timer. */
+static void __stop_timer(void)
+{
+	/* Disable timer A */
+	LM4_TIMER_CTL(7) &= ~0x01;
+	/* Clear any pending interrupts */
+	LM4_TIMER_ICR(7) = LM4_TIMER_RIS(7);
+}
+
+
+/* Starts the timer with the specified delay.  If the timer is already
+ * started, resets it. */
+static void __start_timer(int usec)
+{
+	/* Stop the timer, if it was started */
+	__stop_timer();
+	/* Set the delay, counting function overhead */
+	LM4_TIMER_TAILR(7) = usec;
+	/* Start timer A */
+	LM4_TIMER_CTL(7) |= 0x01;
+}
+
+
+static void __set_state(int new_state, int pin_value, int timeout)
+{
+	LM4_GPIO_DATA(LM4_GPIO_D, 0x08) = (pin_value ? 0x08 : 0);
+	if (timeout)
+		__start_timer(timeout);
+	else
+		__stop_timer();
+	state = new_state;
+}
+
+
+int power_demo_init(void)
+{
+	volatile uint32_t scratch  __attribute__((unused));
+
+	/* Set up TIMER1 as our state timer */
+	/* Enable TIMER1 clock */
+	LM4_SYSTEM_RCGCWTIMER |= 0x02;
+	/* wait 3 clock cycles before using the module */
+	scratch = LM4_SYSTEM_RCGCWTIMER;
+	/* Ensure timer is disabled : TAEN = TBEN = 0 */
+	LM4_TIMER_CTL(7) &= ~0x101;
+	/* 32-bit timer mode */
+	LM4_TIMER_CFG(7) = 4;
+	/* Set the prescaler to increment every microsecond */
+	LM4_TIMER_TAPR(7) = CLOCKSOURCE_DIVIDER;
+	/* One-shot, counting down */
+	LM4_TIMER_TAMR(7) = 0x01;
+	/* Set overflow interrupt */
+	LM4_TIMER_IMR(7) = 0x1;
+
+	/* Enable clock to GPIO module D */
+	LM4_SYSTEM_RCGCGPIO |= 0x0008;
+
+	/* Clear GPIOAFSEL and enable digital function for pins 0-3 */
+	LM4_GPIO_AFSEL(LM4_GPIO_D) &= ~0x0f;
+	LM4_GPIO_DEN(LM4_GPIO_D) |= 0x0f;
+
+	/* Set pins 0-2 as input, pin 3 as output */
+	LM4_GPIO_DIR(LM4_GPIO_D) = (LM4_GPIO_DIR(LM4_GPIO_D) & ~0x0f) | 0x08;
+
+	/* Set pin 0 to edge-sensitive, both edges, pull-up */
+	LM4_GPIO_IS(LM4_GPIO_D) &= ~0x01;
+	LM4_GPIO_IBE(LM4_GPIO_D) |= 0x01;
+	LM4_GPIO_PUR(LM4_GPIO_D) |= 0x01;
+
+	/* Move to idle state */
+	__set_state(POWER_STATE_IDLE, 1, 0);
+
+	/* Enable interrupt on pin 0 */
+	LM4_GPIO_IM(LM4_GPIO_D) |= 0x01;
+
+	return EC_SUCCESS;
+}
+
+
+/* GPIO interrupt handler */
+static void __gpio_d_interrupt(void)
+{
+	uint32_t mis = LM4_GPIO_MIS(LM4_GPIO_D);
+
+	/* Clear the interrupt bits we're handling */
+	LM4_GPIO_ICR(LM4_GPIO_D) = mis;
+
+	/* Handle edges */
+	if (mis & 0x01) {
+		if (LM4_GPIO_DATA(LM4_GPIO_D, 0x01)) {
+			if (state == POWER_STATE_WAIT)
+				__set_state(POWER_STATE_DOWN2, 0, 2000 - 28);
+		} else {
+			if (state == POWER_STATE_IDLE)
+				__set_state(POWER_STATE_DOWN1, 0, 1000 - 28);
+		}
+	}
+}
+
+DECLARE_IRQ(LM4_IRQ_GPIOD, __gpio_d_interrupt, 1);
+
+
+/* Timer interrupt handler */
+static void __timer_w1_interrupt(void)
+{
+	uint32_t mis = LM4_TIMER_RIS(7);
+	/* Clear the interrupt reasons we're handling */
+	LM4_TIMER_ICR(7) = mis;
+
+	/* Transition to next state */
+	switch (state) {
+	case POWER_STATE_IDLE:
+	case POWER_STATE_WAIT:
+		/* Ignore timer events when waiting for GPIO edges */
+		break;
+	case POWER_STATE_DOWN1:
+		__set_state(POWER_STATE_UP1, 1, 1000 - 28);
+		break;
+	case POWER_STATE_UP1:
+		__set_state(POWER_STATE_DOWN10, 0, 10000 - 228);
+		break;
+	case POWER_STATE_DOWN10:
+		__set_state(POWER_STATE_UP5, 1, 5000 - 128);
+		break;
+	case POWER_STATE_UP5:
+		__set_state(POWER_STATE_DOWN15, 0, 15000 - 328);
+		break;
+	case POWER_STATE_DOWN15:
+		if (LM4_GPIO_DATA(LM4_GPIO_D, 0x01)) {
+			/* Button has already been released; go straight to
+			 * idle */
+			__set_state(POWER_STATE_IDLE, 1, 0);
+		} else {
+			/* Wait for button release */
+			__set_state(POWER_STATE_WAIT, 1, 0);
+		}
+		break;
+	case POWER_STATE_DOWN2:
+		__set_state(POWER_STATE_IDLE, 1, 0);
+		break;
+	}
+}
+
+DECLARE_IRQ(LM4_IRQ_TIMERW1A, __timer_w1_interrupt, 1);
+
+int power_demo_task(void)
+{
+	/* Initialize the peripherals */
+	power_demo_init();
+
+	/* suspend this task forever */
+	task_wait_msg(-1);
+
+	return EC_SUCCESS;
+}
diff --git a/test/powerdemo.tasklist b/test/powerdemo.tasklist
new file mode 100644
index 0000000..e0f7466
--- /dev/null
+++ b/test/powerdemo.tasklist
@@ -0,0 +1,4 @@
+
+#define CONFIG_TASK_LIST \
+	TASK(CONSOLE, console_task, NULL) \
+	TASK(POWERDEMO, power_demo_task, NULL)
diff --git a/test/timer_calib.c b/test/timer_calib.c
new file mode 100644
index 0000000..1169491
--- /dev/null
+++ b/test/timer_calib.c
@@ -0,0 +1,55 @@
+/* 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.
+ *
+ * Tasks for scheduling test.
+ */
+
+#include "common.h"
+#include "uart.h"
+#include "task.h"
+#include "timer.h"
+
+uint32_t difftime(timestamp_t t0, timestamp_t t1)
+{
+	return (uint32_t)(t1.val-t0.val);
+}
+
+int timer_calib_task(void *data)
+{
+	timestamp_t t0, t1;
+	unsigned d;
+
+	uart_printf("\n=== Timer calibration ===\n");
+
+	t0 = get_time();
+	t1 = get_time();
+	uart_printf("- back-to-back get_time : %d us\n", difftime(t0, t1));
+
+	/* Sleep for 5 seconds */
+	uart_printf("- sleep 1s :\n  ");
+	uart_flush_output();
+	uart_printf("Go...");
+	t0 = get_time();
+	usleep(1000000);
+	t1 = get_time();
+	uart_printf("done. delay = %d us\n", difftime(t0, t1));
+	
+	/* try small usleep */
+	uart_printf("- short sleep :\n");
+	uart_flush_output();
+	for (d=128 ; d > 0; d = d / 2) {
+		t0 = get_time();
+		usleep(d);
+		t1 = get_time();
+		uart_printf("  %d us => %d us\n", d, difftime(t0, t1));
+		uart_flush_output();
+	}
+
+	uart_printf("Done.\n");
+	/* sleep forever */
+	task_wait_msg(-1);
+
+	return EC_SUCCESS;
+}
diff --git a/test/timer_calib.py b/test/timer_calib.py
new file mode 100644
index 0000000..d62e9f7
--- /dev/null
+++ b/test/timer_calib.py
@@ -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.
+#
+# Check timers behavior
+#
+
+import time
+
+def one_pass(helper):
+      helper.wait_output("=== Timer calibration ===")
+      res = helper.wait_output("back-to-back get_time : (?P<lat>[0-9]+) us",
+                               use_re=True)["lat"]
+      minlat = int(res)
+      helper.trace("get_time latency %d us\n" % minlat)
+
+      helper.wait_output("sleep 1s")
+      t0 = time.time()
+      second = helper.wait_output("done. delay = (?P<second>[0-9]+) us",
+                               use_re=True)["second"]
+      t1 = time.time()
+      secondreal = t1 - t0
+      secondlat = int(second) - 1000000
+      helper.trace("1s timer latency %d us / real time %f s\n" % (secondlat,
+                                                                  secondreal))
+
+
+      us = {}
+      for pow2 in range(7):
+          delay = 1 << (7-pow2)
+          us[delay] = helper.wait_output("%d us => (?P<us>[0-9]+) us" % delay,
+                                  use_re=True)["us"]
+      helper.wait_output("Done.")
+
+      return minlat, secondlat, secondreal
+
+
+def test(helper):
+      one_pass(helper)
+
+      helper.ec_command("reboot")
+      helper.wait_output("--- Chrome EC initialized! ---")
+
+      # get the timing results on the second pass
+      # to avoid binary translation overhead
+      minlat, secondlat, secondreal = one_pass(helper)
+
+      # check that the timings somewhat make sense
+      if minlat > 220 or secondlat > 500 or abs(secondreal-1.0) > 0.200:
+           helper.fail("imprecise timings " +
+                       "(get_time %d us sleep %d us / real time %.3f s)" %
+                       (minlat, secondlat, secondreal))
+
+      return True # PASS !
diff --git a/test/timer_calib.tasklist b/test/timer_calib.tasklist
new file mode 100644
index 0000000..eee74b4d
--- /dev/null
+++ b/test/timer_calib.tasklist
@@ -0,0 +1,10 @@
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ */
+#define CONFIG_TASK_LIST \
+  TASK(WATCHDOG, watchdog_task, NULL) \
+  TASK(TESTTMR, timer_calib_task, (void *)'T')\
+  TASK(CONSOLE, console_task, NULL)
diff --git a/test/timer_dos.c b/test/timer_dos.c
new file mode 100644
index 0000000..b0c37a4
--- /dev/null
+++ b/test/timer_dos.c
@@ -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.
+ * Copyright 2011 Google Inc.
+ *
+ * Tasks for timer test.
+ */
+
+#include "common.h"
+#include "uart.h"
+#include "task.h"
+#include "timer.h"
+
+/* Linear congruential pseudo random number generator*/
+static uint32_t prng(uint32_t x)
+{
+	return 22695477 * x + 1;
+}
+
+/* period between 500us and 128ms */
+#define PERIOD_US(num) (((num % 256) + 1) * 500)
+
+int TaskTimer(void *seed)
+{
+	uint32_t num = (uint32_t)seed;
+	task_id_t id = task_get_current();
+
+	uart_printf("\n[Timer task %d]\n", id);
+
+	while (1) {
+		/* Wait for a "random" period */
+		task_wait_msg(PERIOD_US(num));
+		uart_printf("%01d\n", id);
+		/* next pseudo random delay */
+		num = prng(num);
+	}
+
+	return EC_SUCCESS;
+}
diff --git a/test/timer_dos.py b/test/timer_dos.py
new file mode 100644
index 0000000..86d9b7a
--- /dev/null
+++ b/test/timer_dos.py
@@ -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.
+#
+# Timers test
+#
+
+import time
+
+# Test during 5s
+DURATION=5
+
+# Linear congruential pseudo random number generator*/
+def prng(x):
+    return (22695477 * x + 1) & 0xffffffff
+
+# period between 500us and 128ms
+def period_us(num):
+    return (((num % 256) + 1) * 500)
+
+# build the same pseudo random sequence as the target
+def build_sequence():
+    #TODO
+    return []
+
+def test(helper):
+      helper.wait_output("[Timer task ")
+      deadline = time.time() + DURATION
+      seq = []
+      while time.time() < deadline:
+          tmr = helper.wait_output("(?P<t>[0-9])", use_re=True,
+                                     timeout=1)["t"]
+          seq.append(tmr)
+
+      # Check the results
+      model = build_sequence()
+      #TODO
+
+      helper.trace("Got %d timer IRQ\n" % len(seq))
+
+      return True # PASS !
diff --git a/test/timer_dos.tasklist b/test/timer_dos.tasklist
new file mode 100644
index 0000000..7fced26
--- /dev/null
+++ b/test/timer_dos.tasklist
@@ -0,0 +1,13 @@
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ */
+#define CONFIG_TASK_LIST \
+  TASK(WATCHDOG, watchdog_task, NULL) \
+  TASK(CONSOLE, console_task, NULL) \
+  TASK(TMRA, TaskTimer, (void *)1234) \
+  TASK(TMRB, TaskTimer, (void *)5678) \
+  TASK(TMRC, TaskTimer, (void *)8462) \
+  TASK(TMRD, TaskTimer, (void *)3719)
diff --git a/util/build.mk b/util/build.mk
new file mode 100644
index 0000000..55db717
--- /dev/null
+++ b/util/build.mk
@@ -0,0 +1,9 @@
+# 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 tools build
+#
+
+host-util-bin=ectool
+build-util-bin=ec_uartd stm32mon
diff --git a/util/ec_uartd.c b/util/ec_uartd.c
new file mode 100644
index 0000000..2494954
--- /dev/null
+++ b/util/ec_uartd.c
@@ -0,0 +1,130 @@
+/* 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.
+ */
+/* ec_uartd.c - UART daemon for BD-ICDI-B board for EC debugging
+ *
+ * based on chromeos_public/src/third_party/hdctools/src/ftdiuart.c
+ *
+ * compile with:
+ *    gcc -o ftdi_uartd ftdi_uartd.c -lftdi
+ */
+
+/* Force header files to define grantpt(), posix_openpt(), cfmakeraw() */
+#define _BSD_SOURCE
+#define _XOPEN_SOURCE 600
+/* Force header file to declare ptsname_r(), etc. */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <fcntl.h>
+#include <ftdi.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+int main(int argc, char **argv)
+{
+	struct ftdi_context fcontext;
+	struct termios tty_cfg;
+	char ptname[PATH_MAX];
+	char buf[1024];
+	int fd;
+	int rv;
+
+	/* Init */
+	if (ftdi_init(&fcontext) < 0) {
+		fprintf(stderr, "ftdi_init failed\n");
+		return 1;
+	}
+
+	/* Open interface B (UART) in the FTDI device and set 115kbaud */
+	ftdi_set_interface(&fcontext, INTERFACE_B);
+	rv = ftdi_usb_open(&fcontext, 0x0403, 0xbcda);
+	if (rv < 0) {
+		fprintf(stderr, "error opening ftdi device: %d (%s)\n",
+			rv, ftdi_get_error_string(&fcontext));
+		return 2;
+	}
+	rv = ftdi_set_baudrate(&fcontext, 115200);
+	if (rv < 0) {
+		fprintf(stderr, "error setting baudrate: %d (%s)\n",
+			rv, ftdi_get_error_string(&fcontext));
+		return 2;
+	}
+
+	/* Set DTR; this muxes RX on the ICDI board */
+	ftdi_setdtr(&fcontext, 1);
+
+	/* Open the pty */
+	fd = posix_openpt(O_RDWR | O_NOCTTY);
+	if (fd == -1) {
+		perror("opening pty master");
+		return 3;
+	}
+	if (grantpt(fd) == -1) {
+		perror("grantpt");
+		return 3;
+	}
+	if (unlockpt(fd) == -1) {
+		perror("unlockpt");
+		return 3;
+	}
+	if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+		perror("fcntl setfl -> nonblock");
+		return 3;
+	}
+	if (ptsname_r(fd, ptname, PATH_MAX) != 0) {
+		perror("getting name of pty");
+		return 3;
+	}
+	fprintf(stderr, "pty name = %s\n", ptname);
+	if (!isatty(fd)) {
+		perror("not a TTY device\n");
+		return 3;
+	}
+	cfmakeraw(&tty_cfg);
+	tcsetattr(fd, TCSANOW, &tty_cfg);
+	if (chmod(ptname, 0666) == -1) {
+		perror("setting pty attributes");
+		return 3;
+	}
+
+	/* Read and write data forever */
+	while (1) {
+		int bytes = read(fd, buf, sizeof(buf));
+		if (bytes > 0) {
+			rv = ftdi_write_data(&fcontext, buf, bytes);
+			if (rv != bytes) {
+				perror("writing to uart");
+				break;
+			}
+		}
+
+		usleep(1000);
+
+		bytes = ftdi_read_data(&fcontext, buf, sizeof(buf));
+		if (bytes > 0) {
+			int bytes_remaining = bytes;
+			while ((bytes = write(fd, buf, bytes_remaining)) > 0)
+				bytes_remaining -= bytes;
+
+			if (bytes == -1)
+				perror("writing ftdi data to pty");
+
+		} else if (bytes < 0) {
+			perror("failed ftdi_read_data");
+			break;
+		}
+	}
+
+	/* Cleanup */
+	close(fd);
+	ftdi_usb_close(&fcontext);
+	ftdi_deinit(&fcontext);
+	return 0;
+}
diff --git a/util/ectool.c b/util/ectool.c
new file mode 100644
index 0000000..3c377b4
--- /dev/null
+++ b/util/ectool.c
@@ -0,0 +1,551 @@
+/* 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]))
+/* Don't use a macro where an inline will do... */
+static inline int MIN(int a, int b) { return a < b ? a : b; }
+
+
+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"
+	"  temps <sensorid>\n"
+	"      Print temperature.\n"
+	"  pwmgetfanrpm\n"
+	"      Prints current fan RPM\n"
+	"  pwmsetfanrpm <targetrpm>\n"
+	"      Set target fan RPM\n"
+	"\n"
+	"Not working for you?  Make sure LPC I/O is configured:\n"
+	"  pci_write32 0 0x1f 0 0x88 0x007c0801\n"
+	"  pci_write32 0 0x1f 0 0x8c 0x007c0901\n"
+	"  pci_write16 0 0x1f 0 0x80 0x0010\n"
+	"  pci_write16 0 0x1f 0 0x82 0x3f02\n"
+	"";
+
+
+/* Waits for the EC to be unbusy.  Returns 0 if unbusy, non-zero if
+ * timeout. */
+int wait_for_ec(int status_addr, 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(status_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;
+
+	/* TODO: add command line option to use kernel command/param window */
+	int cmd_addr = EC_LPC_ADDR_USER_CMD;
+	int param_addr = EC_LPC_ADDR_USER_PARAM;
+
+	if (insize > EC_LPC_PARAM_SIZE || outsize > EC_LPC_PARAM_SIZE) {
+		fprintf(stderr, "Data size too big\n");
+		return -1;
+	}
+
+	if (wait_for_ec(cmd_addr, 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, param_addr + i);
+
+	outb(command, cmd_addr);
+
+	if (wait_for_ec(cmd_addr, 1000000)) {
+		fprintf(stderr, "Timeout waiting for EC response\n");
+		return -1;
+	}
+
+	/* Check status */
+	i = inb(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(param_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 cmd_temperature(int argc, char *argv[])
+{
+	struct lpc_params_temp_sensor_get_readings p;
+	struct lpc_response_temp_sensor_get_readings r;
+	int rv;
+	int id;
+	char *e;
+
+	if (argc != 1) {
+		fprintf(stderr, "Usage: temps <sensorid>\n");
+		return -1;
+	}
+
+	id = strtol(argv[0], &e, 0);
+	if (e && *e) {
+		fprintf(stderr, "Bad sensor ID.\n");
+		return -1;
+	}
+
+	p.temp_sensor_id = id;
+	printf("Reading temperature...");
+	rv = ec_command(EC_LPC_COMMAND_TEMP_SENSOR_GET_READINGS,
+			&p, sizeof(p), &r, sizeof(r));
+	if (rv)
+		printf("Error\n");
+	else
+		printf("%d\n", r.value);
+	return rv;
+}
+
+int cmd_pwm_get_fan_rpm(void)
+{
+	struct lpc_response_pwm_get_fan_rpm r;
+	int rv;
+
+	rv = ec_command(EC_LPC_COMMAND_PWM_GET_FAN_RPM, NULL, 0, &r, sizeof(r));
+	if (rv)
+	        return rv;
+
+	printf("Current fan RPM: %d\n", r.rpm);
+
+	return 0;
+}
+
+int cmd_pwm_set_fan_rpm(int argc, char *argv[])
+{
+	struct lpc_params_pwm_set_fan_target_rpm p;
+	char *e;
+	int rv;
+
+	if (argc != 1) {
+	        fprintf(stderr,
+	                "Usage: pwmsetfanrpm <targetrpm>\n");
+	        return -1;
+	}
+	p.rpm = strtol(argv[0], &e, 0);
+	if (e && *e) {
+	        fprintf(stderr, "Bad RPM.\n");
+	        return -1;
+	}
+
+	rv = ec_command(EC_LPC_COMMAND_PWM_SET_FAN_TARGET_RPM,
+	                &p, sizeof(p), NULL, 0);
+	if (rv)
+	        return rv;
+
+	printf("Fan target RPM set.\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 (!strcasecmp(argv[1], "temps"))
+		return cmd_temperature(argc - 2, argv + 2);
+	if (!strcasecmp(argv[1], "pwmgetfanrpm"))
+	        return cmd_pwm_get_fan_rpm();
+	if (!strcasecmp(argv[1], "pwmsetfanrpm"))
+	        return cmd_pwm_set_fan_rpm(argc - 2, argv + 2);
+
+	/* If we're still here, command was unknown */
+	fprintf(stderr, "Unknown command '%s'\n\n", argv[1]);
+	print_help(argv[0]);
+	return -2;
+}
diff --git a/util/qemu-system-arm b/util/qemu-system-arm
new file mode 100755
index 0000000..ad0802b
--- /dev/null
+++ b/util/qemu-system-arm
Binary files differ
diff --git a/util/run_qemu_test b/util/run_qemu_test
new file mode 100755
index 0000000..ffc8221
--- /dev/null
+++ b/util/run_qemu_test
@@ -0,0 +1,253 @@
+#!/usr/bin/python
+# 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.
+#
+# Python wrapper script for running tests under QEMU
+#
+
+import errno
+import imp
+import json
+import os
+import optparse
+import re
+import signal
+import socket
+import subprocess
+import sys
+import threading
+import time
+
+QEMU_BINARY="qemu-system-arm"
+QEMU_OPTIONS=["-machine","lm4f232h5","-serial","stdio","-display","none"]
+
+def trace(msg):
+    sys.stdout.write(msg)
+
+class QEMUError(Exception):
+    def __init__(self, value):
+        self.value = value
+
+    def __str__(self):
+        return "QEMU Error:" + repr(self.value)
+
+class QEMUInstance:
+    PORT=3456
+    QMP_ADDR=("127.0.0.1", PORT)
+
+    def __run_qemu(self, cmdline, redirect_stdio=False):
+        trace("Starting QEMU binary ...\n")
+        if redirect_stdio:
+            stdin = subprocess.PIPE
+            stdout = subprocess.PIPE
+        else:
+            stdin = None
+            stdout = None
+        self.__qemu = subprocess.Popen(cmdline, shell=False, bufsize=16384,
+                                       stdin=stdin, stdout=stdout, close_fds=True)
+        trace("QEMU started pid:%d\n" % (self.__qemu.pid))
+        self.__qemu.wait()
+        trace("QEMU has terminated\n")
+
+    def __init__(self, qemu_bin, firmware, romcode = None, testmode = False):
+        self.__events = []
+        cmdline = [qemu_bin] + QEMU_OPTIONS + ["-kernel",firmware,"-qmp","tcp:%s:%d" % self.QMP_ADDR]
+        if romcode:
+            cmdline += ["-bios",romcode]
+        self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.__sock.bind(self.QMP_ADDR)
+        self.__sock.listen(1)
+
+        self.__thr = threading.Thread(target=QEMUInstance.__run_qemu,args=(self,cmdline,testmode))
+        self.__thr.start()
+        try:
+            trace("Waiting for QEMU connection ...\n")
+            self.__sock, _ = self.__sock.accept()
+            self.__sockfd = self.__sock.makefile()
+        except socket.error:
+            raise QEMUError('Cannot connect to QMP server')
+
+        version = self.__json_recv()
+        if version is None or not version.has_key('QMP'):
+            raise QEMUError('Not QMP support')
+        # Test basic communication with QMP
+        resp = self.send_qmp('qmp_capabilities')
+        if not "return" in resp:
+            raise QEMUError('QMP not working properly')
+        trace("QMP connected\n")
+
+    def __json_recv(self, only_event=False):
+        while True:
+            data = self.__sockfd.readline()
+            if not data:
+                return
+            return json.loads(data)
+
+    def send_qmp(self, name, args=None):
+        qmp_cmd = { 'execute': name }
+        if args:
+            qmp_cmd['arguments'] = args
+        try:
+            self.__sock.sendall(json.dumps(qmp_cmd))
+        except socket.error, err:
+            if err[0] == errno.EPIPE:
+                return
+            raise QEMUError("Error on QMP socket:" + err)
+        return self.__json_recv()
+
+    def serial_readline(self):
+        return self.__qemu.stdout.readline()
+
+    def serial_write(self, string):
+        self.__qemu.stdin.write(string)
+        self.__qemu.stdin.flush()
+
+    def get_event(self, blocking=True):
+        if not blocking:
+            self.__sock.setblocking(0)
+        try:
+            val = self.__json_recv()
+        except socket.error, err:
+            if err[0] == errno.EAGAIN:
+                # Nothing available
+                return None
+        if not blocking:
+            self.__sock.setblocking(1)
+        return val
+
+    def close(self):
+        # Try to terminate QEMU gracefully
+        if self.__qemu.poll() == None:
+            self.send_qmp("quit")
+            time.sleep(0.1)
+        # Force termination if the process is still here :
+        if self.__qemu.poll() == None:
+            self.__qemu.terminate()
+        self.__thr.join()
+        self.__sock.close()
+        self.__sockfd.close()
+
+class TestFailure(Exception):
+    def __init__(self, reason):
+        self.value = reason
+
+    def __str__(self):
+        return "reason:" + repr(self.value)
+
+class EcTest:
+    def __init__(self, qemu_bin, firmware, romcode, test):
+      self.__qemu_bin = qemu_bin
+      self.__firmware = firmware
+      self.__romcode = romcode
+      self.__test = test
+
+    def timeout_handler(self, signum, frame):
+        raise TestFailure("Timeout waiting for %s" % self.__timeout_reason)
+
+    def wait_output(self, string, use_re = False, timeout = 5):
+        self.__timeout_reason = string
+        old_handler = signal.signal(signal.SIGALRM, lambda
+                                    s,f:self.timeout_handler(s,f))
+        if use_re:
+            regexp = re.compile(string)
+        signal.alarm(timeout)
+        while True:
+            ln = self.__qemu.serial_readline()
+            trace("[EC]%s" % ln)
+            if use_re:
+                res = regexp.search(ln)
+                if res:
+                    signal.alarm(0)
+                    signal.signal(signal.SIGALRM, old_handler)
+                    return res.groupdict()
+            else:
+                if string in ln:
+                    signal.alarm(0)
+                    signal.signal(signal.SIGALRM, old_handler)
+                    return
+
+    def wait_prompt(self):
+        self.wait_output("> ")
+
+    def ec_command(self, cmd):
+        self.__qemu.serial_write(cmd + '\r\n')
+
+    def trace(self, msg):
+        trace(msg)
+
+    def report(self, msg):
+        sys.stderr.write("    === TEST %s ===\n" % msg)
+
+    def fail(self, msg):
+        raise TestFailure(msg)
+
+    def run_test(self):
+        try:
+            self.__qemu = QEMUInstance(self.__qemu_bin, self.__firmware,
+                                       self.__romcode, True)
+        except QEMUError as e:
+            self.report("QEMU FATAL ERROR: " + e.value)
+            return 1
+
+        testmod = imp.load_module("testmodule", file(self.__test,"r"),
+                                  self.__test, (".py","r",imp.PY_SOURCE))
+        self.report("RUN: %s" % os.path.basename(self.__test))
+        try:
+            res = testmod.test(self)
+        except TestFailure as e:
+            res = False
+            self.report("FAIL: %s" % e.value)
+        self.__qemu.close()
+        if res:
+            self.report("PASS")
+            return 0
+        return 1
+
+def run_interactive(qemu_bin, firmware, romcode):
+    try:
+        qemu = QEMUInstance(qemu_bin, firmware, romcode, False)
+    except QEMUError as e:
+        sys.stderr.write('FATAL: %s\n' % e.value)
+        return 1
+
+    # Dummy testing code : TODO remove
+    #print qemu.send_qmp("query-commands")
+    #print qemu.send_qmp("human-monitor-command",
+    #                    { 'command-line': "sendkey ctrl-alt-f1 50",'cpu-index': 0 })
+    while True:
+        msg = qemu.get_event()
+        trace("[EVENT]%s\n" % msg)
+        if msg.has_key("event") and msg["event"] == "RESET":
+          break
+    qemu.close()
+    return 0
+
+def parse_cmdline(basedir):
+    parser = optparse.OptionParser("usage: %prog [options] [testname]")
+    parser.add_option("-b", "--board", dest="board", default="bds",
+                      help="board to use")
+    parser.add_option("-i", "--image", dest="image",
+                      help="firmware image filename")
+    parser.add_option("-r", "--rom", dest="romcode",
+                      default=os.path.join(basedir,"util","rom_lm4fs1ge5bb.bin"),
+                      help="ROM code image filename")
+    parser.add_option("-q", "--qemu", dest="qemu_bin",
+                      default=os.path.join(basedir,"util",QEMU_BINARY),
+                      help="Qemu binary path")
+    (options, args) = parser.parse_args()
+    if options.image:
+        image = options.image
+    else:
+        image = os.path.join(basedir,"build",options.board,"ec.bin")
+
+    return options.qemu_bin, image,options.romcode, args
+
+if __name__ == '__main__':
+    basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
+    qemu_bin, image, romcode, tests = parse_cmdline(basedir)
+    if len(tests) > 0:
+        res = EcTest(qemu_bin, image, romcode, tests[0]).run_test()
+    else:
+        res = run_interactive(qemu_bin, image, romcode)
+    sys.exit(res)
diff --git a/util/stm32mon.c b/util/stm32mon.c
new file mode 100644
index 0000000..73850f2
--- /dev/null
+++ b/util/stm32mon.c
@@ -0,0 +1,659 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * STM32L SoC system monitor interface tool
+ */
+
+/* use cfmakeraw() */
+#define _BSD_SOURCE
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+/* Monitor command set */
+#define CMD_INIT 0x7f    /* Starts the monitor */
+
+#define CMD_GETCMD   0x00 /* Gets the allowed commands */
+#define CMD_GETVER   0x01 /* Gets the bootloader version */
+#define CMD_GETID    0x02 /* Gets the Chip ID */
+#define CMD_READMEM  0x11 /* Reads memory */
+#define CMD_GO       0x21 /* Jumps to user code */
+#define CMD_WRITEMEM 0x31 /* Writes memory (SRAM or Flash) */
+#define CMD_ERASE    0x43 /* Erases n pages of Flash memory */
+#define CMD_EXTERASE 0x44 /* Erases n pages of Flash memory */
+#define CMD_WP       0x63 /* Enables write protect */
+#define CMD_WU       0x73 /* Disables write protect */
+#define CMD_RP       0x82 /* Enables the read protection */
+#define CMD_RU       0x92 /* Disables the read protection */
+
+#define RESP_NACK    0x1f
+#define RESP_ACK     0x79
+
+/* Extended erase special parameters */
+#define ERASE_ALL    0xffff
+#define ERASE_BANK1  0xfffe
+#define ERASE_BANK2  0xfffd
+
+/* known STM32 SoC parameters */
+struct stm32_def {
+	uint16_t   id;
+	const char *name;
+	uint32_t flash_start;
+	uint32_t flash_size;
+} chip_defs[] = {
+	{0x416, "STM32L15xx", 0x08000000, 0x20000},
+	{0x420, "STM32F100xx", 0x08000000, 0x20000},
+	{ 0 }
+};
+
+#define DEFAULT_TIMEOUT 2 /* seconds */
+#define DEFAULT_BAUDRATE B38400
+#define PAGE_SIZE 256
+
+/* store custom parameters */
+speed_t baudrate = DEFAULT_BAUDRATE;
+const char *serial_port = "/dev/ttyUSB1";
+const char *input_filename;
+const char *output_filename;
+
+/* optional command flags */
+enum {
+	FLAG_UNPROTECT = 0x01,
+	FLAG_ERASE     = 0x02,
+	FLAG_GO        = 0x04,
+};
+
+typedef struct {
+	int size;
+	uint8_t *data;
+} payload_t;
+
+int open_serial(const char *port)
+{
+	int fd, res;
+	struct termios cfg;
+
+	fd = open(port, O_RDWR | O_NOCTTY);
+	if (fd == -1) {
+		perror("Unable to open serial port");
+		return -1;
+	}
+
+	/* put the tty in "raw" mode at the defined baudrate */
+	res = tcgetattr(fd, &cfg);
+	if (res == -1) {
+		perror("Cannot read tty attributes");
+		close(fd);
+		return -1;
+	}
+	cfmakeraw(&cfg);
+	cfsetspeed(&cfg, baudrate);
+	/* serial mode is 8e1 */
+	cfg.c_cflag |= PARENB;
+	/* 200 ms timeout */
+	cfg.c_cc[VTIME] = 2;
+	cfg.c_cc[VMIN] = 0;
+	res = tcsetattr(fd, TCSANOW, &cfg);
+	if (res == -1) {
+		perror("Cannot set tty attributes");
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+int wait_for_ack(int fd)
+{
+	uint8_t resp;
+	int res;
+	time_t deadline = time(NULL) + DEFAULT_TIMEOUT;
+
+	while (time(NULL) < deadline) {
+		res = read(fd, &resp, 1);
+		if ((res < 0) && (errno != EAGAIN)) {
+			perror("Failed to read answer");
+			return -EIO;
+		}
+		if (res == 1) {
+			if (resp == RESP_ACK)
+				return 0;
+			else if (resp == RESP_NACK) {
+				fprintf(stderr, "NACK\n");
+				return -EINVAL;
+			} else {
+				fprintf(stderr, "Receive junk: %02x\n", resp);
+			}
+		}
+	}
+	return -ETIMEDOUT;
+}
+
+int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
+		 uint8_t *resp, int resp_size)
+{
+	int res, i, c;
+	payload_t *p;
+	int readcnt = 0;
+	int size;
+	uint8_t *data;
+	uint8_t crc = 0xff ^ cmd; /* XOR checksum */
+
+	/* Send the command index */
+	res = write(fd, &cmd, 1);
+	if (res <= 0) {
+		perror("Failed to write command");
+		return -1;
+	}
+	/* Send the checksum */
+	res = write(fd, &crc, 1);
+	if (res <= 0) {
+		perror("Failed to write checksum");
+		return -1;
+	}
+	/* Wait for the ACK */
+	if (wait_for_ack(fd) < 0) {
+		fprintf(stderr, "Failed to get command %02x ACK\n", cmd);
+		return -1;
+	}
+
+	/* Send the command payloads */
+	for (p = loads, c = 0; c < cnt ; c++, p++) {
+		crc = 0;
+		size = p->size;
+		data = p->data;
+		for (i = 0; i < size ; i++)
+			crc ^= data[i];
+		if (size == 1)
+			crc = 0xff ^ crc;
+
+		while (size) {
+			res = write(fd, data, size);
+			if (res < 0) {
+				perror("Failed to write command payload");
+				return -1;
+			}
+			size -= res;
+			data += res;
+		}
+
+		/* Send the checksum */
+		res = write(fd, &crc, 1);
+		if (res <= 0) {
+			perror("Failed to write checksum");
+			return -1;
+		}
+
+		/* Wait for the ACK */
+		if (wait_for_ack(fd) < 0) {
+			fprintf(stderr, "Failed to get payload %d ACK\n", c);
+			return -1;
+		}
+
+	}
+
+	/* Read the answer payload */
+	if (resp) {
+		while ((res = read(fd, resp, resp_size))) {
+			if (res < 0) {
+				perror("Failed to read payload");
+				return -1;
+			}
+			readcnt += res;
+			resp += res;
+			resp_size -= res;
+		}
+	}
+
+	return readcnt;
+}
+
+struct stm32_def *command_get_id(int fd)
+{
+	int res;
+	uint8_t id[4];
+	uint16_t chipid;
+	struct stm32_def *def;
+
+	res = send_command(fd, CMD_GETID, NULL, 0, id, sizeof(id));
+	if (res > 0) {
+		if (id[0] != 1 || id[3] != RESP_ACK) {
+			fprintf(stderr, "unknown ID : %02x %02x %02x %02x\n",
+				id[0], id[1], id[2], id[3]);
+			return NULL;
+		}
+		chipid = (id[1] << 8) | id[2];
+		for (def = chip_defs ; def->id ; def++)
+			if (def->id == chipid)
+				break;
+		if (def->id == 0)
+			def = NULL;
+		printf("ChipID 0x%03x : %s\n", chipid, def ? def->name : "???");
+		return def;
+	}
+
+	return NULL;
+}
+
+int init_monitor(int fd)
+{
+	int res, i;
+	uint8_t init = CMD_INIT;
+	uint8_t buffer[64];
+
+	printf("Waiting for the monitor startup ...");
+	fflush(stdout);
+
+	while (1) {
+		/* Send the command index */
+		res = write(fd, &init, 1);
+		if (res <= 0) {
+			perror("Failed to write command");
+			return -1;
+		}
+		/* Wait for the ACK */
+		res = wait_for_ack(fd);
+		if (res == 0)
+			break;
+		if (res == -EINVAL) {
+			/* we got NACK'ed, the loader might be already started
+			 * let's ping it to check
+			 */
+			if (command_get_id(fd)) {
+				printf("Monitor already started.\n");
+				return 0;
+			}
+		}
+		if (res < 0 && res != -ETIMEDOUT)
+			return -1;
+		printf(".");
+		fflush(stdout);
+	}
+	printf("Done.\n");
+
+	/* read trailing chars */
+	res = read(fd, buffer, sizeof(buffer));
+	if (res > 0) {
+		printf("Recv[%d]:", res);
+		for (i = 0; i < res; i++)
+			printf("%02x ", buffer[i]);
+		printf("\n");
+	}
+
+	return 0;
+}
+
+int command_get_commands(int fd)
+{
+	int res, i;
+	uint8_t cmds[64];
+
+	res = send_command(fd, CMD_GETCMD, NULL, 0, cmds, sizeof(cmds));
+	if (res > 0) {
+		if ((cmds[0] > sizeof(cmds) - 2) ||
+		    (cmds[cmds[0] + 2] != RESP_ACK)) {
+			fprintf(stderr, "invalid GET answer (%02x...)\n",
+				cmds[0]);
+			return -1;
+		}
+		printf("Bootloader v%d.%d, commands : ",
+		       cmds[1] >> 4, cmds[1] & 0xf);
+		for (i = 2; i < 2 + cmds[0]; i++)
+			printf("%02x ", cmds[i]);
+		printf("\n");
+		return 0;
+	}
+
+	return -1;
+}
+
+int command_read_mem(int fd, uint32_t address, uint32_t size, uint8_t *buffer)
+{
+	int res;
+	uint32_t remaining = size;
+	uint32_t addr_be;
+	uint8_t cnt;
+	payload_t loads[2] = {
+		{4, (uint8_t *)&addr_be},
+		{1, &cnt}
+	};
+
+	while (remaining) {
+		cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE - 1 : remaining - 1;
+		addr_be = htonl(address);
+
+		printf(".");
+		fflush(stdout);
+		res = send_command(fd, CMD_READMEM, loads, 2, buffer, cnt + 1);
+		if (res < 0)
+			return -EIO;
+
+		buffer += cnt + 1;
+		address += cnt + 1;
+		remaining -= cnt + 1;
+	}
+
+	return size;
+}
+
+int command_write_mem(int fd, uint32_t address, uint32_t size, uint8_t *buffer)
+{
+	int res;
+	uint32_t remaining = size;
+	uint32_t addr_be;
+	uint32_t cnt;
+	uint8_t outbuf[257];
+	payload_t loads[2] = {
+		{4, (uint8_t *)&addr_be},
+		{sizeof(outbuf), outbuf}
+	};
+
+	while (remaining) {
+		cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining;
+		addr_be = htonl(address);
+		outbuf[0] = cnt - 1;
+		loads[1].size = cnt + 1;
+		memcpy(outbuf + 1, buffer, cnt);
+
+		printf(".");
+		fflush(stdout);
+		res = send_command(fd, CMD_WRITEMEM, loads, 2, NULL, 0);
+		if (res < 0)
+			return -EIO;
+
+		buffer += cnt;
+		address += cnt;
+		remaining -= cnt;
+	}
+
+	return size;
+}
+
+int command_ext_erase(int fd, uint16_t count, uint16_t *pages)
+{
+	int res;
+	uint16_t count_be = htons(count);
+	payload_t load = { 2, (uint8_t *)&count_be };
+
+	if (count < 0xfff0) {
+		/* not a special value */
+		/* TODO implement page-list erase */
+		if (!pages)
+			return -EINVAL;
+	}
+
+	res = send_command(fd, CMD_EXTERASE, &load, 1, NULL, 0);
+	if (res >= 0)
+		printf("Flash erased.\n");
+	printf("ERASE res = %d\n", res);
+
+	return res;
+}
+
+int command_write_unprotect(int fd)
+{
+	int res;
+
+	res = send_command(fd, CMD_WU, NULL, 0, NULL, 0);
+	if (res < 0)
+		return -EIO;
+
+	/* Wait for the ACK */
+	if (wait_for_ack(fd) < 0) {
+		fprintf(stderr, "Failed to get write-protect ACK\n");
+		return -EINVAL;
+	}
+	printf("Flash write unprotected.\n");
+
+	/* This commands triggers a reset */
+	if (init_monitor(fd) < 0) {
+		fprintf(stderr, "Cannot recover after WP reset\n");
+		return -EIO;
+	}
+
+
+	return 0;
+}
+
+int command_go(int fd, uint32_t address)
+{
+	int res;
+	uint32_t addr_be = htonl(address);
+	payload_t load = { 4, (uint8_t *)&addr_be };
+
+	res = send_command(fd, CMD_GO, &load, 1, NULL, 0);
+	if (res < 0)
+		return -EIO;
+
+#if 0 /* this ACK should exist according to the documentation ... */
+	/* Wait for the ACK */
+	if (wait_for_ack(fd) < 0) {
+		fprintf(stderr, "Failed to get GO ACK\n");
+		return -EINVAL;
+	}
+#endif
+
+	printf("Program started at 0x%08x.\n", address);
+	return 0;
+}
+
+int read_flash(int fd, struct stm32_def *chip, const char *filename,
+	       uint32_t offset, uint32_t size)
+{
+	int res;
+	FILE *hnd;
+	uint8_t *buffer = malloc(size);
+
+	if (!buffer) {
+		fprintf(stderr, "Cannot allocate %d bytes\n", size);
+		return -ENOMEM;
+	}
+
+	hnd = fopen(filename, "w");
+	if (!hnd) {
+		fprintf(stderr, "Cannot open file %s for writing\n", filename);
+		free(buffer);
+		return -EIO;
+	}
+
+	if (!size)
+		size = chip->flash_size;
+	offset += chip->flash_start;
+	printf("Reading %d bytes at 0x%08x ", size, offset);
+	res = command_read_mem(fd, offset, size, buffer);
+	if (res > 0) {
+		if (fwrite(buffer, res, 1, hnd) != 1)
+			fprintf(stderr, "Cannot write %s\n", filename);
+	}
+	printf("   %d bytes read.\n", res);
+
+	fclose(hnd);
+	free(buffer);
+	return res;
+}
+
+int write_flash(int fd, struct stm32_def *chip, const char *filename,
+		uint32_t offset)
+{
+	int res, written;
+	FILE *hnd;
+	int size = chip->flash_size;
+	uint8_t *buffer = malloc(size);
+
+	if (!buffer) {
+		fprintf(stderr, "Cannot allocate %d bytes\n", size);
+		return -ENOMEM;
+	}
+
+	hnd = fopen(filename, "r");
+	if (!hnd) {
+		fprintf(stderr, "Cannot open file %s for reading\n", filename);
+		free(buffer);
+		return -EIO;
+	}
+	if ((res = fread(buffer, 1, size, hnd)) <= 0) {
+		fprintf(stderr, "Cannot read %s\n", filename);
+		return -EIO;
+	}
+	fclose(hnd);
+
+	offset += chip->flash_start;
+	printf("Writing %d bytes at 0x%08x ", res, offset);
+	written = command_write_mem(fd, offset, res, buffer);
+	if (written != res)
+		fprintf(stderr, "Error writing to flash\n");
+	printf("Done.\n");
+
+	free(buffer);
+	return written;
+}
+
+static const struct option longopts[] = {
+	{"device", 1, 0, 'd'},
+	{"read", 1, 0, 'r'},
+	{"write", 1, 0, 'w'},
+	{"erase", 0, 0, 'e'},
+	{"go", 0, 0, 'g'},
+	{"help", 0, 0, 'h'},
+	{"unprotect", 0, 0, 'u'},
+	{"baudrate", 1, 0, 'b'},
+	{NULL, 0, 0, 0}
+};
+
+void display_usage(char *program)
+{
+	fprintf(stderr, "Usage: %s [-d <tty>] [-b <baudrate>] [-u] [-e]"
+		" [-r <file>] [-w <file>] [-g]\n", program);
+	fprintf(stderr, "--d[evice] <tty> : use <tty> as the serial port\n");
+	fprintf(stderr, "--b[audrate] <baudrate> : set serial port speed "
+			"to <baudrate> bauds\n");
+	fprintf(stderr, "--u[nprotect] : remove flash write protect\n");
+	fprintf(stderr, "--e[rase] : erase all the flash content\n");
+	fprintf(stderr, "--r[ead] <file> : read the flash content and "
+			"write it into <file>\n");
+	fprintf(stderr, "--w[rite] <file> : read <file> and "
+			"write it to flash\n");
+	fprintf(stderr, "--g[o] : jump to execute flash entrypoint\n");
+
+	exit(2);
+}
+
+speed_t parse_baudrate(const char *value)
+{
+	int rate = atoi(value);
+
+	switch (rate) {
+	case 9600:
+		return B9600;
+	case 19200:
+		return B19200;
+	case 38400:
+		return B38400;
+	case 57600:
+		return B57600;
+	case 115200:
+		return B115200;
+	default:
+		fprintf(stderr, "Invalid baudrate %s, using %d\n",
+			value, DEFAULT_BAUDRATE);
+		return DEFAULT_BAUDRATE;
+	}
+}
+
+int parse_parameters(int argc, char **argv)
+{
+	int opt, idx;
+	int flags = 0;
+
+	while ((opt = getopt_long(argc, argv, "b:d:eghr:w:u?",
+				  longopts, &idx)) != -1) {
+		switch (opt) {
+		case 'b':
+			baudrate = parse_baudrate(optarg);
+			break;
+		case 'd':
+			serial_port = optarg;
+			break;
+		case 'e':
+			flags |= FLAG_ERASE;
+			break;
+		case 'g':
+			flags |= FLAG_GO;
+			break;
+		case 'h':
+		case '?':
+			display_usage(argv[0]);
+			break;
+		case 'r':
+			input_filename = optarg;
+			break;
+		case 'w':
+			output_filename = optarg;
+			break;
+		case 'u':
+			flags |= FLAG_UNPROTECT;
+			break;
+		}
+	}
+	return flags;
+}
+
+int main(int argc, char **argv)
+{
+	int ser;
+	struct stm32_def *chip;
+	int ret = 1;
+	int flags;
+
+	/* Parse command line options */
+	flags = parse_parameters(argc, argv);
+
+	/* Open the serial port tty */
+	ser = open_serial(serial_port);
+	if (ser < 0)
+		return 1;
+
+	/* Trigger embedded monitor detection */
+	if (init_monitor(ser) < 0)
+		goto terminate;
+
+	chip = command_get_id(ser);
+	if (!chip)
+		goto terminate;
+
+	command_get_commands(ser);
+
+	if (flags & FLAG_UNPROTECT)
+		command_write_unprotect(ser);
+
+	if (flags & FLAG_ERASE)
+		command_ext_erase(ser, ERASE_ALL, NULL);
+
+	if (input_filename)
+		read_flash(ser, chip, input_filename, 0, 0);
+
+	if (output_filename)
+		write_flash(ser, chip, output_filename, 0);
+
+	/* Run the program from flash */
+	if (flags & FLAG_GO)
+		command_go(ser, chip->flash_start);
+
+	/* Normal exit */
+	ret = 0;
+terminate:
+	/* Close serial port */
+	close(ser);
+	return ret;
+}