summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Parker <dparker@chromium.org>2012-02-09 13:39:00 -0800
committerDave Parker <dparker@chromium.org>2012-02-09 13:39:00 -0800
commit7c01418f9c0a9c9a01bdb25d9c4a5884ec157501 (patch)
tree224011b767001cad8b095b11424fbe9b5b793442
parente11243c9a2bf23e94dfe74658c525b14e644854e (diff)
parente42cd379ded4f31487b863d84db11c8c24e22a76 (diff)
downloadchrome-ec-7c01418f9c0a9c9a01bdb25d9c4a5884ec157501.tar.gz
Merge development work from private blizzard.git repo.
BUG=chrome-os-partner:7564 TEST=None Conflicts: .gitignore
-rw-r--r--.gitignore7
-rw-r--r--Makefile48
-rw-r--r--Makefile.rules100
-rw-r--r--Makefile.toolchain34
-rw-r--r--PRESUBMIT.cfg6
-rw-r--r--README26
-rw-r--r--board/bds/board.c76
-rw-r--r--board/bds/board.h129
-rw-r--r--board/bds/board_temp_sensor.c22
-rw-r--r--board/bds/build.mk13
-rw-r--r--board/bds/dummy_charger.c88
-rw-r--r--board/bds/ec.tasklist23
-rw-r--r--board/discovery/board.c67
-rw-r--r--board/discovery/board.h34
-rw-r--r--board/discovery/build.mk10
-rw-r--r--board/discovery/ec.tasklist18
-rw-r--r--board/link/board.c123
-rw-r--r--board/link/board.h170
-rw-r--r--board/link/board_temp_sensor.c37
-rw-r--r--board/link/build.mk12
-rw-r--r--board/link/ec.tasklist23
-rw-r--r--chip/lm4/adc.c183
-rw-r--r--chip/lm4/build.mk18
-rw-r--r--chip/lm4/chip_temp_sensor.c24
-rw-r--r--chip/lm4/clock.c198
-rw-r--r--chip/lm4/config.h42
-rw-r--r--chip/lm4/eeprom.c248
-rw-r--r--chip/lm4/flash.c304
-rw-r--r--chip/lm4/gpio.c260
-rw-r--r--chip/lm4/hwtimer.c91
-rw-r--r--chip/lm4/i2c.c320
-rw-r--r--chip/lm4/jtag.c40
-rw-r--r--chip/lm4/keyboard_scan.c326
-rw-r--r--chip/lm4/lm4_adc.h42
-rw-r--r--chip/lm4/lpc.c287
-rw-r--r--chip/lm4/openocd/lm4x.cfg34
-rw-r--r--chip/lm4/openocd/lm4x_cmds.tcl42
-rw-r--r--chip/lm4/openocd/openocd.cfg7
-rw-r--r--chip/lm4/openocd/servo_v2.cfg10
-rw-r--r--chip/lm4/power_button.c214
-rw-r--r--chip/lm4/pwm.c241
-rw-r--r--chip/lm4/registers.h461
-rw-r--r--chip/lm4/system.c178
-rw-r--r--chip/lm4/temp_sensor.c202
-rw-r--r--chip/lm4/uart.c216
-rw-r--r--chip/lm4/watchdog.c156
-rw-r--r--chip/stm32l/build.mk12
-rw-r--r--chip/stm32l/clock.c83
-rw-r--r--chip/stm32l/config.h35
-rw-r--r--chip/stm32l/gpio.c69
-rw-r--r--chip/stm32l/hwtimer.c142
-rw-r--r--chip/stm32l/registers.h270
-rw-r--r--chip/stm32l/system.c113
-rw-r--r--chip/stm32l/uart.c137
-rw-r--r--chip/stm32l/watchdog.c73
-rw-r--r--common/build.mk20
-rw-r--r--common/charger_bq24725.c273
-rw-r--r--common/charger_bq24725.h53
-rw-r--r--common/console.c189
-rw-r--r--common/firmware_image.S25
-rw-r--r--common/firmware_image.lds.S23
-rw-r--r--common/flash_commands.c333
-rw-r--r--common/gpio_commands.c129
-rw-r--r--common/host_command.c232
-rw-r--r--common/i8042.c154
-rw-r--r--common/keyboard.c582
-rw-r--r--common/main.c99
-rw-r--r--common/memory_commands.c54
-rw-r--r--common/port80.c69
-rw-r--r--common/pwm_commands.c32
-rw-r--r--common/shared_mem.c54
-rw-r--r--common/smart_battery.h58
-rw-r--r--common/system.c208
-rw-r--r--common/temp_sensor.c252
-rw-r--r--common/temp_sensor_commands.c33
-rw-r--r--common/uart_buffering.c665
-rw-r--r--common/usb_charge.c136
-rw-r--r--common/util.c157
-rw-r--r--common/vboot.c98
-rw-r--r--common/x86_power.c354
-rw-r--r--core/cortex-m/atomic.h65
-rw-r--r--core/cortex-m/build.mk11
-rw-r--r--core/cortex-m/cpu.h23
-rw-r--r--core/cortex-m/ec.lds.S63
-rw-r--r--core/cortex-m/init.S378
-rw-r--r--core/cortex-m/panic.S108
-rw-r--r--core/cortex-m/switch.S63
-rw-r--r--core/cortex-m/task.c394
-rw-r--r--core/cortex-m/timer.c205
-rw-r--r--include/adc.h36
-rw-r--r--include/charger.h57
-rw-r--r--include/chip_temp_sensor.h20
-rw-r--r--include/clock.h14
-rw-r--r--include/common.h35
-rw-r--r--include/console.h36
-rw-r--r--include/eeprom.h35
-rw-r--r--include/flash.h88
-rw-r--r--include/flash_commands.h31
-rw-r--r--include/gpio.h78
-rw-r--r--include/host_command.h17
-rw-r--r--include/hwtimer.h43
-rw-r--r--include/i2c.h29
-rw-r--r--include/i8042.h125
-rw-r--r--include/jtag.h16
-rw-r--r--include/keyboard.h94
-rw-r--r--include/keyboard_scan.h16
-rw-r--r--include/lpc.h50
-rw-r--r--include/lpc_commands.h235
-rw-r--r--include/memory_commands.h16
-rw-r--r--include/port80.h19
-rw-r--r--include/power_button.h24
-rw-r--r--include/powerdemo.h16
-rw-r--r--include/pwm.h28
-rw-r--r--include/pwm_commands.h19
-rw-r--r--include/shared_mem.h39
-rw-r--r--include/system.h103
-rw-r--r--include/task.h124
-rw-r--r--include/task_id.h42
-rw-r--r--include/temp_sensor.h60
-rw-r--r--include/temp_sensor_commands.h19
-rw-r--r--include/timer.h60
-rw-r--r--include/uart.h182
-rw-r--r--include/usb_charge.h34
-rw-r--r--include/util.h61
-rw-r--r--include/vboot.h20
-rw-r--r--include/version.h16
-rw-r--r--include/watchdog.h21
-rw-r--r--include/x86_power.h20
-rw-r--r--test/build.mk12
-rw-r--r--test/hello.py15
-rw-r--r--test/hello.tasklist18
-rw-r--r--test/mutex.c112
-rw-r--r--test/mutex.py25
-rw-r--r--test/mutex.tasklist14
-rw-r--r--test/pingpong.c43
-rw-r--r--test/pingpong.py27
-rw-r--r--test/pingpong.tasklist13
-rw-r--r--test/powerdemo.c186
-rw-r--r--test/powerdemo.tasklist4
-rw-r--r--test/timer_calib.c55
-rw-r--r--test/timer_calib.py54
-rw-r--r--test/timer_calib.tasklist10
-rw-r--r--test/timer_dos.c39
-rw-r--r--test/timer_dos.py41
-rw-r--r--test/timer_dos.tasklist13
-rw-r--r--util/build.mk9
-rw-r--r--util/ec_uartd.c130
-rw-r--r--util/ectool.c551
-rwxr-xr-xutil/qemu-system-armbin0 -> 15612250 bytes
-rwxr-xr-xutil/run_qemu_test253
-rw-r--r--util/stm32mon.c659
151 files changed, 15995 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 0aa56f6908..440fd12fdd 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 0000000000..076f7433cc
--- /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 0000000000..b92e9a6efc
--- /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 0000000000..6f75b65a45
--- /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 0000000000..2e8cdb6618
--- /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 0000000000..9f8a5c3b8c
--- /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 0000000000..2e2a1915f6
--- /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 0000000000..e5cab1f212
--- /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 0000000000..65b73005a1
--- /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 0000000000..73f193751f
--- /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 0000000000..c4ee308e1c
--- /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 0000000000..a56e6769e1
--- /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 0000000000..e6a360dbbd
--- /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 0000000000..72fca4690a
--- /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 0000000000..2f0ebe32af
--- /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 0000000000..0e5992b96b
--- /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 0000000000..830426743b
--- /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 0000000000..d199d6c74f
--- /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 0000000000..4a937c9bf3
--- /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 0000000000..b600d69d13
--- /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 0000000000..a56e6769e1
--- /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 0000000000..29eb73ddb6
--- /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 0000000000..028eeecc3f
--- /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 0000000000..d810216851
--- /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 0000000000..c80f36ecc1
--- /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 0000000000..f2224f8226
--- /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 0000000000..c605d0e560
--- /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 0000000000..43f5d5923b
--- /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 0000000000..4c11e30d58
--- /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 0000000000..9b221010c8
--- /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 0000000000..0f31fc39a5
--- /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 0000000000..22c9080917
--- /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 0000000000..4056f7a21e
--- /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 0000000000..98f5293646
--- /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 0000000000..674d35aebd
--- /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 0000000000..3619320c57
--- /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 0000000000..d117db71f6
--- /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 0000000000..dd80e12f10
--- /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 0000000000..caf178e105
--- /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 0000000000..79e7c58f11
--- /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 0000000000..d877e721ab
--- /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 0000000000..a379be16d8
--- /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 0000000000..fa17eccbaf
--- /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 0000000000..a9095bf380
--- /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 0000000000..84916a07fb
--- /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 0000000000..ccc6e63b72
--- /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 0000000000..6f7c8bcda1
--- /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 0000000000..9b4c01ab73
--- /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 0000000000..9309fc54b5
--- /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 0000000000..56d22abf62
--- /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 0000000000..9f5a7c4921
--- /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 0000000000..23bad85a44
--- /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 0000000000..649950abc6
--- /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 0000000000..0127c7bf72
--- /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 0000000000..4a1cd7f6d2
--- /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 0000000000..564cfe80dd
--- /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 0000000000..5b70d0d0c5
--- /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 0000000000..ab0a4324b3
--- /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 0000000000..130b7e4ef1
--- /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 0000000000..78207f96cf
--- /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 0000000000..c1c7d59682
--- /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 0000000000..000be73e20
--- /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 0000000000..66bebb4b8d
--- /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 0000000000..5cebed2131
--- /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 0000000000..785e6a761e
--- /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 0000000000..0c90458db9
--- /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 0000000000..79533b9411
--- /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 0000000000..476aa92498
--- /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 0000000000..114f1cf520
--- /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 0000000000..7581fe56ba
--- /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 0000000000..bff3ec6da3
--- /dev/null
+++ b/common/shared_mem.c
@@ -0,0 +1,54 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Shared memory module for Chrome EC */
+
+#include "shared_mem.h"
+#include "uart.h"
+
+/* Size of shared memory buffer */
+#define SHARED_MEM_SIZE 4096
+
+static char shared_buf[SHARED_MEM_SIZE];
+static int buf_in_use = 0;
+
+
+int shared_mem_init(void)
+{
+ return EC_SUCCESS;
+}
+
+
+int shared_mem_size(void)
+{
+ return SHARED_MEM_SIZE;
+}
+
+
+int shared_mem_acquire(int size, int wait, char **dest_ptr)
+{
+ if (size > SHARED_MEM_SIZE || size <= 0)
+ return EC_ERROR_INVAL;
+
+ /* TODO: if task_start() hasn't been called, fail immediately
+ * if not available. */
+
+ /* TODO: wait if requested; for now, we fail immediately if
+ * not available. */
+ if (buf_in_use)
+ return EC_ERROR_BUSY;
+
+ /* TODO: atomically acquire buf_in_use. */
+ buf_in_use = 1;
+ *dest_ptr = shared_buf;
+ return EC_SUCCESS;
+}
+
+
+void shared_mem_release(void *ptr)
+{
+ /* TODO: use event to wake up a previously-blocking acquire */
+ buf_in_use = 0;
+}
diff --git a/common/smart_battery.h b/common/smart_battery.h
new file mode 100644
index 0000000000..0aa91f6bff
--- /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 0000000000..0bfe3aa193
--- /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 0000000000..d4285653ed
--- /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 0000000000..a532144df2
--- /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 0000000000..4706ce895d
--- /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 0000000000..28c98e4130
--- /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 0000000000..885d830175
--- /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 0000000000..4f8934bcec
--- /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 0000000000..a911ae436b
--- /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 0000000000..a6fff4be08
--- /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 0000000000..8da6792154
--- /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 0000000000..1ec95442fe
--- /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 0000000000..35006ae77f
--- /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 0000000000..6e3f3489e6
--- /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 0000000000..766af75143
--- /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 0000000000..4d974251cd
--- /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 0000000000..65e86f423e
--- /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 0000000000..9a32d6fa66
--- /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 0000000000..3288e90445
--- /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 0000000000..19184c5389
--- /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 0000000000..22c358771b
--- /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 0000000000..c4008672ef
--- /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 0000000000..ceb2bc873f
--- /dev/null
+++ b/include/common.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* common.h - Common includes for Chrome EC */
+
+#ifndef __CROS_EC_COMMON_H
+#define __CROS_EC_COMMON_H
+
+#include <stdint.h>
+
+/* List of common error codes that can be returned */
+enum ec_error_list {
+ /* Success - no error */
+ EC_SUCCESS = 0,
+ /* Unknown error */
+ EC_ERROR_UNKNOWN = 1,
+ /* Function not implemented yet */
+ EC_ERROR_UNIMPLEMENTED = 2,
+ /* Overflow error; too much input provided. */
+ EC_ERROR_OVERFLOW = 3,
+ /* Timeout */
+ EC_ERROR_TIMEOUT = 4,
+ /* Invalid argument */
+ EC_ERROR_INVAL = 5,
+ /* Already in use */
+ EC_ERROR_BUSY = 6,
+
+ /* Module-internal error codes may use this range. */
+ EC_ERROR_INTERNAL_FIRST = 0x10000,
+ EC_ERROR_INTERNAL_LAST = 0x1FFFF
+};
+
+#endif /* __CROS_EC_COMMON_H */
diff --git a/include/console.h b/include/console.h
new file mode 100644
index 0000000000..4aeca1c5f4
--- /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 0000000000..70290e17d4
--- /dev/null
+++ b/include/eeprom.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* EEPROM module for Chrome EC */
+
+#ifndef __CROS_EC_EEPROM_H
+#define __CROS_EC_EEPROM_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int eeprom_init(void);
+
+/* Returns the number of EEPROM blocks on the system. */
+int eeprom_get_block_count(void);
+
+/* Returns the EEPROM block size in bytes. */
+int eeprom_get_block_size(void);
+
+/* Reads <size> bytes of data from <offset> in <block> of EEPROM. Offset
+ * and size must be a multiple of 32 bits. */
+int eeprom_read(int block, int offset, int size, char *data);
+
+/* Writes <size> bytes of data to <offset> in <block> of EEPROM. Offset
+ * and size must be a multiple of 32 bits. */
+int eeprom_write(int block, int offset, int size, const char *data);
+
+/* Hides an EEPROM block until the next reset. */
+int eeprom_hide(int block);
+
+/* TODO: write protect */
+
+#endif /* __CROS_EC_EEPROM_H */
diff --git a/include/flash.h b/include/flash.h
new file mode 100644
index 0000000000..38944e43cb
--- /dev/null
+++ b/include/flash.h
@@ -0,0 +1,88 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Flash memory module for Chrome EC */
+
+#ifndef __CROS_EC_FLASH_H
+#define __CROS_EC_FLASH_H
+
+#include "common.h"
+
+
+#define FLASH_WRITE_BYTES 4
+#define FLASH_FWB_WORDS 32
+#define FLASH_FWB_BYTES (FLASH_FWB_WORDS * 4)
+#define FLASH_ERASE_BYTES 1024
+#define FLASH_PROTECT_BYTES 2048
+
+
+/* Initializes the module. */
+int flash_init(void);
+
+/* Returns the usable size of flash in bytes. Note that this is
+ * smaller than the actual flash size, */
+int flash_get_size(void);
+
+/* Returns the write / erase / protect block size, in bytes.
+ * Operations must be aligned to and multiples of the granularity.
+ * For example, erase operations must have offset and size which are
+ * multiples of the erase block size. */
+int flash_get_write_block_size(void);
+int flash_get_erase_block_size(void);
+int flash_get_protect_block_size(void);
+
+/* Reads <size> bytes of data from offset <offset> into <data>. */
+int flash_read(int offset, int size, char *data);
+
+/* Writes <size> bytes of data to flash at byte offset <offset>.
+ * <data> must be 32-bit aligned. */
+int flash_write(int offset, int size, const char *data);
+
+/* Erases <size> bytes of flash at byte offset <offset>. */
+int flash_erase(int offset, int size);
+
+/* TODO: not super happy about the following APIs yet.
+ *
+ * The theory of operation is that we'll use the last page of flash to
+ * hold the write protect range, and the flag for whether the last
+ * page itself should be protected. Then when flash_init() is called,
+ * it checks if the write protect pin is asserted, and if so, it
+ * writes (but does not commit) the flash protection registers.
+ *
+ * This simulates what a SPI flash does, where the status register
+ * holds the write protect range, and a bit which protects the status
+ * register itself. The bit is only obeyed if the write protect pin
+ * is enabled.
+ *
+ * It's an imperfect simulation, because in a SPI flash, as soon as
+ * you deassert the pin you can alter the status register, where here
+ * it'll take a cold boot to clear protection. Also, here protection
+ * gets written to the registers as soon as you set the write protect
+ * lock, which is different than SPI, where it's effective as soon as
+ * you set the write protect range. */
+
+/* Gets or sets the write protect range in bytes. This setting is
+ * stored in flash, and persists across reboots. If size is non-zero,
+ * the write protect range is also locked, and may not be subsequently
+ * altered until after a cold boot with the write protect pin
+ * deasserted. */
+int flash_get_write_protect_range(int *offset, int *size);
+int flash_set_write_protect_range(int offset, int size);
+
+/* The write protect range has been stored into the chip registers
+ * this boot. The flash is write protected and the range cannot be
+ * changed without rebooting. */
+#define EC_FLASH_WP_RANGE_LOCKED 0x01
+/* The write protect pin was asserted at init time. */
+#define EC_FLASH_WP_PIN_ASSERTED_AT_INIT 0x02
+/* The write protect pin is asserted now. */
+#define EC_FLASH_WP_PIN_ASSERTED_NOW 0x04
+
+/* Returns the current write protect status; see EC_FLASH_WP_*
+ * for valid flags. */
+int flash_get_write_protect_status(void);
+
+
+#endif /* __CROS_EC_FLASH_H */
diff --git a/include/flash_commands.h b/include/flash_commands.h
new file mode 100644
index 0000000000..5efc7e2a67
--- /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 0000000000..4a351346eb
--- /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 0000000000..9c2dda2a69
--- /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 0000000000..384e3613dd
--- /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 0000000000..34076b1f34
--- /dev/null
+++ b/include/i2c.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* I2C interface for Chrome EC */
+
+#ifndef __CROS_EC_I2C_H
+#define __CROS_EC_I2C_H
+
+#include "common.h"
+
+/* Flags for slave address field, in addition to the 8-bit address */
+#define I2C_FLAG_BIG_ENDIAN 0x100 /* 16 byte values are MSB-first */
+
+/* Initializes the module. */
+int i2c_init(void);
+
+/* Reads a 16-bit register from the slave at 8-bit slave address
+ * <slaveaddr>, at the specified 8-bit <offset> in the slave's address
+ * space. */
+int i2c_read16(int port, int slave_addr, int offset, int* data);
+
+/* Writes a 16-bit register to the slave at 8-bit slave address
+ * <slaveaddr>, at the specified 8-bit <offset> in the slave's address
+ * space. */
+int i2c_write16(int port, int slave_addr, int offset, int data);
+
+#endif /* __CROS_EC_I2C_H */
diff --git a/include/i8042.h b/include/i8042.h
new file mode 100644
index 0000000000..c3d858bee1
--- /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 0000000000..6dd83a92bf
--- /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 0000000000..98394e63c7
--- /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 0000000000..8bc10c2c17
--- /dev/null
+++ b/include/keyboard_scan.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Keyboard scanner module for Chrome EC */
+
+#ifndef __CROS_EC_KEYBOARD_SCAN_H
+#define __CROS_EC_KEYBOARD_SCAN_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int keyboard_scan_init(void);
+
+#endif /* __CROS_KEYBOARD_SCAN_H */
diff --git a/include/lpc.h b/include/lpc.h
new file mode 100644
index 0000000000..d87d9bab7c
--- /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 0000000000..151256cde5
--- /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 0000000000..7967a63800
--- /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 0000000000..060ee7bbd8
--- /dev/null
+++ b/include/port80.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Port 80 module for Chrome EC */
+
+#ifndef __CROS_EC_PORT80_H
+#define __CROS_EC_PORT80_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int port_80_init(void);
+
+/* Called by LPC module when a byte of data is written to port 80. */
+void port_80_write(int data);
+
+#endif /* __CROS_EC_PORT80_H */
diff --git a/include/power_button.h b/include/power_button.h
new file mode 100644
index 0000000000..c6cf4f4e24
--- /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 0000000000..78acfc095b
--- /dev/null
+++ b/include/powerdemo.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Power state machine demo module for Chrome EC */
+
+#ifndef __CROS_EC_POWERDEMO_H
+#define __CROS_EC_POWERDEMO_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int power_demo_init(void);
+
+#endif /* __CROS_EC_POWERDEMO_H */
diff --git a/include/pwm.h b/include/pwm.h
new file mode 100644
index 0000000000..50534f2434
--- /dev/null
+++ b/include/pwm.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* PWM module for Chrome EC */
+
+#ifndef __CROS_EC_PWM_H
+#define __CROS_EC_PWM_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int pwm_init(void);
+
+/* Gets the current fan RPM. */
+int pwm_get_fan_rpm(void);
+
+/* Sets the target fan RPM. Pass -1 to set fan to maximum. */
+int pwm_set_fan_target_rpm(int rpm);
+
+/* Sets the keyboard backlight percentage (0=off, 100=max). */
+int pwm_set_keyboard_backlight(int percent);
+
+/* Sets the power LED brightness to the specified percent (0=off, 100=max). */
+int pwm_set_power_led(int percent);
+
+#endif /* __CROS_EC_PWM_H */
diff --git a/include/pwm_commands.h b/include/pwm_commands.h
new file mode 100644
index 0000000000..31f397c6fe
--- /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 0000000000..14b6613a9a
--- /dev/null
+++ b/include/shared_mem.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Shared memory interface for Chrome EC.
+ *
+ * This is intended to supply a relatively large block of memory for
+ * use by a task for a relatively short amount of time. For example,
+ * verified boot may need a buffer to hold signature data during a
+ * verification operation. It is NOT intended for allocating
+ * long-term buffers; those should in general be static variables
+ * allocated at compile-time. It is NOT a full-featured replacement
+ * for malloc() / free(). */
+
+#ifndef __CROS_EC_SHARED_MEM_H
+#define __CROS_EC_SHARED_MEM_H
+
+#include "common.h"
+
+/* Initializes the module. */
+int shared_mem_init(void);
+
+/* Returns the maximum amount of shared memory which can be acquired,
+ * in bytes. */
+int shared_mem_size(void);
+
+/* Acquires a shared memory area of the requested size in bytes. If
+ * wait != 0, will wait for the area to be available; if wait == 0,
+ * will fail with EC_ERROR_BUSY if the request cannot be fulfilled
+ * immediately. On success, sets *dest_ptr to the start of the memory
+ * area and returns EC_SUCCESS. */
+int shared_mem_acquire(int size, int wait, char **dest_ptr);
+
+/* Releases a shared memory area previously allocated via
+ * shared_mem_acquire(). */
+void shared_mem_release(void *ptr);
+
+#endif /* __CROS_EC_SHARED_MEM_H */
diff --git a/include/system.h b/include/system.h
new file mode 100644
index 0000000000..23607cfcaa
--- /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 0000000000..17a83def98
--- /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 0000000000..a2fb17ca7a
--- /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 0000000000..a89dbe7e5a
--- /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 0000000000..6971804895
--- /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 0000000000..b73ac292bb
--- /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 0000000000..334ee624bb
--- /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 0000000000..0b3b7a497f
--- /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 0000000000..5a76a66a72
--- /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 0000000000..430c617a52
--- /dev/null
+++ b/include/vboot.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Verified boot module for Chrome EC */
+
+#ifndef __CROS_EC_VBOOT_H
+#define __CROS_EC_VBOOT_H
+
+#include "common.h"
+
+/* Pre-initializes the module. This occurs before clocks or tasks are
+ * set up. */
+int vboot_pre_init(void);
+
+/* Initializes the module. */
+int vboot_init(void);
+
+#endif /* __CROS_EC_VBOOT_H */
diff --git a/include/version.h b/include/version.h
new file mode 100644
index 0000000000..0031339c34
--- /dev/null
+++ b/include/version.h
@@ -0,0 +1,16 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Version number for Chrome EC */
+
+#ifndef __CROS_EC_VERSION_H
+#define __CROS_EC_VERSION_H
+
+#define CROS_EC_VERSION_MAJOR 0
+#define CROS_EC_VERSION_MINOR 1
+#define CROS_EC_VERSION_SUBMINOR 2
+#define CROS_EC_VERSION_STRING "Link.0.1.2"
+
+#endif /* __CROS_EC_VERSION_H */
diff --git a/include/watchdog.h b/include/watchdog.h
new file mode 100644
index 0000000000..75a5e0b4a5
--- /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 0000000000..ce28e1b571
--- /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 0000000000..7bc5731ac9
--- /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 0000000000..a717185fd0
--- /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 0000000000..d0c0002bd9
--- /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 0000000000..a5b7244e36
--- /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 0000000000..afd342938c
--- /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 0000000000..92b2ae72d7
--- /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 0000000000..18115060d3
--- /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 0000000000..df7d4b38c5
--- /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 0000000000..dd95534238
--- /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 0000000000..c809c1eac5
--- /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 0000000000..e0f7466954
--- /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 0000000000..1169491c86
--- /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 0000000000..d62e9f7634
--- /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 0000000000..eee74b4df7
--- /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 0000000000..b0c37a4eaf
--- /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 0000000000..86d9b7ab49
--- /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 0000000000..7fced26663
--- /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 0000000000..55db7177b8
--- /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 0000000000..24949545f5
--- /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 0000000000..3c377b4c18
--- /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 0000000000..ad0802ba06
--- /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 0000000000..ffc82215d2
--- /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 0000000000..73850f2aab
--- /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;
+}