diff options
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, ®); + 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, ®); + 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 Binary files differnew file mode 100755 index 0000000000..ad0802ba06 --- /dev/null +++ b/util/qemu-system-arm 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; +} |