summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJett Rink <jettrink@chromium.org>2019-06-06 10:06:43 -0600
committerJett Rink <jettrink@chromium.org>2019-06-06 10:25:59 -0600
commit19533aef25fcfd8cd61a2349adecb49b47b3e4db (patch)
tree1dc7c5b664a14698e0f6cac8b0166f55a2358cca
parent03172d95f0be5f8e67a8d334e0ae6d45019b6206 (diff)
parentd1a9035163f19b9130d417c58d646879f243617b (diff)
downloadchrome-ec-19533aef25fcfd8cd61a2349adecb49b47b3e4db.tar.gz
merge R75 branch into sarien factory
Sarien factory branch is used to build ISH FW, and this needs to be as up to date as possible for ISH. There have been many ISH and driver commits on R75 that also need to make it to the sarien factory branch. Just pull everything over to the factory branch since the platform/ec code in the factory branch is only used to build ISH. BRANCH=none BUG=b:130614915 TEST=build arcada_ish on factory branch Change-Id: I5c6b1ecea60fd67203e5750820f9d95f6d50c59d
-rw-r--r--Makefile.rules42
-rw-r--r--Makefile.toolchain4
-rw-r--r--baseboard/dragonegg/baseboard.c9
-rw-r--r--baseboard/grunt/baseboard.c7
-rw-r--r--baseboard/grunt/usb_pd_policy.c14
-rw-r--r--baseboard/hatch/baseboard.c83
-rw-r--r--baseboard/hatch/baseboard.h6
-rw-r--r--baseboard/kalista/baseboard.c4
-rw-r--r--baseboard/octopus/variant_usbc_ec_tcpcs.c6
-rw-r--r--baseboard/octopus/variant_usbc_standalone_tcpcs.c9
-rw-r--r--board/arcada_ish/board.c39
-rw-r--r--board/arcada_ish/board.h17
-rw-r--r--board/arcada_ish/gpio.inc8
-rw-r--r--board/atlas/board.c6
-rw-r--r--board/bloog/board.c28
-rw-r--r--board/bloog/board.h5
-rw-r--r--board/bobba/gpio.inc4
-rw-r--r--board/casta/board.h3
-rw-r--r--board/casta/gpio.inc3
-rw-r--r--board/casta/led.c2
-rw-r--r--board/cheza/board.c7
-rw-r--r--board/chocodile_vpdmcu/board.c70
-rw-r--r--board/chocodile_vpdmcu/board.h139
-rw-r--r--board/chocodile_vpdmcu/build.mk16
-rw-r--r--board/chocodile_vpdmcu/ec.tasklist22
-rw-r--r--board/chocodile_vpdmcu/gpio.inc80
-rw-r--r--board/chocodile_vpdmcu/usb_pd_config.h163
-rw-r--r--board/chocodile_vpdmcu/vpd_api.c531
-rw-r--r--board/chocodile_vpdmcu/vpd_api.h276
-rw-r--r--board/coral/board.c6
-rw-r--r--board/cr50/board.c54
-rw-r--r--board/cr50/board.h24
-rw-r--r--board/cr50/gpio.inc22
-rw-r--r--board/cr50/power_button.c13
-rw-r--r--board/cr50/u2f.c17
-rw-r--r--board/cr50/wp.c139
-rw-r--r--board/cr50/wp.h4
-rw-r--r--board/eve/board.c6
-rw-r--r--board/fizz/board.c4
-rw-r--r--board/flapjack/battery.c24
-rw-r--r--board/flapjack/board.c90
-rw-r--r--board/flapjack/board.h52
-rw-r--r--board/flapjack/gpio.inc7
-rw-r--r--board/flapjack/usb_pd_policy.c6
-rw-r--r--board/fleex/board.c4
-rw-r--r--board/fleex/gpio.inc4
-rw-r--r--board/glkrvp/chg_usb_pd.c6
-rw-r--r--board/glkrvp_ite/chg_usb_pd.c6
-rw-r--r--board/hatch/board.c53
-rw-r--r--board/hatch/board.h8
-rw-r--r--board/hatch_fp/board.c85
-rw-r--r--board/hatch_fp/board.h184
-rw-r--r--board/hatch_fp/build.mk14
-rw-r--r--board/hatch_fp/dev_key.pem39
-rw-r--r--board/hatch_fp/ec.tasklist23
-rw-r--r--board/hatch_fp/flash_fp_mcu35
-rw-r--r--board/hatch_fp/gpio.inc36
-rw-r--r--board/kohaku/battery.c93
-rw-r--r--board/kohaku/board.c357
-rw-r--r--board/kohaku/board.h136
-rw-r--r--board/kohaku/build.mk15
-rw-r--r--board/kohaku/ec.tasklist27
-rw-r--r--board/kohaku/gpio.inc128
-rw-r--r--board/kohaku/led.c79
-rw-r--r--board/kukui/board.h5
-rw-r--r--board/kukui/led.c69
-rw-r--r--board/kukui_scp/board.h17
-rw-r--r--board/meep/board.c4
-rw-r--r--board/meep/gpio.inc4
-rw-r--r--board/nami/board.c6
-rw-r--r--board/nautilus/board.c6
-rw-r--r--board/nocturne/board.c6
-rw-r--r--board/npcx7_evb/board.h7
-rw-r--r--board/npcx7_evb/build.mk1
-rw-r--r--board/phaser/board.c4
-rw-r--r--board/phaser/gpio.inc4
-rw-r--r--board/poppy/board.c6
-rw-r--r--board/rammus/board.c6
-rw-r--r--board/reef/board.c6
-rw-r--r--board/reef_mchp/board.c6
-rw-r--r--board/rowan/battery.c47
-rw-r--r--board/rowan/board.c499
-rw-r--r--board/rowan/board.h234
-rw-r--r--board/rowan/build.mk14
-rw-r--r--board/rowan/ec.tasklist21
-rw-r--r--board/rowan/gpio.inc76
-rw-r--r--board/rowan/led.c156
-rw-r--r--board/rowan/usb_pd_policy.c367
-rw-r--r--board/servo_v4/usb_pd_policy.c13
-rw-r--r--board/yorp/board.c4
-rw-r--r--chip/g/board_id.c26
-rw-r--r--chip/g/board_space.h12
-rw-r--r--chip/g/flash.c106
-rw-r--r--chip/g/flash_config.h2
-rw-r--r--chip/g/rdd.c6
-rw-r--r--chip/g/uart.c11
-rw-r--r--chip/g/upgrade_fw.c2
-rw-r--r--chip/host/config_chip.h18
-rw-r--r--chip/ish/aontaskfw/ish_aon_share.h2
-rw-r--r--chip/ish/aontaskfw/ish_aontask.c120
-rw-r--r--chip/ish/aontaskfw/ish_aontask.ld.in16
-rw-r--r--chip/ish/build.mk2
-rw-r--r--chip/ish/clock.c11
-rw-r--r--chip/ish/config_chip.h58
-rw-r--r--chip/ish/dma.c32
-rw-r--r--chip/ish/gpio.c5
-rw-r--r--chip/ish/heci.c24
-rw-r--r--chip/ish/heci_client.h2
-rw-r--r--chip/ish/host_command_heci.c13
-rw-r--r--chip/ish/hpet.h10
-rw-r--r--chip/ish/hwtimer.c14
-rw-r--r--chip/ish/i2c.c1
-rw-r--r--chip/ish/ipc_heci.c121
-rw-r--r--chip/ish/ipc_heci.h4
-rw-r--r--chip/ish/ish_dma.h19
-rw-r--r--chip/ish/ish_fwst.h38
-rw-r--r--chip/ish/ish_persistent_data.c59
-rw-r--r--chip/ish/ish_persistent_data.h41
-rw-r--r--chip/ish/power_mgt.c327
-rw-r--r--chip/ish/power_mgt.h19
-rw-r--r--chip/ish/registers.h245
-rw-r--r--chip/ish/system.c102
-rw-r--r--chip/ish/system_state_subsys.c10
-rw-r--r--chip/ish/uart.c154
-rw-r--r--chip/ish/uart_defs.h208
-rw-r--r--chip/ish/watchdog.c45
-rw-r--r--chip/mt_scp/build.mk2
-rw-r--r--chip/mt_scp/clock.c17
-rw-r--r--chip/mt_scp/config_chip.h1
-rw-r--r--chip/mt_scp/ipi.c12
-rw-r--r--chip/mt_scp/memory_regions.inc1
-rw-r--r--chip/mt_scp/stepping_stone.c22
-rw-r--r--chip/npcx/config_chip-npcx7.h27
-rw-r--r--chip/npcx/config_flash_layout.h3
-rw-r--r--chip/npcx/gpio.c2
-rw-r--r--chip/npcx/registers.h6
-rw-r--r--chip/npcx/system.c5
-rw-r--r--chip/npcx/wov.c2
-rw-r--r--chip/stm32/adc-stm32f0.c8
-rw-r--r--chip/stm32/registers.h1
-rw-r--r--chip/stm32/spi.c17
-rw-r--r--chip/stm32/spi_master.c51
-rw-r--r--chip/stm32/trng.c7
-rw-r--r--chip/stm32/usb_pd_phy.c11
-rw-r--r--common/acpi.c4
-rw-r--r--common/build.mk10
-rw-r--r--common/button.c2
-rw-r--r--common/cbi.c3
-rw-r--r--common/ccd_config.c16
-rw-r--r--common/charge_manager.c9
-rw-r--r--common/ec_features.c2
-rw-r--r--common/factory_mode.c39
-rw-r--r--common/flash.c2
-rw-r--r--common/gpio.c2
-rw-r--r--common/keyboard_backlight.c2
-rw-r--r--common/led_pwm.c3
-rw-r--r--common/mkbp_event.c291
-rw-r--r--common/motion_sense.c161
-rw-r--r--common/new_nvmem.c2957
-rw-r--r--common/nvmem.c185
-rw-r--r--common/nvmem_vars.c411
-rw-r--r--common/panic_output.c1
-rw-r--r--common/pinweaver.c56
-rw-r--r--common/shmalloc.c2
-rw-r--r--common/system.c39
-rw-r--r--common/tablet_mode.c42
-rw-r--r--common/tpm_registers.c24
-rw-r--r--common/usb_pd_protocol.c80
-rw-r--r--common/usb_pd_tcpc.c28
-rw-r--r--common/usb_pe_sm.c12
-rw-r--r--common/usb_prl_sm.c2163
-rw-r--r--common/usb_sm.c166
-rw-r--r--common/usb_tc_sm.c203
-rw-r--r--core/minute-ia/build.mk3
-rw-r--r--core/minute-ia/ec.lds.S4
-rw-r--r--core/minute-ia/ia_structs.h2
-rw-r--r--core/minute-ia/interrupts.c114
-rw-r--r--core/minute-ia/irq_handler.h72
-rw-r--r--core/minute-ia/irq_handler_common.S68
-rw-r--r--core/minute-ia/panic.c58
-rw-r--r--core/minute-ia/switch.S93
-rw-r--r--core/minute-ia/task.c32
-rw-r--r--core/minute-ia/task_defs.h5
-rw-r--r--driver/accelgyro_bmi160.c2
-rw-r--r--driver/accelgyro_bmi160.h11
-rw-r--r--driver/accelgyro_lsm6dsm.c196
-rw-r--r--driver/accelgyro_lsm6dsm.h14
-rw-r--r--driver/als_si114x.c2
-rw-r--r--driver/bc12/pi3usb9201.c48
-rw-r--r--driver/mag_bmm150.h16
-rw-r--r--driver/mag_lis2mdl.c218
-rw-r--r--driver/mag_lis2mdl.h51
-rw-r--r--driver/tcpm/anx74xx.c9
-rw-r--r--driver/tcpm/tcpci.c7
-rwxr-xr-xextra/i2c_pseudo/install20
-rw-r--r--fuzz/build.mk5
-rw-r--r--fuzz/cr50_fuzz.cc1
-rw-r--r--fuzz/fuzz_config.h23
-rw-r--r--fuzz/nvmem_tpm2_mock.c228
-rw-r--r--include/ccd_config.h13
-rw-r--r--include/common.h32
-rw-r--r--include/config.h130
-rw-r--r--include/ec_commands.h2
-rw-r--r--include/flash_log.h33
-rw-r--r--include/gpio.h6
-rw-r--r--include/host_command_heci.h2
-rw-r--r--include/link_defs.h2
-rw-r--r--include/mkbp_event.h2
-rw-r--r--include/motion_sense.h31
-rw-r--r--include/new_nvmem.h155
-rw-r--r--include/nvmem.h18
-rw-r--r--include/nvmem_vars.h35
-rw-r--r--include/panic.h9
-rw-r--r--include/reset_flag_desc.inc28
-rw-r--r--include/tablet_mode.h9
-rw-r--r--include/task.h5
-rw-r--r--include/timer.h29
-rw-r--r--include/usb_emsg.h23
-rw-r--r--include/usb_pd.h75
-rw-r--r--include/usb_pd_tcpm.h28
-rw-r--r--include/usb_pe_ctvpd_sm.h239
-rw-r--r--include/usb_pe_sm.h79
-rw-r--r--include/usb_prl_sm.h213
-rw-r--r--include/usb_sm.h67
-rw-r--r--include/usb_tc_ctvpd_sm.h2039
-rw-r--r--include/usb_tc_sm.h91
-rw-r--r--include/usb_tc_vpd_sm.h486
-rw-r--r--power/common.c1
-rw-r--r--test/build.mk23
-rw-r--r--test/kb_mkbp.c59
-rw-r--r--test/legacy_nvmem_dump.h1043
-rw-r--r--test/motion_angle.c2
-rw-r--r--test/motion_angle_tablet.c2
-rw-r--r--test/motion_common.h1
-rw-r--r--test/motion_lid.c3
-rw-r--r--test/nvmem.c1655
-rw-r--r--test/nvmem_test.h28
-rw-r--r--test/nvmem_tpm2_mock.c377
-rw-r--r--test/nvmem_vars.c538
-rw-r--r--test/pinweaver.c84
-rw-r--r--test/test_config.h151
-rw-r--r--test/usb_pd_test_util.h2
-rw-r--r--test/usb_prl.c1306
-rw-r--r--test/usb_prl.tasklist19
l---------test/usb_sm_framework_h0.tasklist1
l---------test/usb_sm_framework_h1.tasklist1
l---------test/usb_sm_framework_h2.tasklist1
-rw-r--r--test/usb_sm_framework_h3.c1219
-rw-r--r--test/usb_sm_framework_h3.tasklist18
-rw-r--r--test/usb_typec_ctvpd.c1488
-rw-r--r--test/usb_typec_ctvpd.tasklist (renamed from test/nvmem_vars.tasklist)5
l---------test/usb_typec_vpd.tasklist1
-rw-r--r--test/vpd_api.c586
-rw-r--r--test/vpd_api.h333
-rw-r--r--util/build.mk4
-rwxr-xr-xutil/ecst.c8
-rw-r--r--util/ectool.c54
-rwxr-xr-xutil/flash_ec22
-rw-r--r--util/flash_fp_mcu_common.sh4
-rwxr-xr-xutil/getversion.sh3
-rw-r--r--util/iteflash.md119
-rwxr-xr-xutil/signer/bs15
-rw-r--r--util/stm32mon.c663
-rw-r--r--util/usb_if.c17
264 files changed, 23959 insertions, 5066 deletions
diff --git a/Makefile.rules b/Makefile.rules
index 4a6cca7542..c37e9e3a32 100644
--- a/Makefile.rules
+++ b/Makefile.rules
@@ -170,12 +170,8 @@ buildall: build_boards
$(MAKE) runtests
@touch .tests-passed
@echo "$@ completed successfully!"
- @echo "Tightest boards' RW flash images, bytes free:"
- @grep . build/*/RW/space_free_flash.txt | \
- sed 's,build/\([^/]*\)/RW/space_free_flash.txt:\(.*\)$$,\2 \1,' | \
- sort -n | head -3 | while read size board; do \
- printf "%-10s: %6d\n" $$board $$size; \
- done
+ $(call cmd_stats,RO)
+ $(call cmd_stats,RW)
@echo "Tightest boards' RW RAM images, bytes free:"
@grep . build/*/RW/space_free_ram.txt | \
sed 's,build/\([^/]*\)/RW/space_free_ram.txt:\(.*\)$$,\2 \1,' | \
@@ -291,9 +287,11 @@ $(FAILED_BOARDS_DIR):
.PHONY: buildfuzztests
buildfuzztests: $(fuzz-test-targets)
-.PHONY: hosttests runtests
+.PHONY: hosttests runhosttests runfuzztests runtests
hosttests: $(host-test-targets)
-runtests: $(run-test-targets) $(run-fuzz-test-targets)
+runhosttests: $(run-test-targets)
+runfuzztests: $(run-fuzz-test-targets)
+runtests: runhosttests runfuzztests
# Automatically enumerate all suites.
cts_excludes := common
@@ -340,6 +338,7 @@ cmd_report_cov=genhtml -q -o build/host/coverage_rpt -t \
# Unless V is set to 0 we always want the 'size:' target to report its output,
# there is no point in generating a short form command trace when calculating
# size.
+# $1: 'RO' or 'RW'
ifeq ($(V),0)
cmd_size=
else
@@ -348,16 +347,27 @@ cmd_size=$(Q)awk '\
/^FLASH/ {flash_size = strtonum($$3)} \
/__ram_free =/ {ram_free = strtonum($$1)} \
END {room_free = flash_size - image_size; \
- print ram_free > "$(out)/RW/space_free_ram.txt"; \
+ print ram_free > "$(out)/$(1)/space_free_ram.txt"; \
printf " *** "; \
if (flash_size > 0) { \
- print room_free > "$(out)/RW/space_free_flash.txt"; \
+ print room_free > "$(out)/$(1)/space_free_flash.txt"; \
printf ("%s bytes in flash and ", room_free);\
} \
printf ("%s bytes in RAM still available on $(BOARD) ****\n", ram_free) \
- }' $(out)/RW/$(PROJECT).RW.map
+ }' $(out)/$(1)/$(PROJECT).$(1).map
endif
+# List the smallest free flash spaces
+# $1: 'RO' or 'RW'
+cmd_stats= \
+ $(Q)echo "Smallest free spaces in $(1) flash (bytes):"; \
+ grep . build/*/$(1)/space_free_flash.txt | \
+ sed 's,build/\([^/]*\)/$(1)/space_free_flash.txt:\(.*\)$$,\2 \1,' | \
+ sort -n | head -3 | \
+ while read size board; do \
+ printf "%-10s: %6d\n" $$board $$size; \
+ done
+
build/host/%.info: run-%
$(call quiet,lcov,COV )
@@ -740,7 +750,15 @@ analyzestack: $(out)/util/export_taskinfo.so
# Calculate size of remaining room in flash, using variables generated by
# linker.
size: $(out)/$(PROJECT).bin
- $(call cmd_size)
+ifneq ($(CONFIG_FW_INCLUDE_RO),)
+ $(call cmd_size,RO)
+endif
+ $(call cmd_size,RW)
+
+# Print the smallest spaces in flash
+stats: build_boards
+ $(call cmd_stats,RO)
+ $(call cmd_stats,RW)
.SECONDARY:
diff --git a/Makefile.toolchain b/Makefile.toolchain
index f6fa2b51eb..338caf0672 100644
--- a/Makefile.toolchain
+++ b/Makefile.toolchain
@@ -125,12 +125,14 @@ endif
BUILD_CFLAGS = $(LIBFTDIUSB_CFLAGS) $(BUILD_CPPFLAGS) -O3 $(CFLAGS_DEBUG)
BUILD_CFLAGS += $(CFLAGS_WARN)
HOST_CFLAGS=$(HOST_CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN) -DHOST_TOOLS_BUILD
+HOST_CFLAGS+=$(LIBFTDIUSB_CFLAGS)
ifneq (${SYSROOT},)
LDFLAGS_EXTRA+=--sysroot=${SYSROOT}
endif
LDFLAGS=-nostdlib -g -Wl,-X -Wl,--gc-sections -Wl,--build-id=none \
$(LDFLAGS_EXTRA) $(CFLAGS_CPU)
BUILD_LDFLAGS=$(LIBFTDIUSB_LDLIBS)
+HOST_LDFLAGS=$(LIBFTDIUSB_LDLIBS)
HOST_TEST_LDFLAGS=-Wl,-T core/host/host_exe.lds -lrt -pthread -rdynamic -lm\
-fuse-ld=bfd \
$(if $(TEST_COVERAGE),-fprofile-arcs,) \
@@ -143,4 +145,4 @@ HOST_TEST_LDFLAGS=-Wl,-T core/host/host_exe.lds -lrt -pthread -rdynamic -lm\
# $1: name of variable to set
# $2: first default to use
# $3: second default to use
-set-option = $(eval $1=$$(if $(2),$(2),$(3)))
+set-option = $(eval $1=$$(strip $$(if $(2),$(2),$(3))))
diff --git a/baseboard/dragonegg/baseboard.c b/baseboard/dragonegg/baseboard.c
index 54cc16a98d..dbf3a8fedd 100644
--- a/baseboard/dragonegg/baseboard.c
+++ b/baseboard/dragonegg/baseboard.c
@@ -166,20 +166,23 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
[USB_PD_PORT_ITE_0] = {
/* TCPC is embedded within EC so no i2c config needed */
.drv = &it83xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
[USB_PD_PORT_ITE_1] = {
/* TCPC is embedded within EC so no i2c config needed */
.drv = &it83xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
[USB_PD_PORT_TUSB422_2] = {
.i2c_host_port = I2C_PORT_USBC1C2,
.i2c_slave_addr = TUSB422_I2C_ADDR,
.drv = &tusb422_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/baseboard/grunt/baseboard.c b/baseboard/grunt/baseboard.c
index 70350cffec..e743c195a4 100644
--- a/baseboard/grunt/baseboard.c
+++ b/baseboard/grunt/baseboard.c
@@ -89,14 +89,15 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = I2C_PORT_TCPC0,
.i2c_slave_addr = ANX74XX_I2C_ADDR1,
.drv = &anx74xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
- .od = TCPC_ALERT_OPEN_DRAIN,
+ /* Alert is active-low, open-drain */
+ .flags = TCPC_FLAGS_ALERT_OD,
},
[USB_PD_PORT_PS8751] = {
.i2c_host_port = I2C_PORT_TCPC1,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/baseboard/grunt/usb_pd_policy.c b/baseboard/grunt/usb_pd_policy.c
index ab15a03172..97673cf162 100644
--- a/baseboard/grunt/usb_pd_policy.c
+++ b/baseboard/grunt/usb_pd_policy.c
@@ -240,11 +240,21 @@ static uint32_t dp_status[CONFIG_USB_PD_PORT_COUNT];
static void svdm_safe_dp_mode(int port)
{
+ const char *dp_str, *usb_str;
+
/* make DP interface safe until configure */
dp_flags[port] = 0;
dp_status[port] = 0;
- usb_mux_set(port, TYPEC_MUX_NONE,
- USB_SWITCH_CONNECT, pd_get_polarity(port));
+
+ /* Don't disconnect USB when initializing DP. This avoids an unnecessary
+ * disconnect if the result of DP negotiation is DOCK.
+ */
+ if (usb_mux_get(port, &dp_str, &usb_str) && usb_str)
+ usb_mux_set(port, TYPEC_MUX_USB, USB_SWITCH_CONNECT,
+ pd_get_polarity(port));
+ else
+ usb_mux_set(port, TYPEC_MUX_NONE, USB_SWITCH_CONNECT,
+ pd_get_polarity(port));
}
static int svdm_enter_dp_mode(int port, uint32_t mode_caps)
diff --git a/baseboard/hatch/baseboard.c b/baseboard/hatch/baseboard.c
index ad89d52284..6253c43469 100644
--- a/baseboard/hatch/baseboard.c
+++ b/baseboard/hatch/baseboard.c
@@ -33,9 +33,6 @@
#define CPRINTSUSB(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTFUSB(format, args...) cprintf(CC_USBCHARGE, format, ## args)
-#define USB_PD_PORT_TCPC_0 0
-#define USB_PD_PORT_TCPC_1 1
-
/******************************************************************************/
/* Wake up pins */
const enum gpio_signal hibernate_wake_pins[] = {
@@ -145,23 +142,6 @@ DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, baseboard_chipset_shutdown,
HOOK_PRIO_DEFAULT);
/******************************************************************************/
-/* USB-C TPCP Configuration */
-const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
- [USB_PD_PORT_TCPC_0] = {
- .i2c_host_port = I2C_PORT_TCPC0,
- .i2c_slave_addr = AN7447_TCPC0_I2C_ADDR,
- .drv = &anx7447_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
- },
- [USB_PD_PORT_TCPC_1] = {
- .i2c_host_port = I2C_PORT_TCPC1,
- .i2c_slave_addr = PS8751_I2C_ADDR1,
- .drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
- },
-};
-
-/******************************************************************************/
/* USB-C PPC Configuration */
struct ppc_config_t ppc_chips[CONFIG_USB_PD_PORT_COUNT] = {
[USB_PD_PORT_TCPC_0] = {
@@ -178,17 +158,6 @@ struct ppc_config_t ppc_chips[CONFIG_USB_PD_PORT_COUNT] = {
};
unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips);
-struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = {
- [USB_PD_PORT_TCPC_0] = {
- .driver = &anx7447_usb_mux_driver,
- .hpd_update = &anx7447_tcpc_update_hpd_status,
- },
- [USB_PD_PORT_TCPC_1] = {
- .driver = &tcpci_tcpm_usb_mux_driver,
- .hpd_update = &ps8xxx_tcpc_update_hpd_status,
- }
-};
-
const struct pi3usb2901_config_t pi3usb2901_bc12_chips[] = {
[USB_PD_PORT_TCPC_0] = {
.i2c_port = I2C_PORT_PPC0,
@@ -249,34 +218,38 @@ uint16_t tcpc_get_alert_status(void)
return status;
}
+static void reset_pd_port(int port, enum gpio_signal reset_gpio,
+ int hold_delay, int finish_delay)
+{
+ int level = !!(tcpc_config[port].flags & TCPC_FLAGS_RESET_ACTIVE_HIGH);
+
+ gpio_set_level(reset_gpio, level);
+ msleep(hold_delay);
+ gpio_set_level(reset_gpio, !level);
+ if (finish_delay)
+ msleep(finish_delay);
+}
+
void board_reset_pd_mcu(void)
{
/*
- * C0: Assert reset to TCPC0 (ANX7447) for required delay (1ms) only if
- * we have a battery
- *
- */
- if (battery_is_present() == BP_YES) {
- gpio_set_level(GPIO_USB_C0_TCPC_RST, 1);
- msleep(ANX74XX_RESET_HOLD_MS);
- gpio_set_level(GPIO_USB_C0_TCPC_RST, 0);
- msleep(ANX74XX_RESET_FINISH_MS);
- }
- /*
- * C1: Assert reset to TCPC1 (PS8751) for required delay (1ms) only if
- * we have a battery, otherwise we may brown out the system.
+ * TODO(b/130194590): This should be replaced with a common function
+ * once the gpio signal and delays are added to tcpc_config struct.
*/
- if (battery_is_present() == BP_YES) {
- /*
- * TODO(crbug:846412): After refactor, ensure that battery has
- * enough charge to last the reboot as well
- */
- gpio_set_level(GPIO_USB_C1_TCPC_RST_ODL, 0);
- msleep(PS8XXX_RESET_DELAY_MS);
- gpio_set_level(GPIO_USB_C1_TCPC_RST_ODL, 1);
- } else {
- CPRINTS("Skipping C1 TCPC reset because no battery");
- }
+
+ /* Assert reset to TCPC for required delay only if we have a battery. */
+ if (battery_is_present() != BP_YES)
+ return;
+
+ /* Reset TCPC0 */
+ reset_pd_port(USB_PD_PORT_TCPC_0, GPIO_USB_C0_TCPC_RST,
+ BOARD_TCPC_C0_RESET_HOLD_DELAY,
+ BOARD_TCPC_C0_RESET_POST_DELAY);
+
+ /* Reset TCPC1 */
+ reset_pd_port(USB_PD_PORT_TCPC_1, GPIO_USB_C1_TCPC_RST_ODL,
+ BOARD_TCPC_C1_RESET_HOLD_DELAY,
+ BOARD_TCPC_C1_RESET_POST_DELAY);
}
void board_pd_vconn_ctrl(int port, int cc_pin, int enabled)
diff --git a/baseboard/hatch/baseboard.h b/baseboard/hatch/baseboard.h
index 3bfb1e06cd..81b91dcb6c 100644
--- a/baseboard/hatch/baseboard.h
+++ b/baseboard/hatch/baseboard.h
@@ -40,6 +40,7 @@
#define CONFIG_POWER_COMMON
#define CONFIG_POWER_PP5000_CONTROL
#define CONFIG_POWER_S0IX
+#define CONFIG_POWER_S0IX_FAILURE_DETECTION
#define CONFIG_POWER_TRACK_HOST_SLEEP_STATE
/* Common Keyboard Defines */
@@ -95,8 +96,6 @@
#define CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT TYPEC_RP_3A0
#define CONFIG_USB_PD_TCPC_LOW_POWER
#define CONFIG_USB_PD_DUAL_ROLE_AUTO_TOGGLE
-#define CONFIG_USB_PD_TCPM_ANX7447
-#define CONFIG_USB_PD_TCPM_PS8751
#define CONFIG_USB_PD_DUAL_ROLE
#define CONFIG_USB_PD_LOGGING
#define CONFIG_USB_PD_ALT_MODE
@@ -115,6 +114,9 @@
#define CONFIG_CMD_PD_CONTROL
#define CONFIG_CMD_PPC_DUMP
+#define USB_PD_PORT_TCPC_0 0
+#define USB_PD_PORT_TCPC_1 1
+
/* BC 1.2 */
#define CONFIG_USB_CHARGER
#define CONFIG_BC12_DETECT_PI3USB9201
diff --git a/baseboard/kalista/baseboard.c b/baseboard/kalista/baseboard.c
index 53e75508a9..08fa73dd92 100644
--- a/baseboard/kalista/baseboard.c
+++ b/baseboard/kalista/baseboard.c
@@ -150,8 +150,8 @@ const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
/* TCPC mux configuration */
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
- {I2C_PORT_TCPC0, I2C_ADDR_TCPC0, &ps8xxx_tcpm_drv,
- TCPC_ALERT_ACTIVE_LOW},
+ /* Alert is active-low, push-pull */
+ {I2C_PORT_TCPC0, I2C_ADDR_TCPC0, &ps8xxx_tcpm_drv, 0},
};
static int ps8751_tune_mux(int port)
diff --git a/baseboard/octopus/variant_usbc_ec_tcpcs.c b/baseboard/octopus/variant_usbc_ec_tcpcs.c
index 570a919b6c..2bdb7d2c1d 100644
--- a/baseboard/octopus/variant_usbc_ec_tcpcs.c
+++ b/baseboard/octopus/variant_usbc_ec_tcpcs.c
@@ -31,12 +31,14 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
[USB_PD_PORT_ITE_0] = {
/* TCPC is embedded within EC so no i2c config needed */
.drv = &it83xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
[USB_PD_PORT_ITE_1] = {
/* TCPC is embedded within EC so no i2c config needed */
.drv = &it83xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/baseboard/octopus/variant_usbc_standalone_tcpcs.c b/baseboard/octopus/variant_usbc_standalone_tcpcs.c
index fbae3e70d0..4d4ac83b6d 100644
--- a/baseboard/octopus/variant_usbc_standalone_tcpcs.c
+++ b/baseboard/octopus/variant_usbc_standalone_tcpcs.c
@@ -35,18 +35,21 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
#if defined(VARIANT_OCTOPUS_TCPC_0_PS8751)
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW
+ /* Alert is active-low, push-pull */
+ .flags = 0,
#else
.i2c_slave_addr = AN7447_TCPC0_I2C_ADDR,
.drv = &anx7447_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
#endif
},
[USB_PD_PORT_TCPC_1] = {
.i2c_host_port = I2C_PORT_TCPC1,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/arcada_ish/board.c b/board/arcada_ish/board.c
index 22533e0d8c..8d1566723e 100644
--- a/board/arcada_ish/board.c
+++ b/board/arcada_ish/board.c
@@ -8,10 +8,12 @@
#include "console.h"
#include "driver/accel_lis2dh.h"
#include "driver/accelgyro_lsm6dsm.h"
+#include "driver/mag_lis2mdl.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "i2c.h"
+#include "lid_switch.h"
#include "motion_sense.h"
#include "power.h"
#include "tablet_mode.h"
@@ -31,11 +33,13 @@ const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
/* Sensor config */
static struct mutex g_lid_mutex;
+static struct mutex g_lid_mag_mutex;
static struct mutex g_base_mutex;
/* sensor private data */
static struct lsm6dsm_data lsm6dsm_a_data;
static struct stprivate_data g_lis2dh_data;
+static struct lis2mdl_private_data lis2mdl_a_data;
/* Matrix to rotate lid sensor into standard reference frame */
const mat33_fp_t lid_rot_ref = {
@@ -56,6 +60,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_lid_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_a_data,
MOTIONSENSE_TYPE_ACCEL),
+ .int_signal = GPIO_ACCEL_GYRO_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR1,
.rot_standard_ref = &lid_rot_ref,
@@ -84,10 +90,12 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_lid_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_a_data,
MOTIONSENSE_TYPE_GYRO),
+ .int_signal = GPIO_ACCEL_GYRO_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR1,
.default_range = 1000 | ROUND_UP_FLAG, /* dps */
- .rot_standard_ref = NULL, /* TODO rotate correctly */
+ .rot_standard_ref = &lid_rot_ref,
.min_frequency = LSM6DSM_ODR_MIN_VAL,
.max_frequency = LSM6DSM_ODR_MAX_VAL,
},
@@ -120,10 +128,37 @@ struct motion_sensor_t motion_sensors[] = {
},
},
- /* TODO(b/122281217): Add remain sensors */
+ [LID_MAG] = {
+ .name = "Lid Mag",
+ .active_mask = SENSOR_ACTIVE_S0,
+ .chip = MOTIONSENSE_CHIP_LIS2MDL,
+ .type = MOTIONSENSE_TYPE_MAG,
+ .location = MOTIONSENSE_LOC_LID,
+ .drv = &lis2mdl_drv,
+ .mutex = &g_lid_mag_mutex,
+ .drv_data = LIS2MDL_ST_DATA(lis2mdl_a_data),
+ .port = I2C_PORT_SENSOR,
+ .addr = LIS2MDL_ADDR,
+ .default_range = 1 << 11, /* 16LSB / uT, fixed */
+ .rot_standard_ref = &lid_rot_ref,
+ .min_frequency = LIS2MDL_ODR_MIN_VAL,
+ .max_frequency = LIS2MDL_ODR_MAX_VAL,
+ },
};
+
const unsigned int motion_sensor_count = ARRAY_SIZE(motion_sensors);
+int board_sensor_at_360(void)
+{
+ /*
+ * The 360 degree sensor is too sensitive and is active when the lid is
+ * closed at 0 degrees. Ignore the hall sensor when the lid close is
+ * also active.
+ */
+ return lid_is_open() &&
+ !gpio_get_level(HALL_SENSOR_GPIO_L);
+}
+
/* Initialize board. */
static void board_init(void)
{
diff --git a/board/arcada_ish/board.h b/board/arcada_ish/board.h
index 4dc4f89abb..8101180b30 100644
--- a/board/arcada_ish/board.h
+++ b/board/arcada_ish/board.h
@@ -25,14 +25,20 @@
#define CONFIG_I2C
#define CONFIG_I2C_MASTER
-#define CONFIG_ACCEL_LNG2DM /* Base sensor: LNG2DM (uses LIS2DH driver) */
-#define CONFIG_ACCELGYRO_LSM6DSM /* Lid sensor: LSM6DS3 (uses LSM6DSM driver) */
+#define CONFIG_ACCEL_LNG2DM /* Base sensor: LNG2DM
+ * (uses LIS2DH driver)
+ */
+#define CONFIG_ACCELGYRO_LSM6DSM /* Lid sensor: LSM6DS3
+ * (uses LSM6DSM driver)
+ */
+#define CONFIG_MAG_LIS2MDL /* Lid sensor: LIS2DML */
+#define CONFIG_MAG_CALIBRATE
#define CONFIG_ACCEL_INTERRUPTS
#define CONFIG_ACCEL_FIFO 256
#define CONFIG_ACCEL_FIFO_THRES (CONFIG_ACCEL_FIFO / 3)
/* Sensors without hardware FIFO are in forced mode */
-#define CONFIG_ACCEL_FORCE_MODE_MASK (1 << BASE_ACCEL)
+#define CONFIG_ACCEL_FORCE_MODE_MASK (BIT(BASE_ACCEL) | BIT(LID_MAG))
#define CONFIG_ACCEL_LSM6DSM_INT_EVENT \
TASK_EVENT_MOTION_SENSOR_INTERRUPT(LID_ACCEL)
@@ -45,6 +51,9 @@
#define CONFIG_LID_ANGLE_SENSOR_LID LID_ACCEL
#define CONFIG_TABLET_MODE
+#define CONFIG_HALL_SENSOR
+#define CONFIG_HALL_SENSOR_CUSTOM
+#define HALL_SENSOR_GPIO_L GPIO_TABLET_MODE_L
/* DMA paging between SRAM and DRAM */
#define CONFIG_DMA_PAGING
@@ -106,7 +115,7 @@ enum sensor_id {
LID_ACCEL,
LID_GYRO,
BASE_ACCEL,
- /* TODO(b/122281217): Add remain sensors */
+ LID_MAG,
SENSOR_COUNT
};
diff --git a/board/arcada_ish/gpio.inc b/board/arcada_ish/gpio.inc
index 60c8e959ae..91c25fe632 100644
--- a/board/arcada_ish/gpio.inc
+++ b/board/arcada_ish/gpio.inc
@@ -5,11 +5,11 @@
* found in the LICENSE file.
*/
-GPIO_INT(ACCEL_GYRO_INT_L, PIN(0), GPIO_INT_FALLING | GPIO_SEL_1P8V, lsm6dsm_interrupt)
+GPIO_INT(ACCEL_GYRO_INT_L, PIN(0), GPIO_INT_FALLING, lsm6dsm_interrupt)
+GPIO_INT(LID_OPEN, PIN(5), GPIO_INT_BOTH, lid_interrupt) /* LID_CL_NB_L */
+GPIO_INT(TABLET_MODE_L, PIN(6), GPIO_INT_BOTH, hall_sensor_isr) /* LID_CL_TAB_L */
-GPIO(NB_MODE_L, PIN(4), GPIO_OUT_LOW)
-GPIO(LID_OPEN, PIN(5), GPIO_INPUT) /* LID_CL_NB_L */
-GPIO(LID_CL_TAB_L, PIN(6), GPIO_INPUT)
+GPIO(NB_MODE_L, PIN(4), GPIO_OUT_LOW)
/*
* We don't have a ENTERING_RW signal wired to the cr50 but common code needs
diff --git a/board/atlas/board.c b/board/atlas/board.c
index e0fc4d07e7..fd38f1cee2 100644
--- a/board/atlas/board.c
+++ b/board/atlas/board.c
@@ -174,14 +174,16 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = I2C_PORT_TCPC0,
.i2c_slave_addr = I2C_ADDR_TCPC,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
{
/* right port */
.i2c_host_port = I2C_PORT_TCPC1,
.i2c_slave_addr = I2C_ADDR_TCPC,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/bloog/board.c b/board/bloog/board.c
index ea8b246e89..f61621c95b 100644
--- a/board/bloog/board.c
+++ b/board/bloog/board.c
@@ -111,15 +111,15 @@ static struct mutex g_base_mutex;
/* Matrix to rotate accelrator into standard reference frame */
const mat33_fp_t lid_standrd_ref = {
- { FLOAT_TO_FP(1), 0, 0},
- { 0, FLOAT_TO_FP(-1), 0},
+ { FLOAT_TO_FP(-1), 0, 0},
+ { 0, FLOAT_TO_FP(1), 0},
{ 0, 0, FLOAT_TO_FP(-1)}
};
const mat33_fp_t base_standard_ref = {
- { FLOAT_TO_FP(1), 0, 0},
+ { FLOAT_TO_FP(-1), 0, 0},
{ 0, FLOAT_TO_FP(-1), 0},
- { 0, 0, FLOAT_TO_FP(-1)}
+ { 0, 0, FLOAT_TO_FP(1)}
};
/* sensor private data */
@@ -163,6 +163,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_ACCEL),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.rot_standard_ref = &base_standard_ref,
@@ -193,6 +195,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_GYRO),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.default_range = 1000 | ROUND_UP_FLAG, /* dps */
@@ -300,3 +304,19 @@ void board_overcurrent_event(int port, int is_overcurrented)
/* Note that the level is inverted because the pin is active low. */
gpio_set_level(GPIO_USB_C_OC, !is_overcurrented);
}
+
+uint32_t board_override_feature_flags0(uint32_t flags0)
+{
+ /*
+ * Remove keyboard backlight feature for devices that don't support it.
+ */
+ if (sku_id == 33)
+ return (flags0 & ~EC_FEATURE_MASK_0(EC_FEATURE_PWM_KEYB));
+ else
+ return flags0;
+}
+
+uint32_t board_override_feature_flags1(uint32_t flags1)
+{
+ return flags1;
+}
diff --git a/board/bloog/board.h b/board/bloog/board.h
index 48716744a5..075d70ecc0 100644
--- a/board/bloog/board.h
+++ b/board/bloog/board.h
@@ -24,6 +24,8 @@
#define CONFIG_LED_COMMON
#define OCTOPUS_POWER_LED
+#define CONFIG_EC_FEATURE_BOARD_OVERRIDE
+
/* Sensors */
#define CONFIG_ACCEL_KX022 /* Lid accel */
#define CONFIG_ACCELGYRO_LSM6DSM /* Base accel */
@@ -37,7 +39,8 @@
#define CONFIG_LID_ANGLE_UPDATE
#define CONFIG_LID_ANGLE_SENSOR_BASE BASE_ACCEL
#define CONFIG_LID_ANGLE_SENSOR_LID LID_ACCEL
-
+#define CONFIG_PWM
+#define CONFIG_PWM_KBLIGHT
#define CONFIG_TEMP_SENSOR
#define CONFIG_THERMISTOR
#define CONFIG_STEINHART_HART_3V3_13K7_47K_4050B
diff --git a/board/bobba/gpio.inc b/board/bobba/gpio.inc
index b36cf41cea..5078b5fb59 100644
--- a/board/bobba/gpio.inc
+++ b/board/bobba/gpio.inc
@@ -132,8 +132,8 @@ GPIO(BAT_LED_ORANGE_L, PIN(C, 3), GPIO_OUT_HIGH) /* LED_1_L */
GPIO(BAT_LED_BLUE_L, PIN(C, 4), GPIO_OUT_HIGH) /* LED_2_L */
GPIO(LED_3_L, PIN(D, 7), GPIO_OUT_HIGH)
-/* Keyboard Backlight */
-GPIO(KB_BL_PWR_EN, PIN(6, 2), GPIO_OUT_LOW)
+/* Not implemented in hardware */
+UNIMPLEMENTED(KB_BL_PWR_EN)
/* MKBP event synchronization */
GPIO(EC_INT_L, PIN(9, 4), GPIO_ODR_HIGH)
diff --git a/board/casta/board.h b/board/casta/board.h
index c99ae1e696..e7afd7a51b 100644
--- a/board/casta/board.h
+++ b/board/casta/board.h
@@ -36,9 +36,6 @@
/* TODO(b/119872005): Casta: confirm thermistor parts */
#define CONFIG_STEINHART_HART_3V3_13K7_47K_4050B
#define CONFIG_STEINHART_HART_3V3_51K1_47K_4050B
-#define CONFIG_MKBP_EVENT
-#undef CONFIG_MKBP_USE_GPIO
-#define CONFIG_MKBP_USE_HOST_EVENT
/* Battery W/A */
#define CONFIG_CHARGER_PROFILE_OVERRIDE
diff --git a/board/casta/gpio.inc b/board/casta/gpio.inc
index 7f4a164605..c827d18a97 100644
--- a/board/casta/gpio.inc
+++ b/board/casta/gpio.inc
@@ -126,6 +126,9 @@ GPIO(BAT_LED_RED_L, PIN(C, 3), GPIO_OUT_HIGH)
GPIO(BAT_LED_GREEN_L, PIN(C, 4), GPIO_OUT_HIGH)
GPIO(PWR_LED_BLUE_L, PIN(D, 7), GPIO_OUT_HIGH)
+/* MKBP event synchronization */
+GPIO(EC_INT_L, PIN(9, 4), GPIO_ODR_HIGH) /* EC_AP_INT_ODL */
+
/* Not implemented in hardware */
UNIMPLEMENTED(KB_BL_PWR_EN)
diff --git a/board/casta/led.c b/board/casta/led.c
index 81defdd6d2..f5aecd24bf 100644
--- a/board/casta/led.c
+++ b/board/casta/led.c
@@ -24,7 +24,7 @@ struct led_descriptor led_bat_state_table[LED_NUM_STATES][LED_NUM_PHASES] = {
[STATE_CHARGING_LVL_1] = {{EC_LED_COLOR_RED, LED_INDEFINITE} },
[STATE_CHARGING_LVL_2] = {{EC_LED_COLOR_RED, LED_INDEFINITE} },
[STATE_CHARGING_FULL_CHARGE] = {{EC_LED_COLOR_GREEN, LED_INDEFINITE} },
- [STATE_DISCHARGE_S0] = {{LED_OFF, LED_INDEFINITE} },
+ [STATE_DISCHARGE_S0] = {{EC_LED_COLOR_GREEN, LED_INDEFINITE} },
[STATE_DISCHARGE_S0_BAT_LOW] = {{LED_OFF, LED_INDEFINITE} },
[STATE_DISCHARGE_S3] = {{LED_OFF, LED_INDEFINITE} },
[STATE_DISCHARGE_S5] = {{LED_OFF, LED_INDEFINITE} },
diff --git a/board/cheza/board.c b/board/cheza/board.c
index 460708e566..5d9413b4a2 100644
--- a/board/cheza/board.c
+++ b/board/cheza/board.c
@@ -256,10 +256,11 @@ unsigned int ppc_cnt = ARRAY_SIZE(ppc_chips);
/* TCPC mux configuration */
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
+ /* Alert is active-low, open-drain */
[USB_PD_PORT_ANX3429] = {I2C_PORT_TCPC0, 0x50, &anx74xx_tcpm_drv,
- TCPC_ALERT_ACTIVE_LOW, TCPC_ALERT_OPEN_DRAIN},
- [USB_PD_PORT_PS8751] = {I2C_PORT_TCPC1, 0x16, &ps8xxx_tcpm_drv,
- TCPC_ALERT_ACTIVE_LOW},
+ TCPC_FLAGS_ALERT_OD},
+ /* Alert is active-low, push-pull */
+ [USB_PD_PORT_PS8751] = {I2C_PORT_TCPC1, 0x16, &ps8xxx_tcpm_drv, 0},
};
/*
diff --git a/board/chocodile_vpdmcu/board.c b/board/chocodile_vpdmcu/board.c
new file mode 100644
index 0000000000..ea4d129502
--- /dev/null
+++ b/board/chocodile_vpdmcu/board.c
@@ -0,0 +1,70 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* chocodile board configuration */
+
+#include "adc.h"
+#include "adc_chip.h"
+#include "common.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "i2c.h"
+#include "registers.h"
+#include "switch.h"
+#include "system.h"
+#include "task.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpc.h"
+#include "util.h"
+#include "vpd_api.h"
+
+#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
+
+void board_config_pre_init(void)
+{
+ /* enable SYSCFG clock */
+ STM32_RCC_APB2ENR |= 1 << 0;
+}
+
+#include "gpio_list.h"
+
+/* Initialize board. */
+static void board_init(void)
+{
+ /* Do nothing */
+}
+DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
+
+/* ADC channels */
+const struct adc_t adc_channels[] = {
+ /* USB PD CC lines sensing. Converted to mV (3300mV/4096). */
+ [ADC_VCONN_VSENSE] = {
+ "VCONN_VSENSE", 3000, 4096, 0, STM32_AIN(ADC_VCONN_VSENSE)},
+ [ADC_CC_VPDMCU] = {
+ "CC_VPDMCU", 3000, 4096, 0, STM32_AIN(ADC_CC_VPDMCU)},
+ [ADC_CC_RP3A0_RD_L] = {
+ "CC_RP3A0_RD_L", 3000, 4096, 0, STM32_AIN(ADC_CC_RP3A0_RD_L)},
+ [ADC_RDCONNECT_REF] = {
+ "RDCONNECT_REF", 3000, 4096, 0, STM32_AIN(ADC_RDCONNECT_REF)},
+ [ADC_CC1_RP3A0_RD_L] = {
+ "CC1_RP1A5_ODH", 3000, 4096, 0, STM32_AIN(ADC_CC1_RP3A0_RD_L)},
+ [ADC_CC2_RP3A0_RD_L] = {
+ "CC2_RP1A5_ODH", 3000, 4096, 0, STM32_AIN(ADC_CC2_RP3A0_RD_L)},
+ [ADC_HOST_VBUS_VSENSE] = {
+ "HOST_VBUS_VSENSE", 3000, 4096, 0, STM32_AIN(ADC_HOST_VBUS_VSENSE)},
+ [ADC_CHARGE_VBUS_VSENSE] = {
+ "CHARGE_VBUS_VSENSE", 3000, 4096, 0, STM32_AIN(ADC_CHARGE_VBUS_VSENSE)},
+ [ADC_CC1_RPUSB_ODH] = {
+ "CC1_RPUSB_ODH", 3000, 4096, 0, STM32_AIN(ADC_CC1_RPUSB_ODH)},
+ [ADC_CC2_RPUSB_ODH] = {
+ "CC2_RPUSB_ODH", 3000, 4096, 0, STM32_AIN(ADC_CC2_RPUSB_ODH)},
+};
+BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
+
+void tcpc_alert_clear(int port)
+{
+ /* Do nothing */
+}
diff --git a/board/chocodile_vpdmcu/board.h b/board/chocodile_vpdmcu/board.h
new file mode 100644
index 0000000000..850d1d0011
--- /dev/null
+++ b/board/chocodile_vpdmcu/board.h
@@ -0,0 +1,139 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* chocodile_mcu board configuration */
+
+#ifndef __CROS_EC_BOARD_H
+#define __CROS_EC_BOARD_H
+
+/*
+ * The console task is too big to include in both RO and RW images. Therefore,
+ * if the console task is defined, then only build an RW image. This can be
+ * useful for debugging to have a full console. Otherwise, without this task,
+ * a full RO and RW is built with a limited one-way output console.
+ */
+#ifdef HAS_TASK_CONSOLE
+/*
+ * The flash size is only 32kB.
+ * No space for 2 partitions,
+ * put only RW at the beginning of the flash
+ */
+#undef CONFIG_FW_INCLUDE_RO
+#undef CONFIG_RW_MEM_OFF
+#define CONFIG_RW_MEM_OFF 0
+#undef CONFIG_RO_SIZE
+#define CONFIG_RO_SIZE 0
+/* Fake full size if we had a RO partition */
+#undef CONFIG_RW_SIZE
+#define CONFIG_RW_SIZE CONFIG_FLASH_SIZE
+#endif /* HAS_TASK_CONSOLE */
+
+/* 48 MHz SYSCLK clock frequency */
+#define CPU_CLOCK 48000000
+
+/* the UART console is on USART1 (PA9/PA10) */
+#undef CONFIG_UART_CONSOLE
+#define CONFIG_UART_CONSOLE 1
+
+/* Optional features */
+#define CONFIG_ADC
+#undef CONFIG_ADC_WATCHDOG
+#define CONFIG_ADC_SAMPLE_TIME STM32_ADC_SMPR_41_5_CY
+#define CONFIG_BOARD_PRE_INIT
+#define CONFIG_COMMON_GPIO_SHORTNAMES
+#undef CONFIG_DEBUG_ASSERT
+#define CONFIG_FORCE_CONSOLE_RESUME
+#define CONFIG_HIBERNATE
+#undef CONFIG_HOSTCMD_EVENTS
+#define CONFIG_HW_CRC
+#undef CONFIG_LID_SWITCH
+#define CONFIG_LOW_POWER_IDLE
+#define CONFIG_LTO
+#define CONFIG_STM_HWTIMER32
+#undef CONFIG_TASK_PROFILING
+#undef CONFIG_UART_TX_BUF_SIZE
+#undef CONFIG_UART_TX_DMA
+#undef CONFIG_UART_RX_DMA
+#define CONFIG_UART_TX_BUF_SIZE 128
+#define CONFIG_USB_PD_PORT_COUNT 1
+#define CONFIG_USB_PD_TCPC
+#define CONFIG_USB_PD_VBUS_DETECT_NONE
+#define CONFIG_USB_PD_TCPM_STUB
+#define CONFIG_USB_SM_FRAMEWORK
+#define CONFIG_USB_TYPEC_CTVPD
+#define CONFIG_USB_PD_DUAL_ROLE
+#define CONFIG_USB_PD_INTERNAL_COMP
+#define CONFIG_VBOOT_HASH
+#define CONFIG_WATCHDOG
+#undef CONFIG_WATCHDOG_HELP
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 3
+
+#define CONFIG_USB_PID 0x5036
+#define VPD_HW_VERSION 0x0001
+#define VPD_FW_VERSION 0x0001
+
+/* USB bcdDevice */
+#define USB_BCD_DEVICE 0
+
+/* Vbus impedance in milliohms */
+#define VPD_VBUS_IMPEDANCE 65
+
+/* GND impedance in milliohms */
+#define VPD_GND_IMPEDANCE 33
+
+/*
+ * TODO(crosbug.com/p/50519): Remove CONFIG_SYSTEM_UNLOCKED prior to building
+ * MP FW.
+ */
+#define CONFIG_SYSTEM_UNLOCKED
+
+#ifdef HAS_TASK_CONSOLE
+#undef CONFIG_CONSOLE_HISTORY
+#define CONFIG_CONSOLE_HISTORY 2
+
+#else
+#undef CONFIG_CONSOLE_CMDHELP
+#define CONFIG_DEBUG_PRINTF
+#define UARTN CONFIG_UART_CONSOLE
+#define UARTN_BASE STM32_USART_BASE(CONFIG_UART_CONSOLE)
+#endif /* HAS_TASK_CONSOLE */
+
+/* Use PSTATE embedded in the RO image, not in its own erase block */
+#undef CONFIG_FLASH_PSTATE_BANK
+#undef CONFIG_FW_PSTATE_SIZE
+#define CONFIG_FW_PSTATE_SIZE 0
+
+#ifndef __ASSEMBLER__
+
+/* Timer selection */
+#define TIM_CLOCK32 2
+#define TIM_ADC 3
+
+#include "gpio_signal.h"
+
+/* ADC signal */
+enum adc_channel {
+ ADC_VCONN_VSENSE = 0,
+ ADC_CC_VPDMCU,
+ ADC_CC_RP3A0_RD_L,
+ ADC_RDCONNECT_REF,
+ ADC_CC1_RP3A0_RD_L,
+ ADC_CC2_RP3A0_RD_L,
+ ADC_HOST_VBUS_VSENSE,
+ ADC_CHARGE_VBUS_VSENSE,
+ ADC_CC1_RPUSB_ODH,
+ ADC_CC2_RPUSB_ODH,
+ /* Number of ADC channels */
+ ADC_CH_COUNT
+};
+
+/* 1.5A Rp */
+#define PD_SRC_VNC PD_SRC_1_5_VNC_MV
+#define PD_SRC_RD_THRESHOLD PD_SRC_1_5_RD_THRESH_MV
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* __CROS_EC_BOARD_H */
diff --git a/board/chocodile_vpdmcu/build.mk b/board/chocodile_vpdmcu/build.mk
new file mode 100644
index 0000000000..d4e5f58962
--- /dev/null
+++ b/board/chocodile_vpdmcu/build.mk
@@ -0,0 +1,16 @@
+# -*- makefile -*-
+# Copyright 2019 The Chromium OS 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 STM32F051K8U6TR
+CHIP:=stm32
+CHIP_FAMILY:=stm32f0
+CHIP_VARIANT:=stm32f05x
+
+board-y=board.o vpd_api.o
+#
+# This target builds RW only. Therefore, remove RO from dependencies.
+all_deps=$(patsubst ro,,$(def_all_deps))
diff --git a/board/chocodile_vpdmcu/ec.tasklist b/board/chocodile_vpdmcu/ec.tasklist
new file mode 100644
index 0000000000..e386c745e3
--- /dev/null
+++ b/board/chocodile_vpdmcu/ec.tasklist
@@ -0,0 +1,22 @@
+/* Copyright 2019 The Chromium OS 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_ALWAYS(n, r, d, s) for base tasks and
+ * TASK_NOTEST(n, r, d, s) for tasks that can be excluded in test binaries,
+ * 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
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TASK_LIST \
+ TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/board/chocodile_vpdmcu/gpio.inc b/board/chocodile_vpdmcu/gpio.inc
new file mode 100644
index 0000000000..a34c617ef1
--- /dev/null
+++ b/board/chocodile_vpdmcu/gpio.inc
@@ -0,0 +1,80 @@
+/* -*- mode:c -*-
+ *
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Declare symbolic names for all the GPIOs that we care about.
+ * Note: Those with interrupt handlers must be declared first. */
+
+/* Divided Vconn voltage sense */
+GPIO(VCONN_VSENSE, PIN(A, 0), GPIO_ANALOG)
+/* CC ADC, PD in comparator, or tx enable out (low) */
+GPIO(CC_VPDMCU, PIN(A, 1), GPIO_ANALOG)
+/* CC 0.2V comparator during charge-through, active Rd (low) or Rp3A0 (high) */
+GPIO(CC_RP3A0_RD_L, PIN(A, 2), GPIO_ANALOG)
+/* 0.2V resistor divider for various CC comapators */
+GPIO(RDCONNECT_REF, PIN(A, 3), GPIO_ANALOG)
+/* Charger CC1 0.2V comparator and ADC, drive a Rp3A0 (high) or Rd (low) */
+GPIO(CC1_RP3A0_RD_L, PIN(A, 4), GPIO_ANALOG)
+/* Charger CC2 0.2V comparator and ADC, drive a Rp3A0 (high) or Rd (low) */
+GPIO(CC2_RP3A0_RD_L, PIN(A, 5), GPIO_ANALOG)
+/* Divided host VBUS voltage sense */
+GPIO(HOST_VBUS_VSENSE, PIN(A, 6), GPIO_ANALOG)
+/* Divided charger VBUS voltage sense */
+GPIO(CHARGER_VBUS_VSENSE, PIN(A, 7), GPIO_ANALOG)
+/* Charger CC1 ADC, or drive a RpUSB (high) */
+GPIO(CC1_RPUSB_ODH, PIN(B, 0), GPIO_ANALOG)
+/* Charger CC2 ADC, or drive a RpUSB (high) */
+GPIO(CC2_RPUSB_ODH, PIN(B, 1), GPIO_ANALOG)
+
+/* PD TX data output */
+GPIO(CC_TX_DATA, PIN(B, 4), GPIO_INPUT)
+
+/* Enables the VBUS pass-through (high) */
+GPIO(VBUS_PASS_EN, PIN(B, 2), GPIO_OUT_LOW)
+
+/*
+ * Desired billboard state. One of "no billboard/nothing connected" (low),
+ * "source connected but not in charge-through" (pull-up), or "sink connected"
+ * (high)
+ */
+GPIO(PRESENT_BILLBOARD, PIN(A, 8), GPIO_OUT_LOW)
+
+/* Enables cReceiver and the path to the PD RX/TX, RpUSB, and Rp1A5 */
+GPIO(VPDMCU_CC_EN, PIN(A, 11), GPIO_OUT_LOW)
+
+/* Disables dead battery Rd on host side (low) */
+GPIO(CC_DB_EN_OD, PIN(A, 12), GPIO_ODR_HIGH)
+
+/* RpUSB on host side (high) */
+GPIO(CC_RPUSB_ODH, PIN(A, 13), GPIO_INPUT)
+
+/*
+ * Controls the dead-battery pull-downs on charger side; either dead battery
+ * Rd (low) or Hi-Z (high)
+ */
+GPIO(CC1_CC2_DB_EN_L, PIN(A, 15), GPIO_OUT_LOW)
+
+/* Chooses between Vconn (low) and VBUS (high) */
+GPIO(VCONN_PWR_SEL_ODL, PIN(B, 6), GPIO_INPUT)
+
+/* Passes CC1 to the host CC (high) */
+GPIO(CC1_SEL, PIN(F, 0), GPIO_OUT_LOW)
+/* Passes CC2 to the host CC (high) */
+GPIO(CC2_SEL, PIN(F, 1), GPIO_OUT_LOW)
+/* Debug red LED driver (low). Keep off for power measurements */
+GPIO(DEBUG_LED_R_L, PIN(B, 5), GPIO_ODR_HIGH)
+/* Debug green LED driver (low). Keep off for power measurements */
+GPIO(DEBUG_LED_G_L, PIN(B, 7), GPIO_ODR_HIGH)
+
+UNIMPLEMENTED(WP_L)
+UNIMPLEMENTED(ENTERING_RW)
+
+/* SCK(PB3): PD_TX_CLK_IN - Clock input for PD TX */
+ALTERNATE(PIN_MASK(B, 0x0008), 0, MODULE_USB_PD, 0)
+/* TIM16_CH1(PB8): PD_TX_CLK_OUT - Clock generator for PD TX */
+ALTERNATE(PIN_MASK(B, 0x0100), 2, MODULE_USB_PD, 0)
+/* USART1 (PA9/PA10): TX/RX for debug and programming */
+ALTERNATE(PIN_MASK(A, 0x0600), 1, MODULE_UART, 0)
diff --git a/board/chocodile_vpdmcu/usb_pd_config.h b/board/chocodile_vpdmcu/usb_pd_config.h
new file mode 100644
index 0000000000..a6c1adbc61
--- /dev/null
+++ b/board/chocodile_vpdmcu/usb_pd_config.h
@@ -0,0 +1,163 @@
+/* Copyright 2019 The Chromium OS 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 "adc.h"
+#include "chip/stm32/registers.h"
+#include "ec_commands.h"
+#include "gpio.h"
+#include "vpd_api.h"
+
+/* USB Power delivery board configuration */
+
+#ifndef __CROS_EC_USB_PD_CONFIG_H
+#define __CROS_EC_USB_PD_CONFIG_H
+
+/* Timer selection for baseband PD communication */
+#define TIM_CLOCK_PD_TX_C0 16
+#define TIM_CLOCK_PD_RX_C0 1
+
+#define TIM_CLOCK_PD_TX(p) TIM_CLOCK_PD_TX_C0
+#define TIM_CLOCK_PD_RX(p) TIM_CLOCK_PD_RX_C0
+
+/* Timer channel */
+#define TIM_TX_CCR_C0 1
+#define TIM_RX_CCR_C0 1
+
+/* RX timer capture/compare register */
+#define TIM_CCR_C0 (&STM32_TIM_CCRx(TIM_CLOCK_PD_RX_C0, TIM_RX_CCR_C0))
+#define TIM_RX_CCR_REG(p) TIM_CCR_C0
+
+/* TX and RX timer register */
+#define TIM_REG_TX_C0 (STM32_TIM_BASE(TIM_CLOCK_PD_TX_C0))
+#define TIM_REG_RX_C0 (STM32_TIM_BASE(TIM_CLOCK_PD_RX_C0))
+#define TIM_REG_TX(p) TIM_REG_TX_C0
+#define TIM_REG_RX(p) TIM_REG_RX_C0
+
+/* use the hardware accelerator for CRC */
+#define CONFIG_HW_CRC
+
+/* TX uses SPI1 on PB3-4 for port C0 */
+#define SPI_REGS(p) STM32_SPI1_REGS
+
+static inline void spi_enable_clock(int port)
+{
+ STM32_RCC_APB2ENR |= STM32_RCC_PB2_SPI1;
+}
+
+/* SPI1_TX no remap needed */
+#define DMAC_SPI_TX(p) STM32_DMAC_CH3
+
+/* RX is using COMP1 triggering TIM1 CH1 */
+#define CMP1OUTSEL STM32_COMP_CMP1OUTSEL_TIM1_IC1
+#define CMP2OUTSEL 0
+
+#define TIM_TX_CCR_IDX(p) TIM_TX_CCR_C0
+#define TIM_RX_CCR_IDX(p) TIM_RX_CCR_C0
+#define TIM_CCR_CS 1
+
+/* EXTI line 21 is connected to the CMP1 output */
+#define EXTI_COMP1_MASK (1 << 21)
+/* EXTI line 22 is connected to the CMP1 output */
+#define EXTI_COMP2_MASK (1 << 22)
+
+#define EXTI_COMP_MASK(p) (EXTI_COMP1_MASK | EXTI_COMP2_MASK)
+#define IRQ_COMP STM32_IRQ_COMP
+/* triggers packet detection on comparator falling edge */
+#define EXTI_XTSR STM32_EXTI_FTSR
+
+/* TIM1_CH1 no remap needed */
+#define DMAC_TIM_RX(p) STM32_DMAC_CH2
+
+/* the pins used for communication need to be hi-speed */
+static inline void pd_set_pins_speed(int port)
+{
+ /*
+ * 40 MHz pin speed on SPI PB3&4,
+ * (USB_C0_TX_CLKIN & USB_C0_CC1_TX_DATA)
+ *
+ * 40 MHz pin speed on TIM17_CH1 (PB7),
+ * (PD_TX_CLK_OUT)
+ */
+ STM32_GPIO_OSPEEDR(GPIO_B) |= 0x0000C3C0;
+}
+
+/* Reset SPI peripheral used for TX */
+static inline void pd_tx_spi_reset(int port)
+{
+ /* Reset SPI1 */
+ STM32_RCC_APB2RSTR |= (1 << 12);
+ STM32_RCC_APB2RSTR &= ~(1 << 12);
+}
+
+/* Drive the CC line from the TX block */
+static inline void pd_tx_enable(int port, int polarity)
+{
+ /* USB_CC_TX_DATA: PB4 is SPI1 MISO */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ & ~(3 << (2*4))) /* PB4 disable ADC */
+ | (2 << (2*4)); /* Set as SPI1_MISO */
+ /* MCU ADC PA1 pin output low */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ & ~(3 << (2*1))) /* PA1 disable ADC */
+ | (1 << (2*1)); /* Set as GPO */
+ gpio_set_level(GPIO_CC_VPDMCU, 0);
+}
+
+/* Put the TX driver in Hi-Z state */
+static inline void pd_tx_disable(int port, int polarity)
+{
+ /* Set CC_TX_DATA to Hi-Z, PB4 is SPI1 MISO */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ & ~(3 << (2*4)));
+ /* set ADC PA1 pin to ADC function (Hi-Z) */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*1))); /* PA1 as ADC */
+}
+
+/* we know the plug polarity, do the right configuration */
+static inline void pd_select_polarity(int port, int polarity)
+{
+ /*
+ * use the right comparator : CC1 -> PA1 (COMP1 INP)
+ * use VrefInt / 2 as INM (about 600mV)
+ */
+ STM32_COMP_CSR = (STM32_COMP_CSR & ~STM32_COMP_CMP1INSEL_MASK)
+ | STM32_COMP_CMP1EN | STM32_COMP_CMP1INSEL_VREF12;
+}
+
+/* Initialize pins used for TX and put them in Hi-Z */
+static inline void pd_tx_init(void)
+{
+ gpio_config_module(MODULE_USB_PD, 1);
+}
+
+static inline void pd_set_host_mode(int port, int enable)
+{
+ /* Do nothing */
+}
+
+/**
+ * Initialize various GPIOs and interfaces to safe state at start of pd_task.
+ *
+ * These include:
+ * Physical layer CC transmit.
+ *
+ * @param port USB-C port number
+ * @param power_role Power role of device
+ */
+static inline void pd_config_init(int port, uint8_t power_role)
+{
+ /* Initialize TX pins and put them in Hi-Z */
+ pd_tx_init();
+ pd_tx_disable(0, 0);
+}
+
+static inline int pd_adc_read(int port, int cc)
+{
+ return 0;
+}
+
+#endif /* __CROS_EC_USB_PD_CONFIG_H */
+
diff --git a/board/chocodile_vpdmcu/vpd_api.c b/board/chocodile_vpdmcu/vpd_api.c
new file mode 100644
index 0000000000..ecbedd082f
--- /dev/null
+++ b/board/chocodile_vpdmcu/vpd_api.c
@@ -0,0 +1,531 @@
+/* Copyright 2019 The Chromium OS 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 "adc.h"
+#include "gpio.h"
+#include "registers.h"
+#include "vpd_api.h"
+#include "driver/tcpm/tcpm.h"
+
+/*
+ * Polarity based on 'DFP Perspective' (see table 4-10 USB Type-C Cable and
+ * Connector Specification Release 1.3)
+ *
+ * CC1 CC2 STATE POSITION
+ * ----------------------------------------
+ * open open NC N/A
+ * Rd open UFP attached 1
+ * open Rd UFP attached 2
+ * open Ra pwr cable no UFP N/A
+ * Ra open pwr cable no UFP N/A
+ * Rd Ra pwr cable & UFP 1
+ * Ra Rd pwr cable & UFP 2
+ * Rd Rd dbg accessory N/A
+ * Ra Ra audio accessory N/A
+ *
+ * Note, V(Rd) > V(Ra)
+ */
+#ifndef PD_SRC_RD_THRESHOLD
+#define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV
+#endif
+#ifndef PD_SRC_VNC
+#define PD_SRC_VNC PD_SRC_DEF_VNC_MV
+#endif
+
+#undef CC_RA
+#define CC_RA(cc, sel) (cc < pd_src_rd_threshold[sel])
+#undef CC_RD
+#define CC_RD(cc, sel) ((cc >= pd_src_rd_threshold[sel]) && (cc < PD_SRC_VNC))
+
+/* (15.8K / (100K + 15.8K)) * 1000 = 136.4 */
+#define VBUS_SCALE_FACTOR 136
+/* (118K / (100K + 118K)) * 1000 = 541.3 */
+#define VCONN_SCALE_FACTOR 541
+
+#define VBUS_DETECT_THRESHOLD 2500 /* mV */
+#define VCONN_DETECT_THRESHOLD 2500 /* mV */
+
+#define SCALE(vmeas, sfactor) (((vmeas) * 1000) / (sfactor))
+
+/*
+ * Type C power source charge current limits are identified by their cc
+ * voltage (set by selecting the proper Rd resistor). Any voltage below
+ * TYPE_C_SRC_500_THRESHOLD will not be identified as a type C charger.
+ */
+#define TYPE_C_SRC_DEFAULT_THRESHOLD 200 /* mV */
+#define TYPE_C_SRC_1500_THRESHOLD 660 /* mV */
+#define TYPE_C_SRC_3000_THRESHOLD 1230 /* mV */
+
+/* Charge-Through pull up/down enabled */
+static int ct_cc_pull;
+/* Charge-Through pull up value */
+static int ct_cc_rp_value;
+
+/* Charge-Through pull up/down enabled */
+static int host_cc_pull;
+/* Charge-Through pull up value */
+static int host_cc_rp_value;
+
+/* Voltage thresholds for Ra attach in normal SRC mode */
+static int pd_src_rd_threshold[TYPEC_RP_RESERVED] = {
+ PD_SRC_DEF_RD_THRESH_MV,
+ PD_SRC_1_5_RD_THRESH_MV,
+ PD_SRC_3_0_RD_THRESH_MV,
+};
+
+/* Convert CC voltage to CC status */
+static int vpd_cc_voltage_to_status(int cc_volt, int cc_pull)
+{
+ /* If we have a pull-up, then we are source, check for Rd. */
+ if (cc_pull == TYPEC_CC_RP) {
+ if (CC_RD(cc_volt, ct_cc_rp_value))
+ return TYPEC_CC_RD;
+ else if (CC_RA(cc_volt, ct_cc_rp_value))
+ return TYPEC_CC_VOLT_RA;
+ else
+ return TYPEC_CC_VOLT_OPEN;
+ /* If we have a pull-down, then we are sink, check for Rp. */
+ } else if (cc_pull == TYPEC_CC_RD || cc_pull == TYPEC_CC_RA_RD) {
+ if (cc_volt >= TYPE_C_SRC_3000_THRESHOLD)
+ return TYPEC_CC_VOLT_RP_3_0;
+ else if (cc_volt >= TYPE_C_SRC_1500_THRESHOLD)
+ return TYPEC_CC_VOLT_RP_1_5;
+ else if (cc_volt >= TYPE_C_SRC_DEFAULT_THRESHOLD)
+ return TYPEC_CC_VOLT_RP_DEF;
+ else
+ return TYPEC_CC_VOLT_OPEN;
+ } else {
+ /* If we are open, then always return 0 */
+ return 0;
+ }
+}
+
+void vpd_ct_set_pull(int pull, int rp_value)
+{
+ ct_cc_pull = pull;
+
+ switch (pull) {
+ case TYPEC_CC_RP:
+ ct_cc_rp_value = rp_value;
+ vpd_cc1_cc2_db_en_l(GPO_HIGH);
+ switch (rp_value) {
+ case TYPEC_RP_USB:
+ vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc1_rpusb_odh(PIN_GPO, 1);
+ vpd_config_cc2_rpusb_odh(PIN_GPO, 1);
+ break;
+ case TYPEC_RP_3A0:
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_GPO, 1);
+ vpd_config_cc2_rp3a0_rd_l(PIN_GPO, 1);
+ break;
+ }
+ break;
+ case TYPEC_CC_RD:
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_GPO, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_GPO, 0);
+ vpd_cc1_cc2_db_en_l(GPO_HIGH);
+ break;
+ case TYPEC_CC_OPEN:
+ vpd_cc1_cc2_db_en_l(GPO_HIGH);
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0);
+ break;
+ }
+}
+
+void vpd_ct_get_cc(int *cc1, int *cc2)
+{
+ int cc1_v;
+ int cc2_v;
+
+ switch (ct_cc_pull) {
+ case TYPEC_CC_RP:
+ switch (ct_cc_rp_value) {
+ case TYPEC_RP_USB:
+ cc1_v = adc_read_channel(ADC_CC1_RP3A0_RD_L);
+ cc2_v = adc_read_channel(ADC_CC2_RP3A0_RD_L);
+ break;
+ case TYPEC_RP_3A0:
+ cc1_v = adc_read_channel(ADC_CC1_RPUSB_ODH);
+ cc2_v = adc_read_channel(ADC_CC2_RPUSB_ODH);
+ break;
+ }
+ break;
+ case TYPEC_CC_RD:
+ cc1_v = adc_read_channel(ADC_CC1_RPUSB_ODH);
+ cc2_v = adc_read_channel(ADC_CC2_RPUSB_ODH);
+ break;
+ case TYPEC_CC_OPEN:
+ *cc1 = 0;
+ *cc2 = 0;
+ return;
+ }
+
+ *cc1 = vpd_cc_voltage_to_status(cc1_v, ct_cc_pull);
+ *cc2 = vpd_cc_voltage_to_status(cc2_v, ct_cc_pull);
+}
+
+void vpd_host_set_pull(int pull, int rp_value)
+{
+ host_cc_pull = pull;
+
+ switch (pull) {
+ case TYPEC_CC_RP:
+ vpd_cc_db_en_od(GPO_LOW);
+ host_cc_rp_value = rp_value;
+ switch (rp_value) {
+ case TYPEC_RP_USB:
+ vpd_config_cc_rp3a0_rd_l(PIN_CMP, 0);
+ vpd_cc_rpusb_odh(GPO_HIGH);
+ break;
+ case TYPEC_RP_3A0:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 1);
+ break;
+ }
+ break;
+ case TYPEC_CC_RD:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_cc_db_en_od(GPO_LOW);
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 0);
+ break;
+ case TYPEC_CC_RA_RD:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 0);
+
+ /*
+ * RA is connected to VCONN
+ * RD is connected to CC
+ */
+ vpd_cc_db_en_od(GPO_HZ);
+ break;
+ case TYPEC_CC_OPEN:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_CMP, 0);
+ vpd_cc_db_en_od(GPO_LOW);
+ break;
+ }
+}
+
+void vpd_host_get_cc(int *cc)
+{
+ *cc = vpd_cc_voltage_to_status(
+ adc_read_channel(ADC_CC_VPDMCU), host_cc_pull);
+}
+
+void vpd_rx_enable(int en)
+{
+ tcpm_set_rx_enable(0, en);
+}
+
+/*
+ * PA2: Configure as COMP2_INM6 or GPO
+ */
+void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Set output value in register */
+ gpio_set_level(GPIO_CC_RP3A0_RD_L, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ & ~(3 << (2*2))) /* PA2 disable ADC */
+ | (1 << (2*2)); /* Set as GPO */
+ } else {
+ /* Set PA2 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*2))); /* PA2 in ANALOG mode */
+
+ /* Set PA3 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*3))); /* PA3 in ANALOG mode */
+
+ /* Disable Window Mode. Select PA3 */
+ STM32_COMP_CSR &= ~STM32_COMP_WNDWEN;
+
+ /* No output selection. We will use Interrupt */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2OUTSEL_NONE;
+
+ /* Not inverting */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2POL;
+
+ /* Select COMP2_INM6 (PA2) */
+ STM32_COMP_CSR |= STM32_COMP_CMP2INSEL_INM6;
+
+ /* COMP Enable */
+ STM32_COMP_CSR |= STM32_COMP_CMP2EN;
+ }
+}
+
+/*
+ * PA4: Configure as ADC, CMP, or GPO
+ */
+void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Default high. Enable cc1 Rp3A0 pullup */
+ gpio_set_level(GPIO_CC1_RP3A0_RD_L, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ & ~(3 << (2*4))) /* PA4 disable ADC */
+ | (1 << (2*4)); /* Set as GPO */
+ }
+
+ if (cfg == PIN_ADC || cfg == PIN_CMP) {
+ /* Disable COMP2 */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2EN;
+
+ /* Set PA4 pin to Analog mode */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*4))); /* PA4 in ANALOG mode */
+
+ if (cfg == PIN_CMP) {
+ /* Set PA3 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*3))); /* PA3 in ANALOG mode */
+
+ /* Disable Window Mode. Select PA3*/
+ STM32_COMP_CSR &= ~STM32_COMP_WNDWEN;
+
+ /* No output selection. We will use Interrupt */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2OUTSEL_NONE;
+
+ /* Select COMP2_INM4 (PA4) */
+ STM32_COMP_CSR |= STM32_COMP_CMP2INSEL_INM4;
+
+ /* COMP2 Enable */
+ STM32_COMP_CSR |= STM32_COMP_CMP2EN;
+ }
+ }
+}
+
+/*
+ * PA5: Configure as ADC, COMP, or GPO
+ */
+void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Set output value in register */
+ gpio_set_level(GPIO_CC2_RP3A0_RD_L, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ & ~(3 << (2*5))) /* PA5 disable ADC */
+ | (1 << (2*5)); /* Set as GPO */
+ }
+
+ if (cfg == PIN_ADC || cfg == PIN_CMP) {
+ /* Disable COMP2 */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2EN;
+
+ /* Set PA5 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*5))); /* PA5 in ANALOG mode */
+
+ if (cfg == PIN_CMP) {
+ /* Set PA3 pin to ANALOG function */
+ STM32_GPIO_MODER(GPIO_A) = (STM32_GPIO_MODER(GPIO_A)
+ | (3 << (2*3))); /* PA3 in ANALOG mode */
+
+ /* Disable Window Mode. */
+ STM32_COMP_CSR &= ~STM32_COMP_WNDWEN;
+
+ /* No output selection. We will use Interrupt */
+ STM32_COMP_CSR &= ~STM32_COMP_CMP2OUTSEL_NONE;
+
+ /* Select COMP2_INM5 (PA5) */
+ STM32_COMP_CSR |= STM32_COMP_CMP2INSEL_INM5;
+
+ /* COMP2 Enable */
+ STM32_COMP_CSR |= STM32_COMP_CMP2EN;
+ }
+ }
+}
+
+/*
+ * PB0: Configure as ADC or GPO
+ */
+void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Set output value in register */
+ gpio_set_level(GPIO_CC1_RPUSB_ODH, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ & ~(3 << (2*0))) /* PB0 disable ADC */
+ | (1 << (2*0)); /* Set as GPO */
+ } else {
+ /* Enable Analog mode */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ | (3 << (2*0))); /* PB0 in ANALOG mode */
+ }
+}
+
+/*
+ * PB1: Configure as ADC or GPO
+ */
+void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en)
+{
+ if (cfg == PIN_GPO) {
+ /* Set output value in register */
+ gpio_set_level(GPIO_CC2_RPUSB_ODH, en ? 1 : 0);
+
+ /* Disable Analog mode and Enable GPO */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ & ~(3 << (2*1))) /* PB1 disable ADC */
+ | (1 << (2*1)); /* Set as GPO */
+ } else {
+ /* Enable Analog mode */
+ STM32_GPIO_MODER(GPIO_B) = (STM32_GPIO_MODER(GPIO_B)
+ | (3 << (2*1))); /* PB1 in ANALOG mode */
+ }
+}
+
+inline int vpd_read_cc_vpdmcu(void)
+{
+ return adc_read_channel(ADC_CC_VPDMCU);
+}
+
+inline int vpd_read_host_vbus(void)
+{
+ return SCALE(adc_read_channel(ADC_HOST_VBUS_VSENSE), VBUS_SCALE_FACTOR);
+}
+
+inline int vpd_read_ct_vbus(void)
+{
+ return SCALE(adc_read_channel(ADC_CHARGE_VBUS_VSENSE),
+ VBUS_SCALE_FACTOR);
+}
+
+inline int vpd_read_vconn(void)
+{
+ return SCALE(adc_read_channel(ADC_VCONN_VSENSE), VCONN_SCALE_FACTOR);
+}
+
+inline int vpd_is_host_vbus_present(void)
+{
+ return (vpd_read_host_vbus() >= VBUS_DETECT_THRESHOLD);
+}
+
+inline int vpd_is_ct_vbus_present(void)
+{
+ return (vpd_read_ct_vbus() >= VBUS_DETECT_THRESHOLD);
+}
+
+inline int vpd_is_vconn_present(void)
+{
+ return (vpd_read_vconn() >= VCONN_DETECT_THRESHOLD);
+}
+
+inline int vpd_read_rdconnect_ref(void)
+{
+ return adc_read_channel(ADC_RDCONNECT_REF);
+}
+
+void vpd_red_led(int on)
+{
+ gpio_set_level(GPIO_DEBUG_LED_R_L, (on) ? 0 : 1);
+}
+
+void vpd_green_led(int on)
+{
+ gpio_set_level(GPIO_DEBUG_LED_G_L, (on) ? 0 : 1);
+}
+
+void vpd_vbus_pass_en(int en)
+{
+ gpio_set_level(GPIO_VBUS_PASS_EN, (en) ? 1 : 0);
+}
+
+void vpd_present_billboard(enum vpd_billboard bb)
+{
+ switch (bb) {
+ case BB_NONE:
+ gpio_set_level(GPIO_PRESENT_BILLBOARD, 0);
+ gpio_set_flags(GPIO_PRESENT_BILLBOARD, GPIO_OUTPUT);
+ break;
+ case BB_SRC:
+ gpio_set_flags(GPIO_PRESENT_BILLBOARD, GPIO_INPUT);
+ /* Enable Pull-up on PA8 */
+ STM32_GPIO_PUPDR(GPIO_A) |= (1 << (2 * 8));
+ break;
+ case BB_SNK:
+ gpio_set_level(GPIO_PRESENT_BILLBOARD, 1);
+ gpio_set_flags(GPIO_PRESENT_BILLBOARD, GPIO_OUTPUT);
+ break;
+ }
+}
+
+void vpd_mcu_cc_en(int en)
+{
+ gpio_set_level(GPIO_VPDMCU_CC_EN, (en) ? 1 : 0);
+}
+
+void vpd_ct_cc_sel(enum vpd_cc sel)
+{
+ switch (sel) {
+ case CT_OPEN:
+ gpio_set_level(GPIO_CC1_SEL, 0);
+ gpio_set_level(GPIO_CC2_SEL, 0);
+ break;
+ case CT_CC1:
+ gpio_set_level(GPIO_CC2_SEL, 0);
+ gpio_set_level(GPIO_CC1_SEL, 1);
+ break;
+ case CT_CC2:
+ gpio_set_level(GPIO_CC1_SEL, 0);
+ gpio_set_level(GPIO_CC2_SEL, 1);
+ break;
+ }
+}
+
+/* Set as GPO High, GPO Low, or High-Z */
+void vpd_cc_db_en_od(enum vpd_gpo val)
+{
+ if (val == GPO_HZ) {
+ gpio_set_flags(GPIO_CC_DB_EN_OD, GPIO_INPUT);
+ } else {
+ if (val == GPO_HIGH)
+ gpio_set_level(GPIO_CC_DB_EN_OD, 1);
+ else
+ gpio_set_level(GPIO_CC_DB_EN_OD, 0);
+
+ gpio_set_flags(GPIO_CC_DB_EN_OD, GPIO_OUTPUT);
+ }
+}
+
+void vpd_cc_rpusb_odh(enum vpd_gpo val)
+{
+ if (val == GPO_HZ) {
+ gpio_set_flags(GPIO_CC_RPUSB_ODH, GPIO_INPUT);
+ } else {
+ gpio_set_level(GPIO_CC_RPUSB_ODH, (val == GPO_HIGH) ? 1 : 0);
+ gpio_set_flags(GPIO_CC_RPUSB_ODH, GPIO_OUTPUT);
+ }
+}
+
+void vpd_cc1_cc2_db_en_l(enum vpd_gpo val)
+{
+ if (val == GPO_HZ) {
+ gpio_set_flags(GPIO_CC1_CC2_DB_EN_L, GPIO_INPUT);
+ } else {
+ gpio_set_level(GPIO_CC1_CC2_DB_EN_L, (val == GPO_HIGH) ? 1 : 0);
+ gpio_set_flags(GPIO_CC1_CC2_DB_EN_L, GPIO_OUTPUT);
+ }
+}
+
+void vpd_vconn_pwr_sel_odl(enum vpd_pwr en)
+{
+ gpio_set_level(GPIO_VCONN_PWR_SEL_ODL, (en == PWR_VBUS) ? 1 : 0);
+}
diff --git a/board/chocodile_vpdmcu/vpd_api.h b/board/chocodile_vpdmcu/vpd_api.h
new file mode 100644
index 0000000000..df50f92006
--- /dev/null
+++ b/board/chocodile_vpdmcu/vpd_api.h
@@ -0,0 +1,276 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Vconn Power Device API module */
+
+#ifndef __CROS_EC_VPD_API_H
+#define __CROS_EC_VPD_API_H
+
+#include "adc.h"
+#include "gpio.h"
+#include "usb_pd.h"
+
+enum vpd_pin {
+ PIN_ADC,
+ PIN_CMP,
+ PIN_GPO
+};
+
+enum vpd_gpo {
+ GPO_HZ,
+ GPO_HIGH,
+ GPO_LOW
+};
+
+enum vpd_pwr {
+ PWR_VCONN,
+ PWR_VBUS,
+};
+
+enum vpd_cc {
+ CT_OPEN,
+ CT_CC1,
+ CT_CC2
+};
+
+enum vpd_billboard {
+ BB_NONE,
+ BB_SRC,
+ BB_SNK
+};
+
+/**
+ * Set Charge-Through Rp or Rd on CC lines
+ *
+ * @param pull Either TYPEC_CC_RP or TYPEC_CC_RD
+ * @param rp_value When pull is RP, set this to
+ * TYPEC_RP_USB or TYPEC_RP_1A5. Ignored
+ * for TYPEC_CC_RD
+ */
+void vpd_ct_set_pull(int pull, int rp_value);
+
+/**
+ * Get the status of the Charge-Through CC lines
+ *
+ * @param cc1 Either TYPEC_CC_VOLT_OPEN,
+ * TYPEC_CC_VOLT_RA,
+ * TYPEC_CC_VOLT_RD,
+ * any other value is considered RP
+ * @param cc2 Either TYPEC_CC_VOLT_OPEN,
+ * TYPEC_CC_VOLT_RA,
+ * TYPEC_CC_VOLT_RD,
+ * any other value is considered RP
+ */
+void vpd_ct_get_cc(int *cc1, int *cc2);
+
+/**
+ * Set Host Rp or Rd on CC lines
+ *
+ * @param pull Either TYPEC_CC_RP or TYPEC_CC_RD
+ * @param rp_value When pull is RP, set this to
+ * TYPEC_RP_USB or TYPEC_RP_1A5. Ignored
+ * for TYPEC_CC_RD
+ */
+void vpd_host_set_pull(int pull, int rp_value);
+
+/**
+ * Get the status of the Host CC line
+ *
+ * @param cc Either TYPEC_CC_VOLT_SNK_DEF, TYPEC_CC_VOLT_SNK_1_5,
+ * TYPEC_CC_VOLT_SNK_3_0, or TYPEC_CC_RD
+ */
+void vpd_host_get_cc(int *cc);
+
+/**
+ * Set RX Enable flag
+ *
+ * @param en 1 for enable, 0 for disable
+ */
+void vpd_rx_enable(int en);
+
+/**
+ * Configure the cc_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc1_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc2_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc1_rpusb_odh pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc2_rpusb_odh pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc_db_en_od pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_db_en_od(enum vpd_gpo val);
+
+/**
+ * Configure the cc_rpusb_odh pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_rpusb_odh(enum vpd_gpo val);
+
+/**
+ * Configure the cc_rp1a5_odh pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_rp1a5_odh(enum vpd_gpo val);
+
+/**
+ * Configure the cc1_cc2_db_en_l pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc1_cc2_db_en_l(enum vpd_gpo val);
+
+/**
+ * Get status of host vbus
+ *
+ * @return 1 if host vbus is present, else 0
+ */
+int vpd_is_host_vbus_present(void);
+
+/**
+ * Get status of charge-through vbus
+ *
+ * @return 1 if charge-through vbus is present, else 0
+ */
+int vpd_is_ct_vbus_present(void);
+
+/**
+ * Get status of vconn
+ *
+ * @return 1 if vconn is present, else 0
+ */
+int vpd_is_vconn_present(void);
+
+/**
+ * Read Host VBUS voltage. Range from 22000mV to 3000mV
+ *
+ * @return vbus voltage
+ */
+int vpd_read_host_vbus(void);
+
+/**
+ * Read Host CC voltage.
+ *
+ * @return cc voltage
+ */
+int vpd_read_cc_host(void);
+
+/**
+ * Read voltage on cc_vpdmcu pin
+ *
+ * @return cc_vpdmcu voltage
+ */
+int vpd_read_cc_vpdmcu(void);
+
+/**
+ * Read charge-through VBUS voltage. Range from 22000mV to 3000mV
+ *
+ * @return charge-through vbus voltage
+ */
+int vpd_read_ct_vbus(void);
+
+/**
+ * Read VCONN Voltage. Range from 5500mV to 3000mV
+ *
+ * @return vconn voltage
+ */
+int vpd_read_vconn(void);
+
+/**
+ * Turn ON/OFF Red LED. Should be off when performing power
+ * measurements.
+ *
+ * @param on 0 turns LED off, any other value turns it ON
+ */
+void vpd_red_led(int on);
+
+/**
+ * Turn ON/OFF Green LED. Should be off when performing power
+ * measurements.
+ *
+ * @param on 0 turns LED off, any other value turns it ON
+ */
+void vpd_green_led(int on);
+
+/**
+ * Connects/Disconnects the Host VBUS to the Charge-Through VBUS.
+ *
+ * @param en 0 disconnectes the VBUS, any other value connects VBUS.
+ */
+void vpd_vbus_pass_en(int en);
+
+/**
+ * Preset Billboard device
+ *
+ * @param bb BB_NONE no billboard presented,
+ * BB_SRC source connected but not in charge-through
+ * BB_SNK sink connected
+ */
+void vpd_present_billboard(enum vpd_billboard bb);
+
+/**
+ * Enables the MCU to host cc communication
+ *
+ * @param en 1 enabled, 0 disabled
+ */
+void vpd_mcu_cc_en(int en);
+
+/**
+ * Selects which supply to power the VPD from
+ *
+ * @param en PWR_VCONN or PWR_VBUS
+ */
+void vpd_vconn_pwr_sel_odl(enum vpd_pwr en);
+
+/**
+ * Controls if the Charge-Through's CC1, CC2, or neither is
+ * connected to Host CC
+ *
+ * @param sel CT_OPEN neither, CT_CC1 cc1, CT_CC2 cc2
+ */
+void vpd_ct_cc_sel(enum vpd_cc sel);
+
+#endif /* __CROS_EC_VPD_API_H */
diff --git a/board/coral/board.c b/board/coral/board.c
index 5955474620..ccd63c5022 100644
--- a/board/coral/board.c
+++ b/board/coral/board.c
@@ -245,13 +245,15 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = NPCX_I2C_PORT0_0,
.i2c_slave_addr = ANX74XX_I2C_ADDR1,
.drv = &anx74xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
[USB_PD_PORT_PS8751] = {
.i2c_host_port = NPCX_I2C_PORT0_1,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/cr50/board.c b/board/cr50/board.c
index 20ec0fed44..5de77ddf35 100644
--- a/board/cr50/board.c
+++ b/board/cr50/board.c
@@ -261,6 +261,12 @@ static struct board_cfg board_cfg_table[] = {
BOARD_USE_PLT_RESET | BOARD_NO_INA_SUPPORT |
BOARD_CLOSED_LOOP_RESET,
},
+ /* Kukui: DI0A9 = 5k PU, DIOA1 = 5k PU */
+ {
+ .strap_cfg = 0x0F,
+ .board_properties = BOARD_SLAVE_CONFIG_SPI |
+ BOARD_USE_PLT_RESET,
+ },
/* I2C Variants: DIOA9 = 1M PD, DIOA1 = 1M PD */
/* Reef/Eve: DIOA12 = 5k PD, DIOA6 = 1M PU */
{
@@ -633,17 +639,6 @@ static void configure_board_specific_gpios(void)
closed_source_set1_configure_gpios();
}
-void decrement_retry_counter(void)
-{
- uint32_t counter = GREG32(PMU, LONG_LIFE_SCRATCH0);
-
- if (counter) {
- GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 1);
- GREG32(PMU, LONG_LIFE_SCRATCH0) = counter - 1;
- GWRITE_FIELD(PMU, LONG_LIFE_SCRATCH_WR_EN, REG0, 0);
- }
-}
-
static uint8_t mismatched_board_id;
int board_id_is_mismatched(void)
@@ -712,7 +707,7 @@ static void board_init(void)
* the rolling reboot count.
*/
if (system_get_reset_flags() & RESET_FLAG_HIBERNATE)
- decrement_retry_counter();
+ system_decrement_retry_counter();
configure_board_specific_gpios();
init_pmu();
reset_wake_logic();
@@ -722,8 +717,6 @@ static void board_init(void)
init_runlevel(PERMISSION_MEDIUM);
/* Initialize NvMem partitions */
nvmem_init();
- /* Initialize the persistent storage. */
- initvars();
/*
* If this was a low power wake and not a rollback, restore the ccd
@@ -928,40 +921,23 @@ void tpm_rst_deasserted(enum gpio_signal signal)
void assert_sys_rst(void)
{
- /*
- * We don't have a good (any?) way to easily look up the pinmux/gpio
- * assignments in gpio.inc, so they're hard-coded in this routine. This
- * assertion is just to ensure it hasn't changed.
- */
- ASSERT(GREAD(PINMUX, GPIO0_GPIO4_SEL) == GC_PINMUX_DIOM0_SEL);
-
- /* Set SYS_RST_L_OUT as an output, connected to the pad */
- GWRITE(PINMUX, DIOM0_SEL, GC_PINMUX_GPIO0_GPIO4_SEL);
- gpio_set_flags(GPIO_SYS_RST_L_OUT, GPIO_OUT_HIGH);
-
/* Assert it */
gpio_set_level(GPIO_SYS_RST_L_OUT, 0);
}
void deassert_sys_rst(void)
{
- ASSERT(GREAD(PINMUX, GPIO0_GPIO4_SEL) == GC_PINMUX_DIOM0_SEL);
-
- /* Deassert SYS_RST_L */
+ /* Deassert it */
gpio_set_level(GPIO_SYS_RST_L_OUT, 1);
-
- /* Set SYS_RST_L_OUT as an input, disconnected from the pad */
- gpio_set_flags(GPIO_SYS_RST_L_OUT, GPIO_INPUT);
- GWRITE(PINMUX, DIOM0_SEL, 0);
}
-int is_sys_rst_asserted(void)
+static int is_sys_rst_asserted(void)
{
- return (GREAD(PINMUX, DIOM0_SEL) == GC_PINMUX_GPIO0_GPIO4_SEL)
-#ifdef CONFIG_CMD_GPIO_EXTENDED
- && (gpio_get_flags(GPIO_SYS_RST_L_OUT) & GPIO_OUTPUT)
-#endif
- && (gpio_get_level(GPIO_SYS_RST_L_OUT) == 0);
+ /*
+ * SYS_RST_L is pseudo open drain. It is only an output when it's
+ * asserted.
+ */
+ return gpio_get_flags(GPIO_SYS_RST_L_OUT) & GPIO_OUTPUT;
}
/**
@@ -981,7 +957,7 @@ void board_reboot_ap(void)
/**
* Reboot the EC
*/
-static void board_reboot_ec(void)
+void board_reboot_ec(void)
{
if (board_uses_closed_loop_reset()) {
board_closed_loop_reset();
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 78f6d5915a..016cbc4607 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -38,6 +38,9 @@
#undef CONFIG_FLASH
#endif
+/* Enable getting gpio flags to tell if open drain pins are asserted */
+#define CONFIG_GPIO_GET_EXTENDED
+
/* Flash configuration */
#undef CONFIG_FLASH_PSTATE
#define CONFIG_WP_ALWAYS
@@ -59,12 +62,20 @@
#define CONFIG_FLASH_NVMEM_OFFSET_A (CFG_TOP_A_OFF + CONFIG_FLASH_NVCTR_SIZE)
#define CONFIG_FLASH_NVMEM_OFFSET_B (CFG_TOP_B_OFF + CONFIG_FLASH_NVCTR_SIZE)
/* Address of start of Nvmem area */
-#define CONFIG_FLASH_NVMEM_BASE_A (CONFIG_PROGRAM_MEMORY_BASE + \
- CONFIG_FLASH_NVMEM_OFFSET_A)
-#define CONFIG_FLASH_NVMEM_BASE_B (CONFIG_PROGRAM_MEMORY_BASE + \
- CONFIG_FLASH_NVMEM_OFFSET_B)
+#define CONFIG_FLASH_NVMEM_BASE_A \
+ (CONFIG_PROGRAM_MEMORY_BASE + CONFIG_FLASH_NVMEM_OFFSET_A)
+#define CONFIG_FLASH_NVMEM_BASE_B \
+ (CONFIG_PROGRAM_MEMORY_BASE + CONFIG_FLASH_NVMEM_OFFSET_B)
+#define CONFIG_FLASH_NEW_NVMEM_BASE_A \
+ (CONFIG_FLASH_NVMEM_BASE_A + CONFIG_FLASH_BANK_SIZE)
+#define CONFIG_FLASH_NEW_NVMEM_BASE_B \
+ (CONFIG_FLASH_NVMEM_BASE_B + CONFIG_FLASH_BANK_SIZE)
+
/* Size partition in NvMem */
#define NVMEM_PARTITION_SIZE (CFG_TOP_SIZE - CONFIG_FLASH_NVCTR_SIZE)
+#define NEW_NVMEM_PARTITION_SIZE (NVMEM_PARTITION_SIZE - CONFIG_FLASH_BANK_SIZE)
+#define NEW_NVMEM_TOTAL_PAGES \
+ (2 * NEW_NVMEM_PARTITION_SIZE / CONFIG_FLASH_BANK_SIZE)
/* Size in bytes of NvMem area */
#define CONFIG_FLASH_LOG
#define CONFIG_FLASH_NVMEM_SIZE (NVMEM_PARTITION_SIZE * NVMEM_NUM_PARTITIONS)
@@ -247,7 +258,6 @@ enum nvmem_vars {
void board_configure_deep_sleep_wakepins(void);
void ap_detect_asserted(enum gpio_signal signal);
void ec_detect_asserted(enum gpio_signal signal);
-void ec_tx_cr50_rx(enum gpio_signal signal);
void servo_detect_asserted(enum gpio_signal signal);
void tpm_rst_deasserted(enum gpio_signal signal);
void tpm_rst_asserted(enum gpio_signal signal);
@@ -257,7 +267,6 @@ void post_reboot_request(void);
/* Special controls over EC and AP */
void assert_sys_rst(void);
void deassert_sys_rst(void);
-int is_sys_rst_asserted(void);
void assert_ec_rst(void);
void deassert_ec_rst(void);
int is_ec_rst_asserted(void);
@@ -332,8 +341,9 @@ int board_battery_is_present(void);
int board_fwmp_allows_unlock(void);
int board_vboot_dev_mode_enabled(void);
void board_reboot_ap(void);
+void board_reboot_ec(void);
void board_closed_loop_reset(void);
-int board_wipe_tpm(void);
+int board_wipe_tpm(int reset_required);
int board_is_first_factory_boot(void);
int usb_i2c_board_enable(void);
diff --git a/board/cr50/gpio.inc b/board/cr50/gpio.inc
index cd9348fd2c..03015ba8b9 100644
--- a/board/cr50/gpio.inc
+++ b/board/cr50/gpio.inc
@@ -94,14 +94,18 @@ GPIO(INT_AP_L, PIN(0, 0), GPIO_OUT_HIGH)
GPIO(EC_FLASH_SELECT, PIN(0, 1), GPIO_OUT_LOW)
GPIO(AP_FLASH_SELECT, PIN(0, 2), GPIO_OUT_LOW)
-/* Pull this low to reset the AP. (We reset the EC with the RBOX.) */
-GPIO(SYS_RST_L_OUT, PIN(0, 4), GPIO_INPUT)
+/*
+ * Pull this low to reset the AP. (We reset the EC with the RBOX.)
+ * This is pseudo open drain.
+ */
+GPIO(SYS_RST_L_OUT, PIN(0, 4), GPIO_ODR_HIGH)
/*
* Indicate to EC when CCD is enabled. EC can pull this down too, to tell us if
* it decided instead.
+ * This is pseudo open drain.
*/
-GPIO(CCD_MODE_L, PIN(0, 5), GPIO_INPUT | GPIO_PULL_UP)
+GPIO(CCD_MODE_L, PIN(0, 5), GPIO_ODR_HIGH | GPIO_PULL_UP)
/* Battery present signal is active low */
GPIO(BATT_PRES_L, PIN(0, 6), GPIO_INPUT)
@@ -170,18 +174,8 @@ PINMUX(GPIO(AP_FLASH_SELECT), B3, DIO_INPUT)
* removed.
*/
PINMUX(GPIO(EN_PP3300_INA_L), B7, DIO_INPUT)
-/*
- * To allow the EC to drive the signal we set sys_rst_l_out as an input here and
- * only change it to an output when we want to assert the signal.
- */
PINMUX(GPIO(SYS_RST_L_OUT), M0, DIO_INPUT)
-/*
- * CCD_MODE_L is an input above, but we need to be able to drive it as
- * an output if Rdd detects the debug cable. So set DIO_OUTPUT as
- * well. It will be tristated initially, because we don't set
- * GPIO_OUTPUT above.
- */
-PINMUX(GPIO(CCD_MODE_L), M1, DIO_INPUT | DIO_OUTPUT)
+PINMUX(GPIO(CCD_MODE_L), M1, DIO_INPUT)
PINMUX(GPIO(BATT_PRES_L), M2, 0)
/*
* Update closed_source_set1.c if pinmux for I2C_SCL_INA or I2C_SDA_INA is
diff --git a/board/cr50/power_button.c b/board/cr50/power_button.c
index 83379ae167..5f46a4d0a6 100644
--- a/board/cr50/power_button.c
+++ b/board/cr50/power_button.c
@@ -140,7 +140,18 @@ static enum vendor_cmd_rc vc_get_pwr_btn(enum vendor_cmd_cc code,
size_t input_size,
size_t *response_size)
{
- if (pop_check_presence(1) == POP_TOUCH_YES)
+ /*
+ * The AP uses VENDOR_CC_GET_PWR_BTN to poll both for the press and
+ * release of the power button.
+ *
+ * pop_check_presence(1) returns true if a new power button press was
+ * recorded in the last 10 seconds.
+ *
+ * Indicate button release if no new presses have been recorded and the
+ * current button state is not pressed.
+ */
+ if (pop_check_presence(1) == POP_TOUCH_YES ||
+ rbox_powerbtn_is_pressed())
*(uint8_t *)buf = 1;
else
*(uint8_t *)buf = 0;
diff --git a/board/cr50/u2f.c b/board/cr50/u2f.c
index d9cc8c7989..320f026a6e 100644
--- a/board/cr50/u2f.c
+++ b/board/cr50/u2f.c
@@ -82,25 +82,22 @@ static int load_state(void)
/* create random salt */
if (!DCRYPTO_ladder_random(salt))
return 0;
- if (setvar(&k_salt, sizeof(k_salt),
- (const uint8_t *)salt, sizeof(salt)))
+ if (setvar(&k_salt, sizeof(k_salt), (const uint8_t *)salt,
+ sizeof(salt)))
return 0;
- /* really save the new variable to flash */
- writevars();
} else {
memcpy(salt, tuple_val(t_salt), sizeof(salt));
+ freevar(t_salt);
}
- if (read_tpm_nvmem_hidden(
- TPM_HIDDEN_U2F_KEK,
- sizeof(salt_kek), salt_kek) ==
- tpm_read_not_found) {
+ if (read_tpm_nvmem_hidden(TPM_HIDDEN_U2F_KEK, sizeof(salt_kek),
+ salt_kek) == tpm_read_not_found) {
/*
* Not found means that we have not used u2f before,
* or not used it with updated fw that resets kek seed
* on TPM clear.
*/
- if (t_salt) {
+ if (t_salt) { /* Note that memory has been freed already!. */
/*
* We have previously used u2f, and may have
* existing registrations; we don't want to
@@ -321,7 +318,7 @@ int u2f_gen_kek_seed(int commit)
/* ---- Send/receive U2F APDU over TPM vendor commands ---- */
-enum vendor_cmd_rc vc_u2f_apdu(enum vendor_cmd_cc code, void *body,
+static enum vendor_cmd_rc vc_u2f_apdu(enum vendor_cmd_cc code, void *body,
size_t cmd_size, size_t *response_size)
{
unsigned retlen;
diff --git a/board/cr50/wp.c b/board/cr50/wp.c
index ba7e2f7de1..0e2a4020af 100644
--- a/board/cr50/wp.c
+++ b/board/cr50/wp.c
@@ -7,6 +7,7 @@
#include "console.h"
#include "crc8.h"
#include "extension.h"
+#include "flash_log.h"
#include "gpio.h"
#include "hooks.h"
#include "registers.h"
@@ -21,13 +22,15 @@
#define CPRINTS(format, args...) cprints(CC_RBOX, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_RBOX, format, ## args)
+uint8_t bp_connect;
+uint8_t bp_forced;
/**
* Return non-zero if battery is present
*/
int board_battery_is_present(void)
{
/* Invert because battery-present signal is active low */
- return !gpio_get_level(GPIO_BATT_PRES_L);
+ return bp_forced ? bp_connect : !gpio_get_level(GPIO_BATT_PRES_L);
}
/**
@@ -145,18 +148,64 @@ static enum vendor_cmd_rc vc_set_wp(enum vendor_cmd_cc code,
}
DECLARE_VENDOR_COMMAND(VENDOR_CC_WP, vc_set_wp);
-static int command_wp(int argc, char **argv)
+static int command_bpforce(int argc, char **argv)
{
int val = 1;
int forced = 1;
if (argc > 1) {
+ /* Make sure we're allowed to override battery presence */
+ if (!ccd_is_cap_enabled(CCD_CAP_OVERRIDE_BATT_STATE))
+ return EC_ERROR_ACCESS_DENIED;
+
+ /* Update BP */
+ if (!strncasecmp(argv[1], "follow", 6))
+ forced = 0;
+ else if (!strncasecmp(argv[1], "dis", 3))
+ val = 0;
+ else if (strncasecmp(argv[1], "con", 3))
+ return EC_ERROR_PARAM2;
+
+ bp_forced = forced;
+ bp_connect = val;
+
+ if (argc > 2 && !strcasecmp(argv[2], "atboot")) {
+ /* Change override at boot to match */
+ ccd_set_flag(CCD_FLAG_OVERRIDE_BATT_AT_BOOT, bp_forced);
+ ccd_set_flag(CCD_FLAG_OVERRIDE_BATT_STATE_CONNECT,
+ bp_connect);
+ }
+ /* Update the WP state based on new battery presence setting */
+ check_wp_battery_presence();
+ }
+
+ ccprintf("batt pres: %s%sconnect\n", bp_forced ? "forced " : "",
+ board_battery_is_present() ? "" : "dis");
+ ccprintf(" at boot: ");
+ if (ccd_get_flag(CCD_FLAG_OVERRIDE_BATT_AT_BOOT))
+ ccprintf("forced %sconnect\n",
+ ccd_get_flag(CCD_FLAG_OVERRIDE_BATT_STATE_CONNECT) ? ""
+ : "dis");
+ else
+ ccprintf("follow_batt_pres\n");
+ return EC_SUCCESS;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(bpforce, command_bpforce,
+ "[connect|disconnect|follow_batt_pres [atboot]]",
+ "Get/set BATT_PRES_L signal override");
+
+static int command_wp(int argc, char **argv)
+{
+ int val;
+ int forced;
+
+ if (argc > 1) {
/* Make sure we're allowed to override WP settings */
if (!ccd_is_cap_enabled(CCD_CAP_OVERRIDE_WP))
return EC_ERROR_ACCESS_DENIED;
/* Update WP */
- if (strncasecmp(argv[1], "follow_batt_pres", 16) == 0)
+ if (!strncasecmp(argv[1], "follow", 6))
forced = 0;
else if (parse_bool(argv[1], &val))
forced = 1;
@@ -172,14 +221,13 @@ static int command_wp(int argc, char **argv)
}
}
- ccprintf("Flash WP: %s%s\n", board_forcing_wp() ? "forced " : "",
- wp_is_asserted() ? "enabled" : "disabled");
-
+ ccprintf("Flash WP: %s%sabled\n", board_forcing_wp() ? "forced " : "",
+ wp_is_asserted() ? "en" : "dis");
ccprintf(" at boot: ");
if (ccd_get_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT))
- ccprintf("forced %s\n",
+ ccprintf("forced %sabled\n",
ccd_get_flag(CCD_FLAG_OVERRIDE_WP_STATE_ENABLED)
- ? "enabled" : "disabled");
+ ? "en" : "dis");
else
ccprintf("follow_batt_pres\n");
@@ -189,7 +237,18 @@ DECLARE_SAFE_CONSOLE_COMMAND(wp, command_wp,
"[<BOOLEAN>/follow_batt_pres [atboot]]",
"Get/set the flash HW write-protect signal");
-void set_wp_follow_ccd_config(void)
+void set_bp_follow_ccd_config(void)
+{
+ if (ccd_get_flag(CCD_FLAG_OVERRIDE_BATT_AT_BOOT)) {
+ /* Reset to at-boot state specified by CCD */
+ bp_forced = 1;
+ bp_connect = ccd_get_flag(CCD_FLAG_OVERRIDE_BATT_STATE_CONNECT);
+ } else {
+ bp_forced = 0;
+ }
+}
+
+static void set_wp_follow_ccd_config(void)
{
if (ccd_get_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT)) {
/* Reset to at-boot state specified by CCD */
@@ -201,8 +260,26 @@ void set_wp_follow_ccd_config(void)
}
}
+void board_wp_follow_ccd_config(void)
+{
+ /*
+ * Battery presence can be overidden using CCD. Get that setting before
+ * configuring write protect.
+ */
+ set_bp_follow_ccd_config();
+
+ /* Update write protect setting based on ccd config */
+ set_wp_follow_ccd_config();
+}
+
void init_wp_state(void)
{
+ /*
+ * Battery presence can be overidden using CCD. Get that setting before
+ * configuring write protect.
+ */
+ set_bp_follow_ccd_config();
+
/* Check system reset flags after CCD config is initially loaded */
if ((system_get_reset_flags() & RESET_FLAG_HIBERNATE) &&
!system_rollback_detected()) {
@@ -227,23 +304,18 @@ void init_wp_state(void)
/**
* Wipe the TPM
*
+ * @param reset_required: reset the system after wiping the TPM.
+ *
* @return EC_SUCCESS, or non-zero if error.
*/
-int board_wipe_tpm(void)
+int board_wipe_tpm(int reset_required)
{
int rc;
- /*
- * Blindly zapping the TPM space while the AP is awake and poking at
- * it will bork the TPM task and the AP itself, so force the whole
- * system off by holding the EC in reset.
- */
- CPRINTS("%s: force EC off", __func__);
- assert_ec_rst();
-
/* Wipe the TPM's memory and reset the TPM task. */
rc = tpm_reset_request(1, 1);
if (rc != EC_SUCCESS) {
+ flash_log_add_event(FE_LOG_TPM_WIPE_ERROR, 0, NULL);
/*
* If anything goes wrong (which is unlikely), we REALLY don't
* want to unlock the console. It's possible to fail without
@@ -256,22 +328,39 @@ int board_wipe_tpm(void)
SYSTEM_RESET_HARD);
/*
- * That should never return, but if it did, release EC reset
- * and pass through the error we got.
+ * That should never return, but if it did, reset the EC and
+ * through the error we got.
*/
- deassert_ec_rst();
+ board_reboot_ec();
return rc;
}
+ /*
+ * TPM was wiped out successfully, let's prevent further communications
+ * from the AP until next reboot. The reboot will be triggered below if
+ * a reset is requested. If we aren't resetting the system now, the TPM
+ * will stay disabled until the user resets the system.
+ * This should be done as soon as possible after tpm_reset_request
+ * completes.
+ */
+ tpm_stop();
+
CPRINTS("TPM is erased");
/* Tell the TPM task to re-enable NvMem commits. */
tpm_reinstate_nvmem_commits();
- /* Let the rest of the system boot. */
- CPRINTS("%s: release EC reset", __func__);
- deassert_ec_rst();
-
+ /*
+ * Use board_reboot_ec to ensure the system resets instead of
+ * deassert_ec_reset. Some boards don't reset immediately when EC_RST_L
+ * is asserted. board_reboot_ec will ensure the system has actually
+ * reset before releasing it. If the system has a normal reset scheme,
+ * EC reset will be released immediately.
+ */
+ if (reset_required) {
+ CPRINTS("%s: reset EC", __func__);
+ board_reboot_ec();
+ }
return EC_SUCCESS;
}
diff --git a/board/cr50/wp.h b/board/cr50/wp.h
index 501067233f..d5cbade855 100644
--- a/board/cr50/wp.h
+++ b/board/cr50/wp.h
@@ -29,8 +29,8 @@ int wp_is_asserted(void);
void read_fwmp(void);
/**
- * Set WP as dicated by CCD configuration.
+ * Set WP and battery presence as dicated by CCD configuration.
*/
-void set_wp_follow_ccd_config(void);
+void board_wp_follow_ccd_config(void);
#endif /* ! __EC_BOARD_CR50_WP_H */
diff --git a/board/eve/board.c b/board/eve/board.c
index ea213d9b9f..bb8bef1b55 100644
--- a/board/eve/board.c
+++ b/board/eve/board.c
@@ -218,13 +218,15 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = I2C_PORT_TCPC0,
.i2c_slave_addr = ANX74XX_I2C_ADDR1,
.drv = &anx74xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
{
.i2c_host_port = I2C_PORT_TCPC1,
.i2c_slave_addr = ANX74XX_I2C_ADDR1,
.drv = &anx74xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/fizz/board.c b/board/fizz/board.c
index 8d0c0fd2a2..69759eb68e 100644
--- a/board/fizz/board.c
+++ b/board/fizz/board.c
@@ -191,8 +191,8 @@ const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
/* TCPC mux configuration */
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
- {NPCX_I2C_PORT0_0, I2C_ADDR_TCPC0, &ps8xxx_tcpm_drv,
- TCPC_ALERT_ACTIVE_LOW},
+ /* Alert is active-low, push-pull */
+ {NPCX_I2C_PORT0_0, I2C_ADDR_TCPC0, &ps8xxx_tcpm_drv, 0},
};
static int ps8751_tune_mux(int port)
diff --git a/board/flapjack/battery.c b/board/flapjack/battery.c
index 6ba167b3fe..f7d5afc1ef 100644
--- a/board/flapjack/battery.c
+++ b/board/flapjack/battery.c
@@ -150,35 +150,19 @@ static const struct {
},
};
-static struct {
- enum battery_type type;
- int expect_mv;
-} const batteries[] = {
+static const struct mv_to_id batteries[] = {
{ BATTERY_C18_ATL, 900 }, /* 100K */
{ BATTERY_C19_ATL, 576 }, /* 47K */
};
BUILD_ASSERT(ARRAY_SIZE(batteries) < BATTERY_COUNT);
-#define MARGIN_MV 56 /* Simply assume 1800/16/2 */
-
static enum battery_type batt_type = BATTERY_UNKNOWN;
static void board_get_battery_type(void)
{
- int mv;
- int i;
-
- mv = adc_read_channel(ADC_BATT_ID);
- if (mv == ADC_READ_ERROR)
- mv = adc_read_channel(ADC_BATT_ID);
-
- for (i = 0; i < ARRAY_SIZE(batteries); i++) {
- if (ABS(mv - batteries[i].expect_mv) < MARGIN_MV) {
- batt_type = batteries[i].type;
- break;
- }
- }
-
+ int id = board_read_id(ADC_BATT_ID, batteries, ARRAY_SIZE(batteries));
+ if (id != ADC_READ_ERROR)
+ batt_type = id;
CPRINTS("Battery Type: %d", batt_type);
}
DECLARE_HOOK(HOOK_INIT, board_get_battery_type, HOOK_PRIO_FIRST);
diff --git a/board/flapjack/board.c b/board/flapjack/board.c
index 9c32886ffb..a1a83e4e22 100644
--- a/board/flapjack/board.c
+++ b/board/flapjack/board.c
@@ -29,6 +29,7 @@
#include "i2c.h"
#include "power.h"
#include "power_button.h"
+#include "lid_switch.h"
#include "pwm.h"
#include "pwm_chip.h"
#include "registers.h"
@@ -49,18 +50,56 @@
#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
+static const struct mv_to_id panels[] = {
+ { PANEL_BOE_HIMAX8279D10P, 98 },
+ { PANEL_BOE_HIMAX8279D8P, 280 },
+};
+BUILD_ASSERT(ARRAY_SIZE(panels) < PANEL_COUNT);
+
uint16_t board_version;
uint8_t oem;
uint32_t sku;
+int board_read_id(enum adc_channel ch, const struct mv_to_id *table, int size)
+{
+ int mv = adc_read_channel(ch);
+ int i;
+
+ if (mv == ADC_READ_ERROR)
+ mv = adc_read_channel(ch);
+
+ for (i = 0; i < size; i++) {
+ if (ABS(mv - table[i].median_mv) < ADC_MARGIN_MV)
+ return table[i].id;
+ }
+
+ return ADC_READ_ERROR;
+}
+
static void board_setup_panel(void)
{
uint8_t channel;
uint8_t dim;
int rv = 0;
- channel = sku & SKU_ID_PANEL_SIZE_MASK ? 0xfe : 0xfa;
- dim = sku & SKU_ID_PANEL_SIZE_MASK ? 0xc4 : 0xc8;
+ if (board_version >= 3) {
+ switch ((sku >> PANEL_ID_BIT_POSITION) & 0xf) {
+ case PANEL_BOE_HIMAX8279D8P:
+ channel = 0xfa;
+ dim = 0xc8;
+ break;
+ case PANEL_BOE_HIMAX8279D10P:
+ channel = 0xfe;
+ dim = 0xc4;
+ break;
+ default:
+ return;
+ }
+ } else {
+ /* TODO: to be removed once the boards are deprecated. */
+ channel = sku & SKU_ID_PANEL_SIZE_MASK ? 0xfe : 0xfa;
+ dim = sku & SKU_ID_PANEL_SIZE_MASK ? 0xc4 : 0xc8;
+ }
rv |= i2c_write8(I2C_PORT_CHARGER, RT946X_ADDR, MT6370_BACKLIGHT_BLEN,
channel);
@@ -72,6 +111,15 @@ static void board_setup_panel(void)
CPRINTS("Board setup panel failed\n");
}
+static enum panel_id board_get_panel_id(void)
+{
+ int id = board_read_id(ADC_LCM_ID, panels, ARRAY_SIZE(panels));
+ if (id == ADC_READ_ERROR)
+ id = PANEL_UNKNOWN;
+ CPRINTS("LCM ID: %d", id);
+ return id;
+}
+
static void cbi_init(void)
{
uint32_t val;
@@ -86,6 +134,11 @@ static void cbi_init(void)
if (cbi_get_sku_id(&val) == EC_SUCCESS)
sku = val;
+
+ if (board_version >= 3)
+ /* Embed LCM_ID in sku_id bit[19-16] */
+ sku |= ((board_get_panel_id() & 0xf) << PANEL_ID_BIT_POSITION);
+
CPRINTS("SKU: 0x%08x", sku);
}
DECLARE_HOOK(HOOK_INIT, cbi_init, HOOK_PRIO_INIT_I2C + 1);
@@ -95,11 +148,6 @@ static void tcpc_alert_event(enum gpio_signal signal)
schedule_deferred_pd_interrupt(0 /* port */);
}
-static void hall_interrupt(enum gpio_signal signal)
-{
- /* TODO(b/111378000): Implement hall_interrupt */
-}
-
static void gauge_interrupt(enum gpio_signal signal)
{
task_wake(TASK_ID_CHARGER);
@@ -110,10 +158,11 @@ static void gauge_interrupt(enum gpio_signal signal)
/******************************************************************************/
/* ADC channels. Must be in the exactly same order as in enum adc_channel. */
const struct adc_t adc_channels[] = {
- [ADC_BOARD_ID] = {"BOARD_ID", 3300, 4096, 0, STM32_AIN(10)},
+ [ADC_LCM_ID] = {"LCM_ID", 3300, 4096, 0, STM32_AIN(10)},
[ADC_EC_SKU_ID] = {"EC_SKU_ID", 3300, 4096, 0, STM32_AIN(8)},
[ADC_BATT_ID] = {"BATT_ID", 3300, 4096, 0, STM32_AIN(7)},
- [ADC_USBC_THERM] = {"USBC_THERM", 3300, 4096, 0, STM32_AIN(14)},
+ [ADC_USBC_THERM] = {"USBC_THERM", 3300, 4096, 0, STM32_AIN(14),
+ STM32_ADC_SMPR_239_5_CY},
};
BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
@@ -244,8 +293,31 @@ int pd_snk_is_vbus_provided(int port)
return rt946x_is_vbus_ready();
}
+/*
+ * Threshold to detect USB-C board. If the USB-C board isn't connected,
+ * USBC_THERM is floating thus the ADC pin should read about the pull-up
+ * voltage. If it's connected, the voltage is capped by the resistor (429k)
+ * place in parallel to the thermistor. 3.3V x 429k/(39k + 429k) = 3.025V
+ */
+#define USBC_THERM_THRESHOLD 3025
+
static void board_init(void)
{
+#ifdef SECTION_IS_RO
+ /* If USB-C board isn't connected, the device is being assembled.
+ * We cut off the battery until the assembly is done for better yield.
+ * Timing is ok because STM32F0 initializes ADC on demand. */
+ if (board_version > 0x02) {
+ int mv = adc_read_channel(ADC_USBC_THERM);
+ if (mv == ADC_READ_ERROR)
+ mv = adc_read_channel(ADC_USBC_THERM);
+ CPRINTS("USBC_THERM=%d", mv);
+ if (mv > USBC_THERM_THRESHOLD) {
+ cflush();
+ board_cut_off_battery();
+ }
+ }
+#endif
/* Set SPI1 PB13/14/15 pins to high speed */
STM32_GPIO_OSPEEDR(GPIO_B) |= 0xfc000000;
diff --git a/board/flapjack/board.h b/board/flapjack/board.h
index 8eaf824575..c3f697b3ae 100644
--- a/board/flapjack/board.h
+++ b/board/flapjack/board.h
@@ -58,7 +58,6 @@
/* By default, set hcdebug to off */
#undef CONFIG_HOSTCMD_DEBUG_MODE
#define CONFIG_HOSTCMD_DEBUG_MODE HCDEBUG_OFF
-#undef CONFIG_LID_SWITCH
#undef CONFIG_LTO
#define CONFIG_POWER_BUTTON
#define CONFIG_POWER_BUTTON_IGNORE_LID
@@ -130,6 +129,9 @@
#define CONFIG_USBC_VCONN
#define CONFIG_USBC_VCONN_SWAP
#define CONFIG_USB_PD_COMM_LOCKED
+#ifdef SECTION_IS_RO
+#define CONFIG_USB_PD_DEBUG_LEVEL 0
+#endif
#define CONFIG_BATTERY_CUT_OFF
#define CONFIG_BATTERY_PRESENT_CUSTOM
@@ -169,11 +171,6 @@
#undef CONFIG_CMD_FASTCHARGE
#undef CONFIG_CMD_FLASH
#undef CONFIG_CMD_GETTIME
-#ifdef SECTION_IS_RO
-#undef CONFIG_CMD_ADC
-#undef CONFIG_CMD_I2C_XFER
-#undef CONFIG_CMD_IDLE_STATS
-#endif
#undef CONFIG_CMD_HASH
#undef CONFIG_CMD_HCDEBUG
#undef CONFIG_CMD_MD
@@ -181,6 +178,26 @@
#undef CONFIG_CMD_POWERINDEBUG
#undef CONFIG_CMD_TIMERINFO
+#ifdef SECTION_IS_RO
+#undef CONFIG_CMD_ADC
+#undef CONFIG_CMD_APTHROTTLE
+#undef CONFIG_CMD_CBI
+#undef CONFIG_CMD_I2C_SCAN
+#undef CONFIG_CMD_I2C_XFER
+#undef CONFIG_CMD_IDLE_STATS
+#undef CONFIG_CMD_INA
+#undef CONFIG_CMD_MMAPINFO
+#undef CONFIG_CMD_PD
+#undef CONFIG_CMD_PWR_AVG
+#undef CONFIG_CMD_REGULATOR
+#undef CONFIG_CMD_RW
+#undef CONFIG_CMD_SHMEM
+/* TODO: Consider put these back when FSI is (about to be) done. */
+#undef CONFIG_CMD_SLEEPMASK
+#undef CONFIG_CMD_SLEEPMASK_SET
+#undef CONFIG_CMD_SYSLOCK
+#endif
+
#define CONFIG_TASK_PROFILING
/* I2C ports */
@@ -221,13 +238,25 @@ enum oem_id {
enum adc_channel {
/* Real ADC channels begin here */
- ADC_BOARD_ID = 0,
+ ADC_LCM_ID = 0,
ADC_EC_SKU_ID,
ADC_BATT_ID,
ADC_USBC_THERM,
ADC_CH_COUNT
};
+/* Panel ID bit position inside sku_id */
+#define PANEL_ID_BIT_POSITION 16
+
+/* Refer to coreboot/src/mainboard/google/kukui/display.h */
+enum panel_id {
+ PANEL_KUKUI_INNOLUX = 0,
+ PANEL_BOE_HIMAX8279D10P,
+ PANEL_BOE_HIMAX8279D8P,
+ PANEL_UNKNOWN,
+ PANEL_COUNT,
+};
+
/* power signal definitions */
enum power_signal {
AP_IN_S3_L,
@@ -263,6 +292,15 @@ void emmc_cmd_interrupt(enum gpio_signal signal);
void board_reset_pd_mcu(void);
+#define ADC_MARGIN_MV 56 /* Simply assume 1800/16/2 */
+
+struct mv_to_id {
+ int id;
+ int median_mv;
+};
+
+int board_read_id(enum adc_channel, const struct mv_to_id *table, int size);
+
#endif /* !__ASSEMBLER__ */
#endif /* __CROS_EC_BOARD_H */
diff --git a/board/flapjack/gpio.inc b/board/flapjack/gpio.inc
index 4846e0c44c..3ff868d09f 100644
--- a/board/flapjack/gpio.inc
+++ b/board/flapjack/gpio.inc
@@ -39,8 +39,8 @@ GPIO_INT(SPI1_NSS, PIN(A, 15), GPIO_INT_BOTH,
spi_event)
GPIO_INT_RW(SYNC_INT, PIN(A, 8), GPIO_INT_RISING | GPIO_PULL_DOWN,
sync_interrupt)
-GPIO_INT(HALL_INT_L, PIN(C, 5), GPIO_INT_FALLING,
- hall_interrupt)
+GPIO_INT(LID_OPEN, PIN(C, 5), GPIO_INT_BOTH,
+ lid_interrupt) /* HALL_INT_L */
GPIO_INT(GAUGE_INT_ODL, PIN(C, 9), GPIO_INT_FALLING | GPIO_PULL_UP,
gauge_interrupt)
@@ -67,7 +67,7 @@ GPIO(I2C2_SDA, PIN(A, 12), GPIO_INPUT)
/* Analog pins */
GPIO(BATT_ID, PIN(A, 7), GPIO_ANALOG)
-GPIO(BOARD_ID, PIN(C, 0), GPIO_ANALOG)
+GPIO(LCM_ID, PIN(C, 0), GPIO_ANALOG)
GPIO(EC_SKU_ID, PIN(B, 0), GPIO_ANALOG)
GPIO(USBC_THERM, PIN(C, 4), GPIO_ANALOG)
@@ -79,7 +79,6 @@ GPIO(CCD_MODE_ODL, PIN(A, 1), GPIO_INPUT)
/* Other output pins */
GPIO(ENTERING_RW, PIN(C, 6), GPIO_ODR_HIGH) /* EC_ENTERING_RW_ODL */
GPIO(EC_INT_L, PIN(B, 12), GPIO_ODR_HIGH) /* EC_AP_INT_ODL */
-GPIO(EC_BOARD_ID_EN_L, PIN(C, 15), GPIO_ODR_HIGH) /* EC_BOARD_ID_EN_ODL */
GPIO(USB_C0_DP_POLARITY, PIN(C, 14), GPIO_OUT_LOW)
GPIO(USB_C0_HPD_OD, PIN(F, 1), GPIO_ODR_LOW)
GPIO(BOOTBLOCK_EN_L, PIN(C, 1), GPIO_ODR_HIGH)
diff --git a/board/flapjack/usb_pd_policy.c b/board/flapjack/usb_pd_policy.c
index 249237734b..02919e56b6 100644
--- a/board/flapjack/usb_pd_policy.c
+++ b/board/flapjack/usb_pd_policy.c
@@ -315,6 +315,11 @@ static int svdm_dp_attention(int port, uint32_t *payload)
return 1;
}
+ usb_mux_set(port, lvl ? TYPEC_MUX_DP : TYPEC_MUX_NONE,
+ USB_SWITCH_CONNECT, pd_get_polarity(port));
+
+ mux->hpd_update(port, lvl, irq);
+
if (irq & cur_lvl) {
uint64_t now = get_time().val;
/* wait for the minimum spacing between IRQ_HPD if needed */
@@ -342,7 +347,6 @@ static int svdm_dp_attention(int port, uint32_t *payload)
hpd_deadline[port] = get_time().val + HPD_USTREAM_DEBOUNCE_LVL;
}
- mux->hpd_update(port, lvl, irq);
/* ack */
return 1;
}
diff --git a/board/fleex/board.c b/board/fleex/board.c
index d76c1e5dd6..386d8ad61b 100644
--- a/board/fleex/board.c
+++ b/board/fleex/board.c
@@ -161,6 +161,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_ACCEL),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.rot_standard_ref = &base_standard_ref,
@@ -191,6 +193,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_GYRO),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.default_range = 1000 | ROUND_UP_FLAG, /* dps */
diff --git a/board/fleex/gpio.inc b/board/fleex/gpio.inc
index 6993acd251..987a02674b 100644
--- a/board/fleex/gpio.inc
+++ b/board/fleex/gpio.inc
@@ -131,8 +131,8 @@ GPIO(USB_C1_HPD_1V8_ODL, PIN(C, 6), GPIO_INPUT | /* C1 DP Hotplug Detect */
GPIO(LED_1_PWR_WHITE_L, PIN(C, 3), GPIO_OUT_HIGH)
GPIO(LED_2_CHG_AMBER_L, PIN(C, 4), GPIO_OUT_HIGH)
-/* Keyboard Backlight */
-GPIO(KB_BL_PWR_EN, PIN(6, 2), GPIO_OUT_LOW)
+/* Not implemented in hardware */
+UNIMPLEMENTED(KB_BL_PWR_EN)
/* Overcurrent event to host */
GPIO(USB_C_OC, PIN(3, 6), GPIO_ODR_HIGH | GPIO_SEL_1P8V)
diff --git a/board/glkrvp/chg_usb_pd.c b/board/glkrvp/chg_usb_pd.c
index c10c1e14a4..020b628083 100644
--- a/board/glkrvp/chg_usb_pd.c
+++ b/board/glkrvp/chg_usb_pd.c
@@ -31,8 +31,10 @@ enum glkrvp_charge_ports {
};
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
- {NPCX_I2C_PORT7_0, 0xA0, &tcpci_tcpm_drv, TCPC_ALERT_ACTIVE_LOW},
- {NPCX_I2C_PORT7_0, 0xA4, &tcpci_tcpm_drv, TCPC_ALERT_ACTIVE_LOW},
+ /* Alert is active-low, push-pull */
+ {NPCX_I2C_PORT7_0, 0xA0, &tcpci_tcpm_drv, 0},
+ /* Alert is active-low, push-pull */
+ {NPCX_I2C_PORT7_0, 0xA4, &tcpci_tcpm_drv, 0},
};
BUILD_ASSERT(ARRAY_SIZE(tcpc_config) == CONFIG_USB_PD_PORT_COUNT);
diff --git a/board/glkrvp_ite/chg_usb_pd.c b/board/glkrvp_ite/chg_usb_pd.c
index 1ddef2c60d..5fb88a919a 100644
--- a/board/glkrvp_ite/chg_usb_pd.c
+++ b/board/glkrvp_ite/chg_usb_pd.c
@@ -31,8 +31,10 @@ enum glkrvp_charge_ports {
};
const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
- {IT83XX_I2C_CH_B, 0xA0, &tcpci_tcpm_drv, TCPC_ALERT_ACTIVE_LOW},
- {IT83XX_I2C_CH_B, 0xA4, &tcpci_tcpm_drv, TCPC_ALERT_ACTIVE_LOW},
+ /* Alert is active-low, push-pull */
+ {IT83XX_I2C_CH_B, 0xA0, &tcpci_tcpm_drv, 0},
+ /* Alert is active-low, push-pull */
+ {IT83XX_I2C_CH_B, 0xA4, &tcpci_tcpm_drv, 0},
};
BUILD_ASSERT(ARRAY_SIZE(tcpc_config) == CONFIG_USB_PD_PORT_COUNT);
diff --git a/board/hatch/board.c b/board/hatch/board.c
index 29867bcfa8..4b1f8fa883 100644
--- a/board/hatch/board.c
+++ b/board/hatch/board.c
@@ -14,6 +14,9 @@
#include "driver/accelgyro_bmi160.h"
#include "driver/als_opt3001.h"
#include "driver/ppc/sn5s330.h"
+#include "driver/tcpm/anx7447.h"
+#include "driver/tcpm/ps8xxx.h"
+#include "driver/tcpm/tcpci.h"
#include "ec_commands.h"
#include "extpower.h"
#include "fan.h"
@@ -115,6 +118,34 @@ const struct pwm_t pwm_channels[] = {
BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
/******************************************************************************/
+/* USB-C TPCP Configuration */
+const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
+ [USB_PD_PORT_TCPC_0] = {
+ .i2c_host_port = I2C_PORT_TCPC0,
+ .i2c_slave_addr = AN7447_TCPC0_I2C_ADDR,
+ .drv = &anx7447_tcpm_drv,
+ .flags = TCPC_FLAGS_RESET_ACTIVE_HIGH,
+ },
+ [USB_PD_PORT_TCPC_1] = {
+ .i2c_host_port = I2C_PORT_TCPC1,
+ .i2c_slave_addr = PS8751_I2C_ADDR1,
+ .drv = &ps8xxx_tcpm_drv,
+ .flags = 0,
+ },
+};
+
+struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = {
+ [USB_PD_PORT_TCPC_0] = {
+ .driver = &anx7447_usb_mux_driver,
+ .hpd_update = &anx7447_tcpc_update_hpd_status,
+ },
+ [USB_PD_PORT_TCPC_1] = {
+ .driver = &tcpci_tcpm_usb_mux_driver,
+ .hpd_update = &ps8xxx_tcpc_update_hpd_status,
+ }
+};
+
+/******************************************************************************/
/* Sensors */
/* Base Sensor mutex */
static struct mutex g_base_mutex;
@@ -386,3 +417,25 @@ void board_overcurrent_event(int port, int is_overcurrented)
/* Note that the level is inverted because the pin is active low. */
gpio_set_level(GPIO_USB_C_OC_ODL, !is_overcurrented);
}
+
+/* Called on AP S5 -> S3 transition */
+static void board_chipset_startup(void)
+{
+ gpio_set_level(GPIO_EC_INT_L, 1);
+}
+DECLARE_HOOK(HOOK_CHIPSET_STARTUP, board_chipset_startup,
+ HOOK_PRIO_DEFAULT);
+
+/* Called on AP S3 -> S5 transition */
+static void board_chipset_shutdown(void)
+{
+ /*
+ * EC_INT_L is currently a push-pull pin and this causes leakage in G3
+ * onto the PP3300_A_SOC rail. Pull this pin low when host enters S5 to
+ * avoid the leakage. It will be pulled back high when host transitions
+ * out of S5.
+ */
+ gpio_set_level(GPIO_EC_INT_L, 0);
+}
+DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, board_chipset_shutdown,
+ HOOK_PRIO_DEFAULT);
diff --git a/board/hatch/board.h b/board/hatch/board.h
index 0548147c94..3370b46cd4 100644
--- a/board/hatch/board.h
+++ b/board/hatch/board.h
@@ -49,6 +49,14 @@
#define CONFIG_ALS_OPT3001
#define OPT3001_I2C_ADDR OPT3001_I2C_ADDR1
+/* USB Type C and USB PD defines */
+#define CONFIG_USB_PD_TCPM_ANX7447
+#define CONFIG_USB_PD_TCPM_PS8751
+#define BOARD_TCPC_C0_RESET_HOLD_DELAY ANX74XX_RESET_HOLD_MS
+#define BOARD_TCPC_C0_RESET_POST_DELAY ANX74XX_RESET_HOLD_MS
+#define BOARD_TCPC_C1_RESET_HOLD_DELAY PS8XXX_RESET_DELAY_MS
+#define BOARD_TCPC_C1_RESET_POST_DELAY 0
+
/* Volume Button feature */
#define CONFIG_VOLUME_BUTTONS
#define GPIO_VOLUME_UP_L GPIO_EC_VOLUP_BTN_ODL
diff --git a/board/hatch_fp/board.c b/board/hatch_fp/board.c
new file mode 100644
index 0000000000..b86f0b8f46
--- /dev/null
+++ b/board/hatch_fp/board.c
@@ -0,0 +1,85 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* Meowth Fingerprint MCU configuration */
+
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "registers.h"
+#include "spi.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
+
+#ifndef HAS_TASK_FPSENSOR
+void fps_event(enum gpio_signal signal)
+{
+}
+#endif
+
+static void ap_deferred(void)
+{
+ /*
+ * in S3: SLP_S3_L is 0 and SLP_S0_L is X.
+ * in S0ix: SLP_S3_L is X and SLP_S0_L is 0.
+ * in S0: SLP_S3_L is 1 and SLP_S0_L is 1.
+ * in S5/G3, the FP MCU should not be running.
+ */
+ int running = gpio_get_level(GPIO_PCH_SLP_S3_L)
+ && gpio_get_level(GPIO_PCH_SLP_S0_L);
+
+ if (running) { /* S0 */
+ disable_sleep(SLEEP_MASK_AP_RUN);
+ hook_notify(HOOK_CHIPSET_RESUME);
+ } else { /* S0ix/S3 */
+ hook_notify(HOOK_CHIPSET_SUSPEND);
+ enable_sleep(SLEEP_MASK_AP_RUN);
+ }
+}
+DECLARE_DEFERRED(ap_deferred);
+
+/* PCH power state changes */
+void slp_event(enum gpio_signal signal)
+{
+ hook_call_deferred(&ap_deferred_data, 0);
+}
+
+#include "gpio_list.h"
+
+/* SPI devices */
+const struct spi_device_t spi_devices[] = {
+ /* Fingerprint sensor (SCLK at 4Mhz) */
+ { CONFIG_SPI_FP_PORT, 3, GPIO_SPI2_NSS }
+};
+const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices);
+
+static void spi_configure(void)
+{
+ /* Configure SPI GPIOs */
+ gpio_config_module(MODULE_SPI_MASTER, 1);
+ /*
+ * Set all SPI master signal pins to very high speed:
+ * pins B12/13/14/15
+ */
+ STM32_GPIO_OSPEEDR(GPIO_B) |= 0xff000000;
+ /* Enable clocks to SPI2 module (master) */
+ STM32_RCC_APB1ENR |= STM32_RCC_PB1_SPI2;
+
+ spi_enable(CONFIG_SPI_FP_PORT, 1);
+}
+
+/* Initialize board. */
+static void board_init(void)
+{
+ spi_configure();
+
+ /* Enable interrupt on PCH power signals */
+ gpio_enable_interrupt(GPIO_PCH_SLP_S3_L);
+ gpio_enable_interrupt(GPIO_PCH_SLP_S0_L);
+ /* enable the SPI slave interface if the PCH is up */
+ hook_call_deferred(&ap_deferred_data, 0);
+}
+DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
diff --git a/board/hatch_fp/board.h b/board/hatch_fp/board.h
new file mode 100644
index 0000000000..166ee508be
--- /dev/null
+++ b/board/hatch_fp/board.h
@@ -0,0 +1,184 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Hatch Fingerprint MCU configuration */
+
+#ifndef __BOARD_H
+#define __BOARD_H
+
+#define CONFIG_SYSTEM_UNLOCKED
+
+/*
+ * Flash layout: we redefine the sections offsets and sizes as we want to
+ * include a rollback region, and will use RO/RW regions of different sizes.
+ */
+#undef _IMAGE_SIZE
+#undef CONFIG_ROLLBACK_OFF
+#undef CONFIG_ROLLBACK_SIZE
+#undef CONFIG_FLASH_PSTATE
+#undef CONFIG_FW_PSTATE_SIZE
+#undef CONFIG_FW_PSTATE_OFF
+#undef CONFIG_SHAREDLIB_SIZE
+#undef CONFIG_RO_MEM_OFF
+#undef CONFIG_RO_STORAGE_OFF
+#undef CONFIG_RO_SIZE
+#undef CONFIG_RW_MEM_OFF
+#undef CONFIG_RW_STORAGE_OFF
+#undef CONFIG_RW_SIZE
+#undef CONFIG_EC_PROTECTED_STORAGE_OFF
+#undef CONFIG_EC_PROTECTED_STORAGE_SIZE
+#undef CONFIG_EC_WRITABLE_STORAGE_OFF
+#undef CONFIG_EC_WRITABLE_STORAGE_SIZE
+#undef CONFIG_WP_STORAGE_OFF
+#undef CONFIG_WP_STORAGE_SIZE
+
+#undef CONFIG_RAM_SIZE
+#define CONFIG_RAM_SIZE 0x40000 /* 256 KB */
+#undef CONFIG_FLASH_SIZE
+#define CONFIG_FLASH_SIZE (1 * 1024 * 1024)
+
+#define CONFIG_FLASH_WRITE_SIZE STM32_FLASH_WRITE_SIZE_3300
+
+#define CONFIG_SHAREDLIB_SIZE 0
+
+#define CONFIG_RO_MEM_OFF 0
+#define CONFIG_RO_STORAGE_OFF 0
+#define CONFIG_RO_SIZE (128 * 1024)
+
+/* EC rollback protection block */
+#define CONFIG_ROLLBACK_OFF (CONFIG_RO_MEM_OFF + CONFIG_RO_SIZE)
+#define CONFIG_ROLLBACK_SIZE (128 * 1024 * 2) /* 2 blocks of 128KB each */
+
+#define CONFIG_RW_MEM_OFF (CONFIG_ROLLBACK_OFF + CONFIG_ROLLBACK_SIZE)
+#define CONFIG_RW_STORAGE_OFF 0
+#define CONFIG_RW_SIZE (CONFIG_FLASH_SIZE - \
+ (CONFIG_RW_MEM_OFF - CONFIG_RO_MEM_OFF))
+
+#define CONFIG_EC_PROTECTED_STORAGE_OFF CONFIG_RO_MEM_OFF
+#define CONFIG_EC_PROTECTED_STORAGE_SIZE CONFIG_RO_SIZE
+#define CONFIG_EC_WRITABLE_STORAGE_OFF CONFIG_RW_MEM_OFF
+#define CONFIG_EC_WRITABLE_STORAGE_SIZE CONFIG_RW_SIZE
+
+#define CONFIG_WP_STORAGE_OFF CONFIG_EC_PROTECTED_STORAGE_OFF
+#define CONFIG_WP_STORAGE_SIZE CONFIG_EC_PROTECTED_STORAGE_SIZE
+
+/*
+ * We want to prevent flash readout, and use it as indicator of protection
+ * status.
+ */
+/*TODO(b/125419658): enable CONFIG_FLASH_READOUT_PROTECTION_AS_PSTATE*/
+
+/* the UART console is on USART1 */
+#undef CONFIG_UART_CONSOLE
+#define CONFIG_UART_CONSOLE 1
+
+#define CONFIG_UART_TX_DMA
+#define CONFIG_UART_TX_DMA_PH DMAMUX1_REQ_USART1_TX
+#undef CONFIG_UART_TX_BUF_SIZE
+#define CONFIG_UART_TX_BUF_SIZE 2048
+#undef CONFIG_UART_TX_DMA
+#undef CONFIG_UART_RX_DMA
+#define CONFIG_UART_TX_REQ_CH 4
+#define CONFIG_UART_RX_REQ_CH 4
+
+/* Optional features */
+#undef CONFIG_ADC
+#define CONFIG_CMD_IDLE_STATS
+#define CONFIG_DMA
+/*FIXME*/
+/*#define CONFIG_FORCE_CONSOLE_RESUME*/
+#define CONFIG_FPU
+#undef CONFIG_HIBERNATE
+#define CONFIG_HOST_COMMAND_STATUS
+#undef CONFIG_I2C
+#undef CONFIG_LID_SWITCH
+/*FIXME*/
+/*#define CONFIG_LOW_POWER_IDLE*/
+#define CONFIG_MKBP_EVENT
+#define CONFIG_MKBP_USE_GPIO
+#define CONFIG_PRINTF_LEGACY_LI_FORMAT
+#define CONFIG_SHA256
+#define CONFIG_SHA256_UNROLLED
+#define CONFIG_SPI
+#define CONFIG_STM_HWTIMER32
+#define CONFIG_SUPPRESSED_HOST_COMMANDS \
+ EC_CMD_CONSOLE_SNAPSHOT, EC_CMD_CONSOLE_READ, EC_CMD_PD_GET_LOG_ENTRY
+#undef CONFIG_TASK_PROFILING
+#define CONFIG_WATCHDOG_HELP
+#define CONFIG_WP_ACTIVE_HIGH
+
+/* SPI configuration for the fingerprint sensor */
+#define CONFIG_SPI_MASTER
+#define CONFIG_SPI_FP_PORT 0 /* SPI2: first master config */
+#ifdef SECTION_IS_RW
+/* TODO(b/124773209): Enable FP once rollback code has been fixed */
+#if 0
+#define CONFIG_FP_SENSOR_FPC1025
+#define CONFIG_CMD_FPSENSOR_DEBUG
+#endif
+/*
+ * Use the malloc code only in the RW section (for the private library),
+ * we cannot enable it in RO since it is not compatible with the RW verification
+ * (shared_mem_init done too late).
+ */
+#define CONFIG_MALLOC
+/* Special memory regions to store large arrays */
+#define FP_FRAME_SECTION __SECTION(ahb4)
+#define FP_TEMPLATE_SECTION __SECTION(ahb)
+
+#else /* SECTION_IS_RO */
+/* RO verifies the RW partition signature */
+#define CONFIG_RSA
+#define CONFIG_RSA_KEY_SIZE 3072
+#define CONFIG_RSA_EXPONENT_3
+#define CONFIG_RWSIG
+#endif
+#define CONFIG_RWSIG_TYPE_RWSIG
+
+/* RW does slow compute, RO does slow flash erase. */
+#undef CONFIG_WATCHDOG_PERIOD_MS
+#define CONFIG_WATCHDOG_PERIOD_MS 10000
+
+/*
+ * Add rollback protection
+ */
+/*TODO(b/125506600): Support rollback protection */
+#if 0
+#define CONFIG_ROLLBACK
+#define CONFIG_ROLLBACK_SECRET_SIZE 32
+
+#define CONFIG_ROLLBACK_MPU_PROTECT
+#endif
+
+/*
+ * We do not use any "locally" generated entropy: this is normally used
+ * to add local entropy when the main source of entropy is remote.
+ */
+#undef CONFIG_ROLLBACK_SECRET_LOCAL_ENTROPY_SIZE
+#ifdef SECTION_IS_RW
+#undef CONFIG_ROLLBACK_UPDATE
+#endif
+
+#define CONFIG_AES
+#define CONFIG_AES_GCM
+
+#define CONFIG_RNG
+
+#define CONFIG_CMD_FLASH
+#define CONFIG_CMD_SPI_XFER
+
+#ifndef __ASSEMBLER__
+
+/* Timer selection */
+#define TIM_CLOCK32 2
+#define TIM_WATCHDOG 16
+
+#include "gpio_signal.h"
+
+void fps_event(enum gpio_signal signal);
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* __BOARD_H */
diff --git a/board/hatch_fp/build.mk b/board/hatch_fp/build.mk
new file mode 100644
index 0000000000..038819ff4d
--- /dev/null
+++ b/board/hatch_fp/build.mk
@@ -0,0 +1,14 @@
+# Copyright 2019 The Chromium OS 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 STM32F412
+CHIP:=stm32
+CHIP_FAMILY:=stm32f4
+CHIP_VARIANT:=stm32f412
+
+board-y=board.o
+
+test-list-y=aes sha256 sha256_unrolled
diff --git a/board/hatch_fp/dev_key.pem b/board/hatch_fp/dev_key.pem
new file mode 100644
index 0000000000..e3273cbccf
--- /dev/null
+++ b/board/hatch_fp/dev_key.pem
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIG4wIBAAKCAYEAwVaB9PsLDGaHIMGp+uouwQvQGhNbIifTTX40aO7Sh00Pw9va
+tqggEe7AyeEKQLy7uxCfwLFkUABCmEIusLpsp7iGvAXz3R1N80pyszNGhsqV2UQH
+WW/M5L/3nPNjqjffje0ZMwoCNeE4YBqn+puiKEBEZXnnZsPV/f5lOn6v4GP7wzkF
+lTEq9InLhoWEKjuyL6gwfVZiEvNs52umzjSx/OaY9ux1SrnR6768xQdCRpah/RDC
+DAdL1v7lnzagBXq1p5WFFkAsIQhgSk7FhC0MX3BPqGE2c68t/g5AkyT1M7SZk4+5
+sY6oor7vVmfzUsShJOP1xb/Gv91cgRMOIU4y6mnQcf7YO68ex9YpnTnL2JGpZj/j
+MHzocI9l9a6R0PX17UOvXfVg2tQg+mU+zLqCG2xMe8R7+sA893/wgQdSXDiB5Nvh
+Vbp+89WxrX2vQK5lIObCUlKNLABsdeAMiTBN9IXMLdK5EP3gbL+wKG+/82DgAwaz
+l6hJZ2TLhV+BWdE3AgEDAoIBgQCA5FajUgddma9rK8anRsnWB+ARYjzBb+IzqXhF
+9IxaM1/X5+ckcBVhSdXb61wrKH0nYGqAdkLgACxlgXR10Z3FJa8oA/fovjP3hvci
+Ii8Ehw6Q2ATmSoiYf/pookJxepUJSLt3XAF5QNBAEcVRvRbFgC2Y+++Z1+P+qZjR
+qcqVl/0s0K5jdhyjBoevA61xfSF1Gsr+OZa3TPNE8m80IyFTRGX58vjce+FH1H3Y
+r4GEZGv+CywIBN05/0O/ecADpyKZ1m8PYrkpCwuNc+BK+BkEeEary61Y/IoQLVUx
+ntb4Y2meciFj5yr7PtRLcuwllWjEU5IfKPl2bB96fxITC6ALZVI9ksC6YDfCBXuU
+rWQNG1UFC6Ux//g1BdXhuPgl9MHS0nA37oJ8BxhdIgbQ1OxLlkY+VLwWN0IrC3vp
++MDTufSPh7sR7r4sMVTYcncyc4kE0pnXQw+LHg3lnwadwlFeKP2mJKAyeveMqTWd
+GdB0eMuyv2cp77/nrESWYDUa9ysCgcEA/dwIdGjXmhz9T4zleZUTM9/D+uzW5kG0
+eB/br+ztzP/9YC+W0+DDlHVG2bdrsJsooZEyuzDaiGd/JiW9wPTjdjtSpCksJUEE
+KImymQ2GFbs7If1ZCgcxFqdywjk8WVqxCcv/Bqhsa7lcIGOFiV9X8x067xpwNU3t
+yw8IRXchfUK80BKFPf8quP4RoYy6o4rkos28+Q+zIPSZlBaZXKsSKPQElyN0SysN
+UwGSpOJ4b9TOH88GZFLymKOY4DUhvSJXAoHBAML31grDPsla0aaUD5oj06TcIavC
+24fyqm2qZRjJxPIffcW08MfTJJVraguEJWnJW1zVZ9vRdgXTriMutUPH32MWgnF5
+iv7dxvxEPaUoL68tbryxElt1wwpfMmDf4T6sIic8CANnMLUQIE5Orwobx7btqC8q
+8aQfa+vfrlybD6Fe1j19w3zVNviNoMdFQdF2MvbdHpZeQrpevgla6T/hwb5USx14
+VHoaX8bATRfmjtTW4FcYknRttvM+y8OaD/Q8IQKBwQCpPVr4ReURaKjfs0OmY2Ii
+lS1R8znu1nhQFT0f80kzVVOVdQ836y0No4SRJPJ1vMXBC3cndecFmlTEGSkrTez5
+fOHCxh1uK1gbBncQs665J3zBU5CxWiC5xPcsJig7kctb3VSvGvLye5LAQlkGP4/3
+aNH0vErOM/PctLAuT2uo1yiKtwN+qhx7VAvBCHxtB0MXM9NQtSIV+GZiubuTHLbF
++AMPbPgyHLOMq7cYlvr1OIlqigRC4fcQbRCVeMEowY8CgcEAgfqOsdd/MOc2bw1f
+vBfibegWcoHnr/ccSRxDZdvYoWpT2SNLL+IYY5zxXQLDm9uSPeOakoukA+J0F3R4
+19qU7LmsS6ZcqekvUtgpGMV1H3OfKHYMPPkssZTMQJVA1HLBb31arO91zgrANDR0
+sWfaefPFdMdLwr+dR+p0Pby1Fj85flPXqI4kpbPAhNjWi6Qh+ei/DumB0ZR+sOdG
+KpaBKY2HaPri/BGVLyreD+8J4znq5LsMTZ55938ygma1TX1rAoHASPiGXtnpXS5d
+TH2LAGcvUyopOMgdEHbm9Xvkdet3rLrNPkJ+tuTsv7MwUprnoQQhCowbVwQ8IzS0
+MHSMcqBT68dJsq9Y3OB7tYHtSYDEcHEpbdIt1oRHO0tWo/XMC/qRvTSTiEqCv4LQ
+x2buZlD4KfmQOHh24EwuZMB7MsyvdMvY56LWrJExx+Cb1VcItGme9pxf5Tir0ho/
+xzKyVSGh59GI0weB/PQl1queFbSYDWeKF6Ra74appkWF1cb9z8P4
+-----END RSA PRIVATE KEY-----
diff --git a/board/hatch_fp/ec.tasklist b/board/hatch_fp/ec.tasklist
new file mode 100644
index 0000000000..ec2bc3c268
--- /dev/null
+++ b/board/hatch_fp/ec.tasklist
@@ -0,0 +1,23 @@
+/* Copyright 2017 The Chromium OS 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_ALWAYS(n, r, d, s) for base tasks and
+ * TASK_NOTEST(n, r, d, s) for tasks that can be excluded in test binaries,
+ * 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
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TASK_LIST \
+ TASK_ALWAYS_RO(RWSIG, rwsig_task, NULL, 1280) \
+ TASK_ALWAYS(HOOKS, hook_task, NULL, 1024) \
+ TASK_ALWAYS(HOSTCMD, host_command_task, NULL, 4096) \
+ TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/board/hatch_fp/flash_fp_mcu b/board/hatch_fp/flash_fp_mcu
new file mode 100644
index 0000000000..7b014961bf
--- /dev/null
+++ b/board/hatch_fp/flash_fp_mcu
@@ -0,0 +1,35 @@
+#!/bin/bash
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+. "$(dirname "$(readlink -f "$0")")"/../share/flash_fp_mcu/flash_fp_mcu_common.sh
+
+# Kabylake PCH GPIOs
+readonly KBL_GPIOCHIP="gpiochip200"
+
+if [[ -e "/sys/class/gpio/${KBL_GPIOCHIP}" ]]; then
+ # Hatch configuration
+ echo "NOTE: For use with updating FP MCU on HATCH boards only"
+ readonly SPIDEV="/dev/spidev1.1"
+ # GSPI1 ACPI device for FP MCU
+ readonly SPIID="spi-PRP0001:01"
+ # FPMCU RST_ODL is on GPP_A12 = 200 + 12 = 212
+ readonly GPIO_NRST=212
+ # FPMCU BOOT0 is on GPP_A22 = 200 + 22 = 222
+ readonly GPIO_BOOT0=222
+ # No PWREN GPIO on Hatch, FPMCU is always on
+ readonly GPIO_PWREN=""
+else
+ echo "Cannot find a known GPIO chip."
+ exit 1
+fi
+
+flash_fp_mcu_stm32 \
+ "${SPIDEV}" \
+ "${SPIID}" \
+ "${GPIO_NRST}" \
+ "${GPIO_BOOT0}" \
+ "${GPIO_PWREN}" \
+ "${1}"
+
diff --git a/board/hatch_fp/gpio.inc b/board/hatch_fp/gpio.inc
new file mode 100644
index 0000000000..7a9dfd152f
--- /dev/null
+++ b/board/hatch_fp/gpio.inc
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Interrupts */
+GPIO_INT(FPS_INT, PIN(A, 0), GPIO_INT_RISING, fps_event)
+GPIO_INT(SPI1_NSS, PIN(A, 4), GPIO_INPUT, spi_event)
+
+GPIO_INT(PCH_SLP_S0_L, PIN(B, 0), GPIO_INT_BOTH, slp_event)
+GPIO_INT(PCH_SLP_S3_L, PIN(B, 1), GPIO_INT_BOTH, slp_event)
+GPIO(PCH_SLP_S4_L, PIN(B, 2), GPIO_INPUT)
+GPIO(PCH_SLP_SUS_L, PIN(B, 5), GPIO_INPUT)
+
+GPIO(WP, PIN(B, 7), GPIO_INPUT)
+
+/* Outputs */
+GPIO(EC_INT_L, PIN(A, 1), GPIO_OUT_HIGH)
+GPIO(FP_RST_ODL, PIN(B,10), GPIO_OUT_HIGH)
+GPIO(SPI2_NSS, PIN(B,12), GPIO_OUT_HIGH)
+GPIO(USER_PRES_L, PIN(B, 9), GPIO_ODR_HIGH)
+
+UNIMPLEMENTED(ENTERING_RW)
+
+/* USART1: PA9/PA10 */
+ALTERNATE(PIN_MASK(A, 0x0600), GPIO_ALT_USART, MODULE_UART, GPIO_PULL_UP)
+/* SPI1 slave from the AP: PA4/5/6/7 */
+ALTERNATE(PIN_MASK(A, 0x00f0), GPIO_ALT_SPI, MODULE_SPI, 0)
+/*
+ * SPI2 master to sensor: PB13/14/15
+ * Note that we're not configuring NSS (PB12) here because we have already
+ * configured it as a GPIO above and the SPI_MASTER module expects to use it
+ * in software NSS management mode, not hardware management mode.
+ */
+ALTERNATE(PIN_MASK(B, 0xE000), GPIO_ALT_SPI, MODULE_SPI_MASTER, 0)
diff --git a/board/kohaku/battery.c b/board/kohaku/battery.c
new file mode 100644
index 0000000000..b81fa795b9
--- /dev/null
+++ b/board/kohaku/battery.c
@@ -0,0 +1,93 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Battery pack vendor provided charging profile
+ */
+
+#include "battery_fuel_gauge.h"
+#include "common.h"
+#include "util.h"
+
+/*
+ * Battery info for all Hatch battery types. Note that the fields
+ * start_charging_min/max and charging_min/max are not used for the charger.
+ * The effective temperature limits are given by discharging_min/max_c.
+ *
+ * Fuel Gauge (FG) parameters which are used for determining if the battery
+ * is connected, the appropriate ship mode (battery cutoff) command, and the
+ * charge/discharge FETs status.
+ *
+ * Ship mode (battery cutoff) requires 2 writes to the appropriate smart battery
+ * register. For some batteries, the charge/discharge FET bits are set when
+ * charging/discharging is active, in other types, these bits set mean that
+ * charging/discharging is disabled. Therefore, in addition to the mask for
+ * these bits, a disconnect value must be specified. Note that for TI fuel
+ * gauge, the charge/discharge FET status is found in Operation Status (0x54),
+ * but a read of Manufacturer Access (0x00) will return the lower 16 bits of
+ * Operation status which contains the FET status bits.
+ *
+ * The assumption for battery types supported is that the charge/discharge FET
+ * status can be read with a sb_read() command and therefore, only the register
+ * address, mask, and disconnect value need to be provided.
+ */
+const struct board_batt_params board_battery_info[] = {
+ /* SMP LIS Dell FMXMT Battery Information */
+ [BATTERY_SMP_LIS] = {
+ .fuel_gauge = {
+ .manuf_name = "SMP-LIS3.78",
+ .ship_mode = {
+ .reg_addr = 0x0,
+ .reg_data = { 0x10, 0x10 },
+ },
+ .fet = {
+ .reg_addr = 0x0,
+ .reg_mask = 0x2000,
+ .disconnect_val = 0x2000,
+ }
+ },
+ .batt_info = {
+ .voltage_max = 8800,
+ .voltage_normal = 7660, /* mV */
+ .voltage_min = 6000, /* mV */
+ .precharge_current = 256, /* mA */
+ .start_charging_min_c = 0,
+ .start_charging_max_c = 60,
+ .charging_min_c = 0,
+ .charging_max_c = 60,
+ .discharging_min_c = 0,
+ .discharging_max_c = 60,
+ },
+ },
+
+ /* SMP SDI Dell FMXMT Battery Information */
+ [BATTERY_SMP_SDI] = {
+ .fuel_gauge = {
+ .manuf_name = "SMP-SDI-3727",
+ .ship_mode = {
+ .reg_addr = 0x0,
+ .reg_data = { 0x10, 0x10 },
+ },
+ .fet = {
+ .reg_addr = 0x0,
+ .reg_mask = 0x2000,
+ .disconnect_val = 0x2000,
+ }
+ },
+ .batt_info = {
+ .voltage_max = 8800,
+ .voltage_normal = 7660, /* mV */
+ .voltage_min = 6000, /* mV */
+ .precharge_current = 256, /* mA */
+ .start_charging_min_c = 0,
+ .start_charging_max_c = 60,
+ .charging_min_c = 0,
+ .charging_max_c = 60,
+ .discharging_min_c = 0,
+ .discharging_max_c = 60,
+ },
+ },
+};
+BUILD_ASSERT(ARRAY_SIZE(board_battery_info) == BATTERY_TYPE_COUNT);
+
+const enum battery_type DEFAULT_BATTERY_TYPE = BATTERY_SMP_SDI;
diff --git a/board/kohaku/board.c b/board/kohaku/board.c
new file mode 100644
index 0000000000..50d6cb8f1d
--- /dev/null
+++ b/board/kohaku/board.c
@@ -0,0 +1,357 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Kohaku board-specific configuration */
+
+#include "adc.h"
+#include "adc_chip.h"
+#include "button.h"
+#include "common.h"
+#include "cros_board_info.h"
+#include "driver/accel_bma2x2.h"
+#include "driver/accelgyro_bmi160.h"
+#include "driver/als_opt3001.h"
+#include "driver/ppc/sn5s330.h"
+#include "driver/tcpm/ps8xxx.h"
+#include "driver/tcpm/tcpci.h"
+#include "ec_commands.h"
+#include "extpower.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "lid_switch.h"
+#include "power.h"
+#include "power_button.h"
+#include "pwm.h"
+#include "pwm_chip.h"
+#include "spi.h"
+#include "switch.h"
+#include "system.h"
+#include "task.h"
+#include "temp_sensor.h"
+#include "thermal.h"
+#include "thermistor.h"
+#include "uart.h"
+#include "usb_charge.h"
+#include "usb_pd.h"
+#include "usbc_ppc.h"
+#include "util.h"
+
+#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
+
+static void ppc_interrupt(enum gpio_signal signal)
+{
+ switch (signal) {
+ case GPIO_USB_C0_PPC_INT_ODL:
+ sn5s330_interrupt(0);
+ break;
+
+ case GPIO_USB_C1_PPC_INT_ODL:
+ sn5s330_interrupt(1);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void tcpc_alert_event(enum gpio_signal signal)
+{
+ int port = -1;
+
+ switch (signal) {
+ case GPIO_USB_C0_TCPC_INT_ODL:
+ port = 0;
+ break;
+ case GPIO_USB_C1_TCPC_INT_ODL:
+ port = 1;
+ break;
+ default:
+ return;
+ }
+
+ schedule_deferred_pd_interrupt(port);
+}
+
+static void hdmi_hpd_interrupt(enum gpio_signal signal)
+{
+ baseboard_mst_enable_control(MST_HDMI, gpio_get_level(signal));
+}
+
+static void bc12_interrupt(enum gpio_signal signal)
+{
+ switch (signal) {
+ case GPIO_USB_C0_BC12_INT_ODL:
+ task_set_event(TASK_ID_USB_CHG_P0, USB_CHG_EVENT_BC12, 0);
+ break;
+
+ case GPIO_USB_C1_BC12_INT_ODL:
+ task_set_event(TASK_ID_USB_CHG_P1, USB_CHG_EVENT_BC12, 0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+#include "gpio_list.h" /* Must come after other header files. */
+
+/******************************************************************************/
+/* SPI devices */
+const struct spi_device_t spi_devices[] = {
+};
+const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices);
+
+/******************************************************************************/
+/* PWM channels. Must be in the exactly same order as in enum pwm_channel. */
+const struct pwm_t pwm_channels[] = {
+ [PWM_CH_KBLIGHT] = { .channel = 3, .flags = 0, .freq = 10000 },
+};
+BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
+
+/******************************************************************************/
+/* USB-C TPCP Configuration */
+const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
+ [USB_PD_PORT_TCPC_0] = {
+ .i2c_host_port = I2C_PORT_TCPC0,
+ .i2c_slave_addr = PS8751_I2C_ADDR1,
+ .drv = &ps8xxx_tcpm_drv,
+ .flags = 0,
+ },
+ [USB_PD_PORT_TCPC_1] = {
+ .i2c_host_port = I2C_PORT_TCPC1,
+ .i2c_slave_addr = PS8751_I2C_ADDR1,
+ .drv = &ps8xxx_tcpm_drv,
+ .flags = 0,
+ },
+};
+
+struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = {
+ [USB_PD_PORT_TCPC_0] = {
+ .driver = &tcpci_tcpm_usb_mux_driver,
+ .hpd_update = &ps8xxx_tcpc_update_hpd_status,
+ },
+ [USB_PD_PORT_TCPC_1] = {
+ .driver = &tcpci_tcpm_usb_mux_driver,
+ .hpd_update = &ps8xxx_tcpc_update_hpd_status,
+ }
+};
+
+/******************************************************************************/
+/* Sensors */
+/* Base Sensor mutex */
+static struct mutex g_base_mutex;
+static struct mutex g_lid_mutex;
+
+/* Base accel private data */
+static struct bmi160_drv_data_t g_bmi160_data;
+
+/* BMA255 private data */
+static struct accelgyro_saved_data_t g_bma255_data;
+
+static struct opt3001_drv_data_t g_opt3001_data = {
+ .scale = 1,
+ .uscale = 0,
+ .offset = 0,
+};
+
+/* Matrix to rotate accelrator into standard reference frame */
+static const mat33_fp_t base_standard_ref = {
+ { 0, FLOAT_TO_FP(1), 0},
+ { FLOAT_TO_FP(-1), 0, 0},
+ { 0, 0, FLOAT_TO_FP(1)}
+};
+
+/*
+ * TODO(b/124337208): P0 boards don't have this sensor mounted so the rotation
+ * matrix can't be tested properly. This needs to be revisited after EVT to make
+ * sure the rotaiton matrix for the lid sensor is correct.
+ */
+static const mat33_fp_t lid_standard_ref = {
+ { 0, FLOAT_TO_FP(-1), 0},
+ { FLOAT_TO_FP(-1), 0, 0},
+ { 0, 0, FLOAT_TO_FP(-1)}
+};
+
+struct motion_sensor_t motion_sensors[] = {
+ [LID_ACCEL] = {
+ .name = "Lid Accel",
+ .active_mask = SENSOR_ACTIVE_S0_S3,
+ .chip = MOTIONSENSE_CHIP_BMA255,
+ .type = MOTIONSENSE_TYPE_ACCEL,
+ .location = MOTIONSENSE_LOC_LID,
+ .drv = &bma2x2_accel_drv,
+ .mutex = &g_lid_mutex,
+ .drv_data = &g_bma255_data,
+ .port = I2C_PORT_ACCEL,
+ .addr = BMA2x2_I2C_ADDR1,
+ .rot_standard_ref = &lid_standard_ref,
+ .min_frequency = BMA255_ACCEL_MIN_FREQ,
+ .max_frequency = BMA255_ACCEL_MAX_FREQ,
+ .default_range = 2, /* g, to support tablet mode */
+ .config = {
+ /* EC use accel for angle detection */
+ [SENSOR_CONFIG_EC_S0] = {
+ .odr = 10000 | ROUND_UP_FLAG,
+ },
+ /* Sensor on in S3 */
+ [SENSOR_CONFIG_EC_S3] = {
+ .odr = 10000 | ROUND_UP_FLAG,
+ },
+ },
+ },
+
+ [BASE_ACCEL] = {
+ .name = "Base Accel",
+ .active_mask = SENSOR_ACTIVE_S0_S3,
+ .chip = MOTIONSENSE_CHIP_BMI160,
+ .type = MOTIONSENSE_TYPE_ACCEL,
+ .location = MOTIONSENSE_LOC_BASE,
+ .drv = &bmi160_drv,
+ .mutex = &g_base_mutex,
+ .drv_data = &g_bmi160_data,
+ .port = I2C_PORT_ACCEL,
+ .addr = BMI160_ADDR0,
+ .rot_standard_ref = &base_standard_ref,
+ .min_frequency = BMI160_ACCEL_MIN_FREQ,
+ .max_frequency = BMI160_ACCEL_MAX_FREQ,
+ .default_range = 2, /* g, to support tablet mode */
+ .config = {
+ [SENSOR_CONFIG_EC_S0] = {
+ .odr = 10000 | ROUND_UP_FLAG,
+ },
+ /* Sensor on in S3 */
+ [SENSOR_CONFIG_EC_S3] = {
+ .odr = 10000 | ROUND_UP_FLAG,
+ },
+ },
+ },
+
+ [BASE_GYRO] = {
+ .name = "Base Gyro",
+ .active_mask = SENSOR_ACTIVE_S0_S3,
+ .chip = MOTIONSENSE_CHIP_BMI160,
+ .type = MOTIONSENSE_TYPE_GYRO,
+ .location = MOTIONSENSE_LOC_BASE,
+ .drv = &bmi160_drv,
+ .mutex = &g_base_mutex,
+ .drv_data = &g_bmi160_data,
+ .port = I2C_PORT_ACCEL,
+ .addr = BMI160_ADDR0,
+ .default_range = 1000, /* dps */
+ .rot_standard_ref = &base_standard_ref,
+ .min_frequency = BMI160_GYRO_MIN_FREQ,
+ .max_frequency = BMI160_GYRO_MAX_FREQ,
+ },
+
+ [LID_ALS] = {
+ .name = "Light",
+ .active_mask = SENSOR_ACTIVE_S0_S3,
+ .chip = MOTIONSENSE_CHIP_OPT3001,
+ .type = MOTIONSENSE_TYPE_LIGHT,
+ .location = MOTIONSENSE_LOC_LID,
+ .drv = &opt3001_drv,
+ .drv_data = &g_opt3001_data,
+ .port = I2C_PORT_ACCEL,
+ .addr = OPT3001_I2C_ADDR,
+ .rot_standard_ref = NULL,
+ .default_range = 0x2b11a1,
+ .min_frequency = OPT3001_LIGHT_MIN_FREQ,
+ .max_frequency = OPT3001_LIGHT_MAX_FREQ,
+ .config = {
+ /* Run ALS sensor in S0 */
+ [SENSOR_CONFIG_EC_S0] = {
+ .odr = 1000,
+ },
+ },
+ },
+};
+unsigned int motion_sensor_count = ARRAY_SIZE(motion_sensors);
+
+/* ALS instances when LPC mapping is needed. Each entry directs to a sensor. */
+const struct motion_sensor_t *motion_als_sensors[] = {
+ &motion_sensors[LID_ALS],
+};
+BUILD_ASSERT(ARRAY_SIZE(motion_als_sensors) == ALS_COUNT);
+
+/**********************************************************************/
+/* ADC channels */
+const struct adc_t adc_channels[] = {
+ [ADC_TEMP_SENSOR_1] = {
+ "TEMP_AMB", NPCX_ADC_CH0, ADC_MAX_VOLT, ADC_READ_MAX+1, 0},
+ [ADC_TEMP_SENSOR_2] = {
+ "TEMP_CHARGER", NPCX_ADC_CH1, ADC_MAX_VOLT, ADC_READ_MAX+1, 0},
+};
+BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
+
+const struct temp_sensor_t temp_sensors[] = {
+ [TEMP_SENSOR_1] = {.name = "Temp1",
+ .type = TEMP_SENSOR_TYPE_BOARD,
+ .read = get_temp_3v3_51k1_47k_4050b,
+ .idx = ADC_TEMP_SENSOR_1,
+ .action_delay_sec = 1},
+ [TEMP_SENSOR_2] = {.name = "Temp2",
+ .type = TEMP_SENSOR_TYPE_BOARD,
+ .read = get_temp_3v3_51k1_47k_4050b,
+ .idx = ADC_TEMP_SENSOR_2,
+ .action_delay_sec = 1},
+};
+BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT);
+
+struct ec_thermal_config thermal_params[TEMP_SENSOR_COUNT];
+
+/* Sets the gpio flags correct taking into account warm resets */
+static void reset_gpio_flags(enum gpio_signal signal, int flags)
+{
+ /*
+ * If the system was already on, we cannot set the value otherwise we
+ * may change the value from the previous image which could cause a
+ * brownout.
+ */
+ if (system_is_reboot_warm() || system_jumped_to_this_image())
+ flags &= ~(GPIO_LOW | GPIO_HIGH);
+
+ gpio_set_flags(signal, flags);
+}
+
+/* Runtime GPIO defaults */
+enum gpio_signal gpio_en_pp5000_a = GPIO_EN_PP5000_A_V1;
+
+static void board_gpio_set_pp5000(void)
+{
+ uint32_t board_id = 0;
+
+ /* Errors will count as board_id 0 */
+ cbi_get_board_version(&board_id);
+
+ if (board_id == 0) {
+ reset_gpio_flags(GPIO_EN_PP5000_A_V0, GPIO_OUT_LOW);
+ /* Change runtime default for V0 */
+ gpio_en_pp5000_a = GPIO_EN_PP5000_A_V0;
+ } else if (board_id >= 1) {
+ reset_gpio_flags(GPIO_EN_PP5000_A_V1, GPIO_OUT_LOW);
+ }
+
+}
+
+static void board_init(void)
+{
+ /* Enable gpio interrupt for base accelgyro sensor */
+ gpio_enable_interrupt(GPIO_BASE_SIXAXIS_INT_L);
+ /* Select correct gpio signal for PP5000_A control */
+ board_gpio_set_pp5000();
+}
+DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
+
+void board_overcurrent_event(int port, int is_overcurrented)
+{
+ /* Sanity check the port. */
+ if ((port < 0) || (port >= CONFIG_USB_PD_PORT_COUNT))
+ return;
+
+ /* Note that the level is inverted because the pin is active low. */
+ gpio_set_level(GPIO_USB_C_OC_ODL, !is_overcurrented);
+}
diff --git a/board/kohaku/board.h b/board/kohaku/board.h
new file mode 100644
index 0000000000..98406f6fb6
--- /dev/null
+++ b/board/kohaku/board.h
@@ -0,0 +1,136 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Kohaku board configuration */
+
+#ifndef __CROS_EC_BOARD_H
+#define __CROS_EC_BOARD_H
+
+/* Baseboard features */
+#include "baseboard.h"
+
+/* Optional features */
+#define CONFIG_SYSTEM_UNLOCKED /* Allow dangerous commands while in dev. */
+
+#define CONFIG_POWER_BUTTON
+#define CONFIG_KEYBOARD_BOARD_CONFIG
+#define CONFIG_KEYBOARD_PROTOCOL_8042
+#define CONFIG_LED_COMMON
+#define CONFIG_LOW_POWER_IDLE
+
+#define CONFIG_HOSTCMD_ESPI
+/* #define CONFIG_HOSTCMD_ESPI_VW_SIGNALS */
+
+#undef CONFIG_UART_TX_BUF_SIZE
+#define CONFIG_UART_TX_BUF_SIZE 4096
+
+/* Keyboard features */
+#define CONFIG_PWM_KBLIGHT
+
+/* Sensors */
+/* BMI160 Base accel/gyro */
+#define CONFIG_ACCEL_INTERRUPTS
+#define CONFIG_ACCELGYRO_BMI160
+#define CONFIG_ACCELGYRO_BMI160_INT_EVENT \
+ TASK_EVENT_MOTION_SENSOR_INTERRUPT(BASE_ACCEL)
+#define CONFIG_ACCELGYRO_BMI160_INT2_OUTPUT
+/* BMA253 Lid accel */
+#define CONFIG_ACCEL_BMA255
+#define CONFIG_ACCEL_FORCE_MODE_MASK (BIT(LID_ACCEL) | BIT(LID_ALS))
+#define CONFIG_LID_ANGLE
+#define CONFIG_LID_ANGLE_SENSOR_BASE BASE_ACCEL
+#define CONFIG_LID_ANGLE_SENSOR_LID LID_ACCEL
+#define CONFIG_LID_ANGLE_UPDATE
+/* OPT3001 ALS */
+#define CONFIG_ALS
+#define ALS_COUNT 1
+#define CONFIG_ALS_OPT3001
+#define OPT3001_I2C_ADDR OPT3001_I2C_ADDR1
+
+/* USB Type C and USB PD defines */
+#define CONFIG_USB_PD_TCPM_PS8751
+#define BOARD_TCPC_C0_RESET_HOLD_DELAY PS8XXX_RESET_DELAY_MS
+#define BOARD_TCPC_C0_RESET_POST_DELAY 0
+#define BOARD_TCPC_C1_RESET_HOLD_DELAY PS8XXX_RESET_DELAY_MS
+#define BOARD_TCPC_C1_RESET_POST_DELAY 0
+
+/* Volume Button feature */
+#define CONFIG_VOLUME_BUTTONS
+#define GPIO_VOLUME_UP_L GPIO_EC_VOLUP_BTN_ODL
+#define GPIO_VOLUME_DOWN_L GPIO_EC_VOLDN_BTN_ODL
+
+/* Thermal features */
+#define CONFIG_TEMP_SENSOR_POWER_GPIO GPIO_EN_A_RAILS
+#define CONFIG_THERMISTOR
+#define CONFIG_THROTTLE_AP
+#define CONFIG_STEINHART_HART_3V3_51K1_47K_4050B
+
+/* MST */
+/*
+ * TDOD (b/124068003): This inherently assumes the MST chip is connected to only
+ * one Type C port. This will need to be chagned to support 2 Type C ports
+ * connected to the same MST chip.
+ */
+#define USB_PD_PORT_TCPC 1
+
+/*
+ * Macros for GPIO signals used in common code that don't match the
+ * schematic names. Signal names in gpio.inc match the schematic and are
+ * then redefined here to so it's more clear which signal is being used for
+ * which purpose.
+ */
+#define GPIO_PCH_RSMRST_L GPIO_EC_PCH_RSMRST_L
+#define GPIO_PCH_SLP_S0_L GPIO_SLP_S0_L
+#define GPIO_CPU_PROCHOT GPIO_EC_PROCHOT_ODL
+#define GPIO_AC_PRESENT GPIO_ACOK_OD
+#define GPIO_RSMRST_L_PGOOD GPIO_PG_EC_RSMRST_L
+#define GPIO_PCH_SLP_S3_L GPIO_SLP_S3_L
+#define GPIO_PCH_SLP_S4_L GPIO_SLP_S4_L
+#define GPIO_EN_PP5000 GPIO_EN_PP5000_A
+
+#ifndef __ASSEMBLER__
+
+#include "gpio_signal.h"
+#include "registers.h"
+
+/* GPIO signals updated base on board version. */
+#define GPIO_EN_PP5000_A gpio_en_pp5000_a
+extern enum gpio_signal gpio_en_pp5000_a;
+
+enum adc_channel {
+ ADC_TEMP_SENSOR_1, /* ADC0 */
+ ADC_TEMP_SENSOR_2, /* ADC1 */
+ ADC_CH_COUNT
+};
+
+enum sensor_id {
+ LID_ACCEL = 0,
+ BASE_ACCEL,
+ BASE_GYRO,
+ LID_ALS,
+ SENSOR_COUNT,
+};
+
+enum pwm_channel {
+ PWM_CH_KBLIGHT,
+ PWM_CH_COUNT
+};
+
+enum temp_sensor_id {
+ TEMP_SENSOR_1,
+ TEMP_SENSOR_2,
+ TEMP_SENSOR_COUNT
+};
+
+/* List of possible batteries */
+enum battery_type {
+ BATTERY_SMP_LIS,
+ BATTERY_SMP_SDI,
+ BATTERY_TYPE_COUNT,
+};
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* __CROS_EC_BOARD_H */
diff --git a/board/kohaku/build.mk b/board/kohaku/build.mk
new file mode 100644
index 0000000000..733912454f
--- /dev/null
+++ b/board/kohaku/build.mk
@@ -0,0 +1,15 @@
+# -*- makefile -*-
+# Copyright 2019 The Chromium OS 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
+#
+
+CHIP:=npcx
+CHIP_FAMILY:=npcx7
+CHIP_VARIANT:=npcx7m6fc
+BASEBOARD:=hatch
+
+board-y=board.o led.o
+board-$(CONFIG_BATTERY_SMART)+=battery.o
diff --git a/board/kohaku/ec.tasklist b/board/kohaku/ec.tasklist
new file mode 100644
index 0000000000..bf5a7a436a
--- /dev/null
+++ b/board/kohaku/ec.tasklist
@@ -0,0 +1,27 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * See CONFIG_TASK_LIST in config.h for details.
+ */
+
+#define CONFIG_TASK_LIST \
+ TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(USB_CHG_P0, usb_charger_task, 0, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(USB_CHG_P1, usb_charger_task, 1, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(CHARGER, charger_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(MOTIONSENSE, motion_sense_task, NULL, VENTI_TASK_STACK_SIZE) \
+ TASK_NOTEST(CHIPSET, chipset_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \
+ TASK_NOTEST(PDCMD, pd_command_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(HOSTCMD, host_command_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(CONSOLE, console_task, NULL, VENTI_TASK_STACK_SIZE) \
+ TASK_ALWAYS(POWERBTN, power_button_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(PD_C1, pd_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_ALWAYS(PD_INT_C0, pd_interrupt_handler_task, 0, TASK_STACK_SIZE) \
+ TASK_ALWAYS(PD_INT_C1, pd_interrupt_handler_task, 1, TASK_STACK_SIZE)
+
diff --git a/board/kohaku/gpio.inc b/board/kohaku/gpio.inc
new file mode 100644
index 0000000000..28a7d400f7
--- /dev/null
+++ b/board/kohaku/gpio.inc
@@ -0,0 +1,128 @@
+/* -*- mode:c -*-
+ *
+ * Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Declare symbolic names for all the GPIOs that we care about.
+ * Note: Those with interrupt handlers must be declared first. */
+
+/* Wake Source interrupts */
+GPIO_INT(LID_OPEN, PIN(D, 2), GPIO_INT_BOTH | GPIO_HIB_WAKE_HIGH, lid_interrupt)
+GPIO_INT(WP_L, PIN(A, 1), GPIO_INT_BOTH, switch_interrupt) /* EC_WP_ODL */
+GPIO_INT(POWER_BUTTON_L, PIN(0, 1), GPIO_INT_BOTH, power_button_interrupt) /* MECH_PWR_BTN_ODL */
+GPIO_INT(ACOK_OD, PIN(0, 0), GPIO_INT_BOTH | GPIO_HIB_WAKE_HIGH, extpower_interrupt)
+
+/* Power sequencing interrupts */
+GPIO_INT(SLP_S0_L, PIN(D, 5), GPIO_INT_BOTH, power_signal_interrupt)
+#ifndef CONFIG_HOSTCMD_ESPI_VW_SIGNALS
+GPIO_INT(SLP_S3_L, PIN(A, 5), GPIO_INT_BOTH, power_signal_interrupt)
+GPIO_INT(SLP_S4_L, PIN(D, 4), GPIO_INT_BOTH, power_signal_interrupt)
+#endif
+GPIO_INT(PG_EC_RSMRST_L, PIN(E, 2), GPIO_INT_BOTH, power_signal_interrupt)
+GPIO_INT(PG_EC_ALL_SYS_PWRGD, PIN(F, 4), GPIO_INT_BOTH, power_signal_interrupt)
+
+/* Sensor Interrupts */
+GPIO_INT(BASE_SIXAXIS_INT_L, PIN(5, 6), GPIO_INT_FALLING, bmi160_interrupt)
+
+/* USB-C interrupts */
+GPIO_INT(USB_C0_PPC_INT_ODL, PIN(E, 0), GPIO_INT_FALLING, ppc_interrupt)
+GPIO_INT(USB_C1_PPC_INT_ODL, PIN(A, 2), GPIO_INT_FALLING, ppc_interrupt)
+GPIO_INT(USB_C0_TCPC_INT_ODL, PIN(6, 2), GPIO_INT_FALLING, tcpc_alert_event)
+GPIO_INT(USB_C1_TCPC_INT_ODL, PIN(F, 5), GPIO_INT_FALLING, tcpc_alert_event)
+GPIO_INT(USB_C0_BC12_INT_ODL, PIN(9, 5), GPIO_INT_FALLING, bc12_interrupt)
+GPIO_INT(USB_C1_BC12_INT_ODL, PIN(E, 4), GPIO_INT_FALLING, bc12_interrupt)
+
+GPIO_INT(HDMI_CONN_HPD, PIN(7, 2), GPIO_INT_BOTH, hdmi_hpd_interrupt)
+
+/* Volume button interrupts */
+GPIO_INT(EC_VOLDN_BTN_ODL, PIN(9, 3), GPIO_INT_BOTH | GPIO_PULL_UP, button_interrupt)
+GPIO_INT(EC_VOLUP_BTN_ODL, PIN(7, 5), GPIO_INT_BOTH | GPIO_PULL_UP, button_interrupt)
+
+GPIO(SYS_RESET_L, PIN(0, 2), GPIO_ODR_HIGH) /* SYS_RST_ODL */
+GPIO(ENTERING_RW, PIN(E, 3), GPIO_OUT_LOW) /* EC_ENTERING_RW */
+GPIO(PCH_WAKE_L, PIN(7, 4), GPIO_ODR_HIGH) /* EC_PCH_WAKE_ODL */
+GPIO(PCH_PWRBTN_L, PIN(C, 1), GPIO_ODR_HIGH) /* EC_PCH_PWR_BTN_ODL */
+
+/* Power Sequencing Signals */
+GPIO(EN_PP5000_A_V1, PIN(A, 4), GPIO_DEFAULT)
+GPIO(EN_PP5000_A_V0, PIN(7, 3), GPIO_DEFAULT)
+GPIO(EN_A_RAILS, PIN(A, 3), GPIO_OUT_LOW)
+GPIO(EC_PCH_RSMRST_L, PIN(A, 6), GPIO_OUT_LOW)
+GPIO(EC_PROCHOT_ODL, PIN(6, 3), GPIO_ODR_HIGH)
+GPIO(PP5000_A_PG_OD, PIN(D, 7), GPIO_INPUT)
+GPIO(EC_PCH_SYS_PWROK, PIN(3, 7), GPIO_OUT_LOW)
+GPIO(CPU_C10_GATE_L, PIN(6, 7), GPIO_INPUT)
+
+/* MKBP event synchronization */
+GPIO(EC_INT_L, PIN(7, 0), GPIO_OUT_HIGH)
+
+/* USB and USBC Signals */
+GPIO(USB_C_OC_ODL, PIN(B, 1), GPIO_ODR_HIGH)
+GPIO(USB_C0_TCPC_RST, PIN(9, 7), GPIO_OUT_LOW)
+GPIO(USB_C1_TCPC_RST_ODL, PIN(3, 2), GPIO_ODR_HIGH)
+GPIO(EN_USB_A_5V, PIN(3, 5), GPIO_OUT_LOW)
+GPIO(EN_USB_A_LOW_PWR_OD, PIN(9, 4), GPIO_OUT_LOW)
+
+/* Misc Signals */
+GPIO(EC_BATT_PRES_ODL, PIN(E, 1), GPIO_INPUT)
+GPIO(LED_1_L, PIN(C, 4), GPIO_OUT_HIGH) /* Yellow (hatch) */
+GPIO(LED_2_L, PIN(C, 3), GPIO_OUT_HIGH) /* White (hatch) */
+GPIO(LED_3_L, PIN(C, 2), GPIO_OUT_HIGH)
+GPIO(LED_4_L, PIN(6, 0), GPIO_OUT_HIGH)
+GPIO(EC_KB_BL_EN, PIN(8, 6), GPIO_OUT_LOW) /* Keyboard backlight */
+GPIO(EN_PP5000_FAN, PIN(6, 1), GPIO_OUT_LOW)
+GPIO(EN_MST, PIN(9, 6), GPIO_OUT_LOW)
+
+
+/* I2C pins - Alternate function below configures I2C module on these pins */
+GPIO(I2C0_SCL, PIN(B, 5), GPIO_INPUT |
+ GPIO_SEL_1P8V) /* EC_I2C_SENSOR_1V8_SCL */
+GPIO(I2C0_SDA, PIN(B, 4), GPIO_INPUT |
+ GPIO_SEL_1P8V) /* EC_I2C_SENSOR_1V8_SDA */
+GPIO(I2C1_SCL, PIN(9, 0), GPIO_INPUT) /* EC_I2C_USB_C0_PD_SCL */
+GPIO(I2C1_SDA, PIN(8, 7), GPIO_INPUT) /* EC_I2C_USB_C0_PD_SDA */
+GPIO(I2C2_SCL, PIN(9, 2), GPIO_INPUT) /* EC_I2C_USB_C1_PD_SCL */
+GPIO(I2C2_SDA, PIN(9, 1), GPIO_INPUT) /* EC_I2C_USB_C1_PD_SDA */
+GPIO(I2C3_SCL, PIN(D, 1), GPIO_INPUT) /* EC_I2C_USB_C0_TCPC_SDA */
+GPIO(I2C3_SDA, PIN(D, 0), GPIO_INPUT) /* EC_I2C_USB_C0_TCPC_SCL */
+GPIO(I2C5_SCL, PIN(3, 3), GPIO_INPUT) /* EC_I2C_POWER_SCL */
+GPIO(I2C5_SDA, PIN(3, 6), GPIO_INPUT) /* EC_I2C_POWER_SDA */
+GPIO(I2C7_SCL, PIN(B, 3), GPIO_INPUT) /* EC_I2C_EEPROM_SCL */
+GPIO(I2C7_SDA, PIN(B, 2), GPIO_INPUT) /* EC_I2C_EEPROM_SDA */
+
+/* Keyboard pins */
+#define GPIO_KB_INPUT (GPIO_INPUT | GPIO_PULL_UP)
+GPIO(KBD_KSO2, PIN(1, 7), GPIO_OUT_LOW) /* KSO_02 inverted */
+ALTERNATE(PIN_MASK(3, 0x03), 0, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) /* KSI_00-01 */
+ALTERNATE(PIN_MASK(2, 0xFC), 0, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) /* KSI_02-07 */
+ALTERNATE(PIN_MASK(2, 0x03), 0, MODULE_KEYBOARD_SCAN, GPIO_ODR_HIGH) /* KSO_00-01 */
+ALTERNATE(PIN_MASK(1, 0x7F), 0, MODULE_KEYBOARD_SCAN, GPIO_ODR_HIGH) /* KSO_03-09 */
+ALTERNATE(PIN_MASK(0, 0xF0), 0, MODULE_KEYBOARD_SCAN, GPIO_ODR_HIGH) /* KSO_10-13 */
+ALTERNATE(PIN_MASK(8, 0x04), 0, MODULE_KEYBOARD_SCAN, GPIO_ODR_HIGH) /* KSO_14 */
+
+/* Alternate functions GPIO definitions */
+ALTERNATE(PIN_MASK(B, 0x30), 0, MODULE_I2C, (GPIO_INPUT | GPIO_SEL_1P8V)) /* I2C0 1.8V */
+ALTERNATE(PIN_MASK(9, 0x07), 0, MODULE_I2C, 0) /* I2C1 SCL / I2C2 */
+ALTERNATE(PIN_MASK(8, 0x80), 0, MODULE_I2C, 0) /* I2C1 SDA */
+ALTERNATE(PIN_MASK(D, 0x03), 0, MODULE_I2C, 0) /* I2C3 */
+ALTERNATE(PIN_MASK(3, 0x48), 0, MODULE_I2C, 0) /* I2C5 */
+ALTERNATE(PIN_MASK(B, 0x0C), 0, MODULE_I2C, 0) /* I2C7 */
+
+/* UART */
+ALTERNATE(PIN_MASK(6, 0x30), 0, MODULE_UART, 0) /* UART from EC to Servo */
+
+/* PWM */
+ALTERNATE(PIN_MASK(8, 0x01), 0, MODULE_PWM, 0) /* PWM3 - Keyboard backlight */
+ALTERNATE(PIN_MASK(B, 0x80), 0, MODULE_PWM, 0) /* PWM5 - FAN */
+ALTERNATE(PIN_MASK(4, 0x01), 0, MODULE_PWM, 0) /* TA1 - Fan Tachometer */
+
+/* ADC */
+ALTERNATE(PIN_MASK(4, 0x30), 0, MODULE_ADC, 0) /* ADC0-1 */
+
+/* Power Switch Logic (PSL) inputs */
+ALTERNATE(PIN_MASK(D, 0x04), 0, MODULE_PMU, 0) /* GPIOD2 = LID_OPEN */
+ALTERNATE(PIN_MASK(0, 0x07), 0, MODULE_PMU, 0) /* GPIO00 = ACOK_OD,
+ GPIO01 = MECH_PWR_BTN_ODL
+ GPIO02 = EC_RST_ODL */
diff --git a/board/kohaku/led.c b/board/kohaku/led.c
new file mode 100644
index 0000000000..e890791d4d
--- /dev/null
+++ b/board/kohaku/led.c
@@ -0,0 +1,79 @@
+/* Copyright 2018 The Chromium OS 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 and battery LED control for Kohaku
+ */
+
+#include "ec_commands.h"
+#include "gpio.h"
+#include "led_common.h"
+#include "led_states.h"
+#include "chipset.h"
+
+#define LED_ON_LVL 0
+#define LED_OFF_LVL 1
+
+const int led_charge_lvl_1 = 5;
+
+const int led_charge_lvl_2 = 95;
+
+struct led_descriptor led_bat_state_table[LED_NUM_STATES][LED_NUM_PHASES] = {
+ [STATE_CHARGING_LVL_1] = {{EC_LED_COLOR_AMBER, LED_INDEFINITE} },
+ [STATE_CHARGING_LVL_2] = {{EC_LED_COLOR_AMBER, LED_INDEFINITE} },
+ [STATE_CHARGING_FULL_CHARGE] = {{EC_LED_COLOR_WHITE, LED_INDEFINITE} },
+ [STATE_DISCHARGE_S0] = {{LED_OFF, LED_INDEFINITE} },
+ [STATE_DISCHARGE_S3] = {{LED_OFF, LED_INDEFINITE} },
+ [STATE_DISCHARGE_S5] = {{LED_OFF, LED_INDEFINITE} },
+ [STATE_BATTERY_ERROR] = {{EC_LED_COLOR_AMBER, 1 * LED_ONE_SEC},
+ {LED_OFF, 1 * LED_ONE_SEC} },
+ [STATE_FACTORY_TEST] = {{EC_LED_COLOR_WHITE, 2 * LED_ONE_SEC},
+ {EC_LED_COLOR_AMBER, 2 * LED_ONE_SEC} },
+};
+
+const enum ec_led_id supported_led_ids[] = {
+ EC_LED_ID_BATTERY_LED
+};
+
+const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids);
+
+void led_set_color_battery(enum ec_led_colors color)
+{
+ switch (color) {
+ case EC_LED_COLOR_AMBER:
+ gpio_set_level(GPIO_LED_2_L, LED_OFF_LVL);
+ gpio_set_level(GPIO_LED_1_L, LED_ON_LVL);
+ break;
+ case EC_LED_COLOR_WHITE:
+ gpio_set_level(GPIO_LED_1_L, LED_OFF_LVL);
+ gpio_set_level(GPIO_LED_2_L, LED_ON_LVL);
+ break;
+ default: /* LED_OFF and other unsupported colors */
+ gpio_set_level(GPIO_LED_1_L, LED_OFF_LVL);
+ gpio_set_level(GPIO_LED_2_L, LED_OFF_LVL);
+ break;
+ }
+}
+
+void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range)
+{
+ if (led_id == EC_LED_ID_BATTERY_LED) {
+ brightness_range[EC_LED_COLOR_AMBER] = 1;
+ brightness_range[EC_LED_COLOR_WHITE] = 1;
+ }
+}
+
+int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness)
+{
+ if (led_id == EC_LED_ID_BATTERY_LED) {
+ if (brightness[EC_LED_COLOR_AMBER] != 0)
+ led_set_color_battery(EC_LED_COLOR_AMBER);
+ else if (brightness[EC_LED_COLOR_WHITE] != 0)
+ led_set_color_battery(EC_LED_COLOR_WHITE);
+ else
+ led_set_color_battery(LED_OFF);
+ }
+
+ return EC_SUCCESS;
+}
+
diff --git a/board/kukui/board.h b/board/kukui/board.h
index 40d1fc3930..e2cb3b68d1 100644
--- a/board/kukui/board.h
+++ b/board/kukui/board.h
@@ -174,6 +174,11 @@
#undef CONFIG_CMD_POWERINDEBUG
#undef CONFIG_CMD_TIMERINFO
+#ifdef SECTION_IS_RO
+#undef CONFIG_HOSTCMD_FLASHPD
+#undef CONFIG_HOSTCMD_RWHASHPD
+#endif
+
#define CONFIG_TASK_PROFILING
/* I2C ports */
diff --git a/board/kukui/led.c b/board/kukui/led.c
index f66dd46fbf..ca111ca54b 100644
--- a/board/kukui/led.c
+++ b/board/kukui/led.c
@@ -3,7 +3,6 @@
* found in the LICENSE file.
*
* Battery LED control for Kukui board.
- *TODO(b:80160408): Implement mt6370 led driver.
*/
#include "battery.h"
@@ -17,54 +16,50 @@ const enum ec_led_id supported_led_ids[] = { EC_LED_ID_BATTERY_LED };
const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids);
#define LED_OFF MT6370_LED_ID_OFF
-#define LED_GREEN MT6370_LED_ID1
-#define LED_RED MT6370_LED_ID2
+#define LED_RED MT6370_LED_ID1
+#define LED_GREEN MT6370_LED_ID2
+#define LED_BLUE MT6370_LED_ID3
#define LED_MASK_OFF 0
-#define LED_MASK_GREEN MT6370_MASK_RGB_ISNK1DIM_EN
-#define LED_MASK_RED MT6370_MASK_RGB_ISNK2DIM_EN
+#define LED_MASK_RED MT6370_MASK_RGB_ISNK1DIM_EN
+#define LED_MASK_GREEN MT6370_MASK_RGB_ISNK2DIM_EN
+#define LED_MASK_BLUE MT6370_MASK_RGB_ISNK3DIM_EN
static void kukui_led_set_battery(void)
{
- static int battery_second;
- uint32_t chflags = charge_get_flags();
+ static enum charge_state prv_chstate = PWR_STATE_UNCHANGE;
+ enum charge_state chstate;
+ uint8_t blue = 0, green = 0, red = 0;
- battery_second++;
+ chstate = charge_get_state();
- switch (charge_get_state()) {
+ if (prv_chstate == chstate)
+ return;
+
+ prv_chstate = chstate;
+
+ switch (chstate) {
case PWR_STATE_CHARGE:
/* Always indicate when charging, even in suspend. */
- mt6370_led_set_color(LED_MASK_RED);
+ blue = 1;
break;
case PWR_STATE_DISCHARGE:
if (charge_get_percent() <= 10)
- mt6370_led_set_color((battery_second & 0x4) ?
- LED_MASK_RED : LED_MASK_OFF);
- else
- mt6370_led_set_color(LED_MASK_OFF);
+ red = 1;
break;
case PWR_STATE_ERROR:
- mt6370_led_set_color((battery_second & 0x2) ?
- LED_MASK_RED : LED_MASK_OFF);
+ red = 1;
break;
case PWR_STATE_CHARGE_NEAR_FULL:
- mt6370_led_set_color(LED_MASK_GREEN);
- break;
- case PWR_STATE_IDLE: /* External power connected in IDLE. */
- if (chflags & CHARGE_FLAG_FORCE_IDLE) {
- mt6370_led_set_color(LED_MASK_RED);
- mt6370_led_set_dim_mode(LED_RED,
- MT6370_LED_DIM_MODE_BREATH);
- } else {
- mt6370_led_set_color(LED_MASK_GREEN);
- mt6370_led_set_dim_mode(LED_GREEN,
- MT6370_LED_DIM_MODE_BREATH);
- }
+ green = 1;
break;
default:
/* Other states don't alter LED behavior */
- break;
+ return;
}
+ mt6370_led_set_brightness(LED_RED, red);
+ mt6370_led_set_brightness(LED_BLUE, blue);
+ mt6370_led_set_brightness(LED_GREEN, green);
}
void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range)
@@ -72,8 +67,9 @@ void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range)
if (led_id != EC_LED_ID_BATTERY_LED)
return;
- brightness_range[EC_LED_COLOR_GREEN] = MT6370_LED_BRIGHTNESS_MAX;
brightness_range[EC_LED_COLOR_RED] = MT6370_LED_BRIGHTNESS_MAX;
+ brightness_range[EC_LED_COLOR_GREEN] = MT6370_LED_BRIGHTNESS_MAX;
+ brightness_range[EC_LED_COLOR_BLUE] = MT6370_LED_BRIGHTNESS_MAX;
}
int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness)
@@ -81,8 +77,9 @@ int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness)
if (led_id != EC_LED_ID_BATTERY_LED)
return EC_ERROR_INVAL;
- mt6370_led_set_brightness(LED_GREEN, brightness[EC_LED_COLOR_GREEN]);
mt6370_led_set_brightness(LED_RED, brightness[EC_LED_COLOR_RED]);
+ mt6370_led_set_brightness(LED_GREEN, brightness[EC_LED_COLOR_GREEN]);
+ mt6370_led_set_brightness(LED_BLUE, brightness[EC_LED_COLOR_BLUE]);
return EC_SUCCESS;
}
@@ -93,3 +90,13 @@ static void led_second(void)
kukui_led_set_battery();
}
DECLARE_HOOK(HOOK_SECOND, led_second, HOOK_PRIO_DEFAULT);
+
+static void kukui_led_init(void)
+{
+ /* Enable all LEDs, and set brightness to 0. */
+ mt6370_led_set_brightness(LED_RED, 0);
+ mt6370_led_set_brightness(LED_GREEN, 0);
+ mt6370_led_set_brightness(LED_BLUE, 0);
+ mt6370_led_set_color(LED_MASK_RED | LED_MASK_GREEN | LED_MASK_BLUE);
+}
+DECLARE_HOOK(HOOK_INIT, kukui_led_init, HOOK_PRIO_DEFAULT);
diff --git a/board/kukui_scp/board.h b/board/kukui_scp/board.h
index 85dbd49c66..56eb59909e 100644
--- a/board/kukui_scp/board.h
+++ b/board/kukui_scp/board.h
@@ -18,9 +18,6 @@
/*
* RW only, no flash
* +-------------------- 0x0
- * | ptr to stack_top 0x0
- * | ptr to reset func 0x04
- * +-------------------- 0x00800
* | ROM vectortable, .text, .rodata, .data LMA
* +-------------------- 0x10000
* | RAM .bss, .data
@@ -33,7 +30,7 @@
* +-------------------- 0x80000
*/
#define ICACHE_BASE 0x7C000
-#define CONFIG_ROM_BASE 0x00800
+#define CONFIG_ROM_BASE 0x0
#define CONFIG_RAM_BASE 0x10000
#define CONFIG_ROM_SIZE (CONFIG_RAM_BASE - CONFIG_ROM_BASE)
#define CONFIG_RAM_SIZE (CONFIG_IPC_SHARED_OBJ_ADDR - CONFIG_RAM_BASE)
@@ -62,9 +59,15 @@
#define IPI_VDEC_VP9 3
#define IPI_VENC_H264 4
#define IPI_VENC_VP8 5
-#define IPI_MDP 6
-#define IPI_HOST_COMMAND 7
-#define IPI_COUNT 8
+#define IPI_MDP_INIT 6
+#define IPI_MDP_DEINIT 7
+#define IPI_MDP_FRAME 8
+#define IPI_DIP 9
+#define IPI_ISP_CMD 10
+#define IPI_ISP_FRAME 11
+#define IPI_FD_CMD 12
+#define IPI_HOST_COMMAND 13
+#define IPI_COUNT 14
#define IPI_NS_SERVICE 0xFF
diff --git a/board/meep/board.c b/board/meep/board.c
index 9420a74f40..8c44bda737 100644
--- a/board/meep/board.c
+++ b/board/meep/board.c
@@ -163,6 +163,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_ACCEL),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.rot_standard_ref = &base_standard_ref,
@@ -193,6 +195,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_GYRO),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.default_range = 1000 | ROUND_UP_FLAG, /* dps */
diff --git a/board/meep/gpio.inc b/board/meep/gpio.inc
index d1d29a6031..d1ad178ebd 100644
--- a/board/meep/gpio.inc
+++ b/board/meep/gpio.inc
@@ -135,8 +135,8 @@ GPIO(BAT_LED_AMBER_L, PIN(C, 3), GPIO_OUT_HIGH) /* LED_1_L */
GPIO(BAT_LED_WHITE_L, PIN(C, 4), GPIO_OUT_HIGH) /* LED_2_L */
GPIO(PWR_LED_WHITE_L, PIN(D, 7), GPIO_OUT_HIGH) /* LED_3_L */
-/* Keyboard Backlight */
-GPIO(KB_BL_PWR_EN, PIN(6, 2), GPIO_OUT_LOW)
+/* Not implemented in hardware */
+UNIMPLEMENTED(KB_BL_PWR_EN)
/* MKBP event synchronization */
GPIO(EC_INT_L, PIN(9, 4), GPIO_ODR_HIGH) /* EC_AP_INT_ODL */
diff --git a/board/nami/board.c b/board/nami/board.c
index 87771912f1..1b680f955a 100644
--- a/board/nami/board.c
+++ b/board/nami/board.c
@@ -248,13 +248,15 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = NPCX_I2C_PORT0_0,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
[USB_PD_PORT_ANX7447] = {
.i2c_host_port = NPCX_I2C_PORT0_1,
.i2c_slave_addr = AN7447_TCPC3_I2C_ADDR, /* Verified on v1.1 */
.drv = &anx7447_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/nautilus/board.c b/board/nautilus/board.c
index dd79aebf27..5bb202982e 100644
--- a/board/nautilus/board.c
+++ b/board/nautilus/board.c
@@ -171,13 +171,15 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = NPCX_I2C_PORT0_0,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
{
.i2c_host_port = NPCX_I2C_PORT0_1,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/nocturne/board.c b/board/nocturne/board.c
index c3ac854a26..f8e188b36c 100644
--- a/board/nocturne/board.c
+++ b/board/nocturne/board.c
@@ -316,14 +316,16 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = I2C_PORT_USB_C0,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &tcpci_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
{
.i2c_host_port = I2C_PORT_USB_C1,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &tcpci_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/npcx7_evb/board.h b/board/npcx7_evb/board.h
index a7a5328d17..66766934b1 100644
--- a/board/npcx7_evb/board.h
+++ b/board/npcx7_evb/board.h
@@ -16,7 +16,8 @@
#if defined(CHIP_VARIANT_NPCX7M6G)
#define BOARD_VERSION 1
#elif defined(CHIP_VARIANT_NPCX7M6F) || defined(CHIP_VARIANT_NPCX7M6FB) || \
- defined(CHIP_VARIANT_NPCX7M6FC) || defined(CHIP_VARIANT_NPCX7M7WB)
+ defined(CHIP_VARIANT_NPCX7M6FC) || defined(CHIP_VARIANT_NPCX7M7WB) || \
+ defined(CHIP_VARIANT_NPCX7M7WC)
#define BOARD_VERSION 2
#endif
@@ -62,7 +63,7 @@
#define CONFIG_SPI_FLASH_PORT 0
#define CONFIG_SPI_FLASH
#define CONFIG_SPI_FLASH_REGS
-#if defined(CHIP_VARIANT_NPCX7M6FC)
+#if defined(CHIP_VARIANT_NPCX7M6FC) || defined(CHIP_VARIANT_NPCX7M7WC)
#define CONFIG_SPI_FLASH_W25Q40 /* Internal spi flash type */
#define CONFIG_FLASH_SIZE 0x00080000 /* 512 KB internal spi flash */
#else
@@ -75,7 +76,7 @@
#if (BOARD_VERSION == 2)
#define CONFIG_HIBERNATE_PSL /* Use PSL (Power Switch Logic) for hibernate */
#define CONFIG_CLOCK_SRC_EXTERNAL /* Use external 32kHz OSC as LFCLK source */
-#ifdef CHIP_VARIANT_NPCX7M7WB
+#if defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
#define CONFIG_WAKE_ON_VOICE /* Use Audio front-end for Wake-on-Voice */
#endif
#undef CONFIG_FANS /* Remove fan application */
diff --git a/board/npcx7_evb/build.mk b/board/npcx7_evb/build.mk
index a2831c0d10..01ff3f3aa6 100644
--- a/board/npcx7_evb/build.mk
+++ b/board/npcx7_evb/build.mk
@@ -14,6 +14,7 @@
# npcx7m6fb - for npcx7 ec with internal flash, enhanced features.
# npcx7m6fc - the same as npcx7m6fb but internal flash size is 512 Kbytes.
# npcx7m7wb - for npcx7 ec with internal flash, enhanced features + WOV.
+# npcx7m7wc - the same as npcx7m7wb but internal flash size is 512 Kbytes.
CHIP:=npcx
CHIP_FAMILY:=npcx7
diff --git a/board/phaser/board.c b/board/phaser/board.c
index 630662a75f..68d5bbd633 100644
--- a/board/phaser/board.c
+++ b/board/phaser/board.c
@@ -148,6 +148,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_ACCEL),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.rot_standard_ref = &standard_rot_ref,
@@ -178,6 +180,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_GYRO),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.default_range = 1000 | ROUND_UP_FLAG, /* dps */
diff --git a/board/phaser/gpio.inc b/board/phaser/gpio.inc
index 3579ecb38a..b05a72ff7b 100644
--- a/board/phaser/gpio.inc
+++ b/board/phaser/gpio.inc
@@ -135,8 +135,8 @@ GPIO(BAT_LED_RED_L, PIN(C, 3), GPIO_OUT_HIGH) /* LED_1_L */
GPIO(BAT_LED_GREEN_L, PIN(C, 4), GPIO_OUT_HIGH) /* LED_2_L */
GPIO(PWR_LED_WHITE_L, PIN(D, 7), GPIO_OUT_HIGH) /* LED_3_L */
-/* Keyboard Backlight */
-GPIO(KB_BL_PWR_EN, PIN(6, 2), GPIO_OUT_LOW)
+/* Not implemented in hardware */
+UNIMPLEMENTED(KB_BL_PWR_EN)
/* Overcurrent event to host */
GPIO(USB_C_OC, PIN(3, 6), GPIO_ODR_HIGH | GPIO_SEL_1P8V)
diff --git a/board/poppy/board.c b/board/poppy/board.c
index 36ba4561eb..c65d65260c 100644
--- a/board/poppy/board.c
+++ b/board/poppy/board.c
@@ -213,13 +213,15 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = NPCX_I2C_PORT0_0,
.i2c_slave_addr = ANX74XX_I2C_ADDR1,
.drv = &anx74xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
{
.i2c_host_port = NPCX_I2C_PORT0_0,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/rammus/board.c b/board/rammus/board.c
index 31b0a3fb10..4f703ed00d 100644
--- a/board/rammus/board.c
+++ b/board/rammus/board.c
@@ -166,13 +166,15 @@ struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = I2C_PORT_TCPC1,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
[USB_PD_PORT_ANX7447] = {
.i2c_host_port = I2C_PORT_TCPC0,
.i2c_slave_addr = AN7447_TCPC3_I2C_ADDR, /* Verified on v1.1 */
.drv = &anx7447_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/reef/board.c b/board/reef/board.c
index 2579b103fc..15e1627651 100644
--- a/board/reef/board.c
+++ b/board/reef/board.c
@@ -250,13 +250,15 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = NPCX_I2C_PORT0_0,
.i2c_slave_addr = ANX74XX_I2C_ADDR1,
.drv = &anx74xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
[USB_PD_PORT_PS8751] = {
.i2c_host_port = NPCX_I2C_PORT0_1,
.i2c_slave_addr = PS8751_I2C_ADDR1,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/reef_mchp/board.c b/board/reef_mchp/board.c
index 7547b82167..11266a9ca0 100644
--- a/board/reef_mchp/board.c
+++ b/board/reef_mchp/board.c
@@ -364,13 +364,15 @@ const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
.i2c_host_port = MCHP_I2C_PORT0,
.i2c_slave_addr = 0x50,
.drv = &anx74xx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
[USB_PD_PORT_PS8751] = {
.i2c_host_port = MCHP_I2C_PORT2,
.i2c_slave_addr = 0x16,
.drv = &ps8xxx_tcpm_drv,
- .pol = TCPC_ALERT_ACTIVE_LOW,
+ /* Alert is active-low, push-pull */
+ .flags = 0,
},
};
diff --git a/board/rowan/battery.c b/board/rowan/battery.c
deleted file mode 100644
index 8ec20fa45a..0000000000
--- a/board/rowan/battery.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/* Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Battery pack vendor provided charging profile
- */
-
-#include "battery.h"
-#include "battery_smart.h"
-#include "util.h"
-
-/* Shutdown mode parameter to write to manufacturer access register */
-#define SB_SHIP_MODE_REG 0x3a
-#define SB_SHUTDOWN_DATA 0xC574
-
-static const struct battery_info info = {
- .voltage_max = 8800,
- .voltage_normal = 7600,
- .voltage_min = 6000,
- /* Pre-charge values. */
- .precharge_current = 256, /* mA */
-
- .start_charging_min_c = 0,
- .start_charging_max_c = 50,
- .charging_min_c = 0,
- .charging_max_c = 50,
- .discharging_min_c = 0,
- .discharging_max_c = 60,
-};
-
-const struct battery_info *battery_get_info(void)
-{
- return &info;
-}
-
-int board_cut_off_battery(void)
-{
- int rv;
-
- /* Ship mode command must be sent twice to take effect */
- rv = sb_write(SB_SHIP_MODE_REG, SB_SHUTDOWN_DATA);
-
- if (rv != EC_SUCCESS)
- return rv;
-
- return sb_write(SB_SHIP_MODE_REG, SB_SHUTDOWN_DATA);
-}
diff --git a/board/rowan/board.c b/board/rowan/board.c
deleted file mode 100644
index 1a8ec50102..0000000000
--- a/board/rowan/board.c
+++ /dev/null
@@ -1,499 +0,0 @@
-/* Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* Oak board configuration */
-
-#include "adc.h"
-#include "adc_chip.h"
-#include "als.h"
-#include "atomic.h"
-#include "battery.h"
-#include "button.h"
-#include "charge_manager.h"
-#include "charge_state.h"
-#include "charger.h"
-#include "chipset.h"
-#include "common.h"
-#include "console.h"
-#include "driver/accelgyro_bmi160.h"
-#include "driver/als_isl29035.h"
-#include "driver/tcpm/anx7688.h"
-#include "driver/tcpm/tcpci.h"
-#include "driver/temp_sensor/tmp432.h"
-#include "extpower.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "host_command.h"
-#include "i2c.h"
-#include "keyboard_raw.h"
-#include "keyboard_scan.h"
-#include "lid_switch.h"
-#include "math_util.h"
-#include "motion_lid.h"
-#include "motion_sense.h"
-#include "pi3usb9281.h"
-#include "power.h"
-#include "power_button.h"
-#include "registers.h"
-#include "spi.h"
-#include "switch.h"
-#include "system.h"
-#include "task.h"
-#include "temp_sensor.h"
-#include "temp_sensor_chip.h"
-#include "thermal.h"
-#include "timer.h"
-#include "usb_charge.h"
-#include "usb_mux.h"
-#include "usb_pd.h"
-#include "usb_pd_tcpm.h"
-#include "util.h"
-
-#define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args)
-#define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args)
-
-/* Dispaly port hardware can connect to port 0, 1 or neither. */
-#define PD_PORT_NONE -1
-
-void pd_mcu_interrupt(enum gpio_signal signal)
-{
- schedule_deferred_pd_interrupt(0 /* port */);
-}
-
-void deferred_reset_pd_mcu(void);
-DECLARE_DEFERRED(deferred_reset_pd_mcu);
-
-void usb_evt(enum gpio_signal signal)
-{
- if (!gpio_get_level(GPIO_BC12_WAKE_L))
- task_set_event(TASK_ID_USB_CHG_P0, USB_CHG_EVENT_BC12, 0);
-}
-
-#include "gpio_list.h"
-
-/* power signal list. Must match order of enum power_signal. */
-const struct power_signal_info power_signal_list[] = {
- {GPIO_SOC_POWER_GOOD, POWER_SIGNAL_ACTIVE_HIGH, "POWER_GOOD"},
- {GPIO_SUSPEND_L, POWER_SIGNAL_ACTIVE_LOW, "SUSPEND#_ASSERTED"},
-};
-BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT);
-
-/* ADC channels */
-const struct adc_t adc_channels[] = {
- /*
- * PSYS_MONITOR(PA2): ADC_IN2, 1.44 uA/W on 6.05k Ohm
- * output in mW
- */
- [ADC_PSYS] = {"PSYS", 379415, 4096, 0, STM32_AIN(2)},
- /* AMON_BMON(PC0): ADC_IN10, output in uV */
- [ADC_AMON_BMON] = {"AMON_BMON", 183333, 4096, 0, STM32_AIN(10)},
- /* VDC_BOOSTIN_SENSE(PC1): ADC_IN11, output in mV */
- [ADC_VBUS] = {"VBUS", 33000, 4096, 0, STM32_AIN(11)},
-};
-BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT);
-
-int anx7688_passthru_allowed(const struct i2c_port_t *port, uint16_t address)
-{
- /* Allow access to 0x2c (TCPC) */
- if (address == 0x2c)
- return 1;
-
- CPRINTF("Passthru rejected on %x", address);
-
- return 0;
-}
-
-/* I2C ports */
-const struct i2c_port_t i2c_ports[] = {
- {"battery", I2C_PORT_BATTERY, 100, GPIO_I2C0_SCL, GPIO_I2C0_SDA},
- {"pd", I2C_PORT_PD_MCU, 1000, GPIO_I2C1_SCL, GPIO_I2C1_SDA,
- anx7688_passthru_allowed}
-};
-
-const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
-
-/* SPI devices */
-const struct spi_device_t spi_devices[] = {
- { CONFIG_SPI_ACCEL_PORT, 2, GPIO_SPI2_NSS },
-};
-const unsigned int spi_devices_used = ARRAY_SIZE(spi_devices);
-
-/* TCPC */
-const struct tcpc_config_t tcpc_config[CONFIG_USB_PD_PORT_COUNT] = {
- {I2C_PORT_TCPC, CONFIG_TCPC_I2C_BASE_ADDR, &anx7688_tcpm_drv},
-};
-
-struct pi3usb9281_config pi3usb9281_chips[] = {
- {
- .i2c_port = I2C_PORT_PERICOM,
- .mux_lock = NULL,
- },
-};
-BUILD_ASSERT(ARRAY_SIZE(pi3usb9281_chips) ==
- CONFIG_BC12_DETECT_PI3USB9281_CHIP_COUNT);
-
-/*
- * Temperature sensors data; must be in same order as enum temp_sensor_id.
- * Sensor index and name must match those present in coreboot:
- * src/mainboard/google/${board}/acpi/dptf.asl
- */
-const struct temp_sensor_t temp_sensors[] = {
-#ifdef CONFIG_TEMP_SENSOR_TMP432
- {"TMP432_Internal", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val,
- TMP432_IDX_LOCAL, 4},
- {"TMP432_Sensor_1", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val,
- TMP432_IDX_REMOTE1, 4},
- {"TMP432_Sensor_2", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val,
- TMP432_IDX_REMOTE2, 4},
-#endif
- {"Battery", TEMP_SENSOR_TYPE_BATTERY, charge_get_battery_temp,
- 0, 4},
-};
-BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT);
-
-struct usb_mux usb_muxes[CONFIG_USB_PD_PORT_COUNT] = {
- {
- .driver = &anx7688_usb_mux_driver,
- },
-};
-
-/**
- * Reset PD MCU
- * ANX7688 needs a reset pulse of 50ms after power enable.
- */
-void deferred_reset_pd_mcu(void)
-{
- uint8_t state = gpio_get_level(GPIO_USB_C0_PWR_EN_L) |
- (gpio_get_level(GPIO_USB_C0_RST) << 1);
-
- CPRINTS("%s %d", __func__, state);
- switch (state) {
- case 0:
- /*
- * PWR_EN_L low, RST low
- * start reset sequence by turning off power enable
- * and wait for 1ms.
- */
- gpio_set_level(GPIO_USB_C0_PWR_EN_L, 1);
- hook_call_deferred(&deferred_reset_pd_mcu_data, 1*MSEC);
- break;
- case 1:
- /*
- * PWR_EN_L high, RST low
- * pull PD reset pin and wait for another 1ms
- */
- gpio_set_level(GPIO_USB_C0_RST, 1);
- hook_call_deferred(&deferred_reset_pd_mcu_data, 1*MSEC);
- /* on PD reset, trigger PD task to reset state */
- task_set_event(TASK_ID_PD_C0, PD_EVENT_TCPC_RESET, 0);
- break;
- case 3:
- /*
- * PWR_EN_L high, RST high
- * enable power and wait for 10ms then pull RESET_N
- */
- gpio_set_level(GPIO_USB_C0_PWR_EN_L, 0);
- hook_call_deferred(&deferred_reset_pd_mcu_data, 10*MSEC);
- break;
- case 2:
- /*
- * PWR_EN_L low, RST high
- * leave reset state
- */
- gpio_set_level(GPIO_USB_C0_RST, 0);
- break;
- }
-}
-
-static void board_power_on_pd_mcu(void)
-{
- /* check if power is already on */
- if (!gpio_get_level(GPIO_USB_C0_PWR_EN_L))
- return;
-
- gpio_set_level(GPIO_USB_C0_EXTPWR_EN, 1);
- hook_call_deferred(&deferred_reset_pd_mcu_data, 1*MSEC);
-}
-
-void board_reset_pd_mcu(void)
-{
- /* enable port controller's cable detection before reset */
- anx7688_enable_cable_detection(0);
-
- /* wait for 10ms, then start port controller's reset sequence */
- hook_call_deferred(&deferred_reset_pd_mcu_data, 10*MSEC);
-}
-
-int command_pd_reset(int argc, char **argv)
-{
- board_reset_pd_mcu();
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(resetpd, command_pd_reset,
- "",
- "Reset PD IC");
-
-/**
- * There is a level shift for AC_OK & LID_OPEN signal between AP & EC,
- * disable it (drive high) when AP is off, otherwise enable it (drive low).
- */
-static void board_extpower_buffer_to_soc(void)
-{
- /* Drive high when AP is off (G3), else drive low */
- gpio_set_level(GPIO_LEVEL_SHIFT_EN_L,
- chipset_in_state(CHIPSET_STATE_HARD_OFF) ? 1 : 0);
-}
-
-/* Initialize board. */
-static void board_init(void)
-{
- /* Enable Level shift of AC_OK & LID_OPEN signals */
- board_extpower_buffer_to_soc();
- /* Enable rev1 testing GPIOs */
- gpio_set_level(GPIO_SYSTEM_POWER_H, 1);
- /* Enable PD MCU interrupt */
- gpio_enable_interrupt(GPIO_PD_MCU_INT);
-
- /* Enable BC 1.2 */
- gpio_enable_interrupt(GPIO_BC12_CABLE_INT);
-
- /* Check if typeC is already connected, and do 7688 power on flow */
- board_power_on_pd_mcu();
-
- /* Update VBUS supplier */
- usb_charger_vbus_change(0, !gpio_get_level(GPIO_USB_C0_VBUS_WAKE_L));
-
- /* Remap SPI2 to DMA channels 6 and 7 */
- STM32_DMA_CSELR(STM32_DMAC_CH6) |= (3 << 20) | (3 << 24);
-}
-DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
-
-/**
- * Set active charge port -- only one port can active at a time.
- *
- * @param charge_port Charge port to enable.
- *
- * Return EC_SUCCESS if charge port is accepted and made active.
- * EC_ERROR_* otherwise.
- */
-int board_set_active_charge_port(int charge_port)
-{
- /* charge port is a physical port */
- int is_real_port = (charge_port >= 0 &&
- charge_port < CONFIG_USB_PD_PORT_COUNT);
- /* check if we are source VBUS on the port */
- int source = gpio_get_level(GPIO_USB_C0_5V_EN);
-
- if (is_real_port && source) {
- CPRINTF("Skip enable p%d", charge_port);
- return EC_ERROR_INVAL;
- }
-
- CPRINTF("New chg p%d", charge_port);
-
- if (charge_port == CHARGE_PORT_NONE) {
- /* Disable charging port */
- gpio_set_level(GPIO_USB_C0_CHARGE_L, 1);
- } else {
- /* Enable charging port */
- gpio_set_level(GPIO_USB_C0_CHARGE_L, 0);
- }
-
- return EC_SUCCESS;
-}
-
-/**
- * Set the charge limit based upon desired maximum.
- *
- * @param port Port number.
- * @param supplier Charge supplier type.
- * @param charge_ma Desired charge limit (mA).
- * @param charge_mv Negotiated charge voltage (mV).
- */
-void board_set_charge_limit(int port, int supplier, int charge_ma,
- int max_ma, int charge_mv)
-{
- /* Limit input current 95% ratio on rowan board for safety */
- charge_ma = (charge_ma * 95) / 100;
- charge_set_input_current_limit(MAX(charge_ma,
- CONFIG_CHARGER_INPUT_CURRENT), charge_mv);
- pd_send_host_event(PD_EVENT_POWER_CHANGE);
-}
-
-/**
- * Set AP reset.
- * AP_RESET_L (PC3, CPU_WARM_RESET_L) is connected to PMIC SYSRSTB
- */
-void board_set_ap_reset(int asserted)
-{
- /* Signal is active-low */
- CPRINTS("ap warm reset(%d)", asserted);
- gpio_set_level(GPIO_AP_RESET_L, !asserted);
-}
-
-#ifdef CONFIG_TEMP_SENSOR_TMP432
-static void tmp432_set_power_deferred(void)
-{
- /* Shut tmp432 down if not in S0 && no external power */
- if (!extpower_is_present() && !chipset_in_state(CHIPSET_STATE_ON)) {
- if (EC_SUCCESS != tmp432_set_power(TMP432_POWER_OFF))
- CPRINTS("ERROR: Can't shutdown TMP432.");
- return;
- }
-
- /* else, turn it on. */
- if (EC_SUCCESS != tmp432_set_power(TMP432_POWER_ON))
- CPRINTS("ERROR: Can't turn on TMP432.");
-}
-DECLARE_DEFERRED(tmp432_set_power_deferred);
-#endif
-
-/**
- * Hook of AC change. turn on/off tmp432 depends on AP & AC status.
- */
-static void board_extpower(void)
-{
- board_extpower_buffer_to_soc();
-#ifdef CONFIG_TEMP_SENSOR_TMP432
- hook_call_deferred(&tmp432_set_power_deferred_data, 0);
-#endif
-}
-DECLARE_HOOK(HOOK_AC_CHANGE, board_extpower, HOOK_PRIO_DEFAULT);
-
-/* Called on AP S5 -> S3 transition, and before HOOK_CHIPSET_STARTUP */
-static void board_chipset_pre_init(void)
-{
- /* Enable level shift of AC_OK when power on */
- board_extpower_buffer_to_soc();
-
- /* Enable SPI for BMI160 */
- gpio_config_module(MODULE_SPI_MASTER, 1);
-
- /* Set all four SPI pins to high speed */
- /* pins B12/B13/B14/B15 */
- STM32_GPIO_OSPEEDR(GPIO_B) |= 0xff000000;
-
- /* Enable clocks to SPI2 module */
- STM32_RCC_APB1ENR |= STM32_RCC_PB1_SPI2;
-
- /* Reset SPI2 */
- spi_enable(CONFIG_SPI_ACCEL_PORT, 0);
- STM32_RCC_APB1RSTR |= STM32_RCC_PB1_SPI2;
- STM32_RCC_APB1RSTR &= ~STM32_RCC_PB1_SPI2;
-
- spi_enable(CONFIG_SPI_ACCEL_PORT, 1);
-}
-DECLARE_HOOK(HOOK_CHIPSET_PRE_INIT, board_chipset_pre_init, HOOK_PRIO_DEFAULT);
-
-/* Called on AP S3 -> S5 transition */
-static void board_chipset_shutdown(void)
-{
- /* Disable level shift to SoC when shutting down */
- gpio_set_level(GPIO_LEVEL_SHIFT_EN_L, 1);
-
- spi_enable(CONFIG_SPI_ACCEL_PORT, 0);
-
- /* Disable clocks to SPI2 module */
- STM32_RCC_APB1ENR &= ~STM32_RCC_PB1_SPI2;
-
- gpio_config_module(MODULE_SPI_MASTER, 0);
-
- /*
- * Calling gpio_config_module sets disabled alternate function pins to
- * GPIO_INPUT. But to prevent leakage we want to set GPIO_OUT_LOW
- */
- gpio_set_flags_by_mask(GPIO_B, 0xf000, GPIO_OUT_LOW);
-}
-DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, board_chipset_shutdown, HOOK_PRIO_DEFAULT);
-
-/* Called on AP S3 -> S0 transition */
-static void board_chipset_resume(void)
-{
-#ifdef CONFIG_TEMP_SENSOR_TMP432
- hook_call_deferred(&tmp432_set_power_deferred_data, 0);
-#endif
-}
-DECLARE_HOOK(HOOK_CHIPSET_RESUME, board_chipset_resume, HOOK_PRIO_DEFAULT);
-
-/* Called on AP S0 -> S3 transition */
-static void board_chipset_suspend(void)
-{
-#ifdef CONFIG_TEMP_SENSOR_TMP432
- hook_call_deferred(&tmp432_set_power_deferred_data, 0);
-#endif
-}
-DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, board_chipset_suspend, HOOK_PRIO_DEFAULT);
-
-/* ALS instances. Must be in same order as enum als_id. */
-struct als_t als[] = {
- {"ISL", isl29035_init, isl29035_read_lux, 5},
-};
-BUILD_ASSERT(ARRAY_SIZE(als) == ALS_COUNT);
-
-/* Motion sensors */
-/* Mutexes */
-static struct mutex g_lid_mutex;
-
-/* Matrix to rotate accelerometer into standard reference frame */
-const mat33_fp_t lid_standard_ref = {
- { FLOAT_TO_FP(-1), 0, 0},
- { 0, FLOAT_TO_FP(-1), 0},
- { 0, 0, FLOAT_TO_FP(1)}
-};
-
-static struct bmi160_drv_data_t g_bmi160_data;
-
-struct motion_sensor_t motion_sensors[] = {
- /*
- * Note: bmi160: supports accelerometer and gyro sensor
- * Requirement: accelerometer sensor must init before gyro sensor
- * DO NOT change the order of the following table.
- */
- {.name = "Lid Accel",
- .active_mask = SENSOR_ACTIVE_S0_S3,
- .chip = MOTIONSENSE_CHIP_BMI160,
- .type = MOTIONSENSE_TYPE_ACCEL,
- .location = MOTIONSENSE_LOC_LID,
- .drv = &bmi160_drv,
- .mutex = &g_lid_mutex,
- .drv_data = &g_bmi160_data,
- .port = CONFIG_SPI_ACCEL_PORT,
- .addr = BMI160_SET_SPI_ADDRESS(CONFIG_SPI_ACCEL_PORT),
- .rot_standard_ref = &lid_standard_ref,
- .default_range = 2, /* g, enough for laptop. */
- .min_frequency = BMI160_ACCEL_MIN_FREQ,
- .max_frequency = BMI160_ACCEL_MAX_FREQ,
- .config = {
- /* Sensor on in S0 for Chrome screen orientation */
- [SENSOR_CONFIG_EC_S0] = {
- .odr = 10000 | ROUND_UP_FLAG,
- .ec_rate = 100 * MSEC,
- },
- },
- },
-
- {.name = "Lid Gyro",
- .active_mask = SENSOR_ACTIVE_S0_S3,
- .chip = MOTIONSENSE_CHIP_BMI160,
- .type = MOTIONSENSE_TYPE_GYRO,
- .location = MOTIONSENSE_LOC_LID,
- .drv = &bmi160_drv,
- .mutex = &g_lid_mutex,
- .drv_data = &g_bmi160_data,
- .port = CONFIG_SPI_ACCEL_PORT,
- .addr = BMI160_SET_SPI_ADDRESS(CONFIG_SPI_ACCEL_PORT),
- .default_range = 1000, /* dps */
- .rot_standard_ref = &lid_standard_ref,
- .min_frequency = BMI160_GYRO_MIN_FREQ,
- .max_frequency = BMI160_GYRO_MAX_FREQ,
- },
-};
-const unsigned int motion_sensor_count = ARRAY_SIZE(motion_sensors);
-
-uint16_t tcpc_get_alert_status(void)
-{
- return gpio_get_level(GPIO_PD_MCU_INT) ? PD_STATUS_TCPC_ALERT_0 : 0;
-}
-
diff --git a/board/rowan/board.h b/board/rowan/board.h
deleted file mode 100644
index 95f34d9e3d..0000000000
--- a/board/rowan/board.h
+++ /dev/null
@@ -1,234 +0,0 @@
-/* Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* rowan board configuration */
-
-#ifndef __CROS_EC_BOARD_H
-#define __CROS_EC_BOARD_H
-
-/*
- * Allow dangerous commands.
- * TODO: Remove this config engineering validation.
- */
-#define CONFIG_SYSTEM_UNLOCKED
-#define CONFIG_CMD_SPI_XFER
-#define CONFIG_CMD_GPIO_EXTENDED
-
-/* Button */
-#define CONFIG_BUTTON_TRIGGERED_RECOVERY
-#define CONFIG_VOLUME_BUTTONS
-
-/* Accelero meter and gyro sensor */
-#define CONFIG_ACCELGYRO_BMI160
-#define CONFIG_ALS_ISL29035
-#define CONFIG_CMD_ACCELS
-#define CONFIG_CMD_ACCEL_INFO
-#define CONFIG_CMD_ALS
-
-#define CONFIG_ADC
-#undef CONFIG_ADC_WATCHDOG
-
-/* AC adaptor, charger, battery */
-#define CONFIG_BATTERY_CUT_OFF
-#undef CONFIG_BATTERY_PRECHARGE_TIMEOUT
-#define CONFIG_BATTERY_PRECHARGE_TIMEOUT 300
-#define CONFIG_BATTERY_PRESENT_GPIO GPIO_BAT_PRESENT_L
-#define CONFIG_BATTERY_SMART
-#define CONFIG_CHARGE_MANAGER
-#define CONFIG_CHARGE_MANAGER_EXTERNAL_POWER_LIMIT
-#define CONFIG_CHARGER
-#define CONFIG_CHARGER_INPUT_CURRENT 512
-#define CONFIG_CHARGE_RAMP_HW
-#define CONFIG_CHARGER_ISL9237
-#define CONFIG_CHARGER_MAX_INPUT_CURRENT 3000
-#define CONFIG_CHARGER_SENSE_RESISTOR 10
-#define CONFIG_CHARGER_SENSE_RESISTOR_AC 20
-#define CONFIG_CHARGER_DISCHARGE_ON_AC
-#define CONFIG_CHARGER_V2
-#define CONFIG_CHIPSET_MT817X
-#define CONFIG_CMD_TYPEC
-#define CONFIG_EXTPOWER_GPIO
-
-/* Increase tx buffer size, as we'd like to stream EC log to AP. */
-#undef CONFIG_UART_TX_BUF_SIZE
-#define CONFIG_UART_TX_BUF_SIZE 8192
-
-/* Wakeup pin: EC_WAKE(PA0) - WKUP1 */
-#define CONFIG_FORCE_CONSOLE_RESUME
-#define CONFIG_HIBERNATE
-#define CONFIG_HIBERNATE_WAKEUP_PINS (STM32_PWR_CSR_EWUP1)
-
-/* Other configs */
-#define CONFIG_HOST_COMMAND_STATUS
-#define CONFIG_I2C
-#define CONFIG_I2C_MASTER
-#define CONFIG_KEYBOARD_COL2_INVERTED
-#define CONFIG_KEYBOARD_PROTOCOL_MKBP
-#define CONFIG_LED_COMMON
-#define CONFIG_LID_SWITCH
-#define CONFIG_LOW_POWER_IDLE
-#define CONFIG_MKBP_EVENT
-#define CONFIG_MKBP_USE_GPIO
-#define CONFIG_POWER_BUTTON
-#define CONFIG_POWER_COMMON
-#define CONFIG_USB_CHARGER
-#define CONFIG_SPI
-#define CONFIG_SPI_MASTER
-#define CONFIG_STM_HWTIMER32
-#define CONFIG_VBOOT_HASH
-#undef CONFIG_WATCHDOG_HELP
-#define CONFIG_SWITCH
-#undef CONFIG_UART_CONSOLE
-#define CONFIG_UART_CONSOLE 1
-#define CONFIG_TEMP_SENSOR
-#define CONFIG_DPTF
-
-/* Type-C */
-#define CONFIG_USBC_SS_MUX
-#define CONFIG_USBC_SS_MUX_DFP_ONLY
-#define CONFIG_USBC_VCONN
-#define CONFIG_USBC_VCONN_SWAP
-#define CONFIG_USB_POWER_DELIVERY
-#define CONFIG_USB_PD_ALT_MODE
-#define CONFIG_USB_PD_ALT_MODE_DFP
-#define CONFIG_USB_PD_DUAL_ROLE
-
-#define CONFIG_USB_PD_LOGGING
-
-#define CONFIG_USB_PD_PORT_COUNT 1
-#define CONFIG_USB_PD_TCPM_MUX
-#define CONFIG_USB_PD_TCPM_ANX7688
-#define CONFIG_USB_PD_TCPM_TCPCI
-#define CONFIG_USB_PD_TRY_SRC
-#define CONFIG_USB_PD_VBUS_DETECT_TCPC
-#undef CONFIG_TCPC_I2C_BASE_ADDR
-#define CONFIG_TCPC_I2C_BASE_ADDR 0x58
-#define CONFIG_USB_PD_ANX7688
-
-/* UART DMA */
-#undef CONFIG_UART_TX_DMA
-#undef CONFIG_UART_RX_DMA
-
-/* BC 1.2 charger */
-#define CONFIG_BC12_DETECT_PI3USB9281
-#define CONFIG_BC12_DETECT_PI3USB9281_CHIP_COUNT 1
-
-/* Optional features */
-#define CONFIG_CMD_CHARGER_ADC_AMON_BMON
-#define CONFIG_CMD_HOSTCMD
-/* Mark host command structs as aligned */
-#define CONFIG_HOSTCMD_ALIGNED
-/* By default, set hcdebug to off */
-#undef CONFIG_HOSTCMD_DEBUG_MODE
-#define CONFIG_HOSTCMD_DEBUG_MODE HCDEBUG_OFF
-#define CONFIG_CMD_I2C_PROTECT
-#define CONFIG_CMD_PD_CONTROL
-
-/* Drivers */
-#ifndef __ASSEMBLER__
-
-/* 48 MHz SYSCLK clock frequency */
-#define CPU_CLOCK 48000000
-
-/* Keyboard output port list */
-#define KB_OUT_PORT_LIST GPIO_A, GPIO_B, GPIO_C, GPIO_D
-
-/* 2 I2C master ports, connect to battery, charger, pd and USB switches */
-#define I2C_PORT_MASTER 0
-#define I2C_PORT_ACCEL 0
-#define I2C_PORT_ALS 0
-#define I2C_PORT_BATTERY 0
-#define I2C_PORT_CHARGER 0
-#define I2C_PORT_PERICOM 0
-#define I2C_PORT_THERMAL 0
-#define I2C_PORT_PD_MCU 1
-#define I2C_PORT_USB_MUX 1
-#define I2C_PORT_TCPC 1
-
-/* Enable Accel over SPI */
-#define CONFIG_SPI_ACCEL_PORT 0 /* First SPI master port (SPI2) */
-
-/* Timer selection */
-#define TIM_CLOCK32 2
-#define TIM_WATCHDOG 4
-
-/* Define the MKBP events which are allowed to wakeup AP in S3. */
-#define CONFIG_MKBP_WAKEUP_MASK \
- (EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) |\
- EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON) |\
- EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEY_PRESSED) |\
- EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEYBOARD_RECOVERY) |\
- EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEYBOARD_FASTBOOT))
-
-#include "gpio_signal.h"
-
-enum power_signal {
- MTK_POWER_GOOD = 0,
- MTK_SUSPEND_ASSERTED,
- /* Number of power signals */
- POWER_SIGNAL_COUNT
-};
-
-enum pwm_channel {
- PWM_CH_POWER_LED = 0,
- /* Number of PWM channels */
- PWM_CH_COUNT
-};
-
-enum adc_channel {
- ADC_PSYS = 0, /* PC1: STM32_AIN(2) */
- ADC_AMON_BMON, /* PC0: STM32_AIN(10) */
- ADC_VBUS, /* PA2: STM32_AIN(11) */
- ADC_CH_COUNT
-};
-
-enum temp_sensor_id {
-#ifdef CONFIG_TEMP_SENSOR_TMP432
- /* TMP432 local and remote sensors */
- TEMP_SENSOR_I2C_TMP432_LOCAL,
- TEMP_SENSOR_I2C_TMP432_REMOTE1,
- TEMP_SENSOR_I2C_TMP432_REMOTE2,
-#endif
- /* Battery temperature sensor */
- TEMP_SENSOR_BATTERY,
-
- TEMP_SENSOR_COUNT
-};
-
-/* Light sensors attached to the EC. */
-enum als_id {
- ALS_ISL29035 = 0,
-
- ALS_COUNT,
-};
-
-/* TODO: determine the following board specific type-C power constants */
-/*
- * delay to turn on the power supply max is ~16ms.
- * delay to turn off the power supply max is about ~180ms.
- */
-#define PD_POWER_SUPPLY_TURN_ON_DELAY 30000 /* us */
-#define PD_POWER_SUPPLY_TURN_OFF_DELAY 250000 /* us */
-
-/* delay to turn on/off vconn */
-#define PD_VCONN_SWAP_DELAY 5000 /* us */
-
-/* Define typical operating power and max power */
-#define PD_OPERATING_POWER_MW 15000
-#define PD_MAX_POWER_MW 45000
-#define PD_MAX_CURRENT_MA CONFIG_CHARGER_MAX_INPUT_CURRENT
-#define PD_MAX_VOLTAGE_MV 20000
-
-/* The lower the input voltage, the higher the power efficiency. */
-#define PD_PREFER_LOW_VOLTAGE
-
-/* Reset PD MCU */
-void board_reset_pd_mcu(void);
-/* Set AP reset pin according to parameter */
-void board_set_ap_reset(int asserted);
-
-#endif /* !__ASSEMBLER__ */
-
-#endif /* __CROS_EC_BOARD_H */
diff --git a/board/rowan/build.mk b/board/rowan/build.mk
deleted file mode 100644
index 172a88e843..0000000000
--- a/board/rowan/build.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-#-*- makefile -*-
-# Copyright 2016 The Chromium OS 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
-
-# STmicro STM32F091VC
-CHIP := stm32
-CHIP_FAMILY := stm32f0
-CHIP_VARIANT:= stm32f09x
-
-board-y = board.o battery.o led.o
-board-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_policy.o
diff --git a/board/rowan/ec.tasklist b/board/rowan/ec.tasklist
deleted file mode 100644
index ca349255d9..0000000000
--- a/board/rowan/ec.tasklist
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/*
- * See CONFIG_TASK_LIST in config.h for details.
- */
-
-#define CONFIG_TASK_LIST \
- TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(USB_CHG_P0, usb_charger_task, NULL, TASK_STACK_SIZE) \
- TASK_ALWAYS(CHARGER, charger_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_NOTEST(CHIPSET, chipset_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_NOTEST(PDCMD, pd_command_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(HOSTCMD, host_command_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(MOTIONSENSE, motion_sense_task, NULL, TASK_STACK_SIZE) \
- TASK_ALWAYS(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \
- TASK_ALWAYS(PD_INT_C0, pd_interrupt_handler_task, 0, TASK_STACK_SIZE) \
- TASK_ALWAYS(ALS, als_task, NULL, TASK_STACK_SIZE)
diff --git a/board/rowan/gpio.inc b/board/rowan/gpio.inc
deleted file mode 100644
index 9c0b4b90ab..0000000000
--- a/board/rowan/gpio.inc
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- mode:c -*-
- *
- * Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* Declare symbolic names for all the GPIOs that we care about.
- * Note: Those with interrupt handlers must be declared first. */
-
-GPIO_INT(AC_PRESENT, PIN(C, 6), GPIO_INT_BOTH, extpower_interrupt)
-GPIO_INT(LID_OPEN, PIN(C, 9), GPIO_INT_BOTH, lid_interrupt) /* LID switch detection */
-GPIO_INT(SUSPEND_L, PIN(C, 11), GPIO_INT_BOTH, power_signal_interrupt) /* AP suspend/resume state */
-GPIO_INT(SOC_POWER_GOOD, PIN(A, 3), GPIO_INT_BOTH, power_signal_interrupt)
-GPIO_INT(PD_MCU_INT, PIN(C, 15), GPIO_INT_RISING, pd_mcu_interrupt) /* Signal from PD MCU, external pull-up */
-GPIO_INT(BC12_CABLE_INT, PIN(C, 7), GPIO_INT_FALLING | GPIO_PULL_UP, usb_evt) /* interrupt from BC12 and CABLE_DET */
-GPIO_INT(POWER_BUTTON_L, PIN(B, 5), GPIO_INT_BOTH | GPIO_PULL_UP, power_button_interrupt)
-GPIO_INT(SPI1_NSS, PIN(A, 4), GPIO_INT_BOTH | GPIO_PULL_UP, spi_event) /* SPI Chip Select */
-GPIO_INT(VOLUME_DOWN_L, PIN(A, 14), GPIO_INT_BOTH, button_interrupt)
-GPIO_INT(VOLUME_UP_L, PIN(A, 13), GPIO_INT_BOTH, button_interrupt)
-
-/* Inputs without interrupt handlers */
-GPIO(5V_POWER_GOOD, PIN(A, 1), GPIO_INPUT)
-GPIO(EC_WAKE, PIN(A, 0), GPIO_INPUT | GPIO_PULL_DOWN)
-GPIO(WP_L, PIN(B, 4), GPIO_INPUT) /* Write protect input */
-GPIO(BAT_PRESENT_L, PIN(C, 12), GPIO_INPUT | GPIO_PULL_UP)
-GPIO(USB_C0_VBUS_WAKE_L, PIN(B, 1), GPIO_INPUT)
-GPIO(EC_INT_L, PIN(B, 9), GPIO_ODR_HIGH)
-
-/* Board version */
-GPIO(BOARD_VERSION1, PIN(C, 5), GPIO_INPUT) /* Board ID 0 */
-
-/* Outputs */
-GPIO(BAT_LED0, PIN(B, 11), GPIO_OUT_LOW) /* LED_BLUE */
-GPIO(BAT_LED1, PIN(B, 0), GPIO_OUT_LOW) /* LED_ORANGE */
-
-UNIMPLEMENTED(EC_BL_OVERRIDE)
-GPIO(ENTERING_RW, PIN(F, 0), GPIO_OUT_LOW)
-
-GPIO(AP_RESET_L, PIN(B, 8), GPIO_ODR_HIGH) /* Connect to the PMU_SYSRSTB */
-GPIO(BC12_WAKE_L, PIN(D, 2), GPIO_INPUT)
-GPIO(USB_C0_CABLE_DET_L,PIN(A, 8), GPIO_INPUT | GPIO_PULL_UP)
-
-GPIO(SYSTEM_POWER_H, PIN(B, 10), GPIO_OUT_LOW)
-GPIO(PMIC_PWRON_H, PIN(C, 8), GPIO_OUT_LOW)
-GPIO(PMIC_WARM_RESET_H, PIN(B, 3), GPIO_OUT_LOW)
-GPIO(LEVEL_SHIFT_EN_L, PIN(C, 13), GPIO_OUT_LOW) /* LID/AC level shift */
-
-GPIO(USB_C0_5V_EN, PIN(B, 2), GPIO_OUT_LOW) /* USBC port 0 5V */
-GPIO(USB_C0_CHARGE_L, PIN(C, 2), GPIO_OUT_LOW) /* USBC port 0 charge */
-GPIO(USB_C0_RST, PIN(C, 4), GPIO_ODR_HIGH) /* ANX7688 reset */
-GPIO(USB_C0_PWR_EN_L, PIN(C, 10), GPIO_ODR_HIGH) /* ANX7688 power enable */
-GPIO(USB_C0_EXTPWR_EN, PIN(C, 14), GPIO_OUT_LOW) /* ANX7688 3.3V ext power enable */
-
-/* Analog pins */
-GPIO(VDC_BOOSTIN_SENSE, PIN(C, 1), GPIO_ANALOG) /* ADC_IN11 */
-GPIO(PSYS_MONITOR, PIN(A, 2), GPIO_ANALOG) /* ADC_IN2 */
-GPIO(AMON_BMON, PIN(C, 0), GPIO_ANALOG) /* ADC_IN10 */
-
-/*
- * I2C pins should be configured as inputs until I2C module is
- * initialized. This will avoid driving the lines unintentionally.
- */
-GPIO(I2C0_SCL, PIN(B, 6), GPIO_INPUT) /* EC I2C */
-GPIO(I2C0_SDA, PIN(B, 7), GPIO_INPUT)
-GPIO(I2C1_SCL, PIN(A, 11), GPIO_INPUT) /* PD I2C */
-GPIO(I2C1_SDA, PIN(A, 12), GPIO_INPUT)
-
-/* SPI MASTER. For SPI sensor */
-GPIO(SPI2_NSS, PIN(B, 12), GPIO_OUT_HIGH)
-
-ALTERNATE(PIN_MASK(A, 0x0600), 1, MODULE_UART, 0) /* USART1: PA9/PA10 */
-ALTERNATE(PIN_MASK(B, 0x00c0), 1, MODULE_I2C, 0) /* I2C MASTER:PB6/7 */
-ALTERNATE(PIN_MASK(A, 0x1800), 5, MODULE_I2C, 0) /* I2C MASTER:PA11/12 */
-ALTERNATE(PIN_MASK(A, 0x00f0), 0, MODULE_SPI, 0) /* SPI SLAVE:PA4/5/6/7 */
-ALTERNATE(PIN_MASK(B, 0xe000), 0, MODULE_SPI_MASTER, 0) /* SPI MASTER:PB13/14/15 */
diff --git a/board/rowan/led.c b/board/rowan/led.c
deleted file mode 100644
index 49efa0cbb1..0000000000
--- a/board/rowan/led.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/* Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Battery LED and Power LED control for Rowan Board.
- */
-
-#include "battery.h"
-#include "charge_state.h"
-#include "chipset.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "host_command.h"
-#include "led_common.h"
-#include "util.h"
-#include "system.h"
-
-#define CRITICAL_LOW_BATTERY_PERMILLAGE 71
-#define LOW_BATTERY_PERMILLAGE 137
-#define FULL_BATTERY_PERMILLAGE 937
-
-const enum ec_led_id supported_led_ids[] = {
- EC_LED_ID_BATTERY_LED,
- EC_LED_ID_POWER_LED
-};
-
-const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids);
-
-enum led_color {
- BAT_LED_RED = 0,
- BAT_LED_GREEN,
- LED_COLOR_COUNT /* Number of colors, not a color itself */
-};
-
-static int bat_led_set(enum led_color color, int on)
-{
- switch (color) {
- case BAT_LED_RED:
- gpio_set_level(GPIO_BAT_LED0, on); /* BAT_LED_RED */
- break;
- case BAT_LED_GREEN:
- gpio_set_level(GPIO_BAT_LED1, on); /* BAT_LED_GREEN */
- break;
- default:
- return EC_ERROR_UNKNOWN;
- }
- return EC_SUCCESS;
-}
-
-void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range)
-{
- /* Ignoring led_id as both leds support the same colors */
- brightness_range[EC_LED_COLOR_RED] = 1;
- brightness_range[EC_LED_COLOR_GREEN] = 1;
-}
-
-int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness)
-{
- if (EC_LED_ID_BATTERY_LED == led_id) {
- if (brightness[EC_LED_COLOR_RED] != 0) {
- bat_led_set(BAT_LED_RED, 1);
- bat_led_set(BAT_LED_GREEN, 0);
- } else if (brightness[EC_LED_COLOR_GREEN] != 0) {
- bat_led_set(BAT_LED_RED, 0);
- bat_led_set(BAT_LED_GREEN, 1);
- } else {
- bat_led_set(BAT_LED_RED, 0);
- bat_led_set(BAT_LED_GREEN, 0);
- }
- return EC_SUCCESS;
- } else {
- return EC_ERROR_UNKNOWN;
- }
-}
-
-static unsigned blink_second;
-
-static void rowan_led_set_battery(void)
-{
- /*
- * BAT LED behavior:
- * - Fully charged / normal idle: Blue ON
- * - Charging: Orange ON
- * - Battery discharging capacity<10%, Orange blink(1:3)
- * < 3%, Orange blink(1:1)
- * - Battery error: Orange blink(1:1)
- * - Factory force idle: Blue 2 sec, Orange 2 sec
- */
- uint32_t charge_flags = charge_get_flags();
- int remaining_capacity;
- int full_charge_capacity;
- int permillage;
-
- /* Make the percentage approximate to UI shown */
- remaining_capacity = *(int *)host_get_memmap(EC_MEMMAP_BATT_CAP);
- full_charge_capacity = *(int *)host_get_memmap(EC_MEMMAP_BATT_LFCC);
- permillage = !full_charge_capacity ? 0 :
- (1000 * remaining_capacity) / full_charge_capacity;
-
- switch (charge_get_state()) {
- case PWR_STATE_CHARGE:
- if (permillage < FULL_BATTERY_PERMILLAGE) {
- bat_led_set(BAT_LED_RED, 0);
- bat_led_set(BAT_LED_GREEN, 1);
- } else {
- bat_led_set(BAT_LED_RED, 1);
- bat_led_set(BAT_LED_GREEN, 0);
- }
- break;
- case PWR_STATE_CHARGE_NEAR_FULL:
- bat_led_set(BAT_LED_RED, 1);
- bat_led_set(BAT_LED_GREEN, 0);
- break;
- case PWR_STATE_DISCHARGE:
- bat_led_set(BAT_LED_RED, 0);
- if (!chipset_in_state(CHIPSET_STATE_ANY_OFF) &&
- permillage <= CRITICAL_LOW_BATTERY_PERMILLAGE)
- bat_led_set(BAT_LED_GREEN,
- (blink_second & 1) ? 0 : 1);
- else if (!chipset_in_state(CHIPSET_STATE_ANY_OFF) &&
- permillage <= LOW_BATTERY_PERMILLAGE)
- bat_led_set(BAT_LED_GREEN,
- (blink_second & 3) ? 0 : 1);
- else
- bat_led_set(BAT_LED_GREEN, 0);
- break;
- case PWR_STATE_ERROR:
- bat_led_set(BAT_LED_RED, 0);
- bat_led_set(BAT_LED_GREEN, (blink_second & 1) ? 0 : 1);
- break;
- case PWR_STATE_IDLE: /* Ext. power connected in IDLE. */
- if (charge_flags & CHARGE_FLAG_FORCE_IDLE) {
- bat_led_set(BAT_LED_RED, (blink_second & 2) ? 0 : 1);
- bat_led_set(BAT_LED_GREEN, (blink_second & 2) ? 1 : 0);
- } else {
- bat_led_set(BAT_LED_RED, 1);
- bat_led_set(BAT_LED_GREEN, 0);
- }
- break;
- default:
- /* Other states don't alter LED behavior */
- break;
- }
-}
-
-/**
- * Called by hook task every 1 sec
- */
-static void led_second(void)
-{
- blink_second++;
-
- if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED))
- rowan_led_set_battery();
-}
-DECLARE_HOOK(HOOK_SECOND, led_second, HOOK_PRIO_DEFAULT);
diff --git a/board/rowan/usb_pd_policy.c b/board/rowan/usb_pd_policy.c
deleted file mode 100644
index c83fb2835c..0000000000
--- a/board/rowan/usb_pd_policy.c
+++ /dev/null
@@ -1,367 +0,0 @@
-/* Copyright 2016 The Chromium OS 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 "atomic.h"
-#include "charge_manager.h"
-#include "common.h"
-#include "console.h"
-#include "driver/tcpm/anx7688.h"
-#include "driver/tcpm/tcpci.h"
-#include "driver/tcpm/tcpm.h"
-#include "gpio.h"
-#include "hooks.h"
-#include "host_command.h"
-#include "registers.h"
-#include "system.h"
-#include "task.h"
-#include "timer.h"
-#include "util.h"
-#include "usb_mux.h"
-#include "usb_pd.h"
-
-#define CPRINTF(format, args...) cprintf(CC_USBPD, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_USBPD, format, ## args)
-
-#define PDO_FIXED_FLAGS (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP |\
- PDO_FIXED_COMM_CAP)
-
-/* TODO: fill in correct source and sink capabilities */
-const uint32_t pd_src_pdo[] = {
- PDO_FIXED(5000, 1500, PDO_FIXED_FLAGS),
-};
-const int pd_src_pdo_cnt = ARRAY_SIZE(pd_src_pdo);
-
-const uint32_t pd_snk_pdo[] = {
- PDO_FIXED(5000, 500, PDO_FIXED_FLAGS),
- PDO_BATT(4750, 21000, 15000),
- PDO_VAR(4750, 21000, 3000),
-};
-const int pd_snk_pdo_cnt = ARRAY_SIZE(pd_snk_pdo);
-
-int pd_is_valid_input_voltage(int mv)
-{
- return 1;
-}
-
-void pd_transition_voltage(int idx)
-{
- /* No-operation: we are always 5V */
-}
-
-int pd_set_power_supply_ready(int port)
-{
- /* Disable charging */
- gpio_set_level(GPIO_USB_C0_CHARGE_L, 1);
- /* Provide VBUS */
- gpio_set_level(GPIO_USB_C0_5V_EN, 1);
-
- anx7688_set_power_supply_ready(port);
-
- /* notify host of power info change */
- pd_send_host_event(PD_EVENT_POWER_CHANGE);
-
- return EC_SUCCESS;
-}
-
-void pd_power_supply_reset(int port)
-{
- /* Disable VBUS */
- gpio_set_level(GPIO_USB_C0_5V_EN, 0);
-
- anx7688_power_supply_reset(port);
-
- /* notify host of power info change */
- pd_send_host_event(PD_EVENT_POWER_CHANGE);
-}
-
-int pd_board_checks(void)
-{
- return EC_SUCCESS;
-}
-
-int pd_check_power_swap(int port)
-{
- /*
- * Allow power swap as long as we are acting as a dual role device,
- * otherwise assume our role is fixed (not in S0 or console command
- * to fix our role).
- */
- return pd_get_dual_role(port) == PD_DRP_TOGGLE_ON ? 1 : 0;
-}
-
-int pd_check_data_swap(int port, int data_role)
-{
- /* Allow data swap if we are a UFP, otherwise don't allow */
- return (data_role == PD_ROLE_UFP) ? 1 : 0;
-}
-
-int pd_check_vconn_swap(int port)
-{
- /* in G3, do not allow vconn swap since 5V power source is off */
- return gpio_get_level(GPIO_5V_POWER_GOOD);
-}
-
-void pd_execute_data_swap(int port, int data_role)
-{
- /* Do nothing */
-}
-
-void pd_check_pr_role(int port, int pr_role, int flags)
-{
- /*
- * If partner is dual-role power and dualrole toggling is on, consider
- * if a power swap is necessary.
- */
- if ((flags & PD_FLAGS_PARTNER_DR_POWER) &&
- pd_get_dual_role(port) == PD_DRP_TOGGLE_ON) {
- /*
- * If we are a sink and partner is not externally powered, then
- * swap to become a source. If we are source and partner is
- * externally powered, swap to become a sink.
- */
- int partner_extpower = flags & PD_FLAGS_PARTNER_EXTPOWER;
-
- if ((!partner_extpower && pr_role == PD_ROLE_SINK) ||
- (partner_extpower && pr_role == PD_ROLE_SOURCE))
- pd_request_power_swap(port);
- }
-}
-
-void pd_check_dr_role(int port, int dr_role, int flags)
-{
- /* If UFP, try to switch to DFP */
- if ((flags & PD_FLAGS_PARTNER_DR_DATA) && dr_role == PD_ROLE_UFP)
- pd_request_data_swap(port);
-}
-
-/* ----------------- Vendor Defined Messages ------------------ */
-const struct svdm_response svdm_rsp = {
- .identity = NULL,
- .svids = NULL,
- .modes = NULL,
-};
-
-int pd_custom_vdm(int port, int cnt, uint32_t *payload,
- uint32_t **rpayload)
-{
- int cmd = PD_VDO_CMD(payload[0]);
- uint16_t dev_id = 0;
- int is_rw, is_latest;
-
- /* make sure we have some payload */
- if (cnt == 0)
- return 0;
-
- switch (cmd) {
- case VDO_CMD_VERSION:
- /* guarantee last byte of payload is null character */
- *(payload + cnt - 1) = 0;
- CPRINTF("version: %s\n", (char *)(payload+1));
- break;
- case VDO_CMD_READ_INFO:
- case VDO_CMD_SEND_INFO:
- /* copy hash */
- if (cnt == 7) {
- dev_id = VDO_INFO_HW_DEV_ID(payload[6]);
- is_rw = VDO_INFO_IS_RW(payload[6]);
-
- is_latest = pd_dev_store_rw_hash(port,
- dev_id,
- payload + 1,
- is_rw ?
- SYSTEM_IMAGE_RW :
- SYSTEM_IMAGE_RO);
-
- /*
- * Send update host event unless our RW hash is
- * already known to be the latest update RW.
- */
- if (!is_rw || !is_latest)
- pd_send_host_event(PD_EVENT_UPDATE_DEVICE);
-
- CPRINTF("DevId:%d.%d SW:%d RW:%d\n",
- HW_DEV_ID_MAJ(dev_id),
- HW_DEV_ID_MIN(dev_id),
- VDO_INFO_SW_DBG_VER(payload[6]),
- is_rw);
- } else if (cnt == 6) {
- /* really old devices don't have last byte */
- pd_dev_store_rw_hash(port, dev_id, payload + 1,
- SYSTEM_IMAGE_UNKNOWN);
- }
- break;
- case VDO_CMD_CURRENT:
- CPRINTF("Current: %dmA\n", payload[1]);
- break;
- case VDO_CMD_FLIP:
- usb_mux_flip(port);
- break;
-#ifdef CONFIG_USB_PD_LOGGING
- case VDO_CMD_GET_LOG:
- pd_log_recv_vdm(port, cnt, payload);
- break;
-#endif /* CONFIG_USB_PD_LOGGING */
- }
-
- return 0;
-}
-
-#ifdef CONFIG_USB_PD_ALT_MODE_DFP
-static int dp_flags[CONFIG_USB_PD_PORT_COUNT];
-/* DP Status VDM as returned by UFP */
-static uint32_t dp_status[CONFIG_USB_PD_PORT_COUNT];
-
-static void svdm_safe_dp_mode(int port)
-{
- const char *dp_str, *usb_str;
- enum typec_mux typec_mux_setting;
-
- /* make DP interface safe until configure */
- dp_flags[port] = 0;
- dp_status[port] = 0;
-
- /*
- * Check current status, due to the mux may be switched to SS
- * and SS device was attached before (for example: Type-C dock).
- * To avoid broken the SS connection,
- * keep the current setting if SS connection is enabled already.
- */
- typec_mux_setting = (usb_mux_get(port, &dp_str, &usb_str) && usb_str) ?
- TYPEC_MUX_USB : TYPEC_MUX_NONE;
- usb_mux_set(port, typec_mux_setting,
- USB_SWITCH_CONNECT, pd_get_polarity(port));
-}
-
-static int svdm_enter_dp_mode(int port, uint32_t mode_caps)
-{
- /* Only enter mode if device is DFP_D capable */
- if (mode_caps & MODE_DP_SNK) {
- svdm_safe_dp_mode(port);
- return 0;
- }
-
- return -1;
-}
-
-static int svdm_dp_status(int port, uint32_t *payload)
-{
- int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT);
-
- payload[0] = VDO(USB_SID_DISPLAYPORT, 1,
- CMD_DP_STATUS | VDO_OPOS(opos));
- payload[1] = VDO_DP_STATUS(0, /* HPD IRQ ... not applicable */
- 0, /* HPD level ... not applicable */
- 0, /* exit DP? ... no */
- 0, /* usb mode? ... no */
- 0, /* multi-function ... no */
- (!!(dp_flags[port] & DP_FLAGS_DP_ON)),
- 0, /* power low? ... no */
- (!!(dp_flags[port] & DP_FLAGS_DP_ON)));
- return 2;
-};
-
-static int svdm_dp_config(int port, uint32_t *payload)
-{
- int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT);
- int mf_pref = PD_VDO_DPSTS_MF_PREF(dp_status[port]);
- int pin_mode = pd_dfp_dp_get_pin_mode(port, dp_status[port]);
-
- if (!pin_mode)
- return 0;
-
- usb_mux_set(port, mf_pref ? TYPEC_MUX_DOCK : TYPEC_MUX_DP,
- USB_SWITCH_CONNECT, pd_get_polarity(port));
-
- payload[0] = VDO(USB_SID_DISPLAYPORT, 1,
- CMD_DP_CONFIG | VDO_OPOS(opos));
- payload[1] = VDO_DP_CFG(pin_mode, /* pin mode */
- 1, /* DPv1.3 signaling */
- 2); /* UFP connected */
- return 2;
-};
-
-static void svdm_dp_post_config(int port)
-{
- dp_flags[port] |= DP_FLAGS_DP_ON;
- if (!(dp_flags[port] & DP_FLAGS_HPD_HI_PENDING))
- return;
-}
-
-static int svdm_dp_attention(int port, uint32_t *payload)
-{
- int lvl = PD_VDO_DPSTS_HPD_LVL(payload[1]);
- int irq = PD_VDO_DPSTS_HPD_IRQ(payload[1]);
-
- anx7688_update_hpd(port, lvl, irq);
-
- dp_status[port] = payload[1];
-
- /* Its initial DP status message prior to config */
- if (!(dp_flags[port] & DP_FLAGS_DP_ON)) {
- if (lvl)
- dp_flags[port] |= DP_FLAGS_HPD_HI_PENDING;
- }
-
- /* ack */
- return 1;
-}
-
-static void svdm_exit_dp_mode(int port)
-{
- svdm_safe_dp_mode(port);
- anx7688_hpd_disable(port);
-}
-
-static int svdm_enter_gfu_mode(int port, uint32_t mode_caps)
-{
- /* Always enter GFU mode */
- return 0;
-}
-
-static void svdm_exit_gfu_mode(int port)
-{
-}
-
-static int svdm_gfu_status(int port, uint32_t *payload)
-{
- /*
- * This is called after enter mode is successful, send unstructured
- * VDM to read info.
- */
- pd_send_vdm(port, USB_VID_GOOGLE, VDO_CMD_READ_INFO, NULL, 0);
- return 0;
-}
-
-static int svdm_gfu_config(int port, uint32_t *payload)
-{
- return 0;
-}
-
-static int svdm_gfu_attention(int port, uint32_t *payload)
-{
- return 0;
-}
-
-const struct svdm_amode_fx supported_modes[] = {
- {
- .svid = USB_SID_DISPLAYPORT,
- .enter = &svdm_enter_dp_mode,
- .status = &svdm_dp_status,
- .config = &svdm_dp_config,
- .post_config = &svdm_dp_post_config,
- .attention = &svdm_dp_attention,
- .exit = &svdm_exit_dp_mode,
- },
- {
- .svid = USB_VID_GOOGLE,
- .enter = &svdm_enter_gfu_mode,
- .status = &svdm_gfu_status,
- .config = &svdm_gfu_config,
- .attention = &svdm_gfu_attention,
- .exit = &svdm_exit_gfu_mode,
- }
-};
-const int supported_modes_cnt = ARRAY_SIZE(supported_modes);
-#endif /* CONFIG_USB_PD_ALT_MODE_DFP */
diff --git a/board/servo_v4/usb_pd_policy.c b/board/servo_v4/usb_pd_policy.c
index f874e4dbe9..cb3332a017 100644
--- a/board/servo_v4/usb_pd_policy.c
+++ b/board/servo_v4/usb_pd_policy.c
@@ -660,6 +660,8 @@ static void print_cc_mode(void)
static void do_cc(int disable_dts_new, int allow_src_new)
{
+ int dualrole;
+
if ((disable_dts_new != disable_dts_mode) ||
(allow_src_new != allow_src_mode)) {
/* Force detach */
@@ -667,7 +669,9 @@ static void do_cc(int disable_dts_new, int allow_src_new)
/* Always set to 0 here so both CC lines are changed */
disable_dts_mode = 0;
allow_src_mode = 0;
+
/* Remove Rp/Rd on both CC lines */
+ pd_comm_enable(DUT, 0);
pd_set_rp_rd(DUT, TYPEC_CC_RP, TYPEC_RP_RESERVED);
/* Some time for DUT to detach, use tErrorRecovery */
@@ -676,14 +680,15 @@ static void do_cc(int disable_dts_new, int allow_src_new)
/* Accept new dts/src value */
disable_dts_mode = disable_dts_new;
allow_src_mode = allow_src_new;
+
/* Can we charge? */
- pd_set_dual_role(DUT,
- allow_src_mode && charge_port_is_active() ?
+ dualrole = allow_src_mode && charge_port_is_active();
+ pd_set_dual_role(DUT, dualrole ?
PD_DRP_FORCE_SOURCE : PD_DRP_FORCE_SINK);
/* Present Rp or Rd on CC1 and CC2 based on disable_dts_mode */
- pd_config_init(DUT,
- pd_get_dual_role(DUT) == PD_DRP_FORCE_SOURCE);
+ pd_config_init(DUT, dualrole);
+ pd_comm_enable(DUT, dualrole);
}
print_cc_mode();
diff --git a/board/yorp/board.c b/board/yorp/board.c
index 3a9091aef4..65653ac168 100644
--- a/board/yorp/board.c
+++ b/board/yorp/board.c
@@ -144,6 +144,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_ACCEL),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.rot_standard_ref = &base_standard_ref,
@@ -174,6 +176,8 @@ struct motion_sensor_t motion_sensors[] = {
.mutex = &g_base_mutex,
.drv_data = LSM6DSM_ST_DATA(lsm6dsm_data,
MOTIONSENSE_TYPE_GYRO),
+ .int_signal = GPIO_BASE_SIXAXIS_INT_L,
+ .flags = MOTIONSENSE_FLAG_INT_SIGNAL,
.port = I2C_PORT_SENSOR,
.addr = LSM6DSM_ADDR0,
.default_range = 1000 | ROUND_UP_FLAG, /* dps */
diff --git a/chip/g/board_id.c b/chip/g/board_id.c
index b871f8b6ca..1f46bd8226 100644
--- a/chip/g/board_id.c
+++ b/chip/g/board_id.c
@@ -242,35 +242,11 @@ static int command_board_id(int argc, char **argv)
rv = write_board_id(&id, 0);
}
#endif
-#ifdef CR50_RELAXED
- else if (argc == 2) {
- int clear_flags;
-
- if (strcasecmp(argv[1], "force_pvt"))
- return EC_ERROR_PARAM2;
-
- clear_flags = 1;
- rv = read_board_id(&id);
- if (rv != EC_SUCCESS) {
- CPRINTS("%s: error reading Board ID", __func__);
- return rv;
- }
- if (board_id_is_blank(&id)) {
- CPRINTS("%s: Board ID isn't set", __func__);
- return EC_ERROR_INVAL;
- }
- id.flags = 0;
- rv = write_board_id(&id, clear_flags);
-
- }
-#endif
return rv;
}
DECLARE_SAFE_CONSOLE_COMMAND(bid, command_board_id,
#ifdef CR50_DEV
- "[force_pvt | bid flags]",
-#elif defined(CR50_RELAXED)
- "[force_pvt]",
+ "[bid flags]",
#else
NULL,
#endif
diff --git a/chip/g/board_space.h b/chip/g/board_space.h
index 1884a5c74c..b71733c210 100644
--- a/chip/g/board_space.h
+++ b/chip/g/board_space.h
@@ -8,6 +8,7 @@
#define __EC_CHIP_G_BOARD_SPACE_H
#include "compile_time_macros.h"
+#include "flash_config.h"
#include "flash_info.h"
#include "stdint.h"
@@ -47,6 +48,17 @@ struct info1_board_space {
struct sn_data sn;
};
+/* Layout of the entire 2K INFO1 space. */
+struct info1_layout {
+ uint8_t ro_info_map[INFO_RO_MAP_SIZE];
+ uint8_t rw_info_map[INFO_RW_MAP_SIZE];
+ struct info1_board_space board_space;
+ uint8_t padding[FLASH_INFO_MANUFACTURE_STATE_OFFSET - INFO_RO_MAP_SIZE -
+ INFO_RW_MAP_SIZE - sizeof(struct info1_board_space)];
+ uint8_t manufacture_space[FLASH_INFO_MANUFACTURE_STATE_SIZE];
+};
+BUILD_ASSERT(sizeof(struct info1_layout) == FLASH_INFO_SIZE);
+
#define INFO_BOARD_ID_SIZE sizeof(struct board_id)
#define INFO_BOARD_ID_OFFSET (INFO_BOARD_SPACE_OFFSET + \
offsetof(struct info1_board_space, \
diff --git a/chip/g/flash.c b/chip/g/flash.c
index 8012a34430..8a9622e64c 100644
--- a/chip/g/flash.c
+++ b/chip/g/flash.c
@@ -39,12 +39,11 @@
*/
#include "common.h"
+#include "board_id.h"
#include "console.h"
#include "cryptoc/util.h"
#include "flash.h"
-#include "flash_config.h"
#include "flash_log.h"
-#include "flash_info.h"
#include "registers.h"
#include "shared_mem.h"
#include "task.h"
@@ -191,7 +190,7 @@ static int do_flash_op(enum flash_op op, int is_info_bank,
/* What are we doing? */
switch (op) {
case OP_ERASE_BLOCK:
-#ifndef CR50_DEV
+#ifndef CR50_RELAXED
if (is_info_bank)
/* Erasing the INFO bank from the RW section is
* unsupported. */
@@ -502,37 +501,82 @@ void flash_open_ro_window(uint32_t offset, size_t size_b)
GWRITE_FIELD(GLOBALSEC, FLASH_REGION6_CTRL, WR_EN, 1);
}
-#ifdef CR50_DEV
+#ifdef CR50_RELAXED
static int command_erase_flash_info(int argc, char **argv)
{
- uint32_t *preserved_manufacture_state;
- const size_t manuf_word_count = FLASH_INFO_MANUFACTURE_STATE_SIZE /
- sizeof(uint32_t);
int i;
- int rv = EC_ERROR_BUSY;
+ int rv;
+ struct info1_layout *info1;
+ uint32_t *p;
- if (shared_mem_acquire(FLASH_INFO_MANUFACTURE_STATE_SIZE,
- (char **)&preserved_manufacture_state) !=
- EC_SUCCESS) {
- ccprintf("Failed to allocate memory for manufacture state!\n");
+ rv = shared_mem_acquire(sizeof(*info1), (char **)&info1);
+ if (rv != EC_SUCCESS) {
+ ccprintf("Failed to allocate memory for info1!\n");
return rv;
}
flash_info_read_enable(0, 2048);
flash_info_write_enable(0, 2048);
- /* Preserve manufacturing information. */
- for (i = 0; i < manuf_word_count; i++) {
- if (flash_physical_info_read_word
- (FLASH_INFO_MANUFACTURE_STATE_OFFSET +
- i * sizeof(uint32_t),
- preserved_manufacture_state + i) != EC_SUCCESS) {
+ /* Read the entire info1. */
+ p = (uint32_t *)info1;
+ for (i = 0; i < (sizeof(*info1) / sizeof(*p)); i++) {
+ if (flash_physical_info_read_word(i * sizeof(*p), p + i) !=
+ EC_SUCCESS) {
ccprintf("Failed to read word %d!\n", i);
goto exit;
}
}
+#ifdef CR50_SQA
+ /*
+ * SQA images erase INFO1 RW mask, but do not allow erasing board ID.
+ *
+ * If compiled with CR50_SQA=1, board ID flags will set to zero, if
+ * compiled with CR50_SQA=2 or greater, board ID flags can be set to
+ * an arbitrary value passed in on the command line, but guaranteeing
+ * not to lock out the currently running image.
+ */
+ {
+ uint32_t flags = 0;
+#if CR50_SQA > 1
+ if (argc > 1) {
+ char *e;
+
+ flags = strtoi(argv[1], &e, 0);
+ if (*e) {
+ rv = EC_ERROR_PARAM1;
+ goto exit;
+ }
+ }
+#endif
+ if (board_id_is_blank(&info1->board_space.bid)) {
+ ccprintf("BID is erased. Not modifying flags\n");
+ } else {
+ ccprintf("setting BID flags to %x\n", flags);
+ info1->board_space.bid.flags = flags;
+ }
+ if (check_board_id_vs_header(&info1->board_space.bid,
+ get_current_image_header())) {
+ ccprintf("Flags %x would lock out current image\n",
+ flags);
+ rv = EC_ERROR_PARAM1;
+ goto exit;
+ }
+ }
+#else /* CR50_SQA ^^^^^^ defined vvvvvvv Not defined. */
+ /*
+ * This must be CR50_DEV=1 image, just erase the entire board and
+ * manufacture spaces.
+ */
+ memset(&info1->board_space, 0xff, sizeof(info1->board_space));
+ memset(&info1->manufacture_space, 0xff,
+ sizeof(info1->manufacture_space));
+#endif /* CR50_SQA Not defined. */
+
+ memset(info1->rw_info_map, 0xff, sizeof(info1->rw_info_map));
+
mutex_lock(&flash_mtx);
rv = do_flash_op(OP_ERASE_BLOCK, 1, 0, 512);
@@ -544,23 +588,21 @@ static int command_erase_flash_info(int argc, char **argv)
goto exit;
}
- if (flash_info_physical_write
- (FLASH_INFO_MANUFACTURE_STATE_OFFSET,
- FLASH_INFO_MANUFACTURE_STATE_SIZE,
- (char *)preserved_manufacture_state) != EC_SUCCESS) {
- ccprintf("Failed to restore manufacture state!\n");
- goto exit;
- }
+ rv = flash_info_physical_write(0, sizeof(*info1), (char *)info1);
+ if (rv != EC_SUCCESS)
+ ccprintf("Failed write back info1 contents!\n");
- rv = EC_SUCCESS;
exit:
- always_memset(preserved_manufacture_state, 0,
- FLASH_INFO_MANUFACTURE_STATE_SIZE);
- shared_mem_release(preserved_manufacture_state);
flash_info_write_disable();
+ always_memset(info1, 0, sizeof(*info1));
+ shared_mem_release(info1);
return rv;
}
-DECLARE_CONSOLE_COMMAND(eraseflashinfo, command_erase_flash_info,
- "",
- "Erase INFO1 flash space");
+DECLARE_SAFE_CONSOLE_COMMAND(eraseflashinfo, command_erase_flash_info,
+#if defined(CR50_SQA) && (CR50_SQA > 1)
+ "[bid flags]",
+ "Erase INFO1 flash space and set Board ID flags");
+#else
+ "", "Erase INFO1 flash space");
+#endif
#endif
diff --git a/chip/g/flash_config.h b/chip/g/flash_config.h
index d1bec36871..73dd2eec60 100644
--- a/chip/g/flash_config.h
+++ b/chip/g/flash_config.h
@@ -5,6 +5,8 @@
#ifndef __EC_CHIP_G_FLASH_CONFIG_H
#define __EC_CHIP_G_FLASH_CONFIG_H
+#include "stdint.h"
+
#define FLASH_INFO_SIZE (2 * 1024)
#define FLASH_INFO_MEMORY_BASE 0x28000
/* INFO is a 2-KB flash page that consists of four regions. The
diff --git a/chip/g/rdd.c b/chip/g/rdd.c
index 9460ad35e6..c58dce4806 100644
--- a/chip/g/rdd.c
+++ b/chip/g/rdd.c
@@ -74,7 +74,7 @@ static void rdd_disconnect(void)
* This disables the SBUx muxes, if we were the only one driving
* CCD_MODE_L.
*/
- gpio_set_flags(GPIO_CCD_MODE_L, GPIO_INPUT);
+ gpio_set_level(GPIO_CCD_MODE_L, 1);
}
DECLARE_DEFERRED(rdd_disconnect);
@@ -99,8 +99,8 @@ static void rdd_connect(void)
CPRINTS("Rdd connect");
state = DEVICE_STATE_CONNECTED;
- /* Start pulling CCD_MODE_L low to enable the SBUx muxes */
- gpio_set_flags(GPIO_CCD_MODE_L, GPIO_OUT_LOW);
+ /* Assert CCD_MODE_L to enable the SBUx muxes */
+ gpio_set_level(GPIO_CCD_MODE_L, 0);
}
DECLARE_DEFERRED(rdd_connect);
diff --git a/chip/g/uart.c b/chip/g/uart.c
index e2cd2193b8..33c992dfc2 100644
--- a/chip/g/uart.c
+++ b/chip/g/uart.c
@@ -13,7 +13,7 @@
#include "uartn.h"
#include "util.h"
-static int done_uart_init_yet;
+static uint8_t done_uart_init_yet;
#define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \
defined(SECTION_IS_RO)))
@@ -34,7 +34,6 @@ void uart_tx_start(void)
void uart_tx_stop(void)
{
uartn_tx_stop(UARTN);
-
}
int uart_tx_in_progress(void)
@@ -74,7 +73,7 @@ int uart_read_char(void)
/**
* Interrupt handlers for UART0
*/
-void uart_ec_tx_interrupt(void)
+static void uart_console_tx_interrupt(void)
{
/* Clear transmit interrupt status */
GR_UART_ISTATECLR(UARTN) = GC_UART_ISTATECLR_TX_MASK;
@@ -82,9 +81,9 @@ void uart_ec_tx_interrupt(void)
/* Fill output FIFO */
uart_process_output();
}
-DECLARE_IRQ(GC_IRQNUM_UART0_TXINT, uart_ec_tx_interrupt, 1);
+DECLARE_IRQ(GC_IRQNUM_UART0_TXINT, uart_console_tx_interrupt, 1);
-void uart_ec_rx_interrupt(void)
+static void uart_console_rx_interrupt(void)
{
/* Clear receive interrupt status */
GR_UART_ISTATECLR(UARTN) = GC_UART_ISTATECLR_RX_MASK;
@@ -92,7 +91,7 @@ void uart_ec_rx_interrupt(void)
/* Read input FIFO until empty */
uart_process_input();
}
-DECLARE_IRQ(GC_IRQNUM_UART0_RXINT, uart_ec_rx_interrupt, 1);
+DECLARE_IRQ(GC_IRQNUM_UART0_RXINT, uart_console_rx_interrupt, 1);
#endif /* USE_UART_INTERRUPTS */
void uart_init(void)
diff --git a/chip/g/upgrade_fw.c b/chip/g/upgrade_fw.c
index 9138e6b498..eba5868c3d 100644
--- a/chip/g/upgrade_fw.c
+++ b/chip/g/upgrade_fw.c
@@ -318,7 +318,7 @@ static int contents_allowed(uint32_t block_offset,
size_t body_size, void *upgrade_data,
uint8_t *error_code)
{
-#ifdef CR50_RELAXED
+#ifndef CR50_RELAXED
#ifdef CONFIG_BOARD_ID_SUPPORT
if (block_offset == valid_sections.rw_base_offset) {
/* This block is a rw header of the new image. */
diff --git a/chip/host/config_chip.h b/chip/host/config_chip.h
index 8714b57891..f2b7e29416 100644
--- a/chip/host/config_chip.h
+++ b/chip/host/config_chip.h
@@ -9,15 +9,21 @@
#define __CROS_EC_CONFIG_CHIP_H
/* Memory mapping */
+#if !defined(TEST_NVMEM) && !defined(TEST_CR50_FUZZ)
#define CONFIG_FLASH_SIZE 0x00020000
+#define CONFIG_FLASH_BANK_SIZE 0x1000
+#else
+#define CONFIG_FLASH_SIZE (512 * 1024)
+#define CONFIG_FLASH_BANK_SIZE 0x800
+#endif
+
extern char __host_flash[CONFIG_FLASH_SIZE];
-#define CONFIG_PROGRAM_MEMORY_BASE ((uintptr_t)__host_flash)
-#define CONFIG_FLASH_BANK_SIZE 0x1000
-#define CONFIG_FLASH_ERASE_SIZE 0x0010 /* erase bank size */
-#define CONFIG_FLASH_WRITE_SIZE 0x0002 /* minimum write size */
-#define CONFIG_FLASH_WRITE_IDEAL_SIZE 0x0080 /* ideal write size */
-#define CONFIG_RAM_BASE 0x0 /* Not supported */
+#define CONFIG_PROGRAM_MEMORY_BASE ((uintptr_t)__host_flash)
+#define CONFIG_FLASH_ERASE_SIZE 0x0010 /* erase bank size */
+#define CONFIG_FLASH_WRITE_SIZE 0x0002 /* minimum write size */
+#define CONFIG_FLASH_WRITE_IDEAL_SIZE 0x0080 /* ideal write size */
+#define CONFIG_RAM_BASE 0x0 /* Not supported */
#define CONFIG_RAM_SIZE 0x0 /* Not supported */
#define CONFIG_FPU
diff --git a/chip/ish/aontaskfw/ish_aon_share.h b/chip/ish/aontaskfw/ish_aon_share.h
index ee630a49bf..c8664165ea 100644
--- a/chip/ish/aontaskfw/ish_aon_share.h
+++ b/chip/ish/aontaskfw/ish_aon_share.h
@@ -6,6 +6,7 @@
#ifndef __CROS_EC_ISH_AON_SHARE_H
#define __CROS_EC_ISH_AON_SHARE_H
+#include "common.h"
#include "ia_structs.h"
/* magic ID for valid aontask image sanity check */
@@ -21,7 +22,6 @@
struct ish_aon_share {
/* magic ID */
uint32_t magic_id;
- /* last error */
/* error counter */
uint32_t error_count;
/* last error */
diff --git a/chip/ish/aontaskfw/ish_aontask.c b/chip/ish/aontaskfw/ish_aontask.c
index 3426622357..5e16ee7d6a 100644
--- a/chip/ish/aontaskfw/ish_aontask.c
+++ b/chip/ish/aontaskfw/ish_aontask.c
@@ -85,8 +85,8 @@ static void pmu_wakeup_isr(void)
* Indicate completion of servicing the interrupt to IOAPIC first
* then indicate completion of servicing the interrupt to LAPIC
*/
- REG32(IOAPIC_EOI_REG) = ISH_PMU_WAKEUP_VEC;
- REG32(LAPIC_EOI_REG) = 0x0;
+ IOAPIC_EOI_REG = ISH_PMU_WAKEUP_VEC;
+ LAPIC_EOI_REG = 0x0;
__asm__ volatile ("iret;");
@@ -105,8 +105,8 @@ static void reset_prep_isr(void)
* Indicate completion of servicing the interrupt to IOAPIC first
* then indicate completion of servicing the interrupt to LAPIC
*/
- REG32(IOAPIC_EOI_REG) = ISH_RESET_PREP_VEC;
- REG32(LAPIC_EOI_REG) = 0x0;
+ IOAPIC_EOI_REG = ISH_RESET_PREP_VEC;
+ LAPIC_EOI_REG = 0x0;
handle_reset(ISH_PM_STATE_RESET_PREP);
@@ -172,7 +172,7 @@ void ish_aon_main(void);
static struct tss_entry aon_tss = {
.prev_task_link = 0,
.reserved1 = 0,
- .esp0 = (uint8_t *)(CONFIG_ISH_AON_SRAM_ROM_START - AON_SP_RESERVED),
+ .esp0 = (uint8_t *)(CONFIG_AON_ROM_BASE - AON_SP_RESERVED),
/* entry 1 in LDT for data segment */
.ss0 = 0xc,
.reserved2 = 0,
@@ -191,8 +191,8 @@ static struct tss_entry aon_tss = {
.edx = 0,
.ebx = 0,
/* set stack top pointer at the end of usable aon memory */
- .esp = CONFIG_ISH_AON_SRAM_ROM_START - AON_SP_RESERVED,
- .ebp = AON_SP_RESERVED,
+ .esp = CONFIG_AON_ROM_BASE - AON_SP_RESERVED,
+ .ebp = CONFIG_AON_ROM_BASE - AON_SP_RESERVED,
.esi = 0,
.edi = 0,
/* entry 1 in LDT for data segment */
@@ -295,20 +295,20 @@ static int store_main_fw(void)
SNOWBALL_FW_OFFSET +
ISH_FW_IMAGE_MANIFEST_HEADER_SIZE;
- imr_fw_rw_addr = imr_fw_addr + aon_share.main_fw_rw_addr -
- CONFIG_ISH_SRAM_BASE_START;
+ imr_fw_rw_addr = (imr_fw_addr
+ + aon_share.main_fw_rw_addr
+ - CONFIG_RAM_BASE);
/* disable BCG (Block Clock Gating) for DMA, DMA can be accessed now */
CCU_BCG_EN = CCU_BCG_EN & ~CCU_BCG_BIT_DMA;
/* store main FW's read and write data region to IMR/UMA DDR */
ret = ish_dma_copy(
- PAGING_CHAN,
- imr_fw_rw_addr,
- aon_share.main_fw_rw_addr,
- aon_share.main_fw_rw_size,
- SRAM_TO_UMA
- );
+ PAGING_CHAN,
+ imr_fw_rw_addr,
+ aon_share.main_fw_rw_addr,
+ aon_share.main_fw_rw_size,
+ SRAM_TO_UMA);
/* enable BCG for DMA, DMA can't be accessed now */
CCU_BCG_EN = CCU_BCG_EN | CCU_BCG_BIT_DMA;
@@ -336,23 +336,24 @@ static int restore_main_fw(void)
SNOWBALL_FW_OFFSET +
ISH_FW_IMAGE_MANIFEST_HEADER_SIZE;
- imr_fw_ro_addr = imr_fw_addr + aon_share.main_fw_ro_addr -
- CONFIG_ISH_SRAM_BASE_START;
+ imr_fw_ro_addr = (imr_fw_addr
+ + aon_share.main_fw_ro_addr
+ - CONFIG_RAM_BASE);
- imr_fw_rw_addr = imr_fw_addr + aon_share.main_fw_rw_addr -
- CONFIG_ISH_SRAM_BASE_START;
+ imr_fw_rw_addr = (imr_fw_addr
+ + aon_share.main_fw_rw_addr
+ - CONFIG_RAM_BASE);
/* disable BCG (Block Clock Gating) for DMA, DMA can be accessed now */
CCU_BCG_EN = CCU_BCG_EN & ~CCU_BCG_BIT_DMA;
/* restore main FW's read only code and data region from IMR/UMA DDR */
ret = ish_dma_copy(
- PAGING_CHAN,
- aon_share.main_fw_ro_addr,
- imr_fw_ro_addr,
- aon_share.main_fw_ro_size,
- UMA_TO_SRAM
- );
+ PAGING_CHAN,
+ aon_share.main_fw_ro_addr,
+ imr_fw_ro_addr,
+ aon_share.main_fw_ro_size,
+ UMA_TO_SRAM);
if (ret != DMA_RC_OK) {
@@ -388,12 +389,14 @@ static int restore_main_fw(void)
return AON_SUCCESS;
}
-#ifdef CHIP_FAMILY_ISH3
-/* on ISH3, need reserve last SRAM bank for AON use */
-#define SRAM_POWER_OFF_BANKS (CONFIG_ISH_SRAM_BANKS - 1)
+#if defined(CHIP_FAMILY_ISH3)
+/* on ISH3, the last SRAM bank is reserved for AON use */
+#define SRAM_POWER_OFF_BANKS (CONFIG_RAM_BANKS - 1)
+#elif defined(CHIP_FAMILY_ISH4) || defined(CHIP_FAMILY_ISH5)
+/* ISH4 and ISH5 have separate AON memory, can power off entire main SRAM */
+#define SRAM_POWER_OFF_BANKS CONFIG_RAM_BANKS
#else
-/* from ISH4, has seprated AON memory, can power off entire main SRAM */
-#define SRAM_POWER_OFF_BANKS CONFIG_ISH_SRAM_BANKS
+#error "CHIP_FAMILY_ISH(3|4|5) must be defined"
#endif
/**
@@ -435,8 +438,8 @@ static void sram_power(int on)
uint32_t sram_addr;
uint32_t erase_cfg;
- bank_size = CONFIG_ISH_SRAM_BANK_SIZE;
- sram_addr = CONFIG_ISH_SRAM_BASE_START;
+ bank_size = CONFIG_RAM_BANK_SIZE;
+ sram_addr = CONFIG_RAM_BASE;
/**
* set erase size as one bank, erase control register using DWORD as
@@ -483,7 +486,8 @@ static void sram_power(int on)
static void handle_d0i2(void)
{
/* set main SRAM into retention mode*/
- PMU_LDO_CTRL = PMU_LDO_BIT_RETENTION_ON | PMU_LDO_BIT_ON;
+ PMU_LDO_CTRL = PMU_LDO_ENABLE_BIT
+ | PMU_LDO_RETENTION_BIT;
/* delay some cycles before halt */
delay(SRAM_RETENTION_CYCLES_DELAY);
@@ -492,13 +496,13 @@ static void handle_d0i2(void)
/* wakeup from PMU interrupt */
/* set main SRAM intto normal mode */
- PMU_LDO_CTRL = PMU_LDO_BIT_ON;
+ PMU_LDO_CTRL = PMU_LDO_ENABLE_BIT;
/**
* poll LDO_READY status to make sure SRAM LDO is on
* (exited retention mode)
*/
- while (!(PMU_LDO_CTRL & PMU_LDO_BIT_READY))
+ while (!(PMU_LDO_CTRL & PMU_LDO_READY_BIT))
continue;
}
@@ -539,31 +543,65 @@ static void handle_d3(void)
static void handle_reset(int pm_state)
{
+ /* disable watch dog */
+ WDT_CONTROL &= ~WDT_CONTROL_ENABLE_BIT;
+
+ /* disable all gpio interrupts */
+ ISH_GPIO_GRER = 0;
+ ISH_GPIO_GFER = 0;
+ ISH_GPIO_GIMR = 0;
+
/* disable CSME CSR irq */
- REG32(IPC_PIMR) &= ~IPC_PIMR_CSME_CSR_BIT;
+ IPC_PIMR &= ~IPC_PIMR_CSME_CSR_BIT;
/* power off main SRAM */
sram_power(0);
while (1) {
-
- /* check if host ish driver already set the DMA enable flag */
- if (REG32(IPC_ISH_RMP2) & DMA_ENABLED_MASK) {
+ /**
+ * check if host ish driver already set the DMA enable flag
+ *
+ * ISH FW and ISH ipc host driver using IPC_ISH_RMP2 register
+ * for synchronization during ISH boot.
+ * ISH ipc host driver will set DMA_ENABLED_MASK bit when it
+ * is loaded and starts, and clear this bit when it is removed.
+ *
+ * see: https://github.com/torvalds/linux/blob/master/drivers/
+ * hid/intel-ish-hid/ipc/ipc.c
+ *
+ * we have two kinds of reset situations need to handle here:
+ * 1: reset ISH via uart console cmd or ectool host cmd
+ * 2: S0 -> Sx (reset_prep interrupt)
+ *
+ * for #1, ISH ipc host driver no changed states,
+ * DMA_ENABLED_MASK bit always set, so, will reset ISH directly
+ *
+ * for #2, ISH ipc host driver changed states, and cleared
+ * DMA_ENABLED_MASK bit, then ISH FW received reset_prep
+ * interrupt, ISH will stay in this while loop (most time in
+ * halt state), waiting for DMA_ENABLED_MASK bit was set and
+ * reset ISH then. Since ISH ROM have no power managment, stay
+ * in aontask can save more power especially if system stay in
+ * Sx for long time.
+ *
+ */
+ if (IPC_ISH_RMP2 & DMA_ENABLED_MASK) {
/* clear ISH2HOST doorbell register */
- REG32(IPC_ISH2HOST_DOORBELL) = 0;
+ *IPC_ISH2HOST_DOORBELL_ADDR = 0;
/* clear error register in MISC space */
MISC_ISH_ECC_ERR_SRESP = 1;
/* reset ISH minute-ia cpu core, will goto ISH ROM */
ish_mia_reset();
+
+ __builtin_unreachable();
}
ish_mia_halt();
}
- __builtin_unreachable();
}
static void handle_unknown_state(void)
diff --git a/chip/ish/aontaskfw/ish_aontask.ld.in b/chip/ish/aontaskfw/ish_aontask.ld.in
index 3525519d5b..a839146a92 100644
--- a/chip/ish/aontaskfw/ish_aontask.ld.in
+++ b/chip/ish/aontaskfw/ish_aontask.ld.in
@@ -7,8 +7,8 @@
ENTRY(ish_aon_main);
-#define SRAM_START CONFIG_ISH_AON_SRAM_BASE_START
-#define SRAM_RW_LEN (CONFIG_ISH_AON_SRAM_SIZE - CONFIG_ISH_AON_SRAM_ROM_SIZE)
+#define SRAM_START CONFIG_AON_RAM_BASE
+#define SRAM_RW_LEN (CONFIG_AON_RAM_SIZE - CONFIG_AON_ROM_SIZE)
/* reserved stack size */
#define STACK_SIZE (256)
@@ -23,9 +23,13 @@ ENTRY(ish_aon_main);
/**
* AON memory layout
- * --------------+--------------+-------------------+-------------------+
- * | RAM_LEN | STACK_SIZE | 8 Bytes for GDB | ROM(128 Bytes) |
- * --------------+--------------+-------------------+-------------------+
+ * +---------+------------+-----------------+-----------------+
+ * | RAM_LEN | STACK_SIZE | 8 Bytes for GDB | ROM (384 Bytes) |
+ * +---------+------------+-----------------+-----------------+
+ *
+ * The first 256 bytes of the AON ROM is reserved for ECOS use.
+ * The remaining 128 bytes of the AON ROM may be used by the shim
+ * loader.
*/
MEMORY
@@ -37,7 +41,7 @@ MEMORY
SECTIONS
{
/* AON parts visible to FW are linked to the beginning of the AON area */
- .data.aon_share : AT(CONFIG_ISH_AON_SRAM_BASE_START)
+ .data.aon_share : AT(SRAM_START)
{
KEEP(*(.data.aon_share))
} > RAM
diff --git a/chip/ish/build.mk b/chip/ish/build.mk
index b1481dfaf6..7ef31159eb 100644
--- a/chip/ish/build.mk
+++ b/chip/ish/build.mk
@@ -17,7 +17,7 @@ include core/$(CORE)/build.mk
endif
# Required chip modules
-chip-y+=clock.o gpio.o system.o hwtimer.o uart.o flash.o
+chip-y+=clock.o gpio.o system.o hwtimer.o uart.o flash.o ish_persistent_data.o
chip-$(CONFIG_I2C)+=i2c.o
chip-$(CONFIG_WATCHDOG)+=watchdog.o
chip-$(CONFIG_HOSTCMD_HECI)+=host_command_heci.o
diff --git a/chip/ish/clock.c b/chip/ish/clock.c
index 0be9e3c231..7e0879cc0a 100644
--- a/chip/ish/clock.c
+++ b/chip/ish/clock.c
@@ -8,6 +8,7 @@
#include "clock.h"
#include "common.h"
#include "util.h"
+#include "power_mgt.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_CLOCK, outstr)
@@ -16,7 +17,7 @@
void clock_init(void)
{
- /* No initialization for ISH clock since D0ix is not enabled yet */
+ /* No initialization for clock on ISH */
}
#ifdef CONFIG_LOW_POWER_IDLE
@@ -24,10 +25,12 @@ void clock_init(void)
void clock_refresh_console_in_use(void)
{
/**
- * TODO nothing need to do at current, on ISH, uart interrupt can
- * wakeup ISH from low power state, will understand this function more
- * to see if need anything to handle in ISH
+ * on ISH, uart interrupt can only wakeup ISH from low power state via
+ * CTS pin, but most ISH platforms only have Rx and Tx pins, no CTS pin
+ * exposed, so, we need block ISH enter low power state for a while
+ * when console is in use
*/
+ ish_pm_refresh_console_in_use();
}
#endif
diff --git a/chip/ish/config_chip.h b/chip/ish/config_chip.h
index 74432baca1..3f3f3ee275 100644
--- a/chip/ish/config_chip.h
+++ b/chip/ish/config_chip.h
@@ -9,6 +9,11 @@
/* CPU core BFD configuration */
#include "core/minute-ia/config_core.h"
+#ifndef __ASSEMBLER__
+/* Needed for PANIC_DATA_BASE */
+#include "ish_persistent_data.h"
+#endif
+
/* Number of IRQ vectors on the ISH */
#define CONFIG_IRQ_COUNT (VEC_TO_IRQ(255) + 1)
@@ -26,45 +31,36 @@
/* this macro causes 'pause' and reduces loop counts inside loop. */
#define CPU_RELAX() asm volatile("rep; nop" ::: "memory")
-/****************************************************************************/
-/* Memory mapping */
-/****************************************************************************/
+/*****************************************************************************/
+/* Memory Layout */
+/*****************************************************************************/
-/* Define our SRAM layout. */
-#define CONFIG_ISH_SRAM_BASE_START 0xFF000000
-#define CONFIG_ISH_SRAM_BASE_END 0xFF0A0000
-#define CONFIG_ISH_SRAM_SIZE (CONFIG_ISH_SRAM_BASE_END - \
- CONFIG_ISH_SRAM_BASE_START)
+#define CONFIG_RAM_BASE 0xFF000000
+#define CONFIG_RAM_SIZE 0x000A0000
+#define CONFIG_RAM_BANK_SIZE 0x00008000
#if defined(CHIP_FAMILY_ISH3)
-/* on ISH3, there is no seprated aon memory, using last 4KB of normal memory
- * without poweroff
- */
-#define CONFIG_ISH_AON_SRAM_BASE_START 0xFF09F000
-#define CONFIG_ISH_AON_SRAM_BASE_END 0xFF0A0000
+/* On ISH3, there is no separate AON memory; use last 4KB of SRAM */
+#define CONFIG_AON_RAM_BASE 0xFF09F000
+#define CONFIG_AON_RAM_SIZE 0x00001000
#elif defined(CHIP_FAMILY_ISH4)
-#define CONFIG_ISH_AON_SRAM_BASE_START 0xFF800000
-#define CONFIG_ISH_AON_SRAM_BASE_END 0xFF801000
+#define CONFIG_AON_RAM_BASE 0xFF800000
+#define CONFIG_AON_RAM_SIZE 0x00001000
+#elif defined(CHIP_FAMILY_ISH5)
+#define CONFIG_AON_RAM_BASE 0xFF800000
+#define CONFIG_AON_RAM_SIZE 0x00002000
#else
-#define CONFIG_ISH_AON_SRAM_BASE_START 0xFF800000
-#define CONFIG_ISH_AON_SRAM_BASE_END 0xFF802000
+#error "CHIP_FAMILY_ISH(3|4|5) must be defined"
#endif
-#define CONFIG_ISH_AON_SRAM_SIZE (CONFIG_ISH_AON_SRAM_BASE_END - \
- CONFIG_ISH_AON_SRAM_BASE_START)
-
-/* reserve for readonly use in the last of AON memory */
-#define CONFIG_ISH_AON_SRAM_ROM_SIZE 0x80
-#define CONFIG_ISH_AON_SRAM_ROM_START (CONFIG_ISH_AON_SRAM_BASE_END - \
- CONFIG_ISH_AON_SRAM_ROM_SIZE)
-
-#define CONFIG_ISH_SRAM_BANK_SIZE 0x8000
-#define CONFIG_ISH_SRAM_BANKS (CONFIG_ISH_SRAM_SIZE / \
- CONFIG_ISH_SRAM_BANK_SIZE)
+/* The end of the AON memory is reserved for read-only use */
+#define CONFIG_AON_ROM_SIZE 0x180
+#define CONFIG_AON_ROM_BASE (CONFIG_AON_RAM_BASE \
+ + CONFIG_AON_RAM_SIZE \
+ - CONFIG_AON_ROM_SIZE)
-/* Required for panic_output */
-#define CONFIG_RAM_SIZE CONFIG_ISH_SRAM_SIZE
-#define CONFIG_RAM_BASE CONFIG_ISH_SRAM_BASE_START
+/* Store persistent panic data in AON memory. */
+#define CONFIG_PANIC_DATA_BASE (&(ish_persistent_data.panic_data))
/* System stack size */
#define CONFIG_STACK_SIZE 1024
diff --git a/chip/ish/dma.c b/chip/ish/dma.c
index db4a9a2fc6..a409dc6ad3 100644
--- a/chip/ish/dma.c
+++ b/chip/ish/dma.c
@@ -110,7 +110,7 @@ int ish_dma_copy(uint32_t chan, uint32_t dst, uint32_t src, uint32_t length,
mode |= NON_SNOOP;
MISC_DMA_CTL_REG(chan) = mode; /* Set transfer direction */
- DMA_CFG_REG = DMA_EN_MASK; /* Enable DMA module */
+ DMA_CFG_REG = DMA_ENABLE; /* Enable DMA module */
DMA_LLP(chan_reg) = 0; /* Linked lists are not used */
DMA_CTL_LOW(chan_reg) =
0 /* Set transfer parameters */ |
@@ -183,36 +183,6 @@ int ish_wait_for_dma_done(uint32_t ch)
return dma_poll(DMA_EN_REG_ADDR, 0, DMA_CH_EN_BIT(ch));
}
-static int ish_dma_page_internal(uint32_t dst, uint32_t src, enum dma_mode mode)
-{
- int rc;
- uint32_t eflags = interrupt_lock();
-
- if (!dma_init_called)
- ish_dma_init();
-
- /* Wait for DMA to be free */
- rc = dma_poll(DMA_EN_REG_ADDR, 0,
- DMA_CH_EN_BIT(PAGING_CHAN) | DMA_CH_EN_BIT(KERNEL_CHAN));
-
- if (rc == DMA_RC_OK)
- rc = ish_dma_copy(PAGING_CHAN, dst, src, PAGE_SIZE, mode);
-
- interrupt_unlock(eflags);
- return rc;
-}
-
-/* DMA page between DRAM and SRAM. */
-int ish_dma_page(uint32_t dst, uint32_t src, int page_in)
-{
- int ret = 0;
-
- ret = ish_dma_page_internal(dst, src,
- (page_in ? UMA_TO_SRAM : SRAM_TO_UMA));
-
- return ret;
-}
-
void ish_dma_set_msb(uint32_t chan, uint32_t dst_msb, uint32_t src_msb)
{
uint32_t eflags = interrupt_lock();
diff --git a/chip/ish/gpio.c b/chip/ish/gpio.c
index 7d0a34f18f..18890c111d 100644
--- a/chip/ish/gpio.c
+++ b/chip/ish/gpio.c
@@ -139,6 +139,11 @@ void gpio_pre_init(void)
gpio_set_flags_by_mask(g->port, g->mask, flags);
}
+
+ /* disable GPIO interrupts */
+ ISH_GPIO_GIMR = 0;
+ /* clear pending GPIO interrupts */
+ ISH_GPIO_GISR = 0xFFFFFFFF;
}
static void gpio_init(void)
diff --git a/chip/ish/heci.c b/chip/ish/heci.c
index b78e522909..9d16b6a74e 100644
--- a/chip/ish/heci.c
+++ b/chip/ish/heci.c
@@ -220,7 +220,8 @@ static void heci_build_fixed_client_header(struct heci_header *hdr,
hdr->length |= (uint16_t)1 << HECI_MSG_CMPL_SHIFT;
}
-static int heci_send_heci_msg(struct heci_msg *msg)
+static int heci_send_heci_msg_timestamp(struct heci_msg *msg,
+ uint32_t *timestamp)
{
int length, written;
@@ -228,7 +229,8 @@ static int heci_send_heci_msg(struct heci_msg *msg)
return -1;
length = sizeof(msg->hdr) + HECI_MSG_LENGTH(msg->hdr.length);
- written = ipc_write(heci_bus_ctx.ipc_handle, msg, length);
+ written = ipc_write_timestamp(heci_bus_ctx.ipc_handle, msg, length,
+ timestamp);
if (written != length) {
CPRINTF("%s error : len = %d err = %d\n", __func__,
@@ -239,6 +241,11 @@ static int heci_send_heci_msg(struct heci_msg *msg)
return EC_SUCCESS;
}
+static int heci_send_heci_msg(struct heci_msg *msg)
+{
+ return heci_send_heci_msg_timestamp(msg, NULL);
+}
+
int heci_set_client_data(const heci_handle_t handle, void *data)
{
struct heci_client_context *cli_ctx;
@@ -302,8 +309,8 @@ static int wait_for_flow_ctrl_cred(struct heci_client_connect *connect)
return 1;
}
-int heci_send_msg(const heci_handle_t handle, uint8_t *buf,
- const size_t buf_size)
+int heci_send_msg_timestamp(const heci_handle_t handle, uint8_t *buf,
+ const size_t buf_size, uint32_t *timestamp)
{
int buf_offset = 0, ret = 0, remain, payload_size;
struct heci_client_connect *connect;
@@ -347,7 +354,7 @@ int heci_send_msg(const heci_handle_t handle, uint8_t *buf,
memcpy(msg.payload, buf + buf_offset, payload_size);
- heci_send_heci_msg(&msg);
+ heci_send_heci_msg_timestamp(&msg, timestamp);
remain -= payload_size;
buf_offset += payload_size;
@@ -362,6 +369,13 @@ err_locked:
return ret;
}
+int heci_send_msg(const heci_handle_t handle, uint8_t *buf,
+ const size_t buf_size)
+{
+ return heci_send_msg_timestamp(handle, buf, buf_size, NULL);
+}
+
+
int heci_send_msgs(const heci_handle_t handle,
const struct heci_msg_list *msg_list)
{
diff --git a/chip/ish/heci_client.h b/chip/ish/heci_client.h
index aab8a928fc..9dca4bff90 100644
--- a/chip/ish/heci_client.h
+++ b/chip/ish/heci_client.h
@@ -90,6 +90,8 @@ void *heci_get_client_data(const heci_handle_t handle);
*/
int heci_send_msg(const heci_handle_t handle, uint8_t *buf,
const size_t buf_size);
+int heci_send_msg_timestamp(const heci_handle_t handle, uint8_t *buf,
+ const size_t buf_size, uint32_t *timestamp);
/*
* send client msgs(using list of buffer&size).
* heci_msg_item with size == 0 is not acceptable.
diff --git a/chip/ish/host_command_heci.c b/chip/ish/host_command_heci.c
index 3676451cc9..69736eeae7 100644
--- a/chip/ish/host_command_heci.c
+++ b/chip/ish/host_command_heci.c
@@ -9,6 +9,7 @@
#include "host_command.h"
#include "host_command_heci.h"
#include "ipc_heci.h"
+#include "ish_fwst.h"
#include "util.h"
#define CPUTS(outstr) cputs(CC_LPC, outstr)
@@ -53,14 +54,21 @@ enum heci_cros_ec_channel {
static uint8_t response_buffer[IPC_MAX_PAYLOAD_SIZE] __aligned(4);
static struct host_packet heci_packet;
-void heci_send_mkbp_event(void)
+int heci_send_mkbp_event(uint32_t *timestamp)
{
struct cros_ec_ishtp_msg evt;
+ int rv;
evt.hdr.channel = CROS_MKBP_EVENT;
evt.hdr.status = 0;
- heci_send_msg(heci_cros_ec_handle, (uint8_t *)&evt, sizeof(evt));
+ rv = heci_send_msg_timestamp(heci_cros_ec_handle, (uint8_t *)&evt,
+ sizeof(evt), timestamp);
+ /*
+ * heci_send_msg_timestamp sends back negative error codes. Change to
+ * EC style codes
+ */
+ return rv < 0 ? -rv : EC_SUCCESS;
}
static void heci_send_hostcmd_response(struct host_packet *pkt)
@@ -137,6 +145,7 @@ EC_VER_MASK(0));
static int cros_ec_ishtp_subsys_initialize(const heci_handle_t heci_handle)
{
heci_cros_ec_handle = heci_handle;
+ ish_fwst_set_fw_status(FWSTS_SENSOR_APP_RUNNING);
return EC_SUCCESS;
}
diff --git a/chip/ish/hpet.h b/chip/ish/hpet.h
index 3823a6e858..7a2a420c82 100644
--- a/chip/ish/hpet.h
+++ b/chip/ish/hpet.h
@@ -47,11 +47,13 @@
* Use this register to see HPET timer are settled after a write.
*/
#define HPET_CTRL_STATUS REG32(ISH_HPET_BASE + 0x160)
-#define HPET_T1_CMP_SETTLING BIT(9)
-#define HPET_T0_CMP_SETTLING (BIT(7) | BIT(8))
-#define HPET_T1_CAP_SETTLING BIT(5)
-#define HPET_T0_CAP_SETTLING BIT(4)
+#define HPET_INT_STATUS_SETTLING BIT(1)
#define HPET_MAIN_COUNTER_SETTLING (BIT(2) | BIT(3))
+#define HPET_T0_CAP_SETTLING BIT(4)
+#define HPET_T1_CAP_SETTLING BIT(5)
+#define HPET_T0_CMP_SETTLING (BIT(7) | BIT(8))
+#define HPET_T1_CMP_SETTLING BIT(9)
+#define HPET_MAIN_COUNTER_VALID BIT(13)
#define HPET_T1_SETTLING (HPET_T1_CAP_SETTLING | \
HPET_T1_CMP_SETTLING)
#define HPET_T0_SETTLING (HPET_T0_CAP_SETTLING | \
diff --git a/chip/ish/hwtimer.c b/chip/ish/hwtimer.c
index 393e1af4af..d3d78cadd6 100644
--- a/chip/ish/hwtimer.c
+++ b/chip/ish/hwtimer.c
@@ -146,6 +146,9 @@ static inline uint64_t read_main_timer(void)
timestamp_t t;
uint32_t hi;
+ /* need check main counter if valid when exit low power TCG mode */
+ wait_while_settling(HPET_MAIN_COUNTER_VALID);
+
do {
t.le.hi = HPET_MAIN_COUNTER_64_HI;
t.le.lo = HPET_MAIN_COUNTER_64_LO;
@@ -172,6 +175,7 @@ void __hw_clock_event_set(uint32_t deadline)
* of 12Mhz timer comparator value. Watchdog refresh happens at least
* every 10 seconds.
*/
+ wait_while_settling(HPET_T1_CMP_SETTLING);
HPET_TIMER_COMP(1) = read_main_timer() + scale_us2ticks(remaining_us);
wait_while_settling(HPET_T1_SETTLING);
@@ -187,6 +191,12 @@ uint32_t __hw_clock_event_get(void)
void __hw_clock_event_clear(void)
{
+ /*
+ * we get timer event at every new clksrc_high.
+ * so when there's no event, last_dealine should be
+ * the last value within current clksrc_high.
+ */
+ last_deadline = 0xFFFFFFFF;
wait_while_settling(HPET_T1_SETTLING);
HPET_TIMER_CONF_CAP(1) &= ~HPET_Tn_INT_ENB_CNF;
}
@@ -212,6 +222,7 @@ void __hw_clock_source_set(uint32_t ts)
static void __hw_clock_source_irq(int timer_id)
{
/* Clear interrupt */
+ wait_while_settling(HPET_INT_STATUS_SETTLING);
HPET_INTR_CLEAR = BIT(timer_id);
/*
@@ -276,6 +287,9 @@ int __hw_clock_source_init(uint32_t start_t)
timer0_config |= HPET_Tn_INT_TYPE_CNF;
timer1_config |= HPET_Tn_INT_TYPE_CNF;
+ /* no event until next timer 0 IRQ for clksrc_high++ */
+ last_deadline = 0xFFFFFFFF;
+
/* Enable interrupt */
timer0_config |= HPET_Tn_INT_ENB_CNF;
diff --git a/chip/ish/i2c.c b/chip/ish/i2c.c
index 284a46a1cc..cecb81f40f 100644
--- a/chip/ish/i2c.c
+++ b/chip/ish/i2c.c
@@ -416,6 +416,7 @@ int chip_i2c_xfer(int port, int slave_addr, const uint8_t *out, int out_size,
}
}
+ i2c_intr_switch(ctx->base, DISABLE_INT);
i2c_mmio_write(ctx->base, IC_ENABLE, IC_ENABLE_DISABLE);
if (ctx->error_flag)
diff --git a/chip/ish/ipc_heci.c b/chip/ish/ipc_heci.c
index 85182b3617..98b9f7699d 100644
--- a/chip/ish/ipc_heci.c
+++ b/chip/ish/ipc_heci.c
@@ -31,6 +31,7 @@
#include "ish_fwst.h"
#include "queue.h"
#include "hooks.h"
+#include "hwtimer.h"
#define CPUTS(outstr) cputs(CC_LPC, outstr)
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
@@ -51,27 +52,6 @@
#define MNG_SYNC_FW_CLOCK 5
#define MNG_ILLEGAL_CMD 0xFF
-/* Peripheral Interrupt Satus Register */
-#define IPC_PISR_HOST2ISH_BIT BIT(0)
-#define IPC_PISR_PMC2ISH_BIT BIT(1)
-#define IPC_PISR_CSME2ISH_BIT BIT(2)
-
-/* Peripheral Interrupt Mask Register */
-#define IPC_PIMR_HOST2ISH_BIT BIT(0)
-#define IPC_PIMR_PMC2ISH_BIT BIT(1)
-#define IPC_PIMR_CSME2ISH_BIT BIT(2)
-
-#define IPC_PIMR_ISH2HOST_CLR_BIT BIT(11)
-#define IPC_PIMR_ISH2PMC_CLR_BIT BIT(12)
-#define IPC_PIMR_ISH2CSME_CLR_BIT BIT(13)
-
-/* Peripheral Interrupt DB(DoorBell) Clear Status Register */
-#define IPC_DB_CLR_STS_ISH2HOST_BIT BIT(0)
-#define IPC_DB_CLR_STS_ISH2ISP_BIT BIT(2)
-#define IPC_DB_CLR_STS_ISH2AUDIO_BIT BIT(3)
-#define IPC_DB_CLR_STS_ISH2PMC_BIT BIT(8)
-#define IPC_DB_CLR_STS_ISH2CSME_BIT BIT(16)
-
/* Doorbell */
#define IPC_DB_MSG_LENGTH_FIELD 0x3FF
#define IPC_DB_MSG_LENGTH_SHIFT 0
@@ -130,6 +110,7 @@
struct ipc_msg {
uint32_t drbl;
+ uint32_t *timestamp_of_outgoing_doorbell;
uint8_t payload[IPC_MSG_MAX_SIZE];
} __packed;
@@ -154,10 +135,10 @@ struct ipc_msg_event {
* This is per-IPC context.
*/
struct ipc_if_ctx {
- uint32_t in_msg_reg;
- uint32_t out_msg_reg;
- uint32_t in_drbl_reg;
- uint32_t out_drbl_reg;
+ volatile uint8_t *in_msg_reg;
+ volatile uint8_t *out_msg_reg;
+ volatile uint32_t *in_drbl_reg;
+ volatile uint32_t *out_drbl_reg;
uint32_t clr_busy_bit;
uint32_t pimr_2ish_bit;
uint32_t pimr_2host_clearing_bit;
@@ -176,10 +157,10 @@ struct ipc_if_ctx {
/* list of peer contexts */
static struct ipc_if_ctx ipc_peer_ctxs[IPC_PEERS_COUNT] = {
[IPC_PEER_ID_HOST] = {
- .in_msg_reg = IPC_HOST2ISH_MSG_REGS,
- .out_msg_reg = IPC_ISH2HOST_MSG_REGS,
- .in_drbl_reg = IPC_HOST2ISH_DOORBELL,
- .out_drbl_reg = IPC_ISH2HOST_DOORBELL,
+ .in_msg_reg = IPC_HOST2ISH_MSG_BASE,
+ .out_msg_reg = IPC_ISH2HOST_MSG_BASE,
+ .in_drbl_reg = IPC_HOST2ISH_DOORBELL_ADDR,
+ .out_drbl_reg = IPC_ISH2HOST_DOORBELL_ADDR,
.clr_busy_bit = IPC_DB_CLR_STS_ISH2HOST_BIT,
.pimr_2ish_bit = IPC_PIMR_HOST2ISH_BIT,
.pimr_2host_clearing_bit = IPC_PIMR_ISH2HOST_CLR_BIT,
@@ -202,24 +183,24 @@ static inline struct ipc_if_ctx *ipc_handle_to_if_ctx(const ipc_handle_t handle)
static inline void ipc_enable_pimr_db_interrupt(const struct ipc_if_ctx *ctx)
{
- REG32(IPC_PIMR) |= ctx->pimr_2ish_bit;
+ IPC_PIMR |= ctx->pimr_2ish_bit;
}
static inline void ipc_disable_pimr_db_interrupt(const struct ipc_if_ctx *ctx)
{
- REG32(IPC_PIMR) &= ~ctx->pimr_2ish_bit;
+ IPC_PIMR &= ~ctx->pimr_2ish_bit;
}
static inline void ipc_enable_pimr_clearing_interrupt(
const struct ipc_if_ctx *ctx)
{
- REG32(IPC_PIMR) |= ctx->pimr_2host_clearing_bit;
+ IPC_PIMR |= ctx->pimr_2host_clearing_bit;
}
static inline void ipc_disable_pimr_clearing_interrupt(
const struct ipc_if_ctx *ctx)
{
- REG32(IPC_PIMR) &= ~ctx->pimr_2host_clearing_bit;
+ IPC_PIMR &= ~ctx->pimr_2host_clearing_bit;
}
static void write_payload_and_ring_drbl(const struct ipc_if_ctx *ctx,
@@ -227,30 +208,13 @@ static void write_payload_and_ring_drbl(const struct ipc_if_ctx *ctx,
const uint8_t *payload,
size_t payload_size)
{
- uint32_t msg_idx = 0;
-
- /* write in 32-bits unit */
- while (payload_size >= sizeof(uint32_t)) {
- REG32(ctx->out_msg_reg + msg_idx) =
- *(uint32_t *)(payload + msg_idx);
- msg_idx += sizeof(uint32_t);
- payload_size -= sizeof(uint32_t);
- }
-
- /* write leftovers in 8-bits unit */
- while (payload_size) {
- REG8(ctx->out_msg_reg + msg_idx) =
- *(uint8_t *)(payload + msg_idx);
- msg_idx++;
- payload_size--;
- }
-
- REG32(ctx->out_drbl_reg) = drbl;
+ memcpy((void *)(ctx->out_msg_reg), payload, payload_size);
+ *(ctx->out_drbl_reg) = drbl;
}
-
-static int ipc_write_raw(struct ipc_if_ctx *ctx, uint32_t drbl,
- const uint8_t *payload, size_t payload_size)
+static int ipc_write_raw_timestamp(struct ipc_if_ctx *ctx, uint32_t drbl,
+ const uint8_t *payload, size_t payload_size,
+ uint32_t *timestamp)
{
struct queue *q = &ctx->tx_queue;
struct ipc_msg *msg;
@@ -266,6 +230,7 @@ static int ipc_write_raw(struct ipc_if_ctx *ctx, uint32_t drbl,
tail = q->state->tail & (q->buffer_units - 1);
msg = (struct ipc_msg *)q->buffer + tail;
msg->drbl = drbl;
+ msg->timestamp_of_outgoing_doorbell = timestamp;
memcpy(msg->payload, payload, payload_size);
queue_advance_tail(q, 1);
} else {
@@ -281,11 +246,21 @@ static int ipc_write_raw(struct ipc_if_ctx *ctx, uint32_t drbl,
write_payload_and_ring_drbl(ctx, drbl, payload, payload_size);
+ /* We wrote inline, take timestamp now */
+ if (timestamp)
+ *timestamp = __hw_clock_source_read();
+
write_unlock:
mutex_unlock(&ctx->write_lock);
return res;
}
+static int ipc_write_raw(struct ipc_if_ctx *ctx, uint32_t drbl,
+ const uint8_t *payload, size_t payload_size)
+{
+ return ipc_write_raw_timestamp(ctx, drbl, payload, payload_size, NULL);
+}
+
static int ipc_send_reset_notify(const ipc_handle_t handle)
{
struct ipc_rst_payload *ipc_rst;
@@ -322,7 +297,7 @@ static int ipc_get_protocol_data(const struct ipc_if_ctx *ctx,
struct ipc_msg *msg;
uint32_t drbl_val;
- drbl_val = REG32(ctx->in_drbl_reg);
+ drbl_val = *(ctx->in_drbl_reg);
payload_size = IPC_DB_MSG_LENGTH(drbl_val);
if (payload_size > IPC_MAX_PAYLOAD_SIZE) {
@@ -360,7 +335,6 @@ static int ipc_get_protocol_data(const struct ipc_if_ctx *ctx,
break;
case IPC_PROTOCOL_MNG:
src = (uint8_t *)ctx->in_msg_reg;
-
msg = (struct ipc_msg *)buf;
msg->drbl = drbl_val;
dest = msg->payload;
@@ -389,7 +363,7 @@ static void handle_msg_recv_interrupt(const uint32_t peer_id)
ctx = ipc_get_if_ctx(peer_id);
ipc_disable_pimr_db_interrupt(ctx);
- drbl_val = REG32(ctx->in_drbl_reg);
+ drbl_val = *(ctx->in_drbl_reg);
protocol = IPC_DB_PROTOCOL(drbl_val);
payload_size = IPC_DB_MSG_LENGTH(drbl_val);
@@ -404,9 +378,9 @@ static void handle_msg_recv_interrupt(const uint32_t peer_id)
task_set_event(ctx->msg_events[protocol].task_id,
ctx->msg_events[protocol].event, 0);
} else {
- CPRINTS("discard msg : %d\n", invalid_msg);
+ CPRINTS("discard msg (%d) : %d", protocol, invalid_msg);
- REG32(ctx->in_drbl_reg) = 0;
+ *(ctx->in_drbl_reg) = 0;
set_pimr_and_send_rx_complete(ctx);
}
}
@@ -424,7 +398,7 @@ static void handle_busy_clear_interrupt(const uint32_t peer_id)
* Resetting interrupt status bit should be done
* before sending an item in tx_queue.
*/
- REG32(IPC_BUSY_CLEAR) = ctx->clr_busy_bit;
+ IPC_BUSY_CLEAR = ctx->clr_busy_bit;
/*
* No need to use sync mechanism here since the accesing the queue
@@ -437,6 +411,10 @@ static void handle_busy_clear_interrupt(const uint32_t peer_id)
msg = (struct ipc_msg *)(q->buffer + head * q->unit_bytes);
write_payload_and_ring_drbl(ctx, msg->drbl, msg->payload,
IPC_DB_MSG_LENGTH(msg->drbl));
+ if (msg->timestamp_of_outgoing_doorbell)
+ *msg->timestamp_of_outgoing_doorbell =
+ __hw_clock_source_read();
+
queue_advance_head(q, 1);
} else {
ctx->is_tx_ipc_busy = 0;
@@ -465,8 +443,8 @@ static void handle_busy_clear_interrupt(const uint32_t peer_id)
*/
static void ipc_host2ish_isr(void)
{
- uint32_t pisr = REG32(IPC_PISR);
- uint32_t pimr = REG32(IPC_PIMR);
+ uint32_t pisr = IPC_PISR;
+ uint32_t pimr = IPC_PIMR;
#ifdef CHIP_FAMILY_ISH5
/*
@@ -489,8 +467,8 @@ DECLARE_IRQ(ISH_IPC_HOST2ISH_IRQ, ipc_host2ish_isr);
static void ipc_host2ish_busy_clear_isr(void)
{
- uint32_t busy_clear = REG32(IPC_BUSY_CLEAR);
- uint32_t pimr = REG32(IPC_PIMR);
+ uint32_t busy_clear = IPC_BUSY_CLEAR;
+ uint32_t pimr = IPC_PIMR;
if ((busy_clear & IPC_DB_CLR_STS_ISH2HOST_BIT) &&
(pimr & IPC_PIMR_ISH2HOST_CLR_BIT))
@@ -498,7 +476,8 @@ static void ipc_host2ish_busy_clear_isr(void)
}
DECLARE_IRQ(ISH_IPC_ISH2HOST_CLR_IRQ, ipc_host2ish_busy_clear_isr);
-int ipc_write(const ipc_handle_t handle, const void *buf, const size_t buf_size)
+int ipc_write_timestamp(const ipc_handle_t handle, const void *buf,
+ const size_t buf_size, uint32_t *timestamp)
{
int ret;
struct ipc_if_ctx *ctx;
@@ -547,7 +526,8 @@ int ipc_write(const ipc_handle_t handle, const void *buf, const size_t buf_size)
return -EC_ERROR_OVERFLOW;
}
- ret = ipc_write_raw(ctx, drbl, payload, payload_size);
+ ret = ipc_write_raw_timestamp(ctx, drbl, payload, payload_size,
+ timestamp);
if (ret)
return ret;
@@ -633,7 +613,7 @@ static int do_ipc_read(struct ipc_if_ctx *ctx, const uint32_t protocol,
len = ipc_get_protocol_data(ctx, protocol, buf, buf_size);
- REG32(ctx->in_drbl_reg) = 0;
+ *(ctx->in_drbl_reg) = 0;
set_pimr_and_send_rx_complete(ctx);
return len;
@@ -686,7 +666,7 @@ int ipc_read(const ipc_handle_t handle, void *buf, const size_t buf_size,
return -EC_ERROR_UNKNOWN;
} else {
/* check if msg for the protocol is available */
- drbl_val = REG32(ctx->in_drbl_reg);
+ drbl_val = *(ctx->in_drbl_reg);
drbl_protocol = IPC_DB_PROTOCOL(drbl_val);
if (!(protocol == drbl_protocol) || !IPC_DB_BUSY(drbl_val))
return -IPC_ERR_MSG_NOT_AVAILABLE;
@@ -748,5 +728,8 @@ void ipc_init(void)
ctx = ipc_get_if_ctx(i);
queue_init(&ctx->tx_queue);
}
+
+ /* inform host firmware is running */
+ ish_fwst_set_fw_status(FWSTS_FW_IS_RUNNING);
}
DECLARE_HOOK(HOOK_INIT, ipc_init, HOOK_PRIO_DEFAULT);
diff --git a/chip/ish/ipc_heci.h b/chip/ish/ipc_heci.h
index e7238df83d..183e6a2c6b 100644
--- a/chip/ish/ipc_heci.h
+++ b/chip/ish/ipc_heci.h
@@ -77,7 +77,7 @@ int ipc_read(const ipc_handle_t handle, void *buf, const size_t buf_size,
int timeout_us);
/* Write message to ipc channel. */
-int ipc_write(const ipc_handle_t handle, const void *buf,
- const size_t buf_size);
+int ipc_write_timestamp(const ipc_handle_t handle, const void *buf,
+ const size_t buf_size, uint32_t *timestamp);
#endif /* __IPC_HECI_H */
diff --git a/chip/ish/ish_dma.h b/chip/ish/ish_dma.h
index 9033c9b419..2c76c7d319 100644
--- a/chip/ish/ish_dma.h
+++ b/chip/ish/ish_dma.h
@@ -15,9 +15,9 @@
#define PAGING_CHAN 0
#define KERNEL_CHAN 1
-#define DST_IS_DRAM (1 << 0)
-#define SRC_IS_DRAM (1 << 1)
-#define NON_SNOOP (1 << 2)
+#define DST_IS_DRAM BIT(0)
+#define SRC_IS_DRAM BIT(1)
+#define NON_SNOOP BIT(2)
/* ISH5 and on */
#define RS0 0x0
@@ -44,6 +44,7 @@ enum dma_mode {
void ish_dma_disable(void);
/* Initialize DMA engine */
void ish_dma_init(void);
+
/**
* Main DMA transfer function
*
@@ -64,16 +65,7 @@ int ish_dma_copy(uint32_t chan, uint32_t dst, uint32_t src, uint32_t length,
* @param src_msb Source DRAM upper 32 bits address
*/
void ish_dma_set_msb(uint32_t chan, uint32_t dst_msb, uint32_t src_msb);
-/**
- * Page granularity transfer between SRAM and DRAM
- *
- * @param dst Destination address
- * @param src Source address
- * @param page_in Is from DRAM to SRAM
- * @return DMA_RC_OK, or non-zero if error.
- */
-int ish_dma_page(uint32_t dst, uint32_t src,
- int page_in); /* API for page manager/d0i3 task */
+
/**
* Wait for DMA transfer finish
*
@@ -81,6 +73,7 @@ int ish_dma_page(uint32_t dst, uint32_t src,
* @return DMA_RC_OK, or non-zero if error.
*/
int ish_wait_for_dma_done(uint32_t ch);
+
/* Disable OCP (Open Core Protocol) fabric time out */
void ish_dma_ocp_timeout_disable(void);
#endif
diff --git a/chip/ish/ish_fwst.h b/chip/ish/ish_fwst.h
index c05caaefcb..c114db3241 100644
--- a/chip/ish/ish_fwst.h
+++ b/chip/ish/ish_fwst.h
@@ -99,90 +99,90 @@ enum {
/* get ISH FW status register */
static inline uint32_t ish_fwst_get(void)
{
- return REG32(IPC_ISH_FWSTS);
+ return IPC_ISH_FWSTS;
}
/* set IPC link up */
static inline void ish_fwst_set_ilup(void)
{
- REG32(IPC_ISH_FWSTS) |= (1<<IPC_ISH_FWSTS_ILUP_SHIFT);
+ IPC_ISH_FWSTS |= (1<<IPC_ISH_FWSTS_ILUP_SHIFT);
}
/* clear IPC link up */
static inline void ish_fwst_clear_ilup(void)
{
- REG32(IPC_ISH_FWSTS) &= ~IPC_ISH_FWSTS_ILUP_MASK;
+ IPC_ISH_FWSTS &= ~IPC_ISH_FWSTS_ILUP_MASK;
}
/* return IPC link up state */
static inline int ish_fwst_is_ilup_set(void)
{
- return !!(REG32(IPC_ISH_FWSTS) &= IPC_ISH_FWSTS_ILUP_MASK);
+ return !!(IPC_ISH_FWSTS & IPC_ISH_FWSTS_ILUP_MASK);
}
/* set HECI up */
static inline void ish_fwst_set_hup(void)
{
- REG32(IPC_ISH_FWSTS) |= (1<<IPC_ISH_FWSTS_HUP_SHIFT);
+ IPC_ISH_FWSTS |= (1<<IPC_ISH_FWSTS_HUP_SHIFT);
}
/* clear HECI up */
static inline void ish_fwst_clear_hup(void)
{
- REG32(IPC_ISH_FWSTS) &= ~IPC_ISH_FWSTS_HUP_MASK;
+ IPC_ISH_FWSTS &= ~IPC_ISH_FWSTS_HUP_MASK;
}
/* get HECI up status */
static inline int ish_fwst_is_hup_set(void)
{
- return !!(REG32(IPC_ISH_FWSTS) &= IPC_ISH_FWSTS_HUP_MASK);
+ return !!(IPC_ISH_FWSTS & IPC_ISH_FWSTS_HUP_MASK);
}
/* set fw failure reason */
static inline void ish_fwst_set_fail_reason(uint32_t val)
{
- uint32_t fwst = REG32(IPC_ISH_FWSTS);
+ uint32_t fwst = IPC_ISH_FWSTS;
- REG32(IPC_ISH_FWSTS) = (fwst & ~IPC_ISH_FWSTS_FAIL_REASON_MASK) |
- (val << IPC_ISH_FWSTS_FAIL_REASON_SHIFT);
+ IPC_ISH_FWSTS = (fwst & ~IPC_ISH_FWSTS_FAIL_REASON_MASK) |
+ (val << IPC_ISH_FWSTS_FAIL_REASON_SHIFT);
}
/* get fw failure reason */
static inline uint32_t ish_fwst_get_fail_reason(void)
{
- return (REG32(IPC_ISH_FWSTS) & IPC_ISH_FWSTS_FAIL_REASON_MASK)
+ return (IPC_ISH_FWSTS & IPC_ISH_FWSTS_FAIL_REASON_MASK)
>> IPC_ISH_FWSTS_FAIL_REASON_SHIFT;
}
/* set reset id */
static inline void ish_fwst_set_reset_id(uint32_t val)
{
- uint32_t fwst = REG32(IPC_ISH_FWSTS);
+ uint32_t fwst = IPC_ISH_FWSTS;
- REG32(IPC_ISH_FWSTS) = (fwst & ~IPC_ISH_FWSTS_RESET_ID_MASK) |
- (val << IPC_ISH_FWSTS_RESET_ID_SHIFT);
+ IPC_ISH_FWSTS = (fwst & ~IPC_ISH_FWSTS_RESET_ID_MASK) |
+ (val << IPC_ISH_FWSTS_RESET_ID_SHIFT);
}
/* get reset id */
static inline uint32_t ish_fwst_get_reset_id(void)
{
- return (REG32(IPC_ISH_FWSTS) & IPC_ISH_FWSTS_RESET_ID_MASK)
+ return (IPC_ISH_FWSTS & IPC_ISH_FWSTS_RESET_ID_MASK)
>> IPC_ISH_FWSTS_RESET_ID_SHIFT;
}
/* set general fw status */
static inline void ish_fwst_set_fw_status(uint32_t val)
{
- uint32_t fwst = REG32(IPC_ISH_FWSTS);
+ uint32_t fwst = IPC_ISH_FWSTS;
- REG32(IPC_ISH_FWSTS) = (fwst & ~IPC_ISH_FWSTS_FW_STATUS_MASK) |
- (val << IPC_ISH_FWSTS_FW_STATUS_SHIFT);
+ IPC_ISH_FWSTS = (fwst & ~IPC_ISH_FWSTS_FW_STATUS_MASK) |
+ (val << IPC_ISH_FWSTS_FW_STATUS_SHIFT);
}
/* get general fw status */
static inline uint32_t ish_fwst_get_fw_status(void)
{
- return (REG32(IPC_ISH_FWSTS) & IPC_ISH_FWSTS_FW_STATUS_MASK)
+ return (IPC_ISH_FWSTS & IPC_ISH_FWSTS_FW_STATUS_MASK)
>> IPC_ISH_FWSTS_FW_STATUS_SHIFT;
}
diff --git a/chip/ish/ish_persistent_data.c b/chip/ish/ish_persistent_data.c
new file mode 100644
index 0000000000..c5168475d5
--- /dev/null
+++ b/chip/ish/ish_persistent_data.c
@@ -0,0 +1,59 @@
+/* Copyright 2019 The Chromium OS 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 "common.h"
+#include "config.h"
+#include "hooks.h"
+#include "system.h"
+#include "ish_persistent_data.h"
+
+#define PERSISTENT_DATA_MAGIC 0x49534864 /* "ISHd" */
+
+struct ish_persistent_data ish_persistent_data = {
+ .magic = PERSISTENT_DATA_MAGIC,
+ .reset_flags = RESET_FLAG_POWER_ON,
+ .watchdog_counter = 0,
+ .panic_data = {0},
+};
+
+/*
+ * When AON task firmware is not available (perhaps in the early
+ * stages of bringing up a new board), we have no way to persist data
+ * across reset. Allocate a memory region for "persistent data" which
+ * will never persist, this way we can use ish_persistent_data in a
+ * consistent manner without having to worry if the AON task firmware
+ * is available.
+ *
+ * Otherwise (AON task firmware is available), the
+ * ish_persistent_data_aon symbol is exported by the linker script.
+ */
+#ifdef CONFIG_ISH_PM_AONTASK
+extern struct ish_persistent_data ish_persistent_data_aon;
+#else
+static struct ish_persistent_data ish_persistent_data_aon;
+#endif
+
+void ish_persistent_data_init(void)
+{
+ if (ish_persistent_data_aon.magic == PERSISTENT_DATA_MAGIC) {
+ /* Stored data is valid, load a copy */
+ memcpy(&ish_persistent_data,
+ &ish_persistent_data_aon,
+ sizeof(struct ish_persistent_data));
+
+ /* Invalidate stored data, in case commit fails to happen */
+ ish_persistent_data_aon.magic = 0;
+ }
+
+ /* Update the system module's copy of the reset flags */
+ system_set_reset_flags(chip_read_reset_flags());
+}
+
+void ish_persistent_data_commit(void)
+{
+ memcpy(&ish_persistent_data_aon,
+ &ish_persistent_data,
+ sizeof(struct ish_persistent_data));
+}
diff --git a/chip/ish/ish_persistent_data.h b/chip/ish/ish_persistent_data.h
new file mode 100644
index 0000000000..65a85203fb
--- /dev/null
+++ b/chip/ish/ish_persistent_data.h
@@ -0,0 +1,41 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __CROS_EC_ISH_PERSISTENT_DATA_H
+#define __CROS_EC_ISH_PERSISTENT_DATA_H
+
+#include "panic.h"
+
+/*
+ * If you make backwards-incompatible changes to this struct, (that
+ * is, reading a previous version of the data would be incorrect),
+ * simply change the magic number in ish_persistent_data.c. This will
+ * cause the struct to be re-initialized when the firmware loads.
+ */
+struct ish_persistent_data {
+ uint32_t magic;
+ uint32_t reset_flags;
+ uint32_t watchdog_counter;
+ struct panic_data panic_data;
+};
+
+/*
+ * Local copy of persistent data, which is copied from AON memory only
+ * if the data in AON memory is valid.
+ */
+extern struct ish_persistent_data ish_persistent_data;
+
+/*
+ * Copy the AON persistent data into the local copy and initialize
+ * system reset flags, only if magic number is correct.
+ */
+void ish_persistent_data_init(void);
+
+/*
+ * Commit the local copy to the AON memory (to be called at reset).
+ */
+void ish_persistent_data_commit(void);
+
+#endif /* __CROS_EC_ISH_PERSISTENT_DATA_H */
diff --git a/chip/ish/power_mgt.c b/chip/ish/power_mgt.c
index 4d2fe51ddd..26cc42e104 100644
--- a/chip/ish/power_mgt.c
+++ b/chip/ish/power_mgt.c
@@ -3,16 +3,16 @@
* found in the LICENSE file.
*/
-#include <console.h>
-#include <task.h>
-#include <system.h>
-#include <hwtimer.h>
-#include <util.h>
-#include "interrupts.h"
#include "aontaskfw/ish_aon_share.h"
+#include "console.h"
+#include "hwtimer.h"
+#include "interrupts.h"
+#include "ish_dma.h"
#include "power_mgt.h"
+#include "system.h"
+#include "task.h"
+#include "util.h"
#include "watchdog.h"
-#include "ish_dma.h"
#ifdef CONFIG_ISH_PM_DEBUG
#define CPUTS(outstr) cputs(CC_SYSTEM, outstr)
@@ -24,17 +24,22 @@
#define CPRINTF(format, args...)
#endif
-#ifdef CONFIG_WATCHDOG
-extern void watchdog_enable(void);
-extern void watchdog_disable(void);
-#endif
-
/* defined in link script: core/minute-ia/ec.lds.S */
extern uint32_t __aon_ro_start;
extern uint32_t __aon_ro_end;
extern uint32_t __aon_rw_start;
extern uint32_t __aon_rw_end;
+/**
+ * on ISH, uart interrupt can only wakeup ISH from low power state via
+ * CTS pin, but most ISH platforms only have Rx and Tx pins, no CTS pin
+ * exposed, so, we need block ISH enter low power state for a while when
+ * console is in use.
+ * fixed amount of time to keep the console in use flag true after boot in
+ * order to give a permanent window in which the low speed clock is not used.
+ */
+#define CONSOLE_IN_USE_ON_BOOT_TIME (15*SECOND)
+
/* power management internal context data structure */
struct pm_context {
/* aontask image valid flag */
@@ -43,38 +48,46 @@ struct pm_context {
struct ish_aon_share *aon_share;
/* TSS segment selector for task switching */
int aon_tss_selector[2];
+ /* console expire time */
+ timestamp_t console_expire_time;
+ /* console in use timeout */
+ int console_in_use_timeout_sec;
} __packed;
static struct pm_context pm_ctx = {
.aon_valid = 0,
/* aon shared data located in the start of aon memory */
- .aon_share = (struct ish_aon_share *)CONFIG_ISH_AON_SRAM_BASE_START
+ .aon_share = (struct ish_aon_share *)CONFIG_AON_RAM_BASE,
+ .console_in_use_timeout_sec = 60
};
/* D0ix statistics data, including each state's count and total stay time */
-struct pm_statistics {
- uint64_t d0i0_cnt;
- uint64_t d0i0_time_us;
-
-#ifdef CONFIG_ISH_PM_D0I1
- uint64_t d0i1_cnt;
- uint64_t d0i1_time_us;
-#endif
-
-#ifdef CONFIG_ISH_PM_D0I2
- uint64_t d0i2_cnt;
- uint64_t d0i2_time_us;
-#endif
-
-#ifdef CONFIG_ISH_PM_D0I3
- uint64_t d0i3_cnt;
- uint64_t d0i3_time_us;
-#endif
+struct pm_stat {
+ uint64_t count;
+ uint64_t total_time_us;
+};
-} __packed;
+struct pm_statistics {
+ struct pm_stat d0i0;
+ struct pm_stat d0i1;
+ struct pm_stat d0i2;
+ struct pm_stat d0i3;
+};
static struct pm_statistics pm_stats;
+/*
+ * Log a new statistic
+ *
+ * t0: start time, in us
+ * t1: end time, in us
+ */
+static void log_pm_stat(struct pm_stat *stat, uint64_t t0, uint64_t t1)
+{
+ stat->total_time_us += t1 - t0;
+ stat->count++;
+}
+
#ifdef CONFIG_ISH_PM_AONTASK
/* The GDT which initialized in init.S */
@@ -256,21 +269,63 @@ static void handle_reset_in_aontask(int pm_state)
static void enter_d0i0(void)
{
- timestamp_t t0, t1;
-
- t0 = get_time();
+ uint32_t t0, t1;
+ t0 = __hw_clock_source_read();
pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I0;
/* halt ISH cpu, will wakeup from any interrupt */
ish_mia_halt();
- t1 = get_time();
-
+ t1 = __hw_clock_source_read();
pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0;
+ log_pm_stat(&pm_stats.d0i0, t0, t1);
+}
- pm_stats.d0i0_time_us += t1.val - t0.val;
- pm_stats.d0i0_cnt++;
+/**
+ * ISH PMU does not support both-edge interrupt triggered gpio configuration.
+ * If both edges are configured, then the ISH can't stay in low poer mode
+ * because it will exit immediately.
+ *
+ * As a workaround, we scan all gpio pins which have been configured as
+ * both-edge triggered, and then temporarily set each gpio pin to the single
+ * edge trigger that is opposite of its value, then restore the both-edge
+ * trigger configuration immediately after exiting low power mode.
+ */
+static uint32_t __unused convert_both_edge_gpio_to_single_edge(void)
+{
+ uint32_t both_edge_pins = 0;
+ int i = 0;
+
+ /**
+ * scan GPIO GFER, GRER and GIMR registers to find the both edge
+ * interrupt trigger mode enabled pins.
+ */
+ for (i = 0; i < 32; i++) {
+ if (ISH_GPIO_GIMR & BIT(i) &&
+ ISH_GPIO_GRER & BIT(i) &&
+ ISH_GPIO_GFER & BIT(i)) {
+
+ /* Record the pin so we can restore it later */
+ both_edge_pins |= BIT(i);
+
+ if (ISH_GPIO_GPLR & BIT(i)) {
+ /* pin is high, just keep falling edge mode */
+ ISH_GPIO_GRER &= ~BIT(i);
+ } else {
+ /* pin is low, just keep rising edge mode */
+ ISH_GPIO_GFER &= ~BIT(i);
+ }
+ }
+ }
+
+ return both_edge_pins;
+}
+
+static void __unused restore_both_edge_gpio_config(uint32_t both_edge_pin_map)
+{
+ ISH_GPIO_GRER |= both_edge_pin_map;
+ ISH_GPIO_GFER |= both_edge_pin_map;
}
#ifdef CONFIG_ISH_PM_D0I1
@@ -278,11 +333,8 @@ static void enter_d0i0(void)
static void enter_d0i1(void)
{
uint64_t current_irq_map;
-
- timestamp_t t0, t1;
- t0 = get_time();
-
- pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I1;
+ uint32_t both_edge_gpio_pins;
+ uint32_t t0, t1;
/* only enable PMU wakeup interrupt */
current_irq_map = disable_all_interrupts();
@@ -292,6 +344,11 @@ static void enter_d0i1(void)
task_enable_irq(ISH_RESET_PREP_IRQ);
#endif
+ t0 = __hw_clock_source_read();
+ pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I1;
+
+ both_edge_gpio_pins = convert_both_edge_gpio_to_single_edge();
+
/* enable Trunk Clock Gating (TCG) of ISH */
CCU_TCG_EN = 1;
@@ -301,15 +358,18 @@ static void enter_d0i1(void)
/* disable Trunk Clock Gating (TCG) of ISH */
CCU_TCG_EN = 0;
- /* restore interrupts */
- task_disable_irq(ISH_PMU_WAKEUP_IRQ);
- restore_interrupts(current_irq_map);
+ restore_both_edge_gpio_config(both_edge_gpio_pins);
pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0;
+ t1 = __hw_clock_source_read();
+ log_pm_stat(&pm_stats.d0i1, t0, t1);
+
+ /* Reload watchdog before enabling interrupts again */
+ watchdog_reload();
- t1 = get_time();
- pm_stats.d0i1_time_us += t1.val - t0.val;
- pm_stats.d0i1_cnt++;
+ /* restore interrupts */
+ task_disable_irq(ISH_PMU_WAKEUP_IRQ);
+ restore_interrupts(current_irq_map);
}
#endif
@@ -319,11 +379,8 @@ static void enter_d0i1(void)
static void enter_d0i2(void)
{
uint64_t current_irq_map;
-
- timestamp_t t0, t1;
- t0 = get_time();
-
- pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I2;
+ uint32_t both_edge_gpio_pins;
+ uint32_t t0, t1;
/* only enable PMU wakeup interrupt */
current_irq_map = disable_all_interrupts();
@@ -333,6 +390,11 @@ static void enter_d0i2(void)
task_enable_irq(ISH_RESET_PREP_IRQ);
#endif
+ t0 = __hw_clock_source_read();
+ pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I2;
+
+ both_edge_gpio_pins = convert_both_edge_gpio_to_single_edge();
+
/* enable Trunk Clock Gating (TCG) of ISH */
CCU_TCG_EN = 1;
@@ -349,16 +411,18 @@ static void enter_d0i2(void)
/* disable Trunk Clock Gating (TCG) of ISH */
CCU_TCG_EN = 0;
- /* restore interrupts */
- task_disable_irq(ISH_PMU_WAKEUP_IRQ);
- restore_interrupts(current_irq_map);
-
- t1 = get_time();
+ restore_both_edge_gpio_config(both_edge_gpio_pins);
+ t1 = __hw_clock_source_read();
pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0;
+ log_pm_stat(&pm_stats.d0i2, t0, t1);
+
+ /* Reload watchdog before enabling interrupts again */
+ watchdog_reload();
- pm_stats.d0i2_time_us += t1.val - t0.val;
- pm_stats.d0i2_cnt++;
+ /* restore interrupts */
+ task_disable_irq(ISH_PMU_WAKEUP_IRQ);
+ restore_interrupts(current_irq_map);
}
#endif
@@ -368,11 +432,8 @@ static void enter_d0i2(void)
static void enter_d0i3(void)
{
uint64_t current_irq_map;
- timestamp_t t0, t1;
-
- t0 = get_time();
-
- pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I3;
+ uint32_t both_edge_gpio_pins;
+ uint32_t t0, t1;
/* only enable PMU wakeup interrupt */
current_irq_map = disable_all_interrupts();
@@ -382,6 +443,11 @@ static void enter_d0i3(void)
task_enable_irq(ISH_RESET_PREP_IRQ);
#endif
+ t0 = __hw_clock_source_read();
+ pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I3;
+
+ both_edge_gpio_pins = convert_both_edge_gpio_to_single_edge();
+
/* enable Trunk Clock Gating (TCG) of ISH */
CCU_TCG_EN = 1;
@@ -398,25 +464,38 @@ static void enter_d0i3(void)
/* disable Trunk Clock Gating (TCG) of ISH */
CCU_TCG_EN = 0;
- /* restore interrupts */
- task_disable_irq(ISH_PMU_WAKEUP_IRQ);
- restore_interrupts(current_irq_map);
-
- t1 = get_time();
+ restore_both_edge_gpio_config(both_edge_gpio_pins);
+ t1 = __hw_clock_source_read();
pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0;
+ log_pm_stat(&pm_stats.d0i3, t0, t1);
- pm_stats.d0i3_time_us += t1.val - t0.val;
- pm_stats.d0i3_cnt++;
+ /* Reload watchdog before enabling interrupts again */
+ watchdog_reload();
+
+ /* restore interrupts */
+ task_disable_irq(ISH_PMU_WAKEUP_IRQ);
+ restore_interrupts(current_irq_map);
}
#endif
-static int d0ix_decide(uint32_t idle_us)
+static int d0ix_decide(timestamp_t cur_time, uint32_t idle_us)
{
int pm_state = ISH_PM_STATE_D0I0;
if (DEEP_SLEEP_ALLOWED) {
+
+ /* check if the console use has expired. */
+ if (sleep_mask & SLEEP_MASK_CONSOLE) {
+ if (cur_time.val > pm_ctx.console_expire_time.val) {
+ enable_sleep(SLEEP_MASK_CONSOLE);
+ ccprints("Disabling console in deep sleep");
+ } else {
+ return pm_state;
+ }
+ }
+
#ifdef CONFIG_ISH_PM_D0I1
pm_state = ISH_PM_STATE_D0I1;
#endif
@@ -430,20 +509,17 @@ static int d0ix_decide(uint32_t idle_us)
if (idle_us >= CONFIG_ISH_D0I3_MIN_USEC && pm_ctx.aon_valid)
pm_state = ISH_PM_STATE_D0I3;
#endif
+
}
return pm_state;
}
-static void pm_process(uint32_t idle_us)
+static void pm_process(timestamp_t cur_time, uint32_t idle_us)
{
int decide;
- decide = d0ix_decide(idle_us);
-
-#ifdef CONFIG_WATCHDOG
- watchdog_disable();
-#endif
+ decide = d0ix_decide(cur_time, idle_us);
switch (decide) {
#ifdef CONFIG_ISH_PM_D0I1
@@ -470,12 +546,6 @@ static void pm_process(uint32_t idle_us)
if (decide == ISH_PM_STATE_D0I2 || decide == ISH_PM_STATE_D0I3)
check_aon_task_status();
#endif
-
-#ifdef CONFIG_WATCHDOG
- watchdog_enable();
- watchdog_reload();
-#endif
-
}
void ish_pm_init(void)
@@ -545,67 +615,62 @@ void __idle(void)
timestamp_t t0;
int next_delay = 0;
+ /**
+ * initialize console in use to true and specify the console expire
+ * time in order to give a fixed window on boot
+ */
+ disable_sleep(SLEEP_MASK_CONSOLE);
+ pm_ctx.console_expire_time.val = get_time().val +
+ CONSOLE_IN_USE_ON_BOOT_TIME;
+
while (1) {
t0 = get_time();
next_delay = __hw_clock_event_get() - t0.le.lo;
- pm_process(next_delay);
+ pm_process(t0, next_delay);
}
}
+/*
+ * helper for command_idle_stats
+ */
+static void print_stats(const char *name, const struct pm_stat *stat)
+{
+ if (stat->count)
+ ccprintf(" %s:\n"
+ " counts: %lu\n"
+ " time: %.6lus\n",
+ name, stat->count, stat->total_time_us);
+}
+
/**
* Print low power idle statistics
*/
static int command_idle_stats(int argc, char **argv)
{
-#if defined(CONFIG_ISH_PM_D0I2) || defined(CONFIG_ISH_PM_D0I3)
struct ish_aon_share *aon_share = pm_ctx.aon_share;
-#endif
- ccprintf("Aontask exist: %s\n", pm_ctx.aon_valid ? "Yes" : "No");
+ ccprintf("Aontask exists: %s\n", pm_ctx.aon_valid ? "Yes" : "No");
+ ccprintf("Total time on: %.6lus\n", get_time().val);
ccprintf("Idle sleep:\n");
- ccprintf(" D0i0:\n");
- ccprintf(" counts: %ld\n", pm_stats.d0i0_cnt);
- ccprintf(" time: %.6lds\n", pm_stats.d0i0_time_us);
+ print_stats("D0i0", &pm_stats.d0i0);
ccprintf("Deep sleep:\n");
-#ifdef CONFIG_ISH_PM_D0I1
- ccprintf(" D0i1:\n");
- ccprintf(" counts: %ld\n", pm_stats.d0i1_cnt);
- ccprintf(" time: %.6lds\n", pm_stats.d0i1_time_us);
-#endif
+ print_stats("D0i1", &pm_stats.d0i1);
+ print_stats("D0i2", &pm_stats.d0i2);
+ print_stats("D0i3", &pm_stats.d0i3);
-#ifdef CONFIG_ISH_PM_D0I2
- if (pm_ctx.aon_valid) {
- ccprintf(" D0i2:\n");
- ccprintf(" counts: %ld\n", pm_stats.d0i2_cnt);
- ccprintf(" time: %.6lds\n", pm_stats.d0i2_time_us);
- }
-#endif
-
-#ifdef CONFIG_ISH_PM_D0I3
- if (pm_ctx.aon_valid) {
- ccprintf(" D0i3:\n");
- ccprintf(" counts: %ld\n", pm_stats.d0i3_cnt);
- ccprintf(" time: %.6lds\n", pm_stats.d0i3_time_us);
- }
-#endif
-
-#if defined(CONFIG_ISH_PM_D0I2) || defined(CONFIG_ISH_PM_D0I3)
if (pm_ctx.aon_valid) {
ccprintf(" Aontask status:\n");
- ccprintf(" last error: %d\n", aon_share->last_error);
- ccprintf(" error counts: %d\n", aon_share->error_count);
+ ccprintf(" last error: %lu\n", aon_share->last_error);
+ ccprintf(" error counts: %lu\n", aon_share->error_count);
}
-#endif
-
- ccprintf("Total time on: %.6lds\n", get_time().val);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats, "",
- "Print last idle stats");
+ "Print power management statistics");
#ifdef CONFIG_ISH_PM_D0I1
@@ -641,8 +706,8 @@ static void reset_prep_isr(void)
* Indicate completion of servicing the interrupt to IOAPIC first
* then indicate completion of servicing the interrupt to LAPIC
*/
- REG32(IOAPIC_EOI_REG) = ISH_RESET_PREP_VEC;
- REG32(LAPIC_EOI_REG) = 0x0;
+ IOAPIC_EOI_REG = ISH_RESET_PREP_VEC;
+ LAPIC_EOI_REG = 0x0;
if (pm_ctx.aon_valid) {
handle_reset_in_aontask(ISH_PM_STATE_RESET_PREP);
@@ -673,8 +738,8 @@ static void handle_d3(uint32_t irq_vec)
* first then indicate completion of servicing the interrupt
* to LAPIC
*/
- REG32(IOAPIC_EOI_REG) = irq_vec;
- REG32(LAPIC_EOI_REG) = 0x0;
+ IOAPIC_EOI_REG = irq_vec;
+ LAPIC_EOI_REG = 0x0;
pm_ctx.aon_share->pm_state = ISH_PM_STATE_D3;
@@ -724,3 +789,13 @@ DECLARE_IRQ(ISH_BME_RISE_IRQ, bme_rise_isr);
DECLARE_IRQ(ISH_BME_FALL_IRQ, bme_fall_isr);
#endif
+
+void ish_pm_refresh_console_in_use(void)
+{
+ disable_sleep(SLEEP_MASK_CONSOLE);
+
+ /* Set console in use expire time. */
+ pm_ctx.console_expire_time = get_time();
+ pm_ctx.console_expire_time.val +=
+ pm_ctx.console_in_use_timeout_sec * SECOND;
+}
diff --git a/chip/ish/power_mgt.h b/chip/ish/power_mgt.h
index 2921e0534e..bd2e707cb4 100644
--- a/chip/ish/power_mgt.h
+++ b/chip/ish/power_mgt.h
@@ -6,6 +6,8 @@
#ifndef __CROS_EC_POWER_MGT_H
#define __CROS_EC_POWER_MGT_H
+#include "registers.h"
+
/* power states for ISH */
enum {
/* D0 state: active mode */
@@ -57,16 +59,23 @@ static inline void ish_mia_reset(void)
__builtin_unreachable();
}
-
-/**
- * ish low power management initialization,
- * should be called at system init stage before RTOS task scheduling start
- */
+/* Initialize power management module. */
+#ifdef CONFIG_LOW_POWER_IDLE
void ish_pm_init(void);
+#else
+__maybe_unused static void ish_pm_init(void)
+{
+}
+#endif
/**
* reset ISH (reset minute-ia cpu core, and power off main SRAM)
*/
void ish_pm_reset(void) __attribute__((noreturn));
+/**
+ * notify the power management module that the UART for the console is in use.
+ */
+void ish_pm_refresh_console_in_use(void);
+
#endif /* __CROS_EC_POWER_MGT_H */
diff --git a/chip/ish/registers.h b/chip/ish/registers.h
index f6e1a292fa..a736093b3c 100644
--- a/chip/ish/registers.h
+++ b/chip/ish/registers.h
@@ -51,6 +51,7 @@ enum ish_i2c_port {
/* HW interrupt pins mapped to IOAPIC, from I/O sources */
#define ISH_I2C0_IRQ 0
#define ISH_I2C1_IRQ 1
+#define ISH_FABRIC_IRQ 5
#define ISH_I2C2_IRQ 40
#define ISH_WDT_IRQ 6
#define ISH_GPIO_IRQ 7
@@ -93,7 +94,8 @@ enum ish_i2c_port {
/* APIC interrupt vectors */
#define ISH_TS_VECTOR 0x20 /* Task switch vector */
-#define LAPIC_LVT_ERROR_VECTOR 0x21
+#define LAPIC_LVT_ERROR_VECTOR 0x21 /* Clears IOAPIC/LAPIC sync errors */
+#define SOFTIRQ_VECTOR 0x22 /* Handles software generated IRQs */
#define LAPIC_SPURIOUS_INT_VECTOR 0xff
/* Interrupt to vector mapping. To be programmed into IOAPIC */
@@ -115,58 +117,65 @@ enum ish_i2c_port {
#define ISH_D3_FALL_VEC IRQ_TO_VEC(ISH_D3_FALL_IRQ)
#define ISH_BME_RISE_VEC IRQ_TO_VEC(ISH_BME_RISE_IRQ)
#define ISH_BME_FALL_VEC IRQ_TO_VEC(ISH_BME_FALL_IRQ)
+#define ISH_FABRIC_VEC IRQ_TO_VEC(ISH_FABRIC_IRQ)
#ifdef CONFIG_ISH_UART_0
-#define ISH_DEBUG_UART UART_PORT_0
-#define ISH_DEBUG_UART_IRQ ISH_UART0_IRQ
-#define ISH_DEBUG_UART_VEC ISH_UART0_VEC
+#define ISH_DEBUG_UART UART_PORT_0
+#define ISH_DEBUG_UART_IRQ ISH_UART0_IRQ
+#define ISH_DEBUG_UART_VEC ISH_UART0_VEC
#else
-#define ISH_DEBUG_UART UART_PORT_1
-#define ISH_DEBUG_UART_IRQ ISH_UART1_IRQ
-#define ISH_DEBUG_UART_VEC ISH_UART1_VEC
+#define ISH_DEBUG_UART UART_PORT_1
+#define ISH_DEBUG_UART_IRQ ISH_UART1_IRQ
+#define ISH_DEBUG_UART_VEC ISH_UART1_VEC
#endif
/* IPC_Registers */
-#define IPC_PISR (ISH_IPC_BASE + 0x0)
-#define IPC_PIMR (ISH_IPC_BASE + 0x4)
-#define IPC_PIMR_CSME_CSR_BIT (0x1 << 23)
-#define IPC_ISH2HOST_MSG_REGS (ISH_IPC_BASE + 0x60)
-#define IPC_ISH_FWSTS (ISH_IPC_BASE + 0x34)
-#define IPC_HOST2ISH_DOORBELL (ISH_IPC_BASE + 0x48)
-#define IPC_HOST2ISH_MSG_REGS (ISH_IPC_BASE + 0xE0)
-#define IPC_ISH2HOST_DOORBELL (ISH_IPC_BASE + 0x54)
-#define IPC_ISH2PMC_DOORBELL (ISH_IPC_BASE + 0x58)
-#define IPC_ISH2PMC_MSG_REGS (ISH_IPC_BASE + 0x260)
-#define IPC_ISH_RMP0 (ISH_IPC_BASE + 0x360)
-#define IPC_ISH_RMP1 (ISH_IPC_BASE + 0x364)
-#define IPC_ISH_RMP2 (ISH_IPC_BASE + 0x368)
-#define DMA_ENABLED_MASK (0x1 << 0)
-#define IPC_BUSY_CLEAR (ISH_IPC_BASE + 0x378)
-#define IPC_UMA_RANGE_LOWER_0 REG32(ISH_IPC_BASE + 0x380)
-#define IPC_UMA_RANGE_LOWER_1 REG32(ISH_IPC_BASE + 0x384)
-#define IPC_UMA_RANGE_UPPER_0 REG32(ISH_IPC_BASE + 0x388)
-#define IPC_UMA_RANGE_UPPER_1 REG32(ISH_IPC_BASE + 0x38C)
+#define IPC_PISR REG32(ISH_IPC_BASE + 0x0)
+#define IPC_PISR_HOST2ISH_BIT BIT(0)
+
+#define IPC_PIMR REG32(ISH_IPC_BASE + 0x4)
+#define IPC_PIMR_HOST2ISH_BIT BIT(0)
+#define IPC_PIMR_ISH2HOST_CLR_BIT BIT(11)
+#define IPC_PIMR_CSME_CSR_BIT BIT(23)
+#define IPC_ISH2HOST_MSG_BASE REG8_ADDR(ISH_IPC_BASE + 0x60)
+#define IPC_ISH_FWSTS REG32(ISH_IPC_BASE + 0x34)
+#define IPC_HOST2ISH_DOORBELL_ADDR REG32_ADDR(ISH_IPC_BASE + 0x48)
+#define IPC_HOST2ISH_MSG_BASE REG8_ADDR(ISH_IPC_BASE + 0xE0)
+#define IPC_ISH2HOST_DOORBELL_ADDR REG32_ADDR(ISH_IPC_BASE + 0x54)
+#define IPC_ISH2PMC_DOORBELL REG32(ISH_IPC_BASE + 0x58)
+#define IPC_ISH2PMC_MSG_BASE (ISH_IPC_BASE + 0x260)
+#define IPC_ISH_RMP0 REG32(ISH_IPC_BASE + 0x360)
+#define IPC_ISH_RMP1 REG32(ISH_IPC_BASE + 0x364)
+#define IPC_ISH_RMP2 REG32(ISH_IPC_BASE + 0x368)
+#define DMA_ENABLED_MASK BIT(0)
+#define IPC_BUSY_CLEAR REG32(ISH_IPC_BASE + 0x378)
+#define IPC_DB_CLR_STS_ISH2HOST_BIT BIT(0)
+
+#define IPC_UMA_RANGE_LOWER_0 REG32(ISH_IPC_BASE + 0x380)
+#define IPC_UMA_RANGE_LOWER_1 REG32(ISH_IPC_BASE + 0x384)
+#define IPC_UMA_RANGE_UPPER_0 REG32(ISH_IPC_BASE + 0x388)
+#define IPC_UMA_RANGE_UPPER_1 REG32(ISH_IPC_BASE + 0x38C)
/* PMU Registers */
-#define PMU_SRAM_PG_EN REG32(ISH_PMU_BASE + 0x0)
-#define PMU_D3_STATUS REG32(ISH_PMU_BASE + 0x4)
-#define PMU_D3_BIT_SET (0x1 << 0)
-#define PMU_D3_BIT_RISING_EDGE_STATUS (0x1 << 1)
-#define PMU_D3_BIT_FALLING_EDGE_STATUS (0x1 << 2)
-#define PMU_D3_BIT_RISING_EDGE_MASK (0x1 << 3)
-#define PMU_D3_BIT_FALLING_EDGE_MASK (0x1 << 4)
-#define PMU_BME_BIT_SET (0x1 << 5)
-#define PMU_BME_BIT_RISING_EDGE_STATUS (0x1 << 6)
-#define PMU_BME_BIT_FALLING_EDGE_STATUS (0x1 << 7)
-#define PMU_BME_BIT_RISING_EDGE_MASK (0x1 << 8)
-#define PMU_BME_BIT_FALLING_EDGE_MASK (0x1 << 9)
-#define PMU_VNN_REQ REG32(ISH_PMU_BASE + 0x3c)
+#define PMU_SRAM_PG_EN REG32(ISH_PMU_BASE + 0x0)
+#define PMU_D3_STATUS REG32(ISH_PMU_BASE + 0x4)
+#define PMU_D3_BIT_SET BIT(0)
+#define PMU_D3_BIT_RISING_EDGE_STATUS BIT(1)
+#define PMU_D3_BIT_FALLING_EDGE_STATUS BIT(2)
+#define PMU_D3_BIT_RISING_EDGE_MASK BIT(3)
+#define PMU_D3_BIT_FALLING_EDGE_MASK BIT(4)
+#define PMU_BME_BIT_SET BIT(5)
+#define PMU_BME_BIT_RISING_EDGE_STATUS BIT(6)
+#define PMU_BME_BIT_FALLING_EDGE_STATUS BIT(7)
+#define PMU_BME_BIT_RISING_EDGE_MASK BIT(8)
+#define PMU_BME_BIT_FALLING_EDGE_MASK BIT(9)
+#define PMU_VNN_REQ REG32(ISH_PMU_BASE + 0x3c)
#define VNN_REQ_IPC_HOST_WRITE BIT(3) /* Power for IPC host write */
#define PMU_VNN_REQ_ACK REG32(ISH_PMU_BASE + 0x40)
#define PMU_VNN_REQ_ACK_STATUS BIT(0) /* VNN req and ack status */
-#define PMU_RST_PREP REG32(ISH_PMU_BASE + 0x5c)
+#define PMU_RST_PREP REG32(ISH_PMU_BASE + 0x5c)
#define PMU_RST_PREP_GET BIT(0)
#define PMU_RST_PREP_AVAIL BIT(1)
#define PMU_RST_PREP_INT_MASK BIT(31)
@@ -200,15 +209,14 @@ enum ish_i2c_port {
#define DMA_PSIZE_CHAN0_OFFSET 0
#define DMA_PSIZE_CHAN1_SIZE 128
#define DMA_PSIZE_CHAN1_OFFSET 13
-#define DMA_PSIZE_UPDATE (1 << 26)
+#define DMA_PSIZE_UPDATE BIT(26)
#define DMA_MAX_CHANNEL 4
#define DMA_SAR(chan) REG32(chan + 0x000)
#define DMA_DAR(chan) REG32(chan + 0x008)
#define DMA_LLP(chan) REG32(chan + 0x010)
#define DMA_CTL_LOW(chan) REG32(chan + 0x018)
#define DMA_CTL_HIGH(chan) REG32(chan + 0x018 + 0x4)
-#define DMA_CTL_INT_EN_BIT 0
-#define DMA_CTL_INT_EN_MASK (1 << DMA_CTL_INT_EN_BIT)
+#define DMA_CTL_INT_ENABLE BIT(0)
#define DMA_CTL_DST_TR_WIDTH_SHIFT 1
#define DMA_CTL_SRC_TR_WIDTH_SHIFT 4
#define DMA_CTL_DINC_SHIFT 7
@@ -218,37 +226,34 @@ enum ish_i2c_port {
#define DMA_CTL_SRC_MSIZE_SHIFT 14
#define DMA_CTL_TT_FC_SHIFT 20
#define DMA_CTL_TT_FC_M2M_DMAC 0
-#define DMA_EN_BIT 0
-#define DMA_EN_MASK (1 << DMA_EN_BIT)
-#define DMA_CH_EN_BIT(n) (1 << (n))
-#define DMA_CH_EN_WE_BIT(n) (1 << (8 + (n)))
+#define DMA_ENABLE BIT(0)
+#define DMA_CH_EN_BIT(n) BIT(n)
+#define DMA_CH_EN_WE_BIT(n) BIT(8 + (n))
#define DMA_MAX_BLOCK_SIZE (4096)
#define SRC_TR_WIDTH 2
#define SRC_BURST_SIZE 3
#define DEST_TR_WIDTH 2
#define DEST_BURST_SIZE 3
-#define PMU_MASK_EVENT REG32(ISH_PMU_BASE + 0x10)
-#define PMU_MASK_EVENT_BIT_GPIO(pin) (0x1 << (pin))
-#define PMU_MASK_EVENT_BIT_HPET (0x1 << 16)
-#define PMU_MASK_EVENT_BIT_IPC (0x1 << 17)
-#define PMU_MASK_EVENT_BIT_D3 (0x1 << 18)
-#define PMU_MASK_EVENT_BIT_DMA (0x1 << 19)
-#define PMU_MASK_EVENT_BIT_I2C0 (0x1 << 20)
-#define PMU_MASK_EVENT_BIT_I2C1 (0x1 << 21)
-#define PMU_MASK_EVENT_BIT_SPI (0x1 << 22)
-#define PMU_MASK_EVENT_BIT_UART (0x1 << 23)
+#define PMU_MASK_EVENT REG32(ISH_PMU_BASE + 0x10)
+#define PMU_MASK_EVENT_BIT_GPIO(pin) BIT(pin)
+#define PMU_MASK_EVENT_BIT_HPET BIT(16)
+#define PMU_MASK_EVENT_BIT_IPC BIT(17)
+#define PMU_MASK_EVENT_BIT_D3 BIT(18)
+#define PMU_MASK_EVENT_BIT_DMA BIT(19)
+#define PMU_MASK_EVENT_BIT_I2C0 BIT(20)
+#define PMU_MASK_EVENT_BIT_I2C1 BIT(21)
+#define PMU_MASK_EVENT_BIT_SPI BIT(22)
+#define PMU_MASK_EVENT_BIT_UART BIT(23)
#define PMU_MASK_EVENT_BIT_ALL (0xffffffff)
-#define PMU_RF_ROM_PWR_CTRL REG32(ISH_PMU_BASE + 0x30)
+#define PMU_RF_ROM_PWR_CTRL REG32(ISH_PMU_BASE + 0x30)
-#define PMU_LDO_CTRL REG32(ISH_PMU_BASE + 0x44)
-#define PMU_LDO_BIT_ON (0x1 << 0)
-#define PMU_LDO_BIT_OFF (0)
-#define PMU_LDO_BIT_RETENTION_ON (0x1 << 1)
-#define PMU_LDO_BIT_RETENTION_OFF (0)
-#define PMU_LDO_BIT_CALIBRATION (0x1 << 2)
-#define PMU_LDO_BIT_READY (0x1 << 3)
+#define PMU_LDO_CTRL REG32(ISH_PMU_BASE + 0x44)
+#define PMU_LDO_ENABLE_BIT BIT(0)
+#define PMU_LDO_RETENTION_BIT BIT(1)
+#define PMU_LDO_CALIBRATION_BIT BIT(2)
+#define PMU_LDO_READY_BIT BIT(3)
/* CCU Registers */
#define CCU_TCG_EN REG32(ISH_CCU_BASE + 0x0)
@@ -257,75 +262,87 @@ enum ish_i2c_port {
#define CCU_RST_HST REG32(ISH_CCU_BASE + 0x34) /* Reset history */
#define CCU_TCG_ENABLE REG32(ISH_CCU_BASE + 0x38)
#define CCU_BCG_ENABLE REG32(ISH_CCU_BASE + 0x3c)
-#define CCU_BCG_BIT_MIA (0x1 << 0)
-#define CCU_BCG_BIT_DMA (0x1 << 1)
-#define CCU_BCG_BIT_I2C0 (0x1 << 2)
-#define CCU_BCG_BIT_I2C1 (0x1 << 3)
-#define CCU_BCG_BIT_SPI (0x1 << 4)
-#define CCU_BCG_BIT_SRAM (0x1 << 5)
-#define CCU_BCG_BIT_HPET (0x1 << 6)
-#define CCU_BCG_BIT_UART (0x1 << 7)
-#define CCU_BCG_BIT_GPIO (0x1 << 8)
-#define CCU_BCG_BIT_I2C2 (0x1 << 9)
-#define CCU_BCG_BIT_SPI2 (0x1 << 10)
-#define CCU_BCG_BIT_ALL (0x7ff)
+#define CCU_BCG_BIT_MIA BIT(0)
+#define CCU_BCG_BIT_DMA BIT(1)
+#define CCU_BCG_BIT_I2C0 BIT(2)
+#define CCU_BCG_BIT_I2C1 BIT(3)
+#define CCU_BCG_BIT_SPI BIT(4)
+#define CCU_BCG_BIT_SRAM BIT(5)
+#define CCU_BCG_BIT_HPET BIT(6)
+#define CCU_BCG_BIT_UART BIT(7)
+#define CCU_BCG_BIT_GPIO BIT(8)
+#define CCU_BCG_BIT_I2C2 BIT(9)
+#define CCU_BCG_BIT_SPI2 BIT(10)
+#define CCU_BCG_BIT_ALL (0x7ff)
/* Bitmasks for CCU_RST_HST */
-#define CCU_SW_RST (1 << 0) /* Used to indicate SW reset */
-#define CCU_WDT_RST (1 << 1) /* Used to indicate WDT reset */
-#define CCU_MIASS_RST (1 << 2) /* Used to indicate UIA shutdown reset */
-#define CCU_SRECC_RST (1 << 3) /* Used to indicate SRAM ECC reset */
+#define CCU_SW_RST BIT(0) /* Used to indicate SW reset */
+#define CCU_WDT_RST BIT(1) /* Used to indicate WDT reset */
+#define CCU_MIASS_RST BIT(2) /* Used to indicate UIA shutdown reset */
+#define CCU_SRECC_RST BIT(3) /* Used to indicate SRAM ECC reset */
+
+/* Fabric Agent Status register */
+#define FABRIC_AGENT_STATUS REG32(ISH_OCP_BASE + 0x7828)
+#define FABRIC_INBAND_ERR_SECONDARY_BIT BIT(29)
+#define FABRIC_INBAND_ERR_PRIMARY_BIT BIT(28)
+#define FABRIC_M_ERR_BIT BIT(24)
+#define FABRIC_MIA_STATUS_BIT_ERR (FABRIC_INBAND_ERR_SECONDARY_BIT | \
+ FABRIC_INBAND_ERR_PRIMARY_BIT | \
+ FABRIC_M_ERR_BIT)
/* CSME Registers */
-#define ISH_RST_REG REG32(ISH_IPC_BASE + 0x44)
+#define ISH_RST_REG REG32(ISH_IPC_BASE + 0x44)
/* IOAPIC registers */
-#define IOAPIC_IDX 0xFEC00000
-#define IOAPIC_WDW 0xFEC00010
-#define IOAPIC_EOI_REG 0xFEC00040
-
-#define IOAPIC_VERSION 0x1
-#define IOAPIC_IOREDTBL 0x10
-#define IOAPIC_REDTBL_DELMOD_FIXED 0x00000000
-#define IOAPIC_REDTBL_DESTMOD_PHYS 0x00000000
-#define IOAPIC_REDTBL_INTPOL_HIGH 0x00000000
-#define IOAPIC_REDTBL_INTPOL_LOW 0x00002000
-#define IOAPIC_REDTBL_IRR 0x00004000
-#define IOAPIC_REDTBL_TRIGGER_EDGE 0x00000000
-#define IOAPIC_REDTBL_TRIGGER_LEVEL 0x00008000
-#define IOAPIC_REDTBL_MASK 0x00010000
+#define IOAPIC_IDX REG32(ISH_IOAPIC_BASE + 0x0)
+#define IOAPIC_WDW REG32(ISH_IOAPIC_BASE + 0x10)
+/* Bare address needed for assembler (ISH_IOAPIC_BASE + 0x40) */
+#define IOAPIC_EOI_REG_ADDR 0xFEC00040
+#define IOAPIC_EOI_REG REG32(IOAPIC_EOI_REG_ADDR)
+
+#define IOAPIC_VERSION (0x1)
+#define IOAPIC_IOREDTBL (0x10)
+#define IOAPIC_REDTBL_DELMOD_FIXED (0x00000000)
+#define IOAPIC_REDTBL_DESTMOD_PHYS (0x00000000)
+#define IOAPIC_REDTBL_INTPOL_HIGH (0x00000000)
+#define IOAPIC_REDTBL_INTPOL_LOW (0x00002000)
+#define IOAPIC_REDTBL_IRR (0x00004000)
+#define IOAPIC_REDTBL_TRIGGER_EDGE (0x00000000)
+#define IOAPIC_REDTBL_TRIGGER_LEVEL (0x00008000)
+#define IOAPIC_REDTBL_MASK (0x00010000)
/* WDT (Watchdog Timer) Registers */
-#define WDT_CONTROL REG32(ISH_WDT_BASE + 0x0)
-#define WDT_RELOAD REG32(ISH_WDT_BASE + 0x4)
-#define WDT_VALUES REG32(ISH_WDT_BASE + 0x8)
-#define WDT_CONTROL_ENABLE_BIT (1 << 17)
+#define WDT_CONTROL REG32(ISH_WDT_BASE + 0x0)
+#define WDT_RELOAD REG32(ISH_WDT_BASE + 0x4)
+#define WDT_VALUES REG32(ISH_WDT_BASE + 0x8)
+#define WDT_CONTROL_ENABLE_BIT BIT(17)
/* LAPIC registers */
-#define LAPIC_EOI_REG 0xFEE000B0
-#define LAPIC_ISR_REG 0xFEE00170
-#define LAPIC_IRR_REG (ISH_LAPIC_BASE + 0x200)
-#define LAPIC_ESR_REG (ISH_LAPIC_BASE + 0x280)
-#define LAPIC_ERR_RECV_ILLEGAL BIT(6)
-#define LAPIC_ICR_REG (ISH_LAPIC_BASE + 0x300)
+/* Bare address needed for assembler (ISH_LAPIC_BASE + 0xB0) */
+#define LAPIC_EOI_REG_ADDR 0xFEE000B0
+#define LAPIC_EOI_REG REG32(LAPIC_EOI_REG_ADDR)
+#define LAPIC_ISR_REG REG32(ISH_LAPIC_BASE + 0x100)
+#define LAPIC_ISR_LAST_REG REG32(ISH_LAPIC_BASE + 0x170)
+#define LAPIC_IRR_REG REG32(ISH_LAPIC_BASE + 0x200)
+#define LAPIC_ESR_REG REG32(ISH_LAPIC_BASE + 0x280)
+#define LAPIC_ERR_RECV_ILLEGAL BIT(6)
+#define LAPIC_ICR_REG REG32(ISH_LAPIC_BASE + 0x300)
/* SRAM control registers */
-#define ISH_SRAM_CTRL_BASE 0x00500000
-#define ISH_SRAM_CTRL_CSFGR REG32(ISH_SRAM_CTRL_BASE + 0x00)
-#define ISH_SRAM_CTRL_INTR REG32(ISH_SRAM_CTRL_BASE + 0x04)
-#define ISH_SRAM_CTRL_INTR_MASK REG32(ISH_SRAM_CTRL_BASE + 0x08)
+#define ISH_SRAM_CTRL_BASE 0x00500000
+#define ISH_SRAM_CTRL_CSFGR REG32(ISH_SRAM_CTRL_BASE + 0x00)
+#define ISH_SRAM_CTRL_INTR REG32(ISH_SRAM_CTRL_BASE + 0x04)
+#define ISH_SRAM_CTRL_INTR_MASK REG32(ISH_SRAM_CTRL_BASE + 0x08)
#define ISH_SRAM_CTRL_ERASE_CTRL REG32(ISH_SRAM_CTRL_BASE + 0x0c)
#define ISH_SRAM_CTRL_ERASE_ADDR REG32(ISH_SRAM_CTRL_BASE + 0x10)
#define ISH_SRAM_CTRL_BANK_STATUS REG32(ISH_SRAM_CTRL_BASE + 0x2c)
-/* Software defined registers */
-
#if defined(CHIP_FAMILY_ISH3)
/* on ISH3, reused ISH2PMC IPC message registers */
-#define SNOWBALL_BASE IPC_ISH2PMC_MSG_REGS
+#define SNOWBALL_BASE IPC_ISH2PMC_MSG_BASE
#else
/* from ISH4, used reserved rom part of AON memory */
-#define SNOWBALL_BASE CONFIG_ISH_AON_SRAM_ROM_START
+#define SNOWBALL_BASE (CONFIG_AON_ROM_BASE + 256)
#endif
/**
diff --git a/chip/ish/system.c b/chip/ish/system.c
index 6c6ce75828..6b7af5eb42 100644
--- a/chip/ish/system.c
+++ b/chip/ish/system.c
@@ -3,69 +3,104 @@
* found in the LICENSE file.
*/
-/* System module ISH (Not implemented) */
-
#include "clock.h"
#include "common.h"
#include "console.h"
#include "cpu.h"
#include "gpio.h"
+#include "hooks.h"
#include "host_command.h"
+#include "interrupts.h"
+#include "ish_fwst.h"
+#include "ish_persistent_data.h"
+#include "power_mgt.h"
#include "registers.h"
#include "shared_mem.h"
+#include "spi.h"
#include "system.h"
-#include "hooks.h"
#include "task.h"
#include "timer.h"
#include "util.h"
-#include "spi.h"
-#include "power_mgt.h"
-/* Indices for hibernate data registers (RAM backed by VBAT) */
-enum hibdata_index {
- HIBDATA_INDEX_SCRATCHPAD = 0, /* General-purpose scratchpad */
- HIBDATA_INDEX_SAVED_RESET_FLAGS /* Saved reset flags */
-};
+#define CPUTS(outstr) cputs(CC_SYSTEM, outstr)
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
int system_is_reboot_warm(void)
{
- return 0;
+ return !(system_get_reset_flags() &
+ (RESET_FLAG_POWER_ON | RESET_FLAG_HARD));
}
void system_pre_init(void)
{
-#ifdef CONFIG_LOW_POWER_IDLE
+ ish_fwst_set_fw_status(FWSTS_FW_IS_RUNNING);
+ task_enable_irq(ISH_FABRIC_IRQ);
ish_pm_init();
-#endif
+ ish_persistent_data_init();
}
void chip_save_reset_flags(int flags)
{
+ ish_persistent_data.reset_flags = flags;
}
uint32_t chip_read_reset_flags(void)
{
- return 0;
+ return ish_persistent_data.reset_flags;
}
-void _system_reset(int flags, int wake_from_hibernate)
+/*
+ * Kill the Minute-IA core and don't come back alive.
+ *
+ * Used when the watchdog timer exceeds max retries and we want to
+ * disable ISH completely.
+ */
+__attribute__((noreturn))
+static void system_halt(void)
{
-#ifdef CONFIG_LOW_POWER_IDLE
- /**
- * ish_pm_reset() do more (poweroff main SRAM etc) than
- * ish_mia_reset() which just reset the ISH minute-ia cpu core
- */
- ish_pm_reset();
-#else
- ish_mia_reset();
-#endif
+ cflush();
+
+ while (1) {
+ disable_all_interrupts();
+ WDT_CONTROL = 0;
+ CCU_TCG_EN = 1;
+ __asm__ volatile (
+ "cli\n"
+ "hlt\n");
+ }
}
void system_reset(int flags)
{
- _system_reset(flags, 0);
- while(1)
- ;
+ uint32_t save_flags;
+
+ /*
+ * We can't save any data when we do an ish_mia_reset(). Take
+ * the quick path out.
+ */
+ if (!IS_ENABLED(CONFIG_ISH_PM_AONTASK) || flags & SYSTEM_RESET_HARD) {
+ ish_mia_reset();
+ __builtin_unreachable();
+ }
+
+ system_encode_save_flags(flags, &save_flags);
+
+ if (flags & SYSTEM_RESET_AP_WATCHDOG) {
+ save_flags |= RESET_FLAG_WATCHDOG;
+ ish_persistent_data.watchdog_counter += 1;
+ if (ish_persistent_data.watchdog_counter
+ >= CONFIG_WATCHDOG_MAX_RETRIES) {
+ CPRINTS("Halting ISH due to max watchdog resets");
+ system_halt();
+ }
+ }
+
+ chip_save_reset_flags(save_flags);
+
+ ish_persistent_data_commit();
+ ish_pm_reset();
+ __builtin_unreachable();
}
const char *system_get_chip_vendor(void)
@@ -138,3 +173,16 @@ uint32_t system_get_lfw_address(void)
void system_set_image_copy(enum system_image_copy_t copy)
{
}
+
+static void fabric_isr(void)
+{
+ /**
+ * clear fabric error status, otherwise it will wakeup ISH immediately
+ * when entered low power mode.
+ * TODO(b:130740646): figure out why this issue happens.
+ */
+ if (FABRIC_AGENT_STATUS & FABRIC_MIA_STATUS_BIT_ERR)
+ FABRIC_AGENT_STATUS = FABRIC_AGENT_STATUS;
+}
+
+DECLARE_IRQ(ISH_FABRIC_IRQ, fabric_isr);
diff --git a/chip/ish/system_state_subsys.c b/chip/ish/system_state_subsys.c
index 81a0a6d7b7..5a0fe582b5 100644
--- a/chip/ish/system_state_subsys.c
+++ b/chip/ish/system_state_subsys.c
@@ -22,12 +22,12 @@
/* the following "define"s and structures are from host driver
* and they are slightly modified for look&feel purpose.
*/
-#define SYSTEM_STATE_SUBSCRIBE 0x1
-#define SYSTEM_STATE_STATUS 0x2
-#define SYSTEM_STATE_QUERY_SUBSCRIBERS 0x3
-#define SYSTEM_STATE_STATE_CHANGE_REQ 0x4
+#define SYSTEM_STATE_SUBSCRIBE 0x1
+#define SYSTEM_STATE_STATUS 0x2
+#define SYSTEM_STATE_QUERY_SUBSCRIBERS 0x3
+#define SYSTEM_STATE_STATE_CHANGE_REQ 0x4
-#define SUSPEND_STATE_BIT (1<<1) /* suspend/resume */
+#define SUSPEND_STATE_BIT BIT(1) /* suspend/resume */
/* Cached state of ISH's requested power rails when AP suspends */
#ifdef CHIP_FAMILY_ISH5
diff --git a/chip/ish/uart.c b/chip/ish/uart.c
index 5076f5eeaf..1e5963daf2 100644
--- a/chip/ish/uart.c
+++ b/chip/ish/uart.c
@@ -46,6 +46,13 @@ static struct uart_ctx uart_ctx[UART_DEVICES] = {
.input_freq = UART_ISH_INPUT_FREQ,
.addr_interval = UART_ISH_ADDR_INTERVAL,
.uart_state = UART_STATE_CG,
+ },
+ {
+ .id = 2,
+ .base = UART2_BASE,
+ .input_freq = UART_ISH_INPUT_FREQ,
+ .addr_interval = UART_ISH_ADDR_INTERVAL,
+ .uart_state = UART_STATE_CG,
}
};
@@ -58,82 +65,60 @@ int uart_init_done(void)
void uart_tx_start(void)
{
-#if !defined(CONFIG_POLLING_UART)
-
- enum UART_PORT id = ISH_DEBUG_UART; /* UART for ISH */
-
- if (REG8(IER(id)) & IER_TDRQ)
- return;
-
- /* Do not allow deep sleep while transmit in progress */
- disable_sleep(SLEEP_MASK_UART);
-
- /* TODO: disable low power mode while transmit */
+ if (!IS_ENABLED(CONFIG_POLLING_UART)) {
+ if (IER(ISH_DEBUG_UART) & IER_TDRQ)
+ return;
- REG8(IER(id)) |= IER_TDRQ;
+ /* Do not allow deep sleep while transmit in progress */
+ disable_sleep(SLEEP_MASK_UART);
- task_trigger_irq(ISH_DEBUG_UART_IRQ);
-
-#endif
+ IER(ISH_DEBUG_UART) |= IER_TDRQ;
+ }
}
void uart_tx_stop(void)
{
-#if !defined(CONFIG_POLLING_UART)
- enum UART_PORT id = ISH_DEBUG_UART; /* UART for ISH */
-
- /* Re-allow deep sleep */
- enable_sleep(SLEEP_MASK_UART);
-
- REG8(IER(id)) &= ~IER_TDRQ;
+ if (!IS_ENABLED(CONFIG_POLLING_UART)) {
+ /* Re-allow deep sleep */
+ enable_sleep(SLEEP_MASK_UART);
- /* TODO: re-enable low power mode */
-#endif
+ IER(ISH_DEBUG_UART) &= ~IER_TDRQ;
+ }
}
void uart_tx_flush(void)
{
-#if !defined(CONFIG_POLLING_UART)
- enum UART_PORT id = ISH_DEBUG_UART; /* UART for ISH */
-
- while (!(REG8(LSR(id)) & LSR_TEMT) )
- ;
-#endif
+ if (!IS_ENABLED(CONFIG_POLLING_UART)) {
+ while (!(LSR(ISH_DEBUG_UART) & LSR_TEMT))
+ continue;
+ }
}
int uart_tx_ready(void)
{
- return 1;
+ return LSR(ISH_DEBUG_UART) & LSR_TDRQ;
}
int uart_rx_available(void)
{
-#if !defined(CONFIG_POLLING_UART)
- enum UART_PORT id = ISH_DEBUG_UART; /* UART for ISH */
+ if (IS_ENABLED(CONFIG_POLLING_UART))
+ return 0;
- return REG8(LSR(id)) & LSR_DR;
-#else
- return 0;
-#endif
+ return LSR(ISH_DEBUG_UART) & LSR_DR;
}
void uart_write_char(char c)
{
- enum UART_PORT id = ISH_DEBUG_UART; /* UART for ISH */
-
/* Wait till reciever is ready */
- while ((REG8(LSR(id)) & LSR_TEMT) == 0)
- ;
+ while (!uart_tx_ready())
+ continue;
- REG8(THR(id)) = c;
+ THR(ISH_DEBUG_UART) = c;
}
-#if !defined(CONFIG_POLLING_UART)
int uart_read_char(void)
{
- enum UART_PORT id = ISH_DEBUG_UART; /* UART for ISH */
-
- return REG8(RBR(id));
+ return RBR(ISH_DEBUG_UART);
}
void uart_ec_interrupt(void)
@@ -142,8 +127,9 @@ void uart_ec_interrupt(void)
uart_process_input();
uart_process_output();
}
+#ifndef CONFIG_POLLING_UART
DECLARE_IRQ(ISH_DEBUG_UART_IRQ, uart_ec_interrupt);
-#endif /* !defined(CONFIG_POLLING_UART) */
+#endif
static int uart_return_baud_rate_by_id(int baud_rate_id)
{
@@ -167,42 +153,43 @@ static void uart_hw_init(enum UART_PORT id)
/* Calculate baud rate divisor */
divisor = (ctx->input_freq / ctx->baud_rate) >> 4;
- REG32(MUL(ctx->id)) = (divisor * ctx->baud_rate);
- REG32(DIV(ctx->id)) = (ctx->input_freq / 16);
- REG32(PS(ctx->id)) = 16;
+ MUL(ctx->id) = (divisor * ctx->baud_rate);
+ DIV(ctx->id) = (ctx->input_freq / 16);
+ PS(ctx->id) = 16;
/* Set the DLAB to access the baud rate divisor registers */
- REG8(LCR(ctx->id)) = LCR_DLAB;
- REG8(DLL(ctx->id)) = (divisor & 0xff);
- REG8(DLH(ctx->id)) = ((divisor >> 8) & 0xff);
+ LCR(ctx->id) = LCR_DLAB;
+ DLL(ctx->id) = (divisor & 0xff);
+ DLH(ctx->id) = ((divisor >> 8) & 0xff);
/* 8 data bits, 1 stop bit, no parity, clear DLAB */
- REG8(LCR(ctx->id)) = LCR_8BIT_CHR;
+ LCR(ctx->id) = LCR_8BIT_CHR;
if (ctx->client_flags & UART_CONFIG_HW_FLOW_CONTROL)
mcr = MCR_AUTO_FLOW_EN;
- mcr |= MCR_INTR_ENABLE; /* needs to be set regardless of flow control */
+ /* needs to be set regardless of flow control */
+ mcr |= MCR_INTR_ENABLE;
mcr |= (MCR_RTS | MCR_DTR);
- REG8(MCR(ctx->id)) = mcr;
+ MCR(ctx->id) = mcr;
fcr = FCR_FIFO_SIZE_64 | FCR_ITL_FIFO_64_BYTES_1;
/* configure FIFOs */
- REG8(FCR(ctx->id)) = (fcr | FCR_FIFO_ENABLE
- | FCR_RESET_RX | FCR_RESET_TX);
+ FCR(ctx->id) = (fcr | FCR_FIFO_ENABLE
+ | FCR_RESET_RX | FCR_RESET_TX);
/* enable UART unit */
- REG32(ABR(ctx->id)) = ABR_UUE;
+ ABR(ctx->id) = ABR_UUE;
/* clear the port */
- REG8(RBR(ctx->id));
-#ifdef CONFIG_POLLING_UART
- REG8(IER(ctx->id)) = 0x00;
-#else
- REG8(IER(ctx->id)) = IER_RECV;
-#endif
+ RBR(ctx->id);
+
+ if (IS_ENABLED(CONFIG_POLLING_UART))
+ IER(ctx->id) = 0x00;
+ else
+ IER(ctx->id) = IER_RECV;
}
static void uart_stop_hw(enum UART_PORT id)
@@ -213,25 +200,24 @@ static void uart_stop_hw(enum UART_PORT id)
/* Manually clearing the fifo from possible noise.
* Entering D0i3 when fifo is not cleared may result in a hang.
*/
- fifo_len = (REG32(FOR(id)) & FOR_OCCUPANCY_MASK) >> FOR_OCCUPANCY_OFFS;
+ fifo_len = (FOR(id) & FOR_OCCUPANCY_MASK) >> FOR_OCCUPANCY_OFFS;
for (i = 0; i < fifo_len; i++)
- (void)REG8(RBR(id));
+ (void)RBR(id);
/* No interrupts are enabled */
- REG8(IER(id)) = 0;
- REG8(MCR(id)) = 0;
+ IER(id) = 0;
+ MCR(id) = 0;
/* Clear and disable FIFOs */
- REG8(FCR(id)) = (FCR_RESET_RX | FCR_RESET_TX);
+ FCR(id) = (FCR_RESET_RX | FCR_RESET_TX);
/* Disable uart unit */
- REG32(ABR(id)) = 0;
+ ABR(id) = 0;
}
static int uart_client_init(enum UART_PORT id, uint32_t baud_rate_id, int flags)
{
-
if ((uart_ctx[id].base == 0) || (id >= UART_DEVICES))
return UART_ERROR;
@@ -254,7 +240,6 @@ static int uart_client_init(enum UART_PORT id, uint32_t baud_rate_id, int flags)
static void uart_drv_init(void)
{
int i;
- uint32_t fifo_len;
/* Disable UART */
for (i = 0; i < UART_DEVICES; i++)
@@ -263,34 +248,15 @@ static void uart_drv_init(void)
/* Enable HSU global interrupts (DMA/U0/U1) and set PMEN bit
* to allow PMU to clock gate ISH
*/
- REG32(HSU_BASE + HSU_REG_GIEN) = (GIEN_DMA_EN | GIEN_UART0_EN
- | GIEN_UART1_EN | GIEN_PWR_MGMT);
-
- /* There is a by design HW "bug" where all UARTs are enabled by default
- * but they must be disbled to enter clock gating.
- * UART0 and UART1 are disabled during their init - but we don't init
- * UART2 so as a w/a we disable UART2 even though it isn't being used.
- * we also clear UART 2 fifo, which may cause problem entrying TCG is
- * not empty (we do the same for UART0 and 1 in "uart_stop_hw"
- */
-
- fifo_len = (REG32(UART2_BASE + UART_REG_FOR)
- & FOR_OCCUPANCY_MASK) >> FOR_OCCUPANCY_OFFS;
-
- for (i = 0; i < fifo_len; i++)
- (void)REG8((UART2_BASE + UART_REG_RBR));
-
- REG32(UART2_BASE + UART_REG_ABR) = 0;
+ HSU_REG_GIEN = (GIEN_DMA_EN | GIEN_UART0_EN
+ | GIEN_UART1_EN | GIEN_PWR_MGMT);
task_enable_irq(ISH_DEBUG_UART_IRQ);
}
void uart_init(void)
{
-
uart_drv_init();
-
uart_client_init(ISH_DEBUG_UART, B115200, 0);
-
init_done = 1;
}
diff --git a/chip/ish/uart_defs.h b/chip/ish/uart_defs.h
index 6a04557c58..ed37c47d13 100644
--- a/chip/ish/uart_defs.h
+++ b/chip/ish/uart_defs.h
@@ -11,169 +11,169 @@
#include <stdint.h>
#include <stddef.h>
-#define UART_ERROR -1
+#define UART_ERROR -1
#define UART_BUSY -2
#define HSU_BASE ISH_UART_BASE
-#define UART0_OFFS (0x80)
-#define UART0_BASE (ISH_UART_BASE + UART0_OFFS)
-#define UART0_SIZE (0x80)
+#define UART0_OFFS (0x80)
+#define UART0_BASE (ISH_UART_BASE + UART0_OFFS)
+#define UART0_SIZE (0x80)
-#define UART1_OFFS (0x100)
-#define UART1_BASE (ISH_UART_BASE + UART1_OFFS)
-#define UART1_SIZE (0x80)
+#define UART1_OFFS (0x100)
+#define UART1_BASE (ISH_UART_BASE + UART1_OFFS)
+#define UART1_SIZE (0x80)
-#define UART2_OFFS (0x180)
-#define UART2_BASE (ISH_UART_BASE + UART2_OFFS)
-#define UART2_SIZE (0x80)
+#define UART2_OFFS (0x180)
+#define UART2_BASE (ISH_UART_BASE + UART2_OFFS)
+#define UART2_SIZE (0x80)
+
+#define UART_REG(size, name, n) \
+ REG##size(uart_ctx[n].base + \
+ UART_OFFSET_##name * uart_ctx[n].addr_interval)
/* Register accesses */
-#define LSR(n) (uart_ctx[n].base + UART_REG_LSR * uart_ctx[n].addr_interval)
-#define THR(n) (uart_ctx[n].base + UART_REG_THR * uart_ctx[n].addr_interval)
-#define FOR(n) (uart_ctx[n].base + UART_REG_FOR * uart_ctx[n].addr_interval)
-#define RBR(n) (uart_ctx[n].base + UART_REG_RBR * uart_ctx[n].addr_interval)
-#define DLL(n) (uart_ctx[n].base + UART_REG_DLL * uart_ctx[n].addr_interval)
-#define DLH(n) (uart_ctx[n].base + UART_REG_DLH * uart_ctx[n].addr_interval)
-#define DLD(n) (uart_ctx[n].base + UART_REG_DLD * uart_ctx[n].addr_interval)
-#define IER(n) (uart_ctx[n].base + UART_REG_IER * uart_ctx[n].addr_interval)
-#define IIR(n) (uart_ctx[n].base + UART_REG_IIR * uart_ctx[n].addr_interval)
-#define FCR(n) (uart_ctx[n].base + UART_REG_FCR * uart_ctx[n].addr_interval)
-#define LCR(n) (uart_ctx[n].base + UART_REG_LCR * uart_ctx[n].addr_interval)
-#define MCR(n) (uart_ctx[n].base + UART_REG_MCR * uart_ctx[n].addr_interval)
-#define MSR(n) (uart_ctx[n].base + UART_REG_MSR * uart_ctx[n].addr_interval)
-#define FCTR(n) (uart_ctx[n].base + UART_REG_FCTR * uart_ctx[n].addr_interval)
-#define EFR(n) (uart_ctx[n].base + UART_REG_EFR * uart_ctx[n].addr_interval)
-#define RXTRG(n) \
- (uart_ctx[n].base + UART_REG_RXTRG * uart_ctx[n].addr_interval)
-#define ABR(n) (uart_ctx[n].base + UART_REG_ABR * uart_ctx[n].addr_interval)
-#define PS(n) (uart_ctx[n].base + UART_REG_PS * uart_ctx[n].addr_interval)
-#define MUL(n) (uart_ctx[n].base + UART_REG_MUL * uart_ctx[n].addr_interval)
-#define DIV(n) (uart_ctx[n].base + UART_REG_DIV * uart_ctx[n].addr_interval)
+#define LSR(n) UART_REG(8, LSR, n)
+#define THR(n) UART_REG(8, THR, n)
+#define FOR(n) UART_REG(32, FOR, n)
+#define RBR(n) UART_REG(8, RBR, n)
+#define DLL(n) UART_REG(8, DLL, n)
+#define DLH(n) UART_REG(8, DLH, n)
+#define DLD(n) UART_REG(8, DLD, n)
+#define IER(n) UART_REG(8, IER, n)
+#define IIR(n) UART_REG(8, IIR, n)
+#define FCR(n) UART_REG(8, FCR, n)
+#define LCR(n) UART_REG(8, LCR, n)
+#define MCR(n) UART_REG(8, MCR, n)
+#define MSR(n) UART_REG(8, MSR, n)
+#define ABR(n) UART_REG(32, ABR, n)
+#define PS(n) UART_REG(32, PS, n)
+#define MUL(n) UART_REG(32, MUL, n)
+#define DIV(n) UART_REG(32, DIV, n)
/* RBR: Receive Buffer register (BLAB bit = 0) */
-#define UART_REG_RBR (0)
+#define UART_OFFSET_RBR (0)
/* THR: Transmit Holding register (BLAB bit = 0) */
-#define UART_REG_THR (0)
+#define UART_OFFSET_THR (0)
/* IER: Interrupt Enable register (BLAB bit = 0) */
-#define UART_REG_IER (1)
+#define UART_OFFSET_IER (1)
#define FCR_FIFO_SIZE_16 (0x00)
#define FCR_FIFO_SIZE_64 (0x20)
-#define FCR_ITL_FIFO_64_BYTES_1 (0x00)
+#define FCR_ITL_FIFO_64_BYTES_1 (0x00)
/* FCR: FIFO Control register */
-#define UART_REG_FCR (2)
-#define FCR_FIFO_ENABLE (0x01)
-#define FCR_RESET_RX (0x02)
-#define FCR_RESET_TX (0x04)
+#define UART_OFFSET_FCR (2)
+#define FCR_FIFO_ENABLE BIT(0)
+#define FCR_RESET_RX BIT(1)
+#define FCR_RESET_TX BIT(2)
/* LCR: Line Control register */
-#define UART_REG_LCR (3)
-#define LCR_DLAB (0x80)
-#define LCR_5BIT_CHR (0x00)
-#define LCR_6BIT_CHR (0x01)
-#define LCR_7BIT_CHR (0x02)
-#define LCR_8BIT_CHR (0x03)
-#define LCR_BIT_CHR_MASK (0x03)
-#define LCR_SB (0x40) /*Set Break */
+#define UART_OFFSET_LCR (3)
+#define LCR_DLAB (0x80)
+#define LCR_5BIT_CHR (0x00)
+#define LCR_6BIT_CHR (0x01)
+#define LCR_7BIT_CHR (0x02)
+#define LCR_8BIT_CHR (0x03)
+#define LCR_BIT_CHR_MASK (0x03)
+#define LCR_SB (0x40) /* Set Break */
/* MCR: Modem Control register */
-#define UART_REG_MCR (4)
-#define MCR_DTR (0x1)
-#define MCR_RTS (0x2)
-#define MCR_LOO (0x10)
-#define MCR_INTR_ENABLE (0x08)
-#define MCR_AUTO_FLOW_EN (0x20)
+#define UART_OFFSET_MCR (4)
+#define MCR_DTR BIT(0)
+#define MCR_RTS BIT(1)
+#define MCR_LOO BIT(4)
+#define MCR_INTR_ENABLE BIT(3)
+#define MCR_AUTO_FLOW_EN BIT(5)
/* LSR: Line Status register */
-#define UART_REG_LSR (5)
-#define LSR_DR (0x01) /* Data Ready */
-#define LSR_OE (0x02) /* Overrun error */
-#define LSR_PE (0x04) /* Parity error */
-#define LSR_FE (0x08) /* Framing error */
-#define LSR_BI (0x10) /* Breaking interrupt */
-#define LSR_THR_EMPTY (0x20) /* Non FIFO mode: Transmit holding
+#define UART_OFFSET_LSR (5)
+#define LSR_DR BIT(0) /* Data Ready */
+#define LSR_OE BIT(1) /* Overrun error */
+#define LSR_PE BIT(2) /* Parity error */
+#define LSR_FE BIT(3) /* Framing error */
+#define LSR_BI BIT(4) /* Breaking interrupt */
+#define LSR_THR_EMPTY BIT(5) /* Non FIFO mode: Transmit holding
* register empty
*/
-#define LSR_TDRQ (0x20) /* FIFO mode: Transmit Data request */
-#define LSR_TEMT (0x40) /* Transmitter empty */
+#define LSR_TDRQ BIT(5) /* FIFO mode: Transmit Data request */
+#define LSR_TEMT BIT(6) /* Transmitter empty */
-#define FCR_ITL_FIFO_64_BYTES_56 (0xc0)
+#define FCR_ITL_FIFO_64_BYTES_56 (BIT(6) | BIT(7))
-#define IER_RECV (0x01)
-#define IER_TDRQ (0x02)
-#define IER_LINE_STAT (0x04)
+#define IER_RECV BIT(0)
+#define IER_TDRQ BIT(1)
+#define IER_LINE_STAT BIT(2)
-#define UART_REG_IIR (2)
+#define UART_OFFSET_IIR (2)
/* MSR: Modem Status register */
-#define UART_REG_MSR (6)
+#define UART_OFFSET_MSR (6)
/* DLL: Divisor Latch Reg. low byte (BLAB bit = 1) */
-#define UART_REG_DLL (0)
+#define UART_OFFSET_DLL (0)
/* DLH: Divisor Latch Reg. high byte (BLAB bit = 1) */
-#define UART_REG_DLH (1)
+#define UART_OFFSET_DLH (1)
/* DLH: Divisor Latch Fractional. (BLAB bit = 1) */
-#define UART_REG_DLD (2)
+#define UART_OFFSET_DLD (2)
/* FOR: Fifo O Register (ISH only) */
-#define UART_REG_FOR (0x20)
-#define FOR_OCCUPANCY_OFFS 0
-#define FOR_OCCUPANCY_MASK 0x7F
+#define UART_OFFSET_FOR (0x20)
+#define FOR_OCCUPANCY_OFFS 0
+#define FOR_OCCUPANCY_MASK 0x7F
/* ABR: Auto-Baud Control Register (ISH only) */
-#define UART_REG_ABR (0x24)
-#define ABR_UUE (0x10)
+#define UART_OFFSET_ABR (0x24)
+#define ABR_UUE BIT(4)
/* Pre-Scalar Register (ISH only) */
-#define UART_REG_PS (0x30)
+#define UART_OFFSET_PS (0x30)
/* DDS registers (ISH only) */
-#define UART_REG_MUL (0x34)
-#define UART_REG_DIV (0x38)
+#define UART_OFFSET_MUL (0x34)
+#define UART_OFFSET_DIV (0x38)
/* G_IEN: Global Interrupt Enable (ISH only) */
-#define HSU_REG_GIEN (0)
-#define HSU_REG_GIST (4)
-
-#define GIEN_PWR_MGMT (0x01000000)
-#define GIEN_DMA_EN (0x00000020)
-#define GIEN_UART2_EN (0x00000004)
-#define GIEN_UART1_EN (0x00000002)
-#define GIEN_UART0_EN (0x00000001)
-#define GIST_DMA_EN (0x00000020)
-#define GIST_UART2_EN (0x00000004)
-#define GIST_UART1_EN (0x00000002)
-#define GIST_UART0_EN (0x00000001)
-#define GIST_UARTx_EN (GIST_UART0_EN|GIST_UART1_EN|GIST_UART2_EN)
+#define HSU_REG_GIEN REG32(HSU_BASE + 0x0)
+#define HSU_REG_GIST REG32(HSU_BASE + 0x4)
+
+#define GIEN_PWR_MGMT BIT(24)
+#define GIEN_DMA_EN BIT(5)
+#define GIEN_UART2_EN BIT(2)
+#define GIEN_UART1_EN BIT(1)
+#define GIEN_UART0_EN BIT(0)
+#define GIST_DMA_EN BIT(5)
+#define GIST_UART2_EN BIT(2)
+#define GIST_UART1_EN BIT(1)
+#define GIST_UART0_EN BIT(0)
+#define GIST_UARTx_EN (GIST_UART0_EN|GIST_UART1_EN|GIST_UART2_EN)
/* UART config flag, send to sc_io_control if the current UART line has HW
* flow control lines connected.
*/
-#define UART_CONFIG_HW_FLOW_CONTROL BIT(0)
+#define UART_CONFIG_HW_FLOW_CONTROL BIT(0)
- /* UART config flag for sc_io_control. If defined a sc_io_event_rx_msg is
- * raised only when the rx buffer is completely full. Otherwise, the event
- * is raised after a timeout is received on the UART line,
- * and all data received until now is provided.
- */
-#define UART_CONFIG_DELIVER_FULL_RX_BUF BIT(1)
+/* UART config flag for sc_io_control. If defined a sc_io_event_rx_msg is
+ * raised only when the rx buffer is completely full. Otherwise, the event
+ * is raised after a timeout is received on the UART line,
+ * and all data received until now is provided.
+ */
+#define UART_CONFIG_DELIVER_FULL_RX_BUF BIT(1)
/* UART config flag for sc_io_control. If defined a sc_io_event_rx_buf_depleted
* is raised when all rx buffers that were added are full. Otherwise, no
* event is raised.
*/
-#define UART_CONFIG_ANNOUNCE_DEPLETED_BUF BIT(2)
+#define UART_CONFIG_ANNOUNCE_DEPLETED_BUF BIT(2)
-#define UART_INT_DEVICES 2
+#define UART_INT_DEVICES 3
#define UART_EXT_DEVICES 8
#define UART_DEVICES UART_INT_DEVICES
#define UART_ISH_ADDR_INTERVAL 1
#define B9600 0x0000d
#define B57600 0x00000018
-#define B115200 0x00000011
-#define B921600 0x00000012
+#define B115200 0x00000011
+#define B921600 0x00000012
#define B2000000 0x00000013
#define B3000000 0x00000014
#define B3250000 0x00000015
@@ -223,4 +223,4 @@ struct uart_ctx {
uint32_t client_flags;
};
-#endif /* _CROS_EC_UART_DEFS_H_ */
+#endif /* _CROS_EC_UART_DEFS_H_ */
diff --git a/chip/ish/watchdog.c b/chip/ish/watchdog.c
index d9a9d84008..2e83b57f25 100644
--- a/chip/ish/watchdog.c
+++ b/chip/ish/watchdog.c
@@ -20,8 +20,8 @@
*/
#include "common.h"
-#include "console.h"
#include "hooks.h"
+#include "ish_persistent_data.h"
#include "task.h"
#include "registers.h"
#include "system.h"
@@ -33,6 +33,13 @@
int watchdog_init(void)
{
+ /*
+ * Put reset counter back at zero if last reset was not caused
+ * by watchdog
+ */
+ if ((system_get_reset_flags() & RESET_FLAG_WATCHDOG) == 0)
+ ish_persistent_data.watchdog_counter = 0;
+
/* Initialize WDT clock divider */
CCU_WDT_CD = WDT_CLOCK_HZ / 10; /* 10 Hz => 100 ms period */
@@ -46,42 +53,6 @@ int watchdog_init(void)
return EC_SUCCESS;
}
-void watchdog_enable(void)
-{
- WDT_CONTROL |= WDT_CONTROL_ENABLE_BIT;
-}
-
-void watchdog_disable(void)
-{
- WDT_CONTROL &= ~WDT_CONTROL_ENABLE_BIT;
-}
-
-/* Parameters are pushed by hardware, we only care about %EIP */
-__attribute__ ((noreturn))
-void watchdog_warning(uint32_t errorcode,
- uint32_t eip,
- uint32_t cs,
- uint32_t eflags)
-{
- ccprintf("\nWDT Expired. EIP was 0x%08X. Resetting...\n", eip);
- cflush();
-
- system_reset(SYSTEM_RESET_AP_WATCHDOG);
- __builtin_unreachable();
-}
-
-__attribute__ ((noreturn))
-void watchdog_warning_irq(void)
-{
- /*
- * Parameters to watchdog_warning were pushed by hardware, use
- * asm here to re-use these parameters in the call.
- */
- __asm__ ("call watchdog_warning\n");
- __builtin_unreachable();
-}
-DECLARE_IRQ(ISH_WDT_IRQ, watchdog_warning_irq);
-
void watchdog_reload(void)
{
/*
diff --git a/chip/mt_scp/build.mk b/chip/mt_scp/build.mk
index 03937f8591..af1ba08170 100644
--- a/chip/mt_scp/build.mk
+++ b/chip/mt_scp/build.mk
@@ -10,7 +10,7 @@ CORE:=cortex-m
CFLAGS_CPU+=-march=armv7e-m -mcpu=cortex-m4
# Required chip modules
-chip-y=clock.o gpio.o memmap.o stepping_stone.o system.o uart.o
+chip-y=clock.o gpio.o memmap.o system.o uart.o
# Optional chip modules
chip-$(CONFIG_COMMON_TIMER)+=hrtimer.o
diff --git a/chip/mt_scp/clock.c b/chip/mt_scp/clock.c
index 14ea5ea67a..311346eb8f 100644
--- a/chip/mt_scp/clock.c
+++ b/chip/mt_scp/clock.c
@@ -96,6 +96,19 @@ static void scp_ulposc_config(int osc)
AP_ULPOSC_CON13(osc) |= OSC_DIV2_EN;
}
+static inline void busy_udelay(int usec)
+{
+ /*
+ * Delaying by busy-looping, for place that can't use udelay because of
+ * the clock not configured yet. The value 28 is chosen approximately
+ * from experiment.
+ */
+ volatile int i = usec * 28;
+
+ while (i--)
+ ;
+}
+
void scp_clock_high_enable(int osc)
{
/* Enable high speed clock */
@@ -104,14 +117,14 @@ void scp_clock_high_enable(int osc)
switch (osc) {
case 0:
/* After 25ms, enable ULPOSC */
- udelay(25 * MSEC);
+ busy_udelay(25 * MSEC);
SCP_CLK_EN |= CG_CLK_HIGH;
break;
case 1:
/* Turn off ULPOSC2 high-core-disable switch */
SCP_CLK_ON_CTRL &= ~HIGH_CORE_DIS_SUB;
/* After 25ms, turn on ULPOSC2 high core clock gate */
- udelay(25 * MSEC);
+ busy_udelay(25 * MSEC);
SCP_CLK_HIGH_CORE |= CLK_HIGH_CORE_CG;
break;
default:
diff --git a/chip/mt_scp/config_chip.h b/chip/mt_scp/config_chip.h
index 91ee880985..d2e6ca3b9d 100644
--- a/chip/mt_scp/config_chip.h
+++ b/chip/mt_scp/config_chip.h
@@ -37,7 +37,6 @@
#define CONFIG_RW_STORAGE_OFF 0
#define CONFIG_PROGRAM_MEMORY_BASE 0
#define CONFIG_MAPPED_STORAGE_BASE 0
-#define CONFIG_CHIP_MEMORY_REGIONS
/* Unsupported features/commands */
#undef CONFIG_CMD_FLASHINFO
diff --git a/chip/mt_scp/ipi.c b/chip/mt_scp/ipi.c
index d41b39f4d4..7b42da2423 100644
--- a/chip/mt_scp/ipi.c
+++ b/chip/mt_scp/ipi.c
@@ -28,6 +28,7 @@
#include "system.h"
#include "task.h"
#include "util.h"
+#include "hwtimer.h"
#define CPRINTF(format, args...) cprintf(CC_IPI, format, ##args)
#define CPRINTS(format, args...) cprints(CC_IPI, format, ##args)
@@ -181,11 +182,18 @@ void ipi_inform_ap(void)
#ifdef HAS_TASK_HOSTCMD
#if defined(CONFIG_MKBP_USE_CUSTOM)
-void mkbp_set_host_active_via_custom(int active)
+int mkbp_set_host_active_via_custom(int active, uint32_t *timestamp)
{
static const uint8_t hc_evt_obj = HOSTCMD_TYPE_HOSTEVENT;
+
+ /* This should be moved into ipi_send for more accuracy */
+ if (timestamp)
+ *timestamp = __hw_clock_source_read();
+
if (active)
- ipi_send(IPI_HOST_COMMAND, &hc_evt_obj, sizeof(hc_evt_obj), 1);
+ return ipi_send(IPI_HOST_COMMAND, &hc_evt_obj,
+ sizeof(hc_evt_obj), 1);
+ return EC_SUCCESS;
}
#endif
diff --git a/chip/mt_scp/memory_regions.inc b/chip/mt_scp/memory_regions.inc
deleted file mode 100644
index e87df4c51d..0000000000
--- a/chip/mt_scp/memory_regions.inc
+++ /dev/null
@@ -1 +0,0 @@
-REGION_LOAD(stepping_stone, rwx, 0x00000, 0x8)
diff --git a/chip/mt_scp/stepping_stone.c b/chip/mt_scp/stepping_stone.c
deleted file mode 100644
index 0e880b1924..0000000000
--- a/chip/mt_scp/stepping_stone.c
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Copyright 2018 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * mt_scp Stepping Stone functions on CPU reset.
- *
- * SCP assumes vector table at CONFIG_RAM_BASE. However, on cortex-m resetting,
- * it would load 0x0 to SP(r13) and load 0x04 to PC(r15). Stepping stones copy
- * these two very special values from CONFIG_RAM_BASE, CONFIG_RAM_BASE + 0x04
- * to 0x0, 0x4 resepctively.
- */
-
-#include "common.h"
-#include "link_defs.h"
-
-extern void stack_end(void); /* not technically correct, it's just a pointer */
-extern void reset(void);
-
-__SECTION_KEEP(stepping_stone) const void *ss_header[2] = {
- &stack_end,
- &reset
-};
diff --git a/chip/npcx/config_chip-npcx7.h b/chip/npcx/config_chip-npcx7.h
index a932fa5836..fe88bc3016 100644
--- a/chip/npcx/config_chip-npcx7.h
+++ b/chip/npcx/config_chip-npcx7.h
@@ -20,14 +20,15 @@
/* The optional hardware features depend on chip variant */
#if defined(CHIP_VARIANT_NPCX7M6F) || defined(CHIP_VARIANT_NPCX7M6FB) || \
- defined(CHIP_VARIANT_NPCX7M6FC) || defined(CHIP_VARIANT_NPCX7M7WB)
+ defined(CHIP_VARIANT_NPCX7M6FC) || defined(CHIP_VARIANT_NPCX7M7WB) || \
+ defined(CHIP_VARIANT_NPCX7M7WC)
#define NPCX_INT_FLASH_SUPPORT /* Internal flash support */
#define NPCX_PSL_MODE_SUPPORT /* Power switch logic mode for ultra-low power */
#define NPCX_EXT32K_OSC_SUPPORT /* External 32KHz crytal osc. input support */
#endif
#if defined(CHIP_VARIANT_NPCX7M6FB) || defined(CHIP_VARIANT_NPCX7M6FC) || \
- defined(CHIP_VARIANT_NPCX7M7WB)
+ defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
#define NPCX_UART_FIFO_SUPPORT
/* Number of UART modules. */
#define NPCX_SECOND_UART
@@ -36,7 +37,7 @@
#define UART_MODULE_COUNT 1
#endif
-#ifdef CHIP_VARIANT_NPCX7M7WB
+#if defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
#define NPCX_WOV_SUPPORT /* Audio front-end for Wake-on-Voice support */
#endif
@@ -60,9 +61,17 @@
/*****************************************************************************/
/* Memory mapping */
#define NPCX_BTRAM_SIZE 0x800 /* 2KB data ram used by booter. */
+
+#if defined(CHIP_VARIANT_NPCX7M6F) || defined(CHIP_VARIANT_NPCX7M6FB) || \
+ defined(CHIP_VARIANT_NPCX7M6FC) || defined(CHIP_VARIANT_NPCX7M6G)
#define CONFIG_RAM_BASE 0x200C0000 /* memory address of data ram */
/* 62 KB data RAM + 2 KB BT RAM size */
#define CONFIG_DATA_RAM_SIZE 0x00010000
+#elif defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
+#define CONFIG_RAM_BASE 0x200B0000 /* memory address of data ram */
+/* 126 KB data RAM + 2 KB BT RAM size */
+#define CONFIG_DATA_RAM_SIZE 0x00020000
+#endif
#define CONFIG_RAM_SIZE (CONFIG_DATA_RAM_SIZE - NPCX_BTRAM_SIZE)
/* no low power ram in npcx7 series */
@@ -73,10 +82,10 @@
#define NPCX_PROGRAM_MEMORY_SIZE (192 * 1024)
/* program memory base address for 192KB Code RAM (ie. 0x100C0000 - 192KB) */
#define CONFIG_PROGRAM_MEMORY_BASE 0x10090000
-#elif defined(CHIP_VARIANT_NPCX7M7WB)
-/* 320 RAM for FW code */
-#define NPCX_PROGRAM_MEMORY_SIZE (320 * 1024)
-/* program memory base address for 320KB Code RAM (ie. 0x100C0000 - 320KB) */
+#elif defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
+/* 256KB RAM for FW code */
+#define NPCX_PROGRAM_MEMORY_SIZE (256 * 1024)
+/* program memory base address for 256KB Code RAM (ie. 0x100B0000 - 256KB) */
#define CONFIG_PROGRAM_MEMORY_BASE 0x10070000
#else
#error "Unsupported chip variant"
@@ -90,8 +99,8 @@
#if (NPCX_RAM_SIZE != 0x40000)
#error "Wrong memory mapping layout for NPCX7M6F"
#endif
-#elif defined(CHIP_VARIANT_NPCX7M7WB)
-/* 384KB RAM in NPCX7M7WB */
+#elif defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
+/* 384KB RAM in NPCX7M7WB/NPCX7M7WC */
#if (NPCX_RAM_SIZE != 0x60000)
#error "Wrong memory mapping layout for NPCX7M7W"
#endif
diff --git a/chip/npcx/config_flash_layout.h b/chip/npcx/config_flash_layout.h
index 86891cd26d..3ed9af4bc3 100644
--- a/chip/npcx/config_flash_layout.h
+++ b/chip/npcx/config_flash_layout.h
@@ -31,7 +31,8 @@
#define CONFIG_EC_WRITABLE_STORAGE_OFF 0x40000
#define CONFIG_EC_WRITABLE_STORAGE_SIZE 0x40000
#elif defined(CHIP_VARIANT_NPCX7M6F) || defined(CHIP_VARIANT_NPCX7M6FB) || \
- defined(CHIP_VARIANT_NPCX7M6FC) || defined(CHIP_VARIANT_NPCX7M6G)
+ defined(CHIP_VARIANT_NPCX7M6FC) || defined(CHIP_VARIANT_NPCX7M6G) || \
+ defined(CHIP_VARIANT_NPCX7M7WC)
#define CONFIG_EC_PROTECTED_STORAGE_OFF 0
#define CONFIG_EC_PROTECTED_STORAGE_SIZE 0x40000
#define CONFIG_EC_WRITABLE_STORAGE_OFF 0x40000
diff --git a/chip/npcx/gpio.c b/chip/npcx/gpio.c
index 8a76043d3d..6354d06ab1 100644
--- a/chip/npcx/gpio.c
+++ b/chip/npcx/gpio.c
@@ -330,7 +330,7 @@ void gpio_set_level(enum gpio_signal signal, int value)
NPCX_PDOUT(gpio_list[signal].port) &= ~gpio_list[signal].mask;
}
-#ifdef CONFIG_CMD_GPIO_EXTENDED
+#ifdef CONFIG_GPIO_GET_EXTENDED
int gpio_get_flags_by_mask(uint32_t port, uint32_t mask)
{
uint32_t flags = 0;
diff --git a/chip/npcx/registers.h b/chip/npcx/registers.h
index c13395eaa4..56b0bd3563 100644
--- a/chip/npcx/registers.h
+++ b/chip/npcx/registers.h
@@ -954,11 +954,11 @@ enum {
#define NPCX_PWDWN_CTL7_SMB6_PD 1
#define NPCX_PWDWN_CTL7_SMB7_PD 2
#if defined(CHIP_VARIANT_NPCX7M6FB) || defined(CHIP_VARIANT_NPCX7M6FC) || \
- defined(CHIP_VARIANT_NPCX7M7WB)
+ defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
#define NPCX_PWDWN_CTL7_ITIM64_PD 5
#define NPCX_PWDWN_CTL7_UART2_PD 6
#endif
-#ifdef CHIP_VARIANT_NPCX7M7WB
+#if defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
#define NPCX_PWDWN_CTL7_WOV_PD 7
#endif
#endif
@@ -1238,7 +1238,7 @@ enum PM_CHANNEL_T {
/* BBRAM register fields */
#define NPCX_BKUP_STS_IBBR 7
#if defined(CHIP_VARIANT_NPCX7M6FB) || defined(CHIP_VARIANT_NPCX7M6FC) || \
- defined(CHIP_VARIANT_NPCX7M7WB)
+ defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
#define NPCX_BKUP_STS_VSBY_STS 1
#define NPCX_BKUP_STS_VCC1_STS 0
#define NPCX_BKUP_STS_ALL_MASK \
diff --git a/chip/npcx/system.c b/chip/npcx/system.c
index 4b2f55ddb4..009dd49017 100644
--- a/chip/npcx/system.c
+++ b/chip/npcx/system.c
@@ -101,7 +101,7 @@ void system_check_bbram_on_reset(void)
/*
* npcx5/npcx7m6g/npcx7m6f:
* Clear IBBR bit
- * npcx7m6fb/npcx7m6fc/npcx7m7wb:
+ * npcx7m6fb/npcx7m6fc/npcx7m7wb/npcx7m7wc:
* Clear IBBR/VSBY_STS/VCC1_STS bit
*/
NPCX_BKUP_STS = NPCX_BKUP_STS_ALL_MASK;
@@ -702,7 +702,7 @@ void system_pre_init(void)
#if defined(CHIP_FAMILY_NPCX7)
#if defined(CHIP_VARIANT_NPCX7M6FB) || defined(CHIP_VARIANT_NPCX7M6FC) || \
- defined(CHIP_VARIANT_NPCX7M7WB)
+ defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC)
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_7) = 0xE7;
#else
NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_7) = 0x07;
@@ -817,6 +817,7 @@ const char *system_get_chip_name(void)
case 0x29:
return "NPCX796F";
case 0x24:
+ case 0x2C:
return "NPCX797W";
#endif
default:
diff --git a/chip/npcx/wov.c b/chip/npcx/wov.c
index 3bbeee90a0..15d442a142 100644
--- a/chip/npcx/wov.c
+++ b/chip/npcx/wov.c
@@ -171,7 +171,7 @@ struct wov_config wov_conf;
static struct wov_cfifo_buf cfifo_buf;
static wov_call_back_t callback_fun;
-const uint32_t voice_buffer[VOICE_BUF_SIZE] = {0};
+static uint32_t voice_buffer[VOICE_BUF_SIZE] = {0};
#define WOV_RATE_ERROR_THRESH_MSEC 10
#define WOV_RATE_ERROR_THRESH 5
diff --git a/chip/stm32/adc-stm32f0.c b/chip/stm32/adc-stm32f0.c
index c448b4b42f..add5c0c30c 100644
--- a/chip/stm32/adc-stm32f0.c
+++ b/chip/stm32/adc-stm32f0.c
@@ -40,7 +40,11 @@ static const struct adc_profile_t profile = {
/* Sample all channels once using DMA */
.cfgr1_reg = STM32_ADC_CFGR1_OVRMOD,
.cfgr2_reg = 0,
+#ifdef CONFIG_ADC_SAMPLE_TIME
+ .smpr_reg = CONFIG_ADC_SAMPLE_TIME,
+#else
.smpr_reg = STM32_ADC_SMPR_13_5_CY,
+#endif
.ier_reg = 0,
.dma_option = &dma_single,
.dma_buffer_size = 1,
@@ -60,7 +64,11 @@ static const struct adc_profile_t profile = {
STM32_ADC_CFGR1_CONT |
STM32_ADC_CFGR1_DMACFG,
.cfgr2_reg = 0,
+#ifdef CONFIG_ADC_SAMPLE_TIME
+ .smpr_reg = CONFIG_ADC_SAMPLE_TIME,
+#else
.smpr_reg = STM32_ADC_SMPR_1_5_CY,
+#endif
/* Fire interrupt at end of sequence. */
.ier_reg = STM32_ADC_IER_EOSEQIE,
.dma_option = &dma_continuous,
diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h
index e9067b5620..a6c9636098 100644
--- a/chip/stm32/registers.h
+++ b/chip/stm32/registers.h
@@ -1375,6 +1375,7 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
#define STM32_RCC_AHB1ENR_OTGHSULPIEN BIT(30)
#define STM32_RCC_AHB2ENR REG32(STM32_RCC_BASE + 0x34)
+#define STM32_RCC_AHB2ENR_RNGEN BIT(6)
#define STM32_RCC_AHB2ENR_OTGFSEN BIT(7)
#define STM32_RCC_AHB3ENR REG32(STM32_RCC_BASE + 0x38)
diff --git a/chip/stm32/spi.c b/chip/stm32/spi.c
index eda4e0960f..057827cc7a 100644
--- a/chip/stm32/spi.c
+++ b/chip/stm32/spi.c
@@ -41,11 +41,17 @@
static const struct dma_option dma_tx_option = {
STM32_DMAC_SPI1_TX, (void *)&SPI_TXDR,
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT
+#ifdef CHIP_FAMILY_STM32F4
+ | STM32_DMA_CCR_CHANNEL(STM32_SPI1_TX_REQ_CH)
+#endif
};
static const struct dma_option dma_rx_option = {
STM32_DMAC_SPI1_RX, (void *)&SPI_RXDR,
STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT
+#ifdef CHIP_FAMILY_STM32F4
+ | STM32_DMA_CCR_CHANNEL(STM32_SPI1_RX_REQ_CH)
+#endif
};
/*
@@ -279,7 +285,7 @@ static void reply(dma_chan_t *txdma,
*/
static void tx_status(uint8_t byte)
{
- stm32_spi_regs_t *spi = STM32_SPI1_REGS;
+ stm32_spi_regs_t *spi __attribute__((unused)) = STM32_SPI1_REGS;
SPI_TXDR = byte;
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32L4)
@@ -302,7 +308,7 @@ static void tx_status(uint8_t byte)
*/
static void setup_for_transaction(void)
{
- stm32_spi_regs_t *spi = STM32_SPI1_REGS;
+ stm32_spi_regs_t *spi __attribute__((unused)) = STM32_SPI1_REGS;
volatile uint8_t dummy __attribute__((unused));
/* clear this as soon as possible */
@@ -667,7 +673,12 @@ static void spi_init(void)
/* Delay 1 APB clock cycle after the clock is enabled */
clock_wait_bus_cycles(BUS_APB, 1);
- /* Select the right DMA request for the variants using it */
+ /*
+ * Select the right DMA request for the variants using it.
+ * This is not required for STM32F4 since the channel (aka request) is
+ * set directly in the respective dma_option. In fact, it would be
+ * overridden in dma-stm32f4::prepare_stream().
+ */
#ifdef CHIP_FAMILY_STM32L4
dma_select_channel(STM32_DMAC_SPI1_TX, 1);
dma_select_channel(STM32_DMAC_SPI1_RX, 1);
diff --git a/chip/stm32/spi_master.c b/chip/stm32/spi_master.c
index 76677423f2..446013a9d9 100644
--- a/chip/stm32/spi_master.c
+++ b/chip/stm32/spi_master.c
@@ -161,6 +161,48 @@ static int spi_master_initialize(int port)
if ((spi_devices[i].port == port) &&
(div < spi_devices[i].div))
div = spi_devices[i].div;
+
+ /*
+ * STM32F412
+ * Section 26.3.5 Slave select (NSS) pin management and Figure 276
+ * https://www.st.com/resource/en/reference_manual/dm00180369.pdf#page=817
+ *
+ * The documentation in this section is a bit confusing, so here's a
+ * summary based on discussion with ST:
+ *
+ * Software NSS management (SSM = 1):
+ * - In master mode, the NSS output is deactivated. You need to use a
+ * GPIO in output mode for slave select. This is generally used for
+ * multi-slave operation, but you can also use it for single slave
+ * operation. In this case, you should make sure to configure a GPIO
+ * for NSS, but *not* activate the SPI alternate function on that
+ * same pin since that will enable hardware NSS management (see
+ * below).
+ * - In slave mode, the NSS input level is equal to the SSI bit value.
+ *
+ * Hardware NSS management (SSM = 0):
+ * - In slave mode, when NSS pin is detected low the slave (MCU) is
+ * selected.
+ * - In master mode, there are two configurations, depending on the
+ * SSOE bit in register SPIx_CR1.
+ * - NSS output enable (SSM=0, SSOE=1):
+ * The MCU (master) drives NSS low as soon as SPI is enabled
+ * (SPE=1) and releases it when SPI is disabled (SPE=0).
+ *
+ * - NSS output disable (SSM=0, SSOE=0):
+ * Allows multimaster capability. The MCU (master) drives NSS
+ * low. If another master tries to takes control of the bus and
+ * NSS is pulled low, a mode fault is generated and the MCU
+ * changes to slave mode.
+ *
+ * - NSS output disable (SSM=0, SSOE=0): if the MCU is acting as
+ * master on the bus, this config allows multimaster capability. If
+ * the NSS pin is pulled low in this mode, the SPI enters master
+ * mode fault state and the device is automatically reconfigured in
+ * slave mode. In slave mode, the NSS pin works as a standard "chip
+ * select" input and the slave is selected while NSS lin is at low
+ * level.
+ */
spi->cr1 = STM32_SPI_CR1_MSTR | STM32_SPI_CR1_SSM | STM32_SPI_CR1_SSI |
(div << 3);
@@ -170,7 +212,14 @@ static int spi_master_initialize(int port)
#endif
/*
* Configure 8-bit datasize, set FRXTH, enable DMA,
- * and enable NSS output
+ * and set data size (applies to STM32F0 only).
+ *
+ * STM32F412:
+ * https://www.st.com/resource/en/reference_manual/dm00180369.pdf#page=852
+ *
+ *
+ * STM32F0:
+ * https://www.st.com/resource/en/reference_manual/dm00031936.pdf#page=803
*/
spi->cr2 = STM32_SPI_CR2_TXDMAEN | STM32_SPI_CR2_RXDMAEN |
STM32_SPI_CR2_FRXTH | STM32_SPI_CR2_DATASIZE(8);
diff --git a/chip/stm32/trng.c b/chip/stm32/trng.c
index 7799c03f93..8ef7b891a7 100644
--- a/chip/stm32/trng.c
+++ b/chip/stm32/trng.c
@@ -68,6 +68,11 @@ void init_trng(void)
STM32_RCC_D2CCIP2R =
(STM32_RCC_D2CCIP2R & ~STM32_RCC_D2CCIP2_RNGSEL_MASK)
| STM32_RCC_D2CCIP2_RNGSEL_HSI48;
+#elif defined(CHIP_FAMILY_STM32F4)
+ /*
+ * The RNG clock is the same as the SDIO/USB OTG clock, already set at
+ * 48 MHz during clock initialisation. Nothing to do.
+ */
#else
#error "Please add support for CONFIG_RNG on this chip family."
#endif
@@ -85,6 +90,8 @@ void exit_trng(void)
STM32_RCC_CRRCR &= ~STM32_RCC_CRRCR_HSI48ON;
#elif defined(CHIP_FAMILY_STM32H7)
STM32_RCC_CR &= ~STM32_RCC_CR_HSI48ON;
+#elif defined(CHIP_FAMILY_STM32F4)
+ /* Nothing to do */
#endif
}
diff --git a/chip/stm32/usb_pd_phy.c b/chip/stm32/usb_pd_phy.c
index 92656a1582..0efa947423 100644
--- a/chip/stm32/usb_pd_phy.c
+++ b/chip/stm32/usb_pd_phy.c
@@ -452,6 +452,16 @@ void pd_rx_handler(void)
int next_idx;
pending = STM32_EXTI_PR;
+#ifdef CONFIG_USB_TYPEC_CTVPD
+ /* Charge-Through Side detach event */
+ if (pending & EXTI_COMP2_MASK) {
+ task_set_event(PD_PORT_TO_TASK_ID(0), PD_EVENT_SM, 0);
+ /* Clear interrupt */
+ STM32_EXTI_PR = EXTI_COMP2_MASK;
+ pending &= ~EXTI_COMP2_MASK;
+ }
+#endif
+
for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) {
if (pending & EXTI_COMP_MASK(i)) {
rx_edge_ts[i][rx_edge_ts_idx[i]].val = get_time().val;
@@ -648,6 +658,7 @@ void pd_hw_init(int port, int role)
phy->tim_tx->ccmr1 = val;
else
phy->tim_tx->ccmr2 = val;
+
phy->tim_tx->ccer = 1 << ((TIM_TX_CCR_IDX(port) - 1) * 4);
phy->tim_tx->bdtr = 0x8000;
/* set prescaler to /1 */
diff --git a/common/acpi.c b/common/acpi.c
index e33ac17bb0..e681fd25a6 100644
--- a/common/acpi.c
+++ b/common/acpi.c
@@ -195,7 +195,7 @@ int acpi_ap_to_ec(int is_cmd, uint8_t value, uint8_t *resultptr)
case EC_ACPI_MEM_TEST_COMPLIMENT:
result = 0xff - acpi_mem_test;
break;
-#ifdef CONFIG_PWM_KBLIGHT
+#ifdef CONFIG_KEYBOARD_BACKLIGHT
case EC_ACPI_MEM_KEYBOARD_BACKLIGHT:
result = kblight_get();
break;
@@ -301,7 +301,7 @@ int acpi_ap_to_ec(int is_cmd, uint8_t value, uint8_t *resultptr)
battery_memmap_set_index(data);
break;
#endif
-#ifdef CONFIG_PWM_KBLIGHT
+#ifdef CONFIG_KEYBOARD_BACKLIGHT
case EC_ACPI_MEM_KEYBOARD_BACKLIGHT:
/*
* Debug output with CR not newline, because the host
diff --git a/common/build.mk b/common/build.mk
index d9418f7a31..78c97149c5 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -66,6 +66,7 @@ common-$(CONFIG_FLASH)+=flash.o
common-$(CONFIG_FLASH_LOG)+=flash_log.o flash_log_vc.o
common-$(CONFIG_FLASH_NVCOUNTER)+=nvcounter.o
common-$(CONFIG_FLASH_NVMEM)+=nvmem.o
+common-$(CONFIG_FLASH_NVMEM)+=new_nvmem.o
common-$(CONFIG_FLASH_NVMEM_VARS)+=nvmem_vars.o
common-$(CONFIG_FMAP)+=fmap.o
common-$(CONFIG_GESTURE_SW_DETECTION)+=gesture.o
@@ -98,7 +99,7 @@ common-$(CONFIG_POWER_BUTTON_X86)+=power_button_x86.o
common-$(CONFIG_PSTORE)+=pstore_commands.o
common-$(CONFIG_PWM)+=pwm.o
common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o
-common-$(CONFIG_PWM_KBLIGHT)+=keyboard_backlight.o
+common-$(CONFIG_KEYBOARD_BACKLIGHT)+=keyboard_backlight.o
common-$(CONFIG_RMA_AUTH)+=rma_auth.o
common-$(CONFIG_RSA)+=rsa.o
common-$(CONFIG_ROLLBACK)+=rollback.o
@@ -127,7 +128,14 @@ common-$(CONFIG_USB_I2C)+=usb_i2c.o
common-$(CONFIG_USB_CHARGER)+=usb_charger.o
common-$(CONFIG_USB_PORT_POWER_DUMB)+=usb_port_power_dumb.o
common-$(CONFIG_USB_PORT_POWER_SMART)+=usb_port_power_smart.o
+ifeq ($(CONFIG_USB_SM_FRAMEWORK),)
common-$(CONFIG_USB_POWER_DELIVERY)+=usb_pd_protocol.o usb_pd_policy.o
+else
+common-$(CONFIG_USB_SM_FRAMEWORK)+=usb_sm.o
+common-$(CONFIG_USB_TYPEC_SM)+=usb_tc_sm.o
+common-$(CONFIG_USB_PRL_SM)+=usb_prl_sm.o
+common-$(CONFIG_USB_PE_SM)+=usb_pe_sm.o
+endif
common-$(CONFIG_USB_PD_LOGGING)+=event_log.o pd_log.o
common-$(CONFIG_USB_PD_TCPC)+=usb_pd_tcpc.o
common-$(CONFIG_USB_UPDATE)+=usb_update.o update_fw.o
diff --git a/common/button.c b/common/button.c
index 88cf9e69b6..cdc8eebbc7 100644
--- a/common/button.c
+++ b/common/button.c
@@ -471,11 +471,13 @@ static int debug_button_pressed(int mask)
return debug_button_mask() == mask;
}
+#ifdef CONFIG_LED_COMMON
static int debug_mode_blink_led(void)
{
return ((curr_debug_state != STATE_DEBUG_NONE) &&
(curr_debug_state != STATE_DEBUG_CHECK));
}
+#endif
static void debug_mode_transition(enum debug_state next_state)
{
diff --git a/common/cbi.c b/common/cbi.c
index cf4605288a..06e0d464c1 100644
--- a/common/cbi.c
+++ b/common/cbi.c
@@ -347,6 +347,7 @@ DECLARE_HOST_COMMAND(EC_CMD_SET_CROS_BOARD_INFO,
hc_cbi_set,
EC_VER_MASK(0));
+#ifdef CONFIG_CMD_CBI
static void dump_flash(void)
{
uint8_t buf[16];
@@ -398,4 +399,6 @@ static int cc_cbi(int argc, char **argv)
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(cbi, cc_cbi, NULL, "Print Cros Board Info from flash");
+#endif /* CONFIG_CMD_CBI */
+
#endif /* !HOST_TOOLS_BUILD */
diff --git a/common/ccd_config.c b/common/ccd_config.c
index 8896e9cde6..5b5eb87876 100644
--- a/common/ccd_config.c
+++ b/common/ccd_config.c
@@ -96,7 +96,9 @@ static const uint8_t k_ccd_config = NVMEM_VAR_CCD_CONFIG;
/* Flags which can be set via ccd_set_flag() */
static const uint32_t k_public_flags =
CCD_FLAG_OVERRIDE_WP_AT_BOOT |
- CCD_FLAG_OVERRIDE_WP_STATE_ENABLED;
+ CCD_FLAG_OVERRIDE_WP_STATE_ENABLED |
+ CCD_FLAG_OVERRIDE_BATT_AT_BOOT |
+ CCD_FLAG_OVERRIDE_BATT_STATE_CONNECT;
/* List of CCD capability info; must be in same order as enum ccd_capability */
static const struct ccd_capability_info cap_info[CCD_CAP_COUNT] = CAP_INFO_DATA;
@@ -387,6 +389,8 @@ static void ccd_load_config(void)
ccd_reset_config(t->val_len < 2 ? CCD_RESET_TEST_LAB : 0);
}
+ freevar(t);
+
ccd_is_loaded:
ccd_config_loaded = 1;
@@ -408,8 +412,6 @@ static int ccd_save_config(void)
if (rv)
return rv;
- rv = writevars();
-
/*
* Notify CCD users of configuration change.
* Protect this notify with the ccd_config_loaded flag so recipients of
@@ -470,6 +472,8 @@ int ccd_reset_config(unsigned int flags)
/* Reset the entire config */
memset(&config, 0, sizeof(config));
config.version = CCD_CONFIG_VERSION;
+ /* Update write protect after resetting the config */
+ board_wp_follow_ccd_config();
}
if (flags & CCD_RESET_FACTORY) {
@@ -485,7 +489,7 @@ int ccd_reset_config(unsigned int flags)
/* Force WP disabled at boot */
raw_set_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT, 1);
raw_set_flag(CCD_FLAG_OVERRIDE_WP_STATE_ENABLED, 0);
- set_wp_follow_ccd_config();
+ board_wp_follow_ccd_config();
}
/* Restore test lab flag unless explicitly resetting it */
@@ -565,7 +569,7 @@ static void ccd_open_done(int sync)
if (sync)
rv = tpm_sync_reset(1);
else
- rv = board_wipe_tpm();
+ rv = board_wipe_tpm(1);
if (rv != EC_SUCCESS) {
CPRINTS("CCD open TPM wipe failed");
@@ -1522,7 +1526,7 @@ static enum vendor_cmd_rc ccd_disable_factory_mode(enum vendor_cmd_cc code,
* TODO(rspangler): sort out CCD state and WP correlation,
* b/73075443.
*/
- set_wp_follow_ccd_config();
+ board_wp_follow_ccd_config();
/*
* Use raw_set_flag() because the factory mode flag is internal
diff --git a/common/charge_manager.c b/common/charge_manager.c
index dcb819ee3a..804376f528 100644
--- a/common/charge_manager.c
+++ b/common/charge_manager.c
@@ -81,11 +81,6 @@ static int override_port = OVERRIDE_OFF;
static int delayed_override_port = OVERRIDE_OFF;
static timestamp_t delayed_override_deadline;
-#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT
-/* Bitmap of ports used as power source */
-static volatile uint32_t source_port_bitmap;
-BUILD_ASSERT(sizeof(source_port_bitmap)*8 >= CONFIG_USB_PD_PORT_COUNT);
-#endif
static uint8_t source_port_rp[CONFIG_USB_PD_PORT_COUNT];
#ifdef CONFIG_USB_PD_MAX_TOTAL_SOURCE_CURRENT
@@ -1048,6 +1043,10 @@ int charge_manager_get_power_limit_uw(void)
#ifdef CONFIG_USB_PD_MAX_SINGLE_SOURCE_CURRENT
+/* Bitmap of ports used as power source */
+static volatile uint32_t source_port_bitmap;
+BUILD_ASSERT(sizeof(source_port_bitmap)*8 >= CONFIG_USB_PD_PORT_COUNT);
+
static inline int has_other_active_source(int port)
{
return source_port_bitmap & ~BIT(port);
diff --git a/common/ec_features.c b/common/ec_features.c
index fb45789a63..4e17764127 100644
--- a/common/ec_features.c
+++ b/common/ec_features.c
@@ -22,7 +22,7 @@ uint32_t get_feature_flags0(void)
#ifdef CONFIG_FANS
| EC_FEATURE_MASK_0(EC_FEATURE_PWM_FAN)
#endif
-#ifdef CONFIG_PWM_KBLIGHT
+#ifdef CONFIG_KEYBOARD_BACKLIGHT
| EC_FEATURE_MASK_0(EC_FEATURE_PWM_KEYB)
#endif
#ifdef HAS_TASK_LIGHTBAR
diff --git a/common/factory_mode.c b/common/factory_mode.c
index c497a212fe..f2ed77cae6 100644
--- a/common/factory_mode.c
+++ b/common/factory_mode.c
@@ -39,10 +39,8 @@ static void factory_enable_failed(void)
ccd_hook_active = 0;
CPRINTS("factory enable failed");
- if (reset_required_) {
+ if (reset_required_)
reset_required_ = 0;
- deassert_ec_rst();
- }
}
DECLARE_DEFERRED(factory_enable_failed);
@@ -65,34 +63,8 @@ static void factory_enable_deferred(void)
{
int rv;
- CPRINTS("%s: reset TPM\n", __func__);
- if (reset_required_)
- assert_ec_rst();
-
- if (tpm_reset_request(1, 1) != EC_SUCCESS) {
- CPRINTS("%s: TPM reset failed\n", __func__);
- /*
- * Attempt to reset TPM failed, let's reboot the device just
- * in case.
- */
- if (!reset_required_)
- assert_ec_rst();
- deassert_ec_rst();
+ if (board_wipe_tpm(reset_required_) != EC_SUCCESS)
return;
- }
-
- /*
- * TPM was wiped out successfully, let's prevent further
- * communications from the AP until next reboot.
- */
- if (!reset_required_)
- tpm_stop();
-
- /*
- * Need this to make sure that CCD state changes are saved in the
- * NVMEM before reboot.
- */
- tpm_reinstate_nvmem_commits();
CPRINTS("%s: TPM reset done, enabling factory mode", __func__);
@@ -103,9 +75,8 @@ static void factory_enable_deferred(void)
if (reset_required_) {
/*
- * Make sure we never end up with the EC held in reset, no
- * matter what prevents the proper factory reset flow from
- * succeeding.
+ * Cr50 will reset once factory mode is enabled. If it hasn't in
+ * TPM_RESET_TIME, declare factory enable failed.
*/
hook_call_deferred(&factory_enable_failed_data, TPM_RESET_TIME);
}
@@ -116,7 +87,7 @@ void enable_ccd_factory_mode(int reset_required)
{
delay_sleep_by(DISABLE_SLEEP_TIME);
- reset_required_ = !!reset_required;
+ reset_required_ |= !!reset_required;
hook_call_deferred(&factory_enable_deferred_data,
TPM_PROCESSING_TIME);
}
diff --git a/common/flash.c b/common/flash.c
index 2eb5f265aa..fa5afa2072 100644
--- a/common/flash.c
+++ b/common/flash.c
@@ -157,7 +157,7 @@ int flash_bank_count(int offset, int size)
}
#endif /* CONFIG_FLASH_MULTIPLE_REGION */
-int flash_range_ok(int offset, int size_req, int align)
+static int flash_range_ok(int offset, int size_req, int align)
{
if (offset < 0 || size_req < 0 ||
offset > CONFIG_FLASH_SIZE ||
diff --git a/common/gpio.c b/common/gpio.c
index 60d4973d61..e20f1f8c93 100644
--- a/common/gpio.c
+++ b/common/gpio.c
@@ -108,7 +108,7 @@ void gpio_set_flags(enum gpio_signal signal, int flags)
gpio_set_flags_by_mask(g->port, g->mask, flags);
}
-#ifdef CONFIG_CMD_GPIO_EXTENDED
+#ifdef CONFIG_GPIO_GET_EXTENDED
int gpio_get_flags(enum gpio_signal signal)
{
const struct gpio_info *g = gpio_list + signal;
diff --git a/common/keyboard_backlight.c b/common/keyboard_backlight.c
index 8fdc5dc0cf..7962fea99c 100644
--- a/common/keyboard_backlight.c
+++ b/common/keyboard_backlight.c
@@ -74,7 +74,9 @@ int kblight_register(const struct kblight_drv *drv)
static void keyboard_backlight_init(void)
{
/* Uses PWM by default. Can be customized by board_kblight_init */
+#ifdef CONFIG_PWM_KBLIGHT
kblight_register(&kblight_pwm);
+#endif
board_kblight_init();
if (kblight_init())
CPRINTS("kblight init failed");
diff --git a/common/led_pwm.c b/common/led_pwm.c
index 87ec59a127..d167a5bd6f 100644
--- a/common/led_pwm.c
+++ b/common/led_pwm.c
@@ -151,7 +151,8 @@ static int show_charge_state(void)
if (chg_st == PWR_STATE_CHARGE) {
led_is_pulsing = 0;
set_led_color(CONFIG_LED_PWM_CHARGE_COLOR);
- } else if (chg_st == PWR_STATE_CHARGE_NEAR_FULL) {
+ } else if (chg_st == PWR_STATE_CHARGE_NEAR_FULL ||
+ chg_st == PWR_STATE_DISCHARGE_FULL) {
led_is_pulsing = 0;
set_led_color(CONFIG_LED_PWM_NEAR_FULL_COLOR);
} else if ((battery_is_present() != BP_YES) ||
diff --git a/common/mkbp_event.c b/common/mkbp_event.c
index 5807d6ba80..28e6a6fc98 100644
--- a/common/mkbp_event.c
+++ b/common/mkbp_event.c
@@ -11,49 +11,104 @@
#include "host_command.h"
#include "host_command_heci.h"
#include "hwtimer.h"
+#include "timer.h"
#include "link_defs.h"
#include "mkbp_event.h"
#include "power.h"
#include "util.h"
-static uint32_t events;
-uint32_t mkbp_last_event_time;
-
-static void set_event(uint8_t event_type)
-{
- atomic_or(&events, 1 << event_type);
-}
+#define CPUTS(outstr) cputs(CC_COMMAND, outstr)
+#define CPRINTS(format, args...) cprints(CC_COMMAND, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_COMMAND, format, ## args)
-static void clear_event(uint8_t event_type)
-{
- atomic_clear(&events, 1 << event_type);
-}
+/*
+ * Tracks the current state of the MKBP interrupt send from the EC to the AP.
+ *
+ * The inactive state is only valid when there are no events to set to the AP.
+ * If the AP is asleep, then some events are not worth waking the AP up, so the
+ * interrupt could remain in an inactive in that case.
+ *
+ * The transition state (INTERRUPT_INACTIVE_TO_ACTIVE) is used to track the
+ * sometimes lock transition for a "rising edge" for platforms that send the
+ * rising edge interrupt through a host communication layer
+ *
+ * The active state represents that a rising edge interrupt has already been
+ * sent to the AP, and the EC is waiting for the AP to call get next event
+ * host command to consume all of the events (at which point the state will
+ * move to inactive).
+ *
+ * The transition from ACTIVE -> INACTIVE is considerer to be simple meaning
+ * the operation can be performed within a blocking mutex (e.g. no-op or setting
+ * a gpio).
+ */
+enum interrupt_state {
+ INTERRUPT_INACTIVE,
+ INTERRUPT_INACTIVE_TO_ACTIVE, /* Transitioning */
+ INTERRUPT_ACTIVE,
+};
+
+struct mkbp_state {
+ struct mutex lock;
+ uint32_t events;
+ enum interrupt_state interrupt;
+ /*
+ * Tracks unique transitions to INTERRUPT_INACTIVE_TO_ACTIVE allowing
+ * only the most recent transition to finish the transition to a final
+ * state -- either active or inactive depending on the result of the
+ * operation.
+ */
+ uint8_t interrupt_id;
+ /*
+ * Tracks the number of consecutive failed attempts for the AP to poll
+ * get_next_events in order to limit the retry logic.
+ */
+ uint8_t failed_attempts;
+};
-static int event_is_set(uint8_t event_type)
-{
- return events & BIT(event_type);
-}
+static struct mkbp_state state;
+uint32_t mkbp_last_event_time;
#ifdef CONFIG_MKBP_USE_GPIO
-static void mkbp_set_host_active_via_gpio(int active)
+static int mkbp_set_host_active_via_gpio(int active, uint32_t *timestamp)
{
+ /*
+ * If we want to take a timestamp, then disable interrupts temporarily
+ * to ensure that the timestamp is as close as possible to the setting
+ * of the GPIO pin in hardware (i.e. we aren't interrupted between
+ * taking the timestamp and setting the gpio)
+ */
+ if (timestamp) {
+ interrupt_disable();
+ *timestamp = __hw_clock_source_read();
+ }
+
gpio_set_level(GPIO_EC_INT_L, !active);
+
+ if (timestamp)
+ interrupt_enable();
+
+ return EC_SUCCESS;
}
#endif
#ifdef CONFIG_MKBP_USE_HOST_EVENT
-static void mkbp_set_host_active_via_event(int active)
+static int mkbp_set_host_active_via_event(int active, uint32_t *timestamp)
{
+ /* This should be moved into host_set_single_event for more accuracy */
+ if (timestamp)
+ *timestamp = __hw_clock_source_read();
if (active)
host_set_single_event(EC_HOST_EVENT_MKBP);
+ return EC_SUCCESS;
}
#endif
#ifdef CONFIG_MKBP_USE_HECI
-static void mkbp_set_host_active_via_heci(int active)
+static int mkbp_set_host_active_via_heci(int active, uint32_t *timestamp)
{
if (active)
- heci_send_mkbp_event();
+ return heci_send_mkbp_event(timestamp);
+ return EC_SUCCESS;
}
#endif
@@ -61,49 +116,23 @@ static void mkbp_set_host_active_via_heci(int active)
* This communicates to the AP whether an MKBP event is currently available
* for processing.
*
+ * NOTE: When active is 0 this function CANNOT de-schedule. It must be very
+ * simple like toggling a GPIO or no-op
+ *
* @param active 1 if there is an event, 0 otherwise
+ * @param timestamp, if non-null this variable will be written as close to the
+ * hardware interrupt from EC->AP as possible.
*/
-static void mkbp_set_host_active(int active)
+static int mkbp_set_host_active(int active, uint32_t *timestamp)
{
#if defined(CONFIG_MKBP_USE_CUSTOM)
- mkbp_set_host_active_via_custom(active);
+ return mkbp_set_host_active_via_custom(active, timestamp);
#elif defined(CONFIG_MKBP_USE_HOST_EVENT)
- mkbp_set_host_active_via_event(active);
+ return mkbp_set_host_active_via_event(active, timestamp);
#elif defined(CONFIG_MKBP_USE_GPIO)
- mkbp_set_host_active_via_gpio(active);
+ return mkbp_set_host_active_via_gpio(active, timestamp);
#elif defined(CONFIG_MKBP_USE_HECI)
- mkbp_set_host_active_via_heci(active);
-#endif
-}
-
-/**
- * Assert host keyboard interrupt line.
- */
-static void set_host_interrupt(int active)
-{
- static int old_active;
- /*
- * If we are going to perform a simple GPIO toggle, then pause
- * interrupts to let last_event_time marker have the best chance of
- * matching the time we toggle the GPIO pin.
- *
- * If we are passing mkbp events through host communication, then
- * pausing interrupts can have unintended consequences (say if that code
- * waits for a mutex and then de-schedules its tasks).
- */
-#ifdef CONFIG_MKBP_USE_GPIO
- interrupt_disable();
-#endif
-
- if (old_active == 0 && active == 1)
- mkbp_last_event_time = __hw_clock_source_read();
-
- mkbp_set_host_active(active);
-
- old_active = active;
-
-#ifdef CONFIG_MKBP_USE_GPIO
- interrupt_enable();
+ return mkbp_set_host_active_via_heci(active, timestamp);
#endif
}
@@ -126,24 +155,130 @@ static inline int host_is_sleeping(void)
}
#endif /* CONFIG_MKBP_WAKEUP_MASK */
-int mkbp_send_event(uint8_t event_type)
+/*
+ * This is the deferred function that ensures that we attempt to set the MKBP
+ * interrupt again if there was a failure in the system (EC or AP) and the AP
+ * never called get_next_event.
+ */
+static void force_mkbp_if_events(void);
+DECLARE_DEFERRED(force_mkbp_if_events);
+
+static void activate_mkbp_with_events(uint32_t events_to_add)
{
- set_event(event_type);
+ int interrupt_id = -1;
+ int skip_interrupt = 0;
+ int rv, schedule_deferred = 0;
#ifdef CONFIG_MKBP_WAKEUP_MASK
/* Only assert interrupt for wake events if host is sleeping */
- if (host_is_sleeping()) {
- /* Skip host wake if this isn't a wake event */
- if (!(host_get_events() & CONFIG_MKBP_WAKEUP_MASK) &&
- event_type != EC_MKBP_EVENT_KEY_MATRIX)
- return 0;
- }
+ skip_interrupt = host_is_sleeping() &&
+ !(host_get_events() & CONFIG_MKBP_WAKEUP_MASK);
#endif
- set_host_interrupt(1);
+ mutex_lock(&state.lock);
+ state.events |= events_to_add;
+
+ /* To skip the interrupt, we cannot have the EC_MKBP_EVENT_KEY_MATRIX */
+ skip_interrupt = skip_interrupt &&
+ !(state.events & BIT(EC_MKBP_EVENT_KEY_MATRIX));
+
+ if (state.events && state.interrupt == INTERRUPT_INACTIVE &&
+ !skip_interrupt) {
+ state.interrupt = INTERRUPT_INACTIVE_TO_ACTIVE;
+ interrupt_id = ++state.interrupt_id;
+ }
+ mutex_unlock(&state.lock);
+
+ /* If we don't need to send an interrupt we are done */
+ if (interrupt_id < 0)
+ return;
+
+ /* Send a rising edge MKBP interrupt */
+ rv = mkbp_set_host_active(1, &mkbp_last_event_time);
+
+ /*
+ * If this was the last interrupt to the AP, update state;
+ * otherwise the latest interrupt should update state.
+ */
+ mutex_lock(&state.lock);
+ if (state.interrupt == INTERRUPT_INACTIVE_TO_ACTIVE &&
+ interrupt_id == state.interrupt_id) {
+ schedule_deferred = 1;
+ state.interrupt = rv == EC_SUCCESS ? INTERRUPT_ACTIVE
+ : INTERRUPT_INACTIVE;
+ }
+ mutex_unlock(&state.lock);
+
+ if (schedule_deferred) {
+ hook_call_deferred(&force_mkbp_if_events_data, SECOND);
+ if (rv != EC_SUCCESS)
+ CPRINTS("Could not activate MKBP (%d). Deferring", rv);
+ }
+}
+
+/*
+ * This is the deferred function that ensures that we attempt to set the MKBP
+ * interrupt again if there was a failure in the system (EC or AP) and the AP
+ * never called get_next_event.
+ */
+static void force_mkbp_if_events(void)
+{
+ int toggled = 0;
+
+ mutex_lock(&state.lock);
+ if (state.interrupt == INTERRUPT_ACTIVE) {
+ if (++state.failed_attempts < 3) {
+ state.interrupt = INTERRUPT_INACTIVE;
+ toggled = 1;
+ }
+ }
+ mutex_unlock(&state.lock);
+
+ if (toggled)
+ CPRINTS("MKBP not cleared within threshold, toggling.");
+
+ activate_mkbp_with_events(0);
+}
+
+int mkbp_send_event(uint8_t event_type)
+{
+ activate_mkbp_with_events(BIT(event_type));
+
return 1;
}
+static int set_inactive_if_no_events(void)
+{
+ int interrupt_cleared;
+
+ mutex_lock(&state.lock);
+ interrupt_cleared = !state.events;
+ if (interrupt_cleared) {
+ state.interrupt = INTERRUPT_INACTIVE;
+ state.failed_attempts = 0;
+ /* Only simple tasks (i.e. gpio set or no-op) allowed here */
+ mkbp_set_host_active(0, NULL);
+ }
+ mutex_unlock(&state.lock);
+
+ /* Cancel our safety net since the events were cleared. */
+ if (interrupt_cleared)
+ hook_call_deferred(&force_mkbp_if_events_data, -1);
+
+ return interrupt_cleared;
+}
+
+/* This can only be called when the state.lock mutex is held */
+static int take_event_if_set(uint8_t event_type)
+{
+ int taken;
+
+ taken = state.events & BIT(event_type);
+ state.events &= ~BIT(event_type);
+
+ return taken;
+}
+
static int mkbp_get_next_event(struct host_cmd_handler_args *args)
{
static int last;
@@ -156,24 +291,22 @@ static int mkbp_get_next_event(struct host_cmd_handler_args *args)
* Find the next event to service. We do this in a round-robin
* way to make sure no event gets starved.
*/
+ mutex_lock(&state.lock);
for (i = 0; i < EC_MKBP_EVENT_COUNT; ++i)
- if (event_is_set((last + i) % EC_MKBP_EVENT_COUNT))
+ if (take_event_if_set((last + i) % EC_MKBP_EVENT_COUNT))
break;
+ mutex_unlock(&state.lock);
if (i == EC_MKBP_EVENT_COUNT) {
- set_host_interrupt(0);
- return EC_RES_UNAVAILABLE;
+ if (set_inactive_if_no_events())
+ return EC_RES_UNAVAILABLE;
+ /* An event was set just now, restart loop. */
+ continue;
}
evt = (i + last) % EC_MKBP_EVENT_COUNT;
last = evt + 1;
- /*
- * Clear the event before retrieving the event data in case the
- * event source wants to send the same event.
- */
- clear_event(evt);
-
for (src = __mkbp_evt_srcs; src < __mkbp_evt_srcs_end; ++src)
if (src->event_type == evt)
break;
@@ -192,13 +325,15 @@ static int mkbp_get_next_event(struct host_cmd_handler_args *args)
* event first.
*/
data_size = src->get_data(resp + 1);
- if (data_size == -EC_ERROR_BUSY)
- set_event(evt);
+ if (data_size == -EC_ERROR_BUSY) {
+ mutex_lock(&state.lock);
+ state.events |= BIT(evt);
+ mutex_unlock(&state.lock);
+ }
} while (data_size == -EC_ERROR_BUSY);
- if (!events)
- set_host_interrupt(0);
- else if (args->version >= 2)
+ /* If there are no more events and we support the "more" flag, set it */
+ if (!set_inactive_if_no_events() && args->version >= 2)
resp[0] |= EC_MKBP_HAS_MORE_EVENTS;
if (data_size < 0)
diff --git a/common/motion_sense.c b/common/motion_sense.c
index 4b4d996cc2..c2df8f080e 100644
--- a/common/motion_sense.c
+++ b/common/motion_sense.c
@@ -45,13 +45,8 @@ const intv3_t orientation_modes[] = {
};
#endif
-/*
- * Sampling interval for measuring acceleration and calculating lid angle.
- */
-test_export_static unsigned int motion_interval;
-
/* Delay between FIFO interruption. */
-static unsigned int motion_int_interval;
+static unsigned int ap_event_interval;
/* Minimum time in between running motion sense task loop. */
unsigned int motion_min_interval = CONFIG_MOTION_MIN_SENSE_WAIT_TIME * MSEC;
@@ -212,22 +207,19 @@ static inline int motion_sensor_in_forced_mode(
#endif
}
-
-
/* Minimal amount of time since last collection before triggering a new one */
static inline int motion_sensor_time_to_read(const timestamp_t *ts,
const struct motion_sensor_t *sensor)
{
- int rate_mhz = sensor->drv->get_data_rate(sensor);
-
- if (rate_mhz == 0)
+ if (sensor->collection_rate == 0)
return 0;
+
/*
- * converting from mHz to us.
- * If within 95% of the time, check sensor.
+ * If the time is within the min motion interval (3 ms) go ahead and
+ * read from the sensor
*/
return time_after(ts->le.lo,
- sensor->last_collection + SECOND * 950 / rate_mhz);
+ sensor->next_collection - motion_min_interval);
}
static enum sensor_config motion_sense_get_ec_config(void)
@@ -301,7 +293,9 @@ int motion_sense_set_data_rate(struct motion_sensor_t *sensor)
* Reset last collection: the last collection may be so much in the past
* it may appear to be in the future.
*/
- sensor->last_collection = ts.le.lo;
+ odr = sensor->drv->get_data_rate(sensor);
+ sensor->collection_rate = odr > 0 ? SECOND * 1000 / odr : 0;
+ sensor->next_collection = ts.le.lo + sensor->collection_rate;
sensor->oversampling = 0;
mutex_unlock(&g_sensor_mutex);
return 0;
@@ -407,9 +401,9 @@ static int motion_sense_ec_rate(struct motion_sensor_t *sensor)
*
* Note: Not static to be tested.
*/
-static int motion_sense_set_motion_intervals(void)
+static void motion_sense_set_motion_intervals(void)
{
- int i, sensor_ec_rate, ec_rate = 0, ec_int_rate = 0;
+ int i, sensor_ec_rate, ec_int_rate = 0;
struct motion_sensor_t *sensor;
for (i = 0; i < motion_sensor_count; ++i) {
sensor = &motion_sensors[i];
@@ -420,37 +414,26 @@ static int motion_sense_set_motion_intervals(void)
(sensor->drv->get_data_rate(sensor) == 0))
continue;
- sensor_ec_rate = motion_sense_ec_rate(sensor);
- if (sensor_ec_rate == 0)
- continue;
- if (ec_rate == 0 || sensor_ec_rate < ec_rate)
- ec_rate = sensor_ec_rate;
-
sensor_ec_rate = motion_sense_select_ec_rate(
sensor, SENSOR_CONFIG_AP, 1);
if (ec_int_rate == 0 ||
(sensor_ec_rate && sensor_ec_rate < ec_int_rate))
ec_int_rate = sensor_ec_rate;
}
- motion_interval = ec_rate;
- motion_int_interval =
+ ap_event_interval =
MAX(0, ec_int_rate - MOTION_SENSOR_INT_ADJUSTMENT_US);
/*
* Wake up the motion sense task: we want to sensor task to take
* in account the new period right away.
*/
task_wake(TASK_ID_MOTIONSENSE);
- return motion_interval;
}
static inline int motion_sense_init(struct motion_sensor_t *sensor)
{
int ret, cnt = 3;
- /* By default, report the actual sensor values. */
- sensor->in_spoof_mode = 0;
-
/* Initialize accelerometers. */
do {
ret = sensor->drv->init(sensor);
@@ -709,7 +692,7 @@ static int motion_sense_read(struct motion_sensor_t *sensor)
* If the sensor is in spoof mode, the readings are already present in
* spoof_xyz.
*/
- if (sensor->in_spoof_mode)
+ if (sensor->flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE)
return EC_SUCCESS;
#endif /* defined(CONFIG_ACCEL_SPOOF_MODE) */
@@ -717,6 +700,28 @@ static int motion_sense_read(struct motion_sensor_t *sensor)
return sensor->drv->read(sensor, sensor->raw_xyz);
}
+
+static inline void increment_sensor_collection(struct motion_sensor_t *sensor,
+ const timestamp_t *ts)
+{
+ sensor->next_collection += sensor->collection_rate;
+
+ while (time_after(ts->le.lo, sensor->next_collection)) {
+ /*
+ * If we get here it means that we completely missed a sensor
+ * collection time and we attempt to recover by incrementing
+ * the next collection time until we recover. This should not
+ * happen and if it does it means that the ec cannot handle the
+ * requested data rate.
+ */
+
+ CPRINTS("%s Missed data collection at %u - rate: %d",
+ sensor->name, sensor->next_collection,
+ sensor->collection_rate);
+ sensor->next_collection += sensor->collection_rate;
+ }
+}
+
static int motion_sense_process(struct motion_sensor_t *sensor,
uint32_t *event,
const timestamp_t *ts)
@@ -750,7 +755,8 @@ static int motion_sense_process(struct motion_sensor_t *sensor,
vector.flags = 0;
vector.sensor_num = sensor - motion_sensors;
#ifdef CONFIG_ACCEL_SPOOF_MODE
- if (sensor->in_spoof_mode)
+ if (sensor->flags &
+ MOTIONSENSE_FLAG_IN_SPOOF_MODE)
v = sensor->spoof_xyz;
#endif /* defined(CONFIG_ACCEL_SPOOF_MODE) */
vector.data[X] = v[X];
@@ -759,7 +765,7 @@ static int motion_sense_process(struct motion_sensor_t *sensor,
motion_sense_fifo_add_data(&vector, sensor, 3,
__hw_clock_source_read());
}
- sensor->last_collection = ts->le.lo;
+ increment_sensor_collection(sensor, ts);
} else {
ret = EC_ERROR_BUSY;
}
@@ -777,7 +783,7 @@ static int motion_sense_process(struct motion_sensor_t *sensor,
if (motion_sensor_time_to_read(ts, sensor)) {
/* Get latest data for local calculation */
ret = motion_sense_read(sensor);
- sensor->last_collection = ts->le.lo;
+ increment_sensor_collection(sensor, ts);
} else {
ret = EC_ERROR_BUSY;
}
@@ -925,6 +931,7 @@ void motion_sense_task(void *u)
{
int i, ret, wait_us;
timestamp_t ts_begin_task, ts_end_task;
+ int32_t time_diff;
uint32_t event = 0;
uint16_t ready_status;
struct motion_sensor_t *sensor;
@@ -998,7 +1005,6 @@ void motion_sense_task(void *u)
update_sense_data(lpc_status, &sample_id);
#endif
- ts_end_task = get_time();
#ifdef CONFIG_ACCEL_FIFO
/*
* Ask the host to flush the queue if
@@ -1010,13 +1016,13 @@ void motion_sense_task(void *u)
event & (TASK_EVENT_MOTION_ODR_CHANGE |
TASK_EVENT_MOTION_FLUSH_PENDING) ||
queue_space(&motion_sense_fifo) < CONFIG_ACCEL_FIFO_THRES ||
- (motion_int_interval > 0 &&
- time_after(ts_end_task.le.lo,
- ts_last_int.le.lo + motion_int_interval))) {
+ (ap_event_interval > 0 &&
+ time_after(ts_begin_task.le.lo,
+ ts_last_int.le.lo + ap_event_interval))) {
if ((event & TASK_EVENT_MOTION_FLUSH_PENDING) == 0)
motion_sense_insert_timestamp(
__hw_clock_source_read());
- ts_last_int = ts_end_task;
+ ts_last_int = ts_begin_task;
/*
* Count the number of event the AP is allowed to
* collect.
@@ -1040,25 +1046,36 @@ void motion_sense_task(void *u)
#endif
}
#endif
- if (motion_interval > 0) {
- /*
- * Delay appropriately to keep sampling time
- * consistent.
- */
- wait_us = motion_interval -
- (ts_end_task.val - ts_begin_task.val);
- /* and it cannnot be negative */
- wait_us = MAX(wait_us, 0);
+ ts_end_task = get_time();
+ wait_us = -1;
+
+ for (i = 0; i < motion_sensor_count; i++) {
+ struct motion_sensor_t *sensor = &motion_sensors[i];
+ if (!motion_sensor_in_forced_mode(sensor) ||
+ sensor->collection_rate == 0)
+ continue;
+
+ time_diff = time_until(ts_end_task.le.lo,
+ sensor->next_collection);
+
+ /* We missed our collection time so wake soon */
+ if (time_diff <= 0) {
+ wait_us = 0;
+ break;
+ }
+
+ if (wait_us == -1 || wait_us > time_diff)
+ wait_us = time_diff;
+ }
+
+ if (wait_us >= 0 && wait_us < motion_min_interval) {
/*
- * Guarantee some minimum delay to allow other lower
- * priority tasks to run.
- */
- if (wait_us < motion_min_interval)
- wait_us = motion_min_interval;
- } else {
- wait_us = -1;
+ * Guarantee some minimum delay to allow other lower
+ * priority tasks to run.
+ */
+ wait_us = motion_min_interval;
}
event = task_wait_event(wait_us);
@@ -1461,7 +1478,7 @@ static int host_cmd_motion_sense(struct host_cmd_handler_args *args)
switch (in->spoof.spoof_enable) {
case MOTIONSENSE_SPOOF_MODE_DISABLE:
/* Disable spoof mode. */
- sensor->in_spoof_mode = 0;
+ sensor->flags &= ~MOTIONSENSE_FLAG_IN_SPOOF_MODE;
break;
case MOTIONSENSE_SPOOF_MODE_CUSTOM:
@@ -1471,7 +1488,7 @@ static int host_cmd_motion_sense(struct host_cmd_handler_args *args)
sensor->spoof_xyz[X] = (int)in->spoof.components[X];
sensor->spoof_xyz[Y] = (int)in->spoof.components[Y];
sensor->spoof_xyz[Z] = (int)in->spoof.components[Z];
- sensor->in_spoof_mode = 1;
+ sensor->flags |= MOTIONSENSE_FLAG_IN_SPOOF_MODE;
break;
case MOTIONSENSE_SPOOF_MODE_LOCK_CURRENT:
@@ -1482,12 +1499,13 @@ static int host_cmd_motion_sense(struct host_cmd_handler_args *args)
sensor->spoof_xyz[X] = sensor->raw_xyz[X];
sensor->spoof_xyz[Y] = sensor->raw_xyz[Y];
sensor->spoof_xyz[Z] = sensor->raw_xyz[Z];
- sensor->in_spoof_mode = 1;
+ sensor->flags |= MOTIONSENSE_FLAG_IN_SPOOF_MODE;
break;
case MOTIONSENSE_SPOOF_MODE_QUERY:
/* Querying the spoof status of the sensor. */
- out->spoof.ret = sensor->in_spoof_mode;
+ out->spoof.ret = !!(sensor->flags &
+ MOTIONSENSE_FLAG_IN_SPOOF_MODE);
args->response_size = sizeof(out->spoof);
break;
@@ -1666,8 +1684,7 @@ static int command_accel_data_rate(int argc, char **argv)
sensor->drv->get_data_rate(sensor));
ccprintf("EC rate for sensor %d: %d\n", id,
motion_sense_ec_rate(sensor));
- ccprintf("Current EC rate: %d\n", motion_interval);
- ccprintf("Current Interrupt rate: %d\n", motion_int_interval);
+ ccprintf("Current Interrupt rate: %d\n", ap_event_interval);
}
return EC_SUCCESS;
@@ -1743,7 +1760,6 @@ DECLARE_CONSOLE_COMMAND(accelinit, command_accel_init,
#ifdef CONFIG_CMD_ACCEL_INFO
static int command_display_accel_info(int argc, char **argv)
{
- char *e;
int val, i, j;
if (argc > 3)
@@ -1780,21 +1796,6 @@ static int command_display_accel_info(int argc, char **argv)
accel_disp = val;
}
- /*
- * Second arg changes the accel task time interval. Note accel
- * sampling interval will be clobbered when chipset suspends or
- * resumes.
- */
- if (argc > 2) {
- val = strtoi(argv[2], &e, 0);
- if (*e)
- return EC_ERROR_PARAM2;
-
- motion_interval = val * MSEC;
- task_wake(TASK_ID_MOTIONSENSE);
-
- }
-
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(accelinfo, command_display_accel_info,
@@ -1841,7 +1842,8 @@ DECLARE_CONSOLE_COMMAND(fiforead, motion_sense_read_fifo,
static void print_spoof_mode_status(int id)
{
CPRINTS("Sensor %d spoof mode is %s. <%d, %d, %d>", id,
- motion_sensors[id].in_spoof_mode ? "enabled" : "disabled",
+ (motion_sensors[id].flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE)
+ ? "enabled" : "disabled",
motion_sensors[id].spoof_xyz[X],
motion_sensors[id].spoof_xyz[Y],
motion_sensors[id].spoof_xyz[Z]);
@@ -1893,7 +1895,10 @@ static int command_accelspoof(int argc, char **argv)
return EC_ERROR_PARAM_COUNT;
}
}
- s->in_spoof_mode = enable;
+ if (enable)
+ s->flags |= MOTIONSENSE_FLAG_IN_SPOOF_MODE;
+ else
+ s->flags &= ~MOTIONSENSE_FLAG_IN_SPOOF_MODE;
print_spoof_mode_status(id);
}
diff --git a/common/new_nvmem.c b/common/new_nvmem.c
new file mode 100644
index 0000000000..9ec6e3c885
--- /dev/null
+++ b/common/new_nvmem.c
@@ -0,0 +1,2957 @@
+/* Copyright 2019 The Chromium OS 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 <stdint.h>
+#include <string.h>
+
+#include "test/nvmem_test.h"
+
+#include "common.h"
+#include "board.h"
+#include "console.h"
+#include "crypto_api.h"
+#include "flash.h"
+#include "flash_log.h"
+#include "new_nvmem.h"
+#include "nvmem.h"
+#include "nvmem_vars.h"
+#include "shared_mem.h"
+#include "system.h"
+#include "timer.h"
+
+/*
+ * ==== Overview
+ *
+ * This file is the implementation of the new TPM NVMEM flash storage layer.
+ * These are major differences compared to the legacy implementation:
+ *
+ * NVMEM TPM objects are stored in flash in separate containers, each one
+ * protected by hash and possibly encrypted. When nvmem_commit() is invoked,
+ * only objects changed in the NVMEM cache are updated in the flash.
+ *
+ * The (key, value) pairs are also stored in flash in the same kind of
+ * separate containers. There is no special area allocated for the (key, value)
+ * pairs in flash, they are interleaved with TPM objects.
+ *
+ * The (key, value) pairs are not kept in the NVMEM cache, they are stored in
+ * flash only. This causes a few deviations from the legacy (key, value) pair
+ * interface:
+ *
+ * - no need to initialize (key, value) storage separately, initvars() API is
+ * not there.
+ *
+ * - when the user is retrieving a (key, value) object, he/she is given a
+ * dynamically allocated buffer, which needs to be explicitly released by
+ * calling the new API: freevar().
+ *
+ * - the (key. value) pairs, if modified, are updated in flash immediately,
+ * not after nvmem_commit() is called.
+ *
+ * Storing (key, value) pairs in the flash frees up 272 bytes of the cache
+ * space previously used, but makes it more difficult to control how flash
+ * memory is split between TPM objects and (key, value) pairs. A soft limit of
+ * 1K is introduced, limiting the total space used by (key, value) pairs data,
+ * not including tuple headers.
+ *
+ * ===== Organizing flash storage
+ *
+ * The total space used by the NVMEM in flash is reduced from 24 to 20
+ * kilobytes, five 2K pages at the top of each flash bank. These pages are
+ * concatenated into a single storage space, based on the page header placed
+ * at the bottom of each page (struct nn_page_header). The page header
+ * includes a 21 bit page number (this allows to order pages properly on
+ * initialization), the offset of the first data object in the page and the
+ * hash of the entire header.
+ *
+ * Yet another limitation of the new scheme is that no object stored in NVMEM
+ * can exceed the flash page size less the page header size and container
+ * header size. This allows for objects as large as 2035 bytes. Objects can
+ * span flash pages. Note that reserved TPM object STATE_CLEAR_DATA exceeds 2K
+ * in size, this is why one of its components (the .pcrSave field) is stored
+ * in flash separately.
+ *
+ * ===== Object containers
+ *
+ * The container header (struct nn_container) describes the contained TPM
+ * object or (key, value) pair, along with the size, and also includes the
+ * hash of the entire container calculated when the hash field is set to zero.
+ *
+ * When an object needs to be updated, it is stored at the end of the used
+ * flash space in a container with the higher .generation field value, and
+ * then the older container's type field is erased, thus marking it as a
+ * deleted object. The idea is that when initializing NVMEM cache after reset,
+ * in case two instances of the same object are found in the flash because the
+ * new instance was saved, but the old instance was not erased because of some
+ * failure, the instance with larger .generation field value wins. Note that
+ * this error recovery procedure is supplemented by use of transaction
+ * delimiter objects described below.
+ *
+ * The container type field is duplicated in the container header, this allows
+ * verification of the container hash after even the object was erased.
+ *
+ * In order to be able to erase the type the container must start at the 4
+ * byte boundary. This in turn requires that each container is padded such
+ * that total storage taken by the container is divisible by 4.
+ *
+ * To be able to tell if two containers contain two instances of the same
+ * object, one needs to be able to identify the object stored in the container.
+ * For the three distinct types of objects it works as follows:
+ *
+ * - (key, value) pair: key, stored in the contained tuple.
+ *
+ * - reserved tpm object: the first byte stored in the container. PCR
+ * values from STATE_CLEAR_DATA.pcrSave field are stored as separate
+ * reserved objects with the appropriate first bytes.
+ *
+ * - evictable tpm object: the first 4 bytes stored in the container, the
+ * evictable TTPM object ID.
+ *
+ * Don't forget that the contents are usually encrypted. Decryption is needed
+ * each time a stored object needs to be examined.
+ *
+ * Reserved objects of types STATE_CLEAR_DATA and STATE_RESET_DATA are very
+ * big and are stored in the flash in marshaled form. On top of 'regular' TPM2
+ * style marshaling, PCRs found in the STATE_CLEAR_DATA object are stored in
+ * separate containers.
+ *
+ * ===== Storage compaction
+ *
+ * Keeping adding changed values at the end of the flash space would
+ * inevitably cause space overflow, unless something is done about it. This is
+ * where flash compaction kicks in: as soon as there are just three free flash
+ * pages left the stored objects are moved to the end of the space, which
+ * results in earlier used pages being freed and added to the pool of
+ * available flash pages.
+ *
+ * A great improvement to this storage compaction process would be grouping
+ * the objects such that the rarely changing ones occupy flash pages at the
+ * lower page indices. In this case when compaction starts, the pages not
+ * containing erased objects would not have to be re-written. This
+ * optimization is left as a future enhancement.
+ *
+ * ===== Committing TPM changes
+ *
+ * When nvmem_commit() is invoked it is necessary to identify which TPM
+ * objects in the cache have changed and require saving. Remember, that (key,
+ * value) pairs are not held in the cache any more and are saved in the flash
+ * immediately, so they do not have to be dealt with during commit.
+ *
+ * The commit procedure starts with iterating over the evictable objects space
+ * in the NVMEM cache, storing in an array offsets of all evictable objects it
+ * finds there. Then it iterates over flash contents skipping over (key,
+ * value) pairs.
+ *
+ * For each reserved object stored in flash, it compares its stored value with
+ * the value stored in the cache at known fixed location. If the value has
+ * changed, a new reserved object instance is saved in flash. This approach
+ * requires that all reserved objects are present in the flash, otherwise
+ * there is nothing to compare the cached instance of the object with. This is
+ * enforced by the init function.
+ *
+ * For each evictable object stored in flash, it checks if that object is
+ * still in the cache using the previously collected array of offsets. If the
+ * object is not in the cache, it must have been deleted by the TPM. The
+ * function deletes it from the flash as well. If the object is in the cache,
+ * its offset is removed from the array to indicate that the object has been
+ * processed. Then if the object value has changed, the new instance is added
+ * and the old instance erased. After this the only offsets left in the array
+ * are offsets of new objects, not yet saved in the flash. All these remaining
+ * objects get saved.
+ *
+ * To ensure transaction integrity, object deletions are just scheduled and
+ * not processed immediately, the deletion happens after all new instances
+ * have been saved in flash. See more about transaction delimiters below.
+ *
+ * ===== Migration from legacy storage and reclaiming flash space
+ *
+ * To be able to migrate existing devices from the legacy storage format the
+ * initialization code checks if a full 12K flash partition is still present,
+ * and if so - copies its contents into the cache and invokes the migration
+ * function. The function erases the alternative partition and creates a list
+ * of 5 pages available for the new format (remember, the flash footprint of
+ * the new scheme is smaller, only 10K is available in each half).
+ *
+ * The (key, value) pairs and TPM objects are stored in the new format as
+ * described, and then the legacy partition is erased and its pages are added
+ * to the list of free pages. This approach would fail if the existing TPM
+ * storage would exceed 10K, but this is extremely unlikely, especially since
+ * the large reserved objects are stored by the new scheme in marshaled form.
+ * This frees up a lot of flash space.
+ *
+ * Eventually it will be possible to reclaim the bottom 2K page per flash half
+ * currently used by the legacy scheme, but this would be possible only after
+ * migration is over. The plan is to keep a few Cr50 versions supporting the
+ * migration process, and then drop the migration code and rearrange the
+ * memory map and reclaim the freed pages. Chrome OS will always carry a
+ * migrating capable Cr50 version along with the latest one to make sure that
+ * even Chrome OS devices which had not updated their Cr50 code in a long
+ * while can be migrated in two steps.
+ *
+ * ===== Initialization, including erased/corrupted flash
+ *
+ * On regular startup (no legacy partition found) the flash pages dedicated to
+ * NVMEM storage are examined, pages with valid headers are included in the
+ * list of available pages, sorted by the page number. Erased pages are kept
+ * in a separate list. Pages which are not fully erased (but do not have a
+ * valid header) are considered corrupted, are erased, and added to the second
+ * list.
+ *
+ * After that the contents of the ordered flash pages is read, all discovered
+ * TPM objects are verified and saved in the cache.
+ *
+ * To enforce that all reserved TPM objects are present in the flash, the init
+ * routine maintains a bitmap of the reserved objects it found while
+ * initializing. In the case when after the scan of the entire NVMEM flash it
+ * turns out that some reserved objects have not been encountered, the init
+ * routine creates new flash instances of the missing reserved objects with
+ * default value of zero. This takes care of both initializing from empty
+ * flash and a case when a reserved object disappears due to a bug.
+ *
+ * ===== Transactions support
+ *
+ * It is important to make sure that TPM changes are grouped together. It came
+ * naturally with the legacy scheme where each time nvmem_save() was called,
+ * the entire cache snapshot was saved in the flash. With the new scheme some
+ * extra effort is required.
+ *
+ * Transaction delimiters are represented by containers of the appropriate
+ * type and the payload size of zero. When nvmem_save() operation is started,
+ * the new objects get written into flash and the objects requiring deletion
+ * are kept in the list. Once all new objects are added to the flash, the
+ * transaction delimiter is written, ending up at the top of the used flash.
+ * After that the objects scheduled for deletion are deleted, and then the
+ * transaction delimiter is also marked 'deleted'.
+ *
+ * So, during initialization the flash could be in one of three states:
+ *
+ * - thre is an erased transaction delimiter at the top
+ * . this is the normal state after successful commit operation.
+ *
+ * - there is transaction delimiter at the top, but it is not erased.
+ * . this is the case where the new objects were saved in flash, but some of
+ * the old instances might still be present not erased. The recovery
+ * procedure finds all duplicates of the objects present between two most
+ * recent delimiters and erases them.
+ *
+ * - there is no transaction delimiter on the top.
+ * . this is the case where nvmem_save() was interrupted before all new
+ * values have been written into the flash. The recovery procedure finds
+ * all TPM objects above the last valid delimiter in the flash and erases
+ * them all.
+ *
+ * ===== Handling failures
+ *
+ * This implementation is no better in handling failures than the legacy one,
+ * it in fact is even worse, because if a failure happened during legacy
+ * commit operation, at least the earlier saved partition would be available.
+ * If failure happens during this implementation's save or compaction process,
+ * there is a risk of ending up with a corrupted or inconsistent flash
+ * contents, even though the use of transaction delimiters narrows the failure
+ * window significantly.
+ *
+ * This first draft is offered for review and to facilitate testing and
+ * discussion about how failures should be addressed.
+ *
+ * ===== Missing stuff
+ *
+ * Presently not much thought has been given to locking and preventing
+ * problems caused by task preemption. The legacy scheme is still in place,
+ * but it might require improvements.
+ */
+
+/*
+ * This code relies on the fact that space dedicated to flash NVMEM storage is
+ * sufficient to guarantee that the entire NVMEM snapshot can fit into it
+ * comfortably. The assert below is a very liberal computation which
+ * guarantees this assumption. Note that marshaling huge reserved structures
+ * reduces amount of required flash space, and this is not accounted for in
+ * this calculation. Space allocated for 200 container descriptors is also way
+ * more than required.
+ */
+
+/*
+ * Fuzz testing does not enforce proper size definitions, causing the below
+ * assert failure.
+ */
+BUILD_ASSERT((NEW_NVMEM_TOTAL_PAGES * CONFIG_FLASH_BANK_SIZE) >
+ (MAX_VAR_TOTAL_SPACE +
+ NV_MEMORY_SIZE +
+ 200 * (sizeof(struct nn_container)) +
+ CONFIG_FLASH_BANK_SIZE * 2));
+
+/* Maximum number of evictable objects we support. */
+#define MAX_STORED_EVICTABLE_OBJECTS 20
+/*
+ * Container for storing (key, value) pairs, a.k.a. vars during read. Actual
+ * vars would never be this large, but when looking for vars we need to be
+ * able to iterate over and verify all objects in the flash, hence the max
+ * body size.
+ */
+struct max_var_container {
+ struct nn_container c_header;
+ struct tuple t_header;
+ uint8_t body[CONFIG_FLASH_BANK_SIZE - sizeof(struct nn_container) -
+ sizeof(struct tuple)];
+} __packed;
+
+/*
+ * Limit of the number of objects which can be updated in one TPM transaction,
+ * reserved and evictable total. This is much more than practical maximum.
+ */
+#define MAX_DELETE_CANDIDATES 30
+static struct delete_candidates {
+ size_t num_candidates;
+ const struct nn_container *candidates[MAX_DELETE_CANDIDATES];
+} *del_candidates;
+
+/*
+ * This array contains a list of flash pages indices (0..255 range) sorted by
+ * the page header page number filed. Erased pages are kept at the tail of the
+ * list.
+ */
+static uint8_t page_list[NEW_NVMEM_TOTAL_PAGES];
+static uint8_t migration_in_progress;
+static uint32_t next_evict_obj_base;
+
+/*
+ * Total space taken by key, value pairs in flash. It is limited to give TPM
+ * objects priority.
+ */
+test_export_static uint16_t total_var_space;
+
+/* The main context used when adding objects to NVMEM. */
+test_export_static struct access_tracker master_at;
+
+test_export_static enum ec_error_list browse_flash_contents(int print);
+static enum ec_error_list save_container(struct nn_container *nc);
+
+/* Log NVMEM problem as per passed in payload and size, and reboot. */
+static void report_failure(struct nvmem_failure_payload *payload,
+ size_t payload_union_size)
+{
+ flash_log_add_event(
+ FE_LOG_NVMEM,
+ payload_union_size +
+ offsetof(struct nvmem_failure_payload, size),
+ payload);
+ ccprintf("Logging failure %d\n", payload->failure_type);
+ system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED | SYSTEM_RESET_HARD);
+}
+
+static void report_no_payload_failure(enum nvmem_failure_type type)
+{
+ struct nvmem_failure_payload fp;
+
+ fp.failure_type = NVMEMF_INCONSISTENT_FLASH_CONTENTS;
+ report_failure(&fp, 0);
+}
+
+/*
+ * This function allocates a buffer of the requested size.
+ *
+ * Heap space could be very limited and at times there could be not enough
+ * memory in the heap to allocate. This should not be considered a failure,
+ * polling should be used instead. On a properly functioning device the memory
+ * would become available. If it is not - there is not much we can do, we'll
+ * have to reboot adding a log entry.
+ */
+static void *get_scratch_buffer(size_t size)
+{
+ char *buf;
+ int i;
+ struct nvmem_failure_payload fp;
+
+ /*
+ * Wait up to a 5 seconds in case some other operation is under
+ * way.
+ */
+ for (i = 0; i < 50; i++) {
+ int rv;
+
+ rv = shared_mem_acquire(size, &buf);
+ if (rv == EC_SUCCESS) {
+ if (i)
+ ccprintf("%s: waited %d cycles!\n", __func__,
+ i);
+ return buf;
+ }
+ usleep(100 * MSEC);
+ }
+
+ fp.failure_type = NVMEMF_MALLOC;
+ fp.size = size;
+ report_failure(&fp, sizeof(fp.size));
+
+ /* Just to keep the compiler happy, this is never reached. */
+ return NULL;
+}
+
+/* Helper function returning actual size used by NVMEM in flash. */
+static size_t total_used_size(void)
+{
+ return master_at.list_index * CONFIG_FLASH_BANK_SIZE +
+ master_at.mt.data_offset;
+}
+/*
+ * Helper functions to set a bit a bit at a certain index in a bitmap array
+ * and to check if the bit is set. The caller must guarantee that the bitmap
+ * array is large enough for the index.
+ */
+static int bitmap_bit_check(const uint8_t *bitmap, size_t index)
+{
+ return bitmap[index / 8] & (1 << (index % 8));
+}
+
+static int bitmap_bit_set(uint8_t *bitmap, size_t index)
+{
+ return bitmap[index / 8] |= (1 << (index % 8));
+}
+
+/* Convenience functions used to reduce amount of typecasting. */
+static void app_compute_hash_wrapper(void *buf, size_t size, void *hash,
+ size_t hash_size)
+{
+ app_compute_hash(buf, size, hash, hash_size);
+}
+
+static STATE_CLEAR_DATA *get_scd(void)
+{
+ NV_RESERVED_ITEM ri;
+
+ NvGetReserved(NV_STATE_CLEAR, &ri);
+
+ return (STATE_CLEAR_DATA *)((uint8_t *)nvmem_cache_base(NVMEM_TPM) +
+ ri.offset);
+}
+
+/* Veirify page header hash. */
+static int page_header_is_valid(struct nn_page_header *ph)
+{
+ uint32_t ph_hash;
+
+ app_compute_hash_wrapper(ph, offsetof(struct nn_page_header, page_hash),
+ &ph_hash, sizeof(ph_hash));
+
+ return ph_hash == ph->page_hash;
+}
+
+/* Convert flash page number in 0..255 range into actual flash address. */
+static struct nn_page_header *flash_index_to_ph(uint8_t index)
+{
+ return (struct nn_page_header *)((index * CONFIG_FLASH_BANK_SIZE) +
+ CONFIG_PROGRAM_MEMORY_BASE);
+}
+
+static const void *page_cursor(const struct page_tracker *pt)
+{
+ return (void *)((uintptr_t)pt->ph + pt->data_offset);
+}
+
+/*
+ * Return flash page pointed at by a certain page_list element if the page is
+ * valid. If the index is out of range, or page is not initialized properly
+ * return NULL.
+ */
+test_export_static struct nn_page_header *list_element_to_ph(size_t el)
+{
+ struct nn_page_header *ph;
+
+ if (el >= ARRAY_SIZE(page_list))
+ return NULL;
+
+ ph = flash_index_to_ph(page_list[el]);
+
+ if (page_header_is_valid(ph))
+ return ph;
+
+ return NULL;
+}
+
+/*
+ * Read into buf or skip if buf is NULL the next num_bytes in the storage, at
+ * the location determined by the passed in access tracker. Start from the
+ * very beginning if the passed in access tracker is empty.
+ *
+ * If necessary - concatenate contents from different pages bypassing page
+ * headers.
+ *
+ * If user is reading the container header (as specified by the
+ * container_fetch argument), save in the context the location of the
+ * container.
+ *
+ * If not enough bytes are available in the storage to satisfy the request -
+ * log error and reboot.
+ */
+static size_t nvmem_read_bytes(struct access_tracker *at, size_t num_bytes,
+ void *buf, int container_fetch)
+{
+ size_t togo;
+ struct nvmem_failure_payload fp;
+
+ if (!at->list_index && !at->mt.data_offset) {
+ /* Start from the beginning. */
+ at->mt.ph = list_element_to_ph(0);
+ at->mt.data_offset = at->mt.ph->data_offset;
+ }
+
+ if (container_fetch) {
+ at->ct.data_offset = at->mt.data_offset;
+ at->ct.ph = at->mt.ph;
+ }
+
+ if ((at->mt.data_offset + num_bytes) < CONFIG_FLASH_BANK_SIZE) {
+ /*
+ * All requested data fits and does not even reach the top of
+ * the page.
+ */
+ if (buf)
+ memcpy(buf, page_cursor(&at->mt), num_bytes);
+
+ at->mt.data_offset += num_bytes;
+ return num_bytes;
+ }
+
+ /* Data is split between pages. */
+ /* To go in the current page. */
+ togo = CONFIG_FLASH_BANK_SIZE - at->mt.data_offset;
+ if (buf) {
+ memcpy(buf, page_cursor(&at->mt), togo);
+ /* Next portion goes here. */
+ buf = (uint8_t *)buf + togo;
+ }
+
+ /*
+ * Determine how much is there to read in the next page.
+ *
+ * Since object size is limited to page size
+ * less page header size, we are guaranteed that the object would not
+ * span more than one page boundary.
+ */
+ togo = num_bytes - togo;
+
+ /* Move to the next page. */
+ at->list_index++;
+ at->mt.ph = list_element_to_ph(at->list_index);
+
+ if (!at->mt.ph && togo) {
+ /*
+ * No more data to read. Could the end of used flash be close
+ * to the page boundary, so that there is no room to read an
+ * erased container header?
+ */
+ if (!container_fetch) {
+ fp.failure_type = NVMEMF_READ_UNDERRUN;
+ fp.underrun_size = num_bytes - togo;
+ /* This will never return. */
+ report_failure(&fp, sizeof(fp.underrun_size));
+ }
+
+ /*
+ * Simulate reading of the container header filled with all
+ * ones, which would be an indication of the end of storage,
+ * the caller will roll back ph, data_offset and list index as
+ * appropriate.
+ */
+ memset(buf, 0xff, togo);
+ } else if (at->mt.ph) {
+ if (at->mt.ph->data_offset < (sizeof(*at->mt.ph) + togo)) {
+ fp.failure_type = NVMEMF_PH_SIZE_MISMATCH;
+ fp.ph.ph_offset = at->mt.ph->data_offset;
+ fp.ph.expected = sizeof(*at->mt.ph) + togo;
+ /* This will never return. */
+ report_failure(&fp, sizeof(fp.ph));
+ }
+ if (buf)
+ memcpy(buf, at->mt.ph + 1, togo);
+
+ at->mt.data_offset = sizeof(*at->mt.ph) + togo;
+ }
+
+ return num_bytes;
+}
+
+/*
+ * Convert passed in absolute address into flash memory offset and write the
+ * passed in blob into the flash.
+ */
+static enum ec_error_list write_to_flash(const void *flash_addr,
+ const void *obj, size_t size)
+{
+ return flash_physical_write(
+ (uintptr_t)flash_addr - CONFIG_PROGRAM_MEMORY_BASE, size, obj);
+}
+
+/*
+ * When initializing flash for the first time - set the proper first page
+ * header.
+ */
+static enum ec_error_list set_first_page_header(void)
+{
+ struct nn_page_header ph = {};
+ enum ec_error_list rv;
+ struct nn_page_header *fph; /* Address in flash. */
+
+ ph.data_offset = sizeof(ph);
+ app_compute_hash_wrapper(&ph,
+ offsetof(struct nn_page_header, page_hash),
+ &ph.page_hash, sizeof(ph.page_hash));
+
+ fph = flash_index_to_ph(page_list[0]);
+ rv = write_to_flash(fph, &ph, sizeof(ph));
+
+ if (rv == EC_SUCCESS) {
+ /* Make sure master page tracker is ready. */
+ memset(&master_at, 0, sizeof(master_at));
+ master_at.mt.data_offset = ph.data_offset;
+ master_at.mt.ph = fph;
+ }
+
+ return rv;
+}
+
+/*
+ * Verify that the passed in container is valid, specifically that its hash
+ * matches its contents.
+ */
+static int container_is_valid(struct nn_container *ch)
+{
+ struct nn_container dummy_c;
+ uint32_t hash;
+ uint32_t preserved_hash;
+ uint8_t preserved_type;
+
+ preserved_hash = ch->container_hash;
+ preserved_type = ch->container_type;
+
+ ch->container_type = ch->container_type_copy;
+ ch->container_hash = 0;
+ app_compute_hash_wrapper(ch, ch->size + sizeof(*ch), &hash,
+ sizeof(hash));
+
+ ch->container_hash = preserved_hash;
+ ch->container_type = preserved_type;
+
+ dummy_c.container_hash = hash;
+
+ return dummy_c.container_hash == ch->container_hash;
+}
+
+static uint32_t aligned_container_size(const struct nn_container *ch)
+{
+ const size_t alignment_mask = CONFIG_FLASH_WRITE_SIZE - 1;
+
+ return (ch->size + sizeof(*ch) + alignment_mask) & ~alignment_mask;
+}
+
+/*
+ * Function which allows to iterate through all objects stored in flash. The
+ * passed in context keeps track of where the previous object retrieval ended.
+ *
+ * Return:
+ * EC_SUCCESS if an object is retrieved and verified
+ * EC_ERROR_MEMORY_ALLOCATION if 'erased' object reached (not an error).
+ * EC_ERROR_INVAL if verification failed or read is out of sync.
+ */
+enum ec_error_list get_next_object(struct access_tracker *at,
+ struct nn_container *ch, int include_deleted)
+{
+ uint32_t salt[4];
+ uint8_t ctype;
+
+ salt[3] = 0;
+
+ do {
+ size_t aligned_remaining_size;
+ struct nn_container temp_ch;
+
+ nvmem_read_bytes(at, sizeof(temp_ch), &temp_ch, 1);
+ ctype = temp_ch.container_type;
+
+ /* Should we check for the container being all 0xff? */
+ if (ctype == NN_OBJ_ERASED) {
+ /* Roll back container size. */
+ at->mt.data_offset = at->ct.data_offset;
+ at->mt.ph = at->ct.ph;
+
+ /*
+ * If the container header happened to span between
+ * two pages - roll back page index saved in the
+ * context.
+ */
+ if ((CONFIG_FLASH_BANK_SIZE - at->mt.data_offset) <
+ sizeof(struct nn_container))
+ at->list_index--;
+
+ return EC_ERROR_MEMORY_ALLOCATION;
+ }
+
+ /*
+ * The read data is a container header, copy it into the user
+ * provided space and continue reading there.
+ */
+ *ch = temp_ch;
+ aligned_remaining_size =
+ aligned_container_size(ch) - sizeof(*ch);
+
+ if (aligned_remaining_size) {
+ if (aligned_remaining_size >
+ (CONFIG_FLASH_BANK_SIZE - sizeof(*ch))) {
+ /* Never returns. */
+ report_no_payload_failure(
+ NVMEMF_INCONSISTENT_FLASH_CONTENTS);
+ }
+
+ nvmem_read_bytes(at, aligned_remaining_size, ch + 1, 0);
+
+ salt[0] = at->ct.ph->page_number;
+ salt[1] = at->ct.data_offset;
+ salt[2] = ch->container_hash;
+
+ /* Decrypt in place. */
+ app_cipher(salt, ch + 1, ch + 1, ch->size);
+ }
+
+ /* And calculate hash. */
+ if (!container_is_valid(ch)) {
+ ccprintf("%s: container hash mismatch!\n", __func__);
+ return EC_ERROR_INVAL;
+ }
+
+ /*
+ * Keep track of the most recently encountered delimiter,
+ * finalized or not.
+ */
+ if (ch->container_type_copy == NN_OBJ_TRANSACTION_DEL) {
+ include_deleted = 1; /* Always return all delimiters. */
+
+ /* But keep track only of finalized ones. */
+ if (ch->container_type == NN_OBJ_OLD_COPY) {
+ at->dt.ph = at->ct.ph;
+ at->dt.data_offset = at->ct.data_offset;
+ }
+ }
+
+ } while (!include_deleted && (ctype == NN_OBJ_OLD_COPY));
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Add a delimiter object at the top of the flash. The container type field is
+ * not erased.
+ *
+ * This is an indication that after nvmem_commit() invocation all updated
+ * objects have been saved in the flash, but the old instances of the objects
+ * have not yet been deleted.
+ */
+static enum ec_error_list add_delimiter(void)
+{
+ struct nn_container ch;
+
+ memset(&ch, 0, sizeof(ch));
+
+ ch.container_type = ch.container_type_copy = NN_OBJ_TRANSACTION_DEL;
+
+ return save_container(&ch);
+}
+
+/*
+ * Erase the container type field of the previously saved delimiter, thus
+ * indicating that nvmem save transaction is completed.
+ */
+static enum ec_error_list finalize_delimiter(const struct nn_container *del)
+{
+ struct nn_container c;
+
+ c = *del;
+ c.container_type = NN_OBJ_OLD_COPY;
+
+ return write_to_flash(del, &c, sizeof(c));
+}
+
+/* Add delimiter indicating that flash is in a consistent state. */
+static enum ec_error_list add_final_delimiter(void)
+{
+ const struct nn_container *del;
+
+ del = page_cursor(&master_at.mt);
+ add_delimiter();
+
+ return finalize_delimiter(del);
+}
+
+/* Erase flash page and add it to the pool of empty pages. */
+static void release_flash_page(struct access_tracker *at)
+{
+ uint8_t page_index = page_list[0];
+ void *flash;
+
+ flash = flash_index_to_ph(page_index);
+ flash_physical_erase((uintptr_t)flash - CONFIG_PROGRAM_MEMORY_BASE,
+ CONFIG_FLASH_BANK_SIZE);
+ memmove(page_list, page_list + 1,
+ (ARRAY_SIZE(page_list) - 1) * sizeof(page_list[0]));
+ page_list[ARRAY_SIZE(page_list) - 1] = page_index;
+ at->list_index--;
+ master_at.list_index--;
+}
+
+/* Reshuffle flash contents dropping deleted objects. */
+test_export_static enum ec_error_list compact_nvmem(void)
+{
+ const void *fence_ph;
+ enum ec_error_list rv = EC_SUCCESS;
+ size_t before;
+ struct nn_container *ch;
+ struct access_tracker at = {};
+ int saved_object_count;
+ int final_delimiter_needed = 1;
+
+ /* How much space was used before compaction. */
+ before = total_used_size();
+
+ /* One page is enough even for the largest object. */
+ ch = get_scratch_buffer(CONFIG_FLASH_BANK_SIZE);
+
+ /*
+ * Page where we should stop compaction, all pages before this would
+ * be recycled.
+ */
+ fence_ph = master_at.mt.ph;
+ saved_object_count = 0;
+
+ do {
+ switch (get_next_object(&at, ch, 0)) {
+ case EC_SUCCESS:
+ break;
+
+ case EC_ERROR_MEMORY_ALLOCATION:
+ shared_mem_release(ch);
+ return EC_SUCCESS;
+
+ default:
+ /*
+ * The error has been reported already.
+ *
+ * This must be compaction after startup with
+ * inconsistent nvmemory state, let's make sure the
+ * top page is recycled.
+ */
+ if (at.mt.ph != fence_ph)
+ release_flash_page(&at);
+ shared_mem_release(ch);
+ return EC_ERROR_INVAL;
+ }
+
+ /* Re-store the object in compacted flash. */
+ switch (ch->container_type) {
+ case NN_OBJ_TUPLE:
+ case NN_OBJ_TPM_RESERVED:
+ case NN_OBJ_TPM_EVICTABLE:
+ ch->generation++;
+ if (save_container(ch) != EC_SUCCESS) {
+ ccprintf("%s: Saving FAILED\n", __func__);
+ shared_mem_release(ch);
+ return EC_ERROR_INVAL;
+ }
+ saved_object_count++;
+ break;
+ default:
+ break;
+ }
+
+ if (at.list_index != 0) {
+ /*
+ * We are done with a pre-compaction page, use a
+ * delimiter to indicate that a bunch of objects are
+ * being deleted and finalize the delimiter once the
+ * old page is erased.
+ *
+ * Return the erased page to the pool of empty pages
+ * and rearrange the list of active pages.
+ */
+ const void *del;
+
+ if (saved_object_count) {
+ del = page_cursor(&master_at.mt);
+ add_delimiter();
+ }
+
+ release_flash_page(&at);
+#if defined(NVMEM_TEST_BUILD)
+ if (failure_mode == TEST_FAIL_WHEN_COMPACTING) {
+ shared_mem_release(ch);
+ return EC_SUCCESS;
+ }
+#endif
+
+ if (saved_object_count) {
+ finalize_delimiter(del);
+ saved_object_count = 0;
+ }
+ /*
+ * No need in another delimiter if data ends on a page
+ * boundary.
+ */
+ final_delimiter_needed = 0;
+ } else {
+ final_delimiter_needed = 1;
+ }
+ } while (at.mt.ph != fence_ph);
+
+ shared_mem_release(ch);
+
+ if (final_delimiter_needed)
+ add_final_delimiter();
+
+ ccprintf("Compaction done, went from %d to %d bytes\n", before,
+ total_used_size());
+ return rv;
+}
+
+static void start_new_flash_page(size_t data_size)
+{
+ struct nn_page_header ph = {};
+
+ ph.data_offset = sizeof(ph) + data_size;
+ ph.page_number = master_at.mt.ph->page_number + 1;
+ app_compute_hash_wrapper(&ph,
+ offsetof(struct nn_page_header, page_hash),
+ &ph.page_hash, sizeof(ph.page_hash));
+ master_at.list_index++;
+ if (master_at.list_index == ARRAY_SIZE(page_list))
+ report_no_payload_failure(NVMEMF_PAGE_LIST_OVERFLOW);
+
+ master_at.mt.ph =
+ (const void *)(((uintptr_t)page_list[master_at.list_index] *
+ CONFIG_FLASH_BANK_SIZE) +
+ CONFIG_PROGRAM_MEMORY_BASE);
+
+ write_to_flash(master_at.mt.ph, &ph, sizeof(ph));
+ master_at.mt.data_offset = sizeof(ph);
+}
+
+/*
+ * Save in the flash an object represented by the passed in container. Add new
+ * pages to the list of used pages if necessary.
+ */
+static enum ec_error_list save_object(const struct nn_container *cont)
+{
+ const void *save_data = cont;
+ size_t save_size = aligned_container_size(cont);
+ size_t top_room;
+
+ top_room = CONFIG_FLASH_BANK_SIZE - master_at.mt.data_offset;
+ if (save_size >= top_room) {
+
+ /* Let's finish the current page. */
+ write_to_flash((uint8_t *)master_at.mt.ph +
+ master_at.mt.data_offset,
+ cont, top_room);
+
+ /* Remaining data and size to be written on the next page. */
+ save_data = (const void *)((uintptr_t)save_data + top_room);
+ save_size -= top_room;
+ start_new_flash_page(save_size);
+ }
+
+ if (save_size) {
+ write_to_flash((uint8_t *)master_at.mt.ph +
+ master_at.mt.data_offset,
+ save_data, save_size);
+ master_at.mt.data_offset += save_size;
+ }
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Functions to check if the passed in blob is all zeros or all 0xff, in both
+ * cases would be considered an uninitialized value. This is used when
+ * marshaling certaing structures and PCRs.
+ */
+static int is_all_value(const uint8_t *p, size_t size, uint8_t value)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ if (p[i] != value)
+ return 0;
+
+ return 1;
+}
+
+test_export_static int is_uninitialized(const void *p, size_t size)
+{
+ return is_all_value(p, size, 0xff);
+}
+
+static int is_all_zero(const void *p, size_t size)
+{
+ return is_all_value(p, size, 0);
+}
+
+static int is_empty(const void *pcr_base, size_t pcr_size)
+{
+ return is_uninitialized(pcr_base, pcr_size) ||
+ is_all_zero(pcr_base, pcr_size);
+}
+
+/*
+ * A convenience function checking if the passed in blob is not empty, and if
+ * so - save the blob in the destination memory.
+ *
+ * Return number of bytes placed in dst or zero, if the blob was empty.
+ */
+static size_t copy_pcr(const uint8_t *pcr_base, size_t pcr_size, uint8_t *dst)
+{
+ /*
+ * We rely on the fact that all 16 PCRs of every PCR bank saved in the
+ * NVMEM's reserved space are originally set to all zeros.
+ *
+ * If all 0xFF is read - this is considered an artifact of trying to
+ * retrieve PCRs from legacy flash snapshot from the state when PCRs
+ * were not saved in the reserved space at all, i.e. also indicates an
+ * empty PCR.
+ */
+ if (is_empty(pcr_base, pcr_size))
+ return 0; /* No need to save this. */
+
+ memcpy(dst, pcr_base, pcr_size);
+
+ return pcr_size;
+}
+
+/*
+ * A convenience structure and array, allowing quick access to PCR banks
+ * contained in the STATE_CLEAR_DATA:pcrSave field. This helps when
+ * marshailing/unmarshaling PCR contents.
+ */
+struct pcr_descriptor {
+ uint16_t pcr_array_offset;
+ uint8_t pcr_size;
+} __packed;
+
+static const struct pcr_descriptor pcr_arrays[] = {
+ {offsetof(PCR_SAVE, sha1), SHA1_DIGEST_SIZE},
+ {offsetof(PCR_SAVE, sha256), SHA256_DIGEST_SIZE},
+ {offsetof(PCR_SAVE, sha384), SHA384_DIGEST_SIZE},
+ {offsetof(PCR_SAVE, sha512), SHA512_DIGEST_SIZE}
+};
+#define NUM_OF_PCRS (ARRAY_SIZE(pcr_arrays) * NUM_STATIC_PCR)
+
+/* Just in case we ever get to reducing the PCR set one way or another. */
+BUILD_ASSERT(ARRAY_SIZE(pcr_arrays) == 4);
+BUILD_ASSERT(NUM_OF_PCRS == 64);
+/*
+ * Iterate over PCRs contained in the STATE_CLEAR_DATA structure in the NVMEM
+ * cache and save nonempty ones in the flash.
+ */
+static void migrate_pcr(STATE_CLEAR_DATA *scd, size_t array_index,
+ size_t pcr_index, struct nn_container *ch)
+{
+ const struct pcr_descriptor *pdsc;
+ uint8_t *p_container_body;
+ uint8_t *pcr_base;
+ uint8_t reserved_index; /* Unique ID of this PCR in reserved storage. */
+
+ p_container_body = (uint8_t *)(ch + 1);
+ pdsc = pcr_arrays + array_index;
+ pcr_base = (uint8_t *)&scd->pcrSave + pdsc->pcr_array_offset +
+ pdsc->pcr_size * pcr_index;
+ reserved_index = NV_VIRTUAL_RESERVE_LAST +
+ array_index * NUM_STATIC_PCR + pcr_index;
+
+ if (!copy_pcr(pcr_base, pdsc->pcr_size, p_container_body + 1))
+ return;
+
+ p_container_body[0] = reserved_index;
+ ch->size = pdsc->pcr_size + 1;
+ save_container(ch);
+}
+
+/*
+ * Some NVMEM structures end up in the NVMEM cache with a wrong alignment. If
+ * a passed in pointer is not aligned at a 4 byte boundary, this function will
+ * save the 4 bytes above the blob in the passed in space and then move the
+ * blob up so that it is properly aligned.
+ */
+static void *preserve_struct(void *p, size_t size, uint32_t *preserved)
+{
+ uint32_t misalignment = ((uintptr_t)p & 3);
+ void *new_p;
+
+ if (!misalignment)
+ return p; /* Nothing to adjust. */
+
+ memcpy(preserved, (uint8_t *)p + size, sizeof(*preserved));
+ new_p = (void *)((((uintptr_t)p) + 3) & ~3);
+ memmove(new_p, p, size);
+
+ return new_p;
+}
+
+static void maybe_restore_struct(void *new_p, void *old_p, size_t size,
+ uint32_t *preserved)
+{
+ if (!memcmp(new_p, old_p, size))
+ return;
+
+ memmove(old_p, new_p, size);
+ memcpy((uint8_t *)old_p + size, preserved, sizeof(*preserved));
+}
+
+/*
+ * Note that PCRs are not marshaled here, but the rest of the structre, below
+ * and above the PCR array is.
+ */
+static uint16_t marshal_state_clear(STATE_CLEAR_DATA *scd, uint8_t *dst)
+{
+ PCR_AUTHVALUE *new_pav;
+ STATE_CLEAR_DATA *new_scd;
+ size_t bottom_size;
+ size_t i;
+ size_t top_size;
+ uint32_t preserved;
+ uint8_t *base;
+ int room;
+
+ bottom_size = offsetof(STATE_CLEAR_DATA, pcrSave);
+ top_size = sizeof(scd->pcrAuthValues);
+
+ if (is_empty(scd, bottom_size) &&
+ is_empty(&scd->pcrAuthValues, top_size) &&
+ is_empty(&scd->pcrSave.pcrCounter, sizeof(scd->pcrSave.pcrCounter)))
+ return 0;
+
+ /* Marshaling STATE_CLEAR_DATA will never need this much. */
+ room = CONFIG_FLASH_BANK_SIZE;
+
+ new_scd = preserve_struct(scd, bottom_size, &preserved);
+
+ base = dst;
+
+ *dst++ = (!!new_scd->shEnable) | ((!!new_scd->ehEnable) << 1) |
+ ((!!new_scd->phEnableNV) << 2);
+
+ memcpy(dst, &new_scd->platformAlg, sizeof(new_scd->platformAlg));
+ dst += sizeof(new_scd->platformAlg);
+
+ room -= (dst - base);
+
+ TPM2B_DIGEST_Marshal(&new_scd->platformPolicy, &dst, &room);
+
+ TPM2B_AUTH_Marshal(&new_scd->platformAuth, &dst, &room);
+
+ memcpy(dst, &new_scd->pcrSave.pcrCounter,
+ sizeof(new_scd->pcrSave.pcrCounter));
+ dst += sizeof(new_scd->pcrSave.pcrCounter);
+ room -= sizeof(new_scd->pcrSave.pcrCounter);
+
+ maybe_restore_struct(new_scd, scd, bottom_size, &preserved);
+
+ new_pav = preserve_struct(&scd->pcrAuthValues, top_size, &preserved);
+ for (i = 0; i < ARRAY_SIZE(new_scd->pcrAuthValues.auth); i++)
+ TPM2B_DIGEST_Marshal(new_pav->auth + i, &dst, &room);
+
+ maybe_restore_struct(new_pav, &scd->pcrAuthValues, top_size,
+ &preserved);
+
+ return dst - base;
+}
+
+static uint16_t marshal_state_reset_data(STATE_RESET_DATA *srd, uint8_t *dst)
+{
+ STATE_RESET_DATA *new_srd;
+ uint32_t preserved;
+ uint8_t *base;
+ int room;
+
+ if (is_empty(srd, sizeof(*srd)))
+ return 0;
+
+ /* Marshaling STATE_RESET_DATA will never need this much. */
+ room = CONFIG_FLASH_BANK_SIZE;
+
+ new_srd = preserve_struct(srd, sizeof(*srd), &preserved);
+
+ base = dst;
+
+ TPM2B_AUTH_Marshal(&new_srd->nullProof, &dst, &room);
+ TPM2B_DIGEST_Marshal((TPM2B_DIGEST *)(&new_srd->nullSeed), &dst, &room);
+ UINT32_Marshal(&new_srd->clearCount, &dst, &room);
+ UINT64_Marshal(&new_srd->objectContextID, &dst, &room);
+
+ memcpy(dst, new_srd->contextArray, sizeof(new_srd->contextArray));
+ room -= sizeof(new_srd->contextArray);
+ dst += sizeof(new_srd->contextArray);
+
+ memcpy(dst, &new_srd->contextCounter, sizeof(new_srd->contextCounter));
+ room -= sizeof(new_srd->contextCounter);
+ dst += sizeof(new_srd->contextCounter);
+
+ TPM2B_DIGEST_Marshal(&new_srd->commandAuditDigest, &dst, &room);
+ UINT32_Marshal(&new_srd->restartCount, &dst, &room);
+ UINT32_Marshal(&new_srd->pcrCounter, &dst, &room);
+
+#ifdef TPM_ALG_ECC
+ UINT64_Marshal(&new_srd->commitCounter, &dst, &room);
+ TPM2B_NONCE_Marshal(&new_srd->commitNonce, &dst, &room);
+
+ memcpy(dst, new_srd->commitArray, sizeof(new_srd->commitArray));
+ room -= sizeof(new_srd->commitArray);
+ dst += sizeof(new_srd->commitArray);
+#endif
+
+ maybe_restore_struct(new_srd, srd, sizeof(*srd), &preserved);
+
+ return dst - base;
+}
+
+/*
+ * Migrate all reserved objects found in the NVMEM cache after intializing
+ * from legacy NVMEM storage.
+ */
+static enum ec_error_list migrate_tpm_reserved(struct nn_container *ch)
+{
+ STATE_CLEAR_DATA *scd;
+ STATE_RESET_DATA *srd;
+ size_t pcr_type_index;
+ uint8_t *p_tpm_nvmem = nvmem_cache_base(NVMEM_TPM);
+ uint8_t *p_container_body = (uint8_t *)(ch + 1);
+ uint8_t index;
+
+ ch->container_type = ch->container_type_copy = NN_OBJ_TPM_RESERVED;
+
+ for (index = 0; index < NV_VIRTUAL_RESERVE_LAST; index++) {
+ NV_RESERVED_ITEM ri;
+ int copy_needed = 1;
+
+ NvGetReserved(index, &ri);
+ p_container_body[0] = index;
+
+ switch (index) {
+ case NV_STATE_CLEAR:
+ scd = (STATE_CLEAR_DATA *)(p_tpm_nvmem + ri.offset);
+ ri.size =
+ marshal_state_clear(scd, p_container_body + 1);
+ copy_needed = 0;
+ break;
+
+ case NV_STATE_RESET:
+ srd = (STATE_RESET_DATA *)(p_tpm_nvmem + ri.offset);
+ ri.size = marshal_state_reset_data(
+ srd, p_container_body + 1);
+ copy_needed = 0;
+ break;
+ }
+
+ if (copy_needed) {
+ /*
+ * Copy data into the stage area unless already done
+ * by marshaling function above.
+ */
+ memcpy(p_container_body + 1, p_tpm_nvmem + ri.offset,
+ ri.size);
+ }
+
+ ch->size = ri.size + 1;
+ save_container(ch);
+ }
+
+ /*
+ * Now all components but the PCRs from STATE_CLEAR_DATA have been
+ * saved, let's deal with those PCR arrays. We want to save each PCR
+ * in a separate container, as if all PCRs are extended, the total
+ * combined size of the arrays would exceed flash page size. Also,
+ * PCRs are most likely to change one or very few at a time.
+ */
+ for (pcr_type_index = 0; pcr_type_index < ARRAY_SIZE(pcr_arrays);
+ pcr_type_index++) {
+ size_t pcr_index;
+
+ for (pcr_index = 0; pcr_index < NUM_STATIC_PCR; pcr_index++)
+ migrate_pcr(scd, pcr_type_index, pcr_index, ch);
+ }
+
+ return EC_SUCCESS;
+}
+
+/*
+ * Migrate all evictable objects found in the NVMEM cache after intializing
+ * from legacy NVMEM storage.
+ */
+static enum ec_error_list migrate_objects(struct nn_container *ch)
+{
+ uint32_t next_obj_base;
+ uint32_t obj_base;
+ uint32_t obj_size;
+ void *obj_addr;
+
+ ch->container_type = ch->container_type_copy = NN_OBJ_TPM_EVICTABLE;
+
+ obj_base = s_evictNvStart;
+ obj_addr = nvmem_cache_base(NVMEM_TPM) + obj_base;
+ memcpy(&next_obj_base, obj_addr, sizeof(next_obj_base));
+
+ while (next_obj_base && (next_obj_base <= s_evictNvEnd)) {
+
+ obj_size = next_obj_base - obj_base - sizeof(obj_size);
+ memcpy(ch + 1, (uint32_t *)obj_addr + 1, obj_size);
+
+ ch->size = obj_size;
+ save_container(ch);
+
+ obj_base = next_obj_base;
+ obj_addr = nvmem_cache_base(NVMEM_TPM) + obj_base;
+
+ memcpy(&next_obj_base, obj_addr, sizeof(next_obj_base));
+ }
+
+ return EC_SUCCESS;
+}
+
+static enum ec_error_list migrate_tpm_nvmem(struct nn_container *ch)
+{
+ /* Call this to initialize NVMEM indices. */
+ NvEarlyStageFindHandle(0);
+
+ migrate_tpm_reserved(ch);
+ migrate_objects(ch);
+
+ return EC_SUCCESS;
+}
+
+static enum ec_error_list save_var(const uint8_t *key, uint8_t key_len,
+ const uint8_t *val, uint8_t val_len,
+ struct max_var_container *vc)
+{
+ const int total_size =
+ key_len + val_len + offsetof(struct max_var_container, body);
+ enum ec_error_list rv;
+ int local_alloc = !vc;
+
+ if (local_alloc) {
+ vc = get_scratch_buffer(total_size);
+ vc->c_header.generation = 0;
+ }
+
+ /* Fill up tuple body. */
+ vc->t_header.key_len = key_len;
+ vc->t_header.val_len = val_len;
+ memcpy(vc->body, key, key_len);
+ memcpy(vc->body + key_len, val, val_len);
+
+ /* Set up container header. */
+ vc->c_header.container_type_copy = vc->c_header.container_type =
+ NN_OBJ_TUPLE;
+ vc->c_header.encrypted = 1;
+ vc->c_header.size = sizeof(struct tuple) + val_len + key_len;
+
+ rv = save_container(&vc->c_header);
+ if (rv == EC_SUCCESS) {
+ total_var_space += key_len + val_len;
+ if (!migration_in_progress)
+ add_final_delimiter();
+ }
+
+ if (local_alloc)
+ shared_mem_release(vc);
+
+ return rv;
+}
+
+/*
+ * Migrate all (key, value) pairs found in the NVMEM cache after intializing
+ * from legacy NVMEM storage.
+ */
+static enum ec_error_list migrate_vars(struct nn_container *ch)
+{
+ const struct tuple *var;
+
+ /*
+ * During migration (key, value) pairs need to be manually copied from
+ * the NVMEM cache.
+ */
+ set_local_copy();
+ var = NULL;
+ total_var_space = 0;
+
+ while ((var = legacy_getnextvar(var)) != NULL)
+ save_var(var->data_, var->key_len, var->data_ + var->key_len,
+ var->val_len, (struct max_var_container *)ch);
+
+ return EC_SUCCESS;
+}
+
+static int erase_partition(unsigned int act_partition, int erase_backup)
+{
+ enum ec_error_list rv;
+ size_t flash_base;
+
+ /*
+ * This is the first time we save using the new scheme, let's prepare
+ * the flash space. First determine which half is the backup now and
+ * erase it.
+ */
+ flash_base = (act_partition ^ erase_backup) ? CONFIG_FLASH_NVMEM_BASE_A
+ : CONFIG_FLASH_NVMEM_BASE_B;
+ flash_base -= CONFIG_PROGRAM_MEMORY_BASE;
+
+ rv = flash_physical_erase(flash_base, NVMEM_PARTITION_SIZE);
+
+ if (rv != EC_SUCCESS) {
+ ccprintf("%s: flash erase failed", __func__);
+ return -rv;
+ }
+
+ return flash_base + CONFIG_FLASH_BANK_SIZE;
+}
+
+/*
+ * This function is called once in a lifetime, when Cr50 boots up and a legacy
+ * partition if found in the flash.
+ */
+enum ec_error_list new_nvmem_migrate(unsigned int act_partition)
+{
+ int flash_base;
+ int i;
+ int j;
+ struct nn_container *ch;
+
+ /*
+ * This is the first time we save using the new scheme, let's prepare
+ * the flash space. First determine which half is the backup now and
+ * erase it.
+ */
+ flash_base = erase_partition(act_partition, 1);
+ if (flash_base < 0) {
+ ccprintf("%s: backup partition erase failed", __func__);
+ return -flash_base;
+ }
+
+ ch = get_scratch_buffer(CONFIG_FLASH_BANK_SIZE);
+
+ /* Populate half of page_list with available page offsets. */
+ for (i = 0; i < ARRAY_SIZE(page_list) / 2; i++)
+ page_list[i] = flash_base / CONFIG_FLASH_BANK_SIZE + i;
+
+ set_first_page_header();
+
+ ch->encrypted = 1;
+ ch->generation = 0;
+
+ migration_in_progress = 1;
+ migrate_vars(ch);
+ migrate_tpm_nvmem(ch);
+ migration_in_progress = 0;
+
+ shared_mem_release(ch);
+
+ add_final_delimiter();
+
+ if (browse_flash_contents(0) != EC_SUCCESS)
+ /* Never returns. */
+ report_no_payload_failure(NVMEMF_MIGRATION_FAILURE);
+
+ ccprintf("Migration success, used %d bytes of flash\n",
+ total_used_size());
+
+ /*
+ * Now we can erase the active partition and add its flash to the pool.
+ */
+ flash_base = erase_partition(act_partition, 0);
+ if (flash_base < 0)
+ /* Never returns. */
+ report_no_payload_failure(NVMEMF_LEGACY_ERASE_FAILURE);
+
+ /*
+ * Populate the second half of the page_list with pages retrieved from
+ * legacy partition.
+ */
+ for (j = 0; j < ARRAY_SIZE(page_list) / 2; j++)
+ page_list[i + j] = flash_base / CONFIG_FLASH_BANK_SIZE + j;
+
+ return EC_SUCCESS;
+}
+
+/* Check if the passed in flash page is empty, if not - erase it. */
+static void verify_empty_page(void *ph)
+{
+ uint32_t *word_p = ph;
+ size_t i;
+
+ for (i = 0; i < (CONFIG_FLASH_BANK_SIZE / sizeof(*word_p)); i++) {
+ if (word_p[i] != (uint32_t)~0) {
+ ccprintf("%s: corrupted page at %p!\n", __func__,
+ word_p);
+ flash_physical_erase((uintptr_t)word_p -
+ CONFIG_PROGRAM_MEMORY_BASE,
+ CONFIG_FLASH_BANK_SIZE);
+ break;
+ }
+ }
+}
+
+/*
+ * At startup initialize the list of pages which contain NVMEM data and erased
+ * pages. The list (in fact an array containing indices of the pages) is
+ * sorted by the page number found in the page header. Pages which do not
+ * contain valid page header are checked to be erased and are placed at the
+ * tail of the list.
+ */
+static void init_page_list(void)
+{
+ size_t i;
+ size_t j;
+ size_t page_list_index = 0;
+ size_t tail_index;
+ struct nn_page_header *ph;
+
+ tail_index = ARRAY_SIZE(page_list);
+
+ for (i = 0; i < ARRAY_SIZE(page_list); i++) {
+ uint32_t page_index;
+
+ /*
+ * This will yield indices of top pages first, first from the
+ * bottom half of the flash, and then from the top half. We
+ * know that flash is 512K in size, and pages are 2K in size,
+ * the indices will be in 123..127 and 251..255 range.
+ */
+ if (i < (ARRAY_SIZE(page_list) / 2)) {
+ page_index = (CONFIG_FLASH_NEW_NVMEM_BASE_A -
+ CONFIG_PROGRAM_MEMORY_BASE) /
+ CONFIG_FLASH_BANK_SIZE +
+ i;
+ } else {
+ page_index = (CONFIG_FLASH_NEW_NVMEM_BASE_B -
+ CONFIG_PROGRAM_MEMORY_BASE) /
+ CONFIG_FLASH_BANK_SIZE -
+ ARRAY_SIZE(page_list) / 2 + i;
+ }
+
+ ph = flash_index_to_ph(page_index);
+
+ if (!page_header_is_valid(ph)) {
+ /*
+ * this is not a valid page, let's plug it in into the
+ * tail of the list.
+ */
+ page_list[--tail_index] = page_index;
+ verify_empty_page(ph);
+ continue;
+ }
+
+ /* This seems a valid page, let's put it in order. */
+ for (j = 0; j < page_list_index; j++) {
+ struct nn_page_header *prev_ph;
+
+ prev_ph = list_element_to_ph(j);
+
+ if (prev_ph->page_number > ph->page_number) {
+ /* Need to move up. */
+ memmove(page_list + j + 1, page_list + j,
+ sizeof(page_list[0]) *
+ (page_list_index - j));
+ break;
+ }
+ }
+
+ page_list[j] = page_index;
+ page_list_index++;
+ }
+
+ if (!page_list_index) {
+ ccprintf("Init nvmem from scratch\n");
+ set_first_page_header();
+ page_list_index++;
+ }
+}
+
+/*
+ * The passed in pointer contains marshaled STATE_CLEAR structure as retrieved
+ * from flash. This function unmarshals it and places in the NVMEM cache where
+ * it belongs. Note that PCRs were not marshaled.
+ */
+static void unmarshal_state_clear(uint8_t *pad, int size, uint32_t offset)
+{
+ STATE_CLEAR_DATA *real_scd;
+ STATE_CLEAR_DATA *scd;
+ size_t i;
+ uint32_t preserved;
+ uint8_t booleans;
+
+ real_scd = (STATE_CLEAR_DATA *)((uint8_t *)nvmem_cache_base(NVMEM_TPM) +
+ offset);
+
+ memset(real_scd, 0, sizeof(*real_scd));
+ if (!size)
+ return;
+
+ memcpy(&preserved, real_scd + 1, sizeof(preserved));
+
+ scd = (void *)(((uintptr_t)real_scd + 3) & ~3);
+
+ /* Need proper unmarshal. */
+ booleans = *pad++;
+ scd->shEnable = !!(booleans & 1);
+ scd->ehEnable = !!(booleans & (1 << 1));
+ scd->phEnableNV = !!(booleans & (1 << 2));
+ size--;
+
+ memcpy(&scd->platformAlg, pad, sizeof(scd->platformAlg));
+ pad += sizeof(scd->platformAlg);
+ size -= sizeof(scd->platformAlg);
+
+ TPM2B_DIGEST_Unmarshal(&scd->platformPolicy, &pad, &size);
+ TPM2B_AUTH_Unmarshal(&scd->platformAuth, &pad, &size);
+
+ memcpy(&scd->pcrSave.pcrCounter, pad, sizeof(scd->pcrSave.pcrCounter));
+ pad += sizeof(scd->pcrSave.pcrCounter);
+ size -= sizeof(scd->pcrSave.pcrCounter);
+
+ for (i = 0; i < ARRAY_SIZE(scd->pcrAuthValues.auth); i++)
+ TPM2B_DIGEST_Unmarshal(scd->pcrAuthValues.auth + i, &pad,
+ &size);
+
+ memmove(real_scd, scd, sizeof(*scd));
+ memcpy(real_scd + 1, &preserved, sizeof(preserved));
+}
+
+/*
+ * The passed in pointer contains marshaled STATE_RESET structure as retrieved
+ * from flash. This function unmarshals it and places in the NVMEM cache where
+ * it belongs.
+ */
+static void unmarshal_state_reset(uint8_t *pad, int size, uint32_t offset)
+{
+ STATE_RESET_DATA *real_srd;
+ STATE_RESET_DATA *srd;
+ uint32_t preserved;
+
+ real_srd = (STATE_RESET_DATA *)((uint8_t *)nvmem_cache_base(NVMEM_TPM) +
+ offset);
+
+ memset(real_srd, 0, sizeof(*real_srd));
+ if (!size)
+ return;
+
+ memcpy(&preserved, real_srd + 1, sizeof(preserved));
+
+ srd = (void *)(((uintptr_t)real_srd + 3) & ~3);
+
+ TPM2B_AUTH_Unmarshal(&srd->nullProof, &pad, &size);
+ TPM2B_DIGEST_Unmarshal((TPM2B_DIGEST *)(&srd->nullSeed), &pad, &size);
+ UINT32_Unmarshal(&srd->clearCount, &pad, &size);
+ UINT64_Marshal(&srd->objectContextID, &pad, &size);
+
+ memcpy(srd->contextArray, pad, sizeof(srd->contextArray));
+ size -= sizeof(srd->contextArray);
+ pad += sizeof(srd->contextArray);
+
+ memcpy(&srd->contextCounter, pad, sizeof(srd->contextCounter));
+ size -= sizeof(srd->contextCounter);
+ pad += sizeof(srd->contextCounter);
+
+ TPM2B_DIGEST_Unmarshal(&srd->commandAuditDigest, &pad, &size);
+ UINT32_Unmarshal(&srd->restartCount, &pad, &size);
+ UINT32_Unmarshal(&srd->pcrCounter, &pad, &size);
+
+#ifdef TPM_ALG_ECC
+ UINT64_Unmarshal(&srd->commitCounter, &pad, &size);
+ TPM2B_NONCE_Unmarshal(&srd->commitNonce, &pad, &size);
+
+ memcpy(srd->commitArray, pad, sizeof(srd->commitArray));
+ size -= sizeof(srd->commitArray);
+#endif
+
+ memmove(real_srd, srd, sizeof(*srd));
+ memcpy(real_srd + 1, &preserved, sizeof(preserved));
+}
+
+/*
+ * Based on the passed in index, find the location of the PCR in the NVMEM
+ * cache and copy it there.
+ */
+static void restore_pcr(size_t pcr_index, uint8_t *pad, size_t size)
+{
+ const STATE_CLEAR_DATA *scd;
+ const struct pcr_descriptor *pcrd;
+ void *cached; /* This PCR's position in the NVMEM cache. */
+
+ if (pcr_index > NUM_OF_PCRS)
+ return; /* This is an error. */
+
+ pcrd = pcr_arrays + pcr_index / NUM_STATIC_PCR;
+ if (pcrd->pcr_size != size)
+ return; /* This is an error. */
+
+ scd = get_scd();
+ cached = (uint8_t *)&scd->pcrSave + pcrd->pcr_array_offset +
+ pcrd->pcr_size * (pcr_index % NUM_STATIC_PCR);
+
+ memcpy(cached, pad, size);
+}
+
+/* Restore a reserved object found in flash on initialization. */
+static void restore_reserved(void *pad, size_t size, uint8_t *bitmap)
+{
+ NV_RESERVED_ITEM ri;
+ uint16_t type;
+ void *cached;
+
+ /*
+ * Index is saved as a single byte, update pad to point at the
+ * payload.
+ */
+ type = *(uint8_t *)pad++;
+ size--;
+
+ if (type < NV_VIRTUAL_RESERVE_LAST) {
+ NvGetReserved(type, &ri);
+
+ bitmap_bit_set(bitmap, type);
+
+ switch (type) {
+ case NV_STATE_CLEAR:
+ unmarshal_state_clear(pad, size, ri.offset);
+ break;
+
+ case NV_STATE_RESET:
+ unmarshal_state_reset(pad, size, ri.offset);
+ break;
+
+ default:
+ cached = ((uint8_t *)nvmem_cache_base(NVMEM_TPM) +
+ ri.offset);
+ memcpy(cached, pad, size);
+ break;
+ }
+ return;
+ }
+
+ restore_pcr(type - NV_VIRTUAL_RESERVE_LAST, pad, size);
+}
+
+/* Restore an evictable object found in flash on initialization. */
+static void restore_object(void *pad, size_t size)
+{
+ uint8_t *dest;
+
+ if (!next_evict_obj_base)
+ next_evict_obj_base = s_evictNvStart;
+
+ dest = ((uint8_t *)nvmem_cache_base(NVMEM_TPM) + next_evict_obj_base);
+ next_evict_obj_base += size + sizeof(next_evict_obj_base);
+ memcpy(dest, &next_evict_obj_base, sizeof(next_evict_obj_base));
+
+ dest += sizeof(next_evict_obj_base);
+ memcpy(dest, pad, size);
+ dest += size;
+
+ memset(dest, 0, sizeof(next_evict_obj_base));
+}
+
+/*
+ * When starting from scratch (flash fully erased) there would be no reserved
+ * objects in NVMEM, and for the commit to work properly, every single
+ * reserved object needs to be present in the flash so that its value is
+ * compared with the cache contents.
+ *
+ * There is also an off chance of a bug where a reserved value is lost in the
+ * flash - it would never be reinstated even after TPM reinitializes.
+ *
+ * The reserved_bitmap array is a bitmap of all detected reserved objects,
+ * those not in the array are initialized to a dummy initial value.
+ */
+static enum ec_error_list verify_reserved(uint8_t *reserved_bitmap,
+ struct nn_container *ch)
+{
+ enum ec_error_list rv;
+ int i;
+ uint8_t *container_body;
+ int delimiter_needed = 0;
+
+ /* All uninitted reserved objects set to zero. */
+ memset(ch, 0, CONFIG_FLASH_BANK_SIZE);
+
+ ch->container_type = ch->container_type_copy = NN_OBJ_TPM_RESERVED;
+ ch->encrypted = 1;
+ container_body = (uint8_t *)(ch + 1);
+
+ rv = EC_SUCCESS;
+
+ for (i = 0; i < NV_VIRTUAL_RESERVE_LAST; i++) {
+ NV_RESERVED_ITEM ri;
+
+ if (bitmap_bit_check(reserved_bitmap, i))
+ continue;
+
+ NvGetReserved(i, &ri);
+ container_body[0] = i;
+
+ switch (i) {
+ /*
+ * No need to save these on initialization from
+ * scratch, unmarshaling code will properly expand
+ * size of zero.
+ */
+ case NV_STATE_CLEAR:
+ case NV_STATE_RESET:
+ ri.size = 0;
+ break;
+
+ /*
+ * This is used for Ram Index field, prepended by
+ * size. Set the size to minimum, the size of the size
+ * field.
+ */
+ case NV_RAM_INDEX_SPACE:
+ ri.size = sizeof(uint32_t);
+ break;
+
+ default:
+ break;
+ }
+
+ delimiter_needed = 1;
+
+ ch->size = ri.size + 1;
+ rv = save_container(ch);
+
+ /* Clean up encrypted contents. */
+ memset(container_body + 1, 0, ri.size);
+
+ if (rv != EC_SUCCESS)
+ break;
+ }
+
+ if (delimiter_needed && (rv == EC_SUCCESS))
+ add_final_delimiter();
+
+ return rv;
+}
+
+static enum ec_error_list invalidate_object(const struct nn_container *ch)
+{
+ struct nn_container c_copy;
+
+ c_copy = *ch;
+ c_copy.container_type = NN_OBJ_OLD_COPY;
+
+ return write_to_flash(ch, &c_copy, sizeof(uint32_t));
+}
+
+static enum ec_error_list delete_object(const struct access_tracker *at,
+ struct nn_container *ch)
+{
+ const void *flash_ch;
+
+ flash_ch = page_cursor(&at->ct);
+
+ if (memcmp(ch, flash_ch, sizeof(uint32_t)))
+ report_no_payload_failure(NVMEMF_PRE_ERASE_MISMATCH);
+
+ if (!del_candidates)
+ return invalidate_object(flash_ch);
+
+ /*
+ * Do not delete the object yet, save it in the list of delete
+ * candidates.
+ */
+ if (del_candidates->num_candidates ==
+ ARRAY_SIZE(del_candidates->candidates))
+ report_no_payload_failure(NVMEMF_EXCESS_DELETE_OBJECTS);
+
+ del_candidates->candidates[del_candidates->num_candidates++] = flash_ch;
+ return EC_SUCCESS;
+}
+
+static enum ec_error_list verify_last_section(
+ const struct page_tracker *prev_del, struct nn_container *ch)
+{
+ /*
+ * This is very inefficient, but we do this only when recovering from
+ * botched nvmem saves.
+ *
+ * For each object found between prev_del and last_del we need to
+ * check if there are earlier instances of these objects in the flash
+ * which are not yet deleted, and delete them if found.
+ */
+ struct object {
+ uint8_t cont_type;
+ union {
+ uint32_t handle; /* For evictables. */
+ uint8_t id; /* For reserved objects. */
+ };
+ };
+ struct new_objects {
+ uint8_t num_objects;
+ struct object objects[2 * MAX_DELETE_CANDIDATES];
+ };
+
+ struct access_tracker at;
+ struct new_objects *newobjs;
+ struct object *po;
+ uint8_t ctype;
+ struct page_tracker top_del;
+ int i;
+
+ newobjs = get_scratch_buffer(sizeof(struct new_objects));
+
+ at.mt = *prev_del;
+ for (i = 0; i < ARRAY_SIZE(page_list); i++)
+ if (list_element_to_ph(i) == at.mt.ph) {
+ at.list_index = i;
+ break;
+ }
+
+ po = newobjs->objects;
+
+ while (get_next_object(&at, ch, 0) == EC_SUCCESS) {
+ ctype = ch->container_type;
+
+ /* Speculative assignment, might be unused. */
+ po->cont_type = ctype;
+ switch (ctype) {
+ case NN_OBJ_TPM_RESERVED:
+ po->id = *((uint8_t *)(ch + 1));
+ break;
+
+ case NN_OBJ_TPM_EVICTABLE:
+ po->handle = *((uint32_t *)(ch + 1));
+ break;
+ default:
+ continue;
+ }
+ if (++(newobjs->num_objects) == ARRAY_SIZE(newobjs->objects))
+ /* Never returns. */
+ report_no_payload_failure(NVMEMF_SECTION_VERIFY);
+ po++;
+ }
+
+ /*
+ * Last object read from flash should have been a non-finalized
+ * delimiter.
+ */
+ if (ch->container_type != NN_OBJ_TRANSACTION_DEL) {
+ struct nvmem_failure_payload fp;
+
+ fp.failure_type = NVMEMF_UNEXPECTED_LAST_OBJ;
+ fp.last_obj_type = ch->container_type;
+ /* Never returns. */
+ report_failure(&fp, sizeof(fp.last_obj_type));
+ }
+
+ /*
+ * Now we have a cache of of objects which were updated but their old
+ * instances could have been left in the flash. Let's iterate over the
+ * flash and delete those if found.
+ */
+ memset(&at, 0, sizeof(at));
+ while ((at.mt.ph != prev_del->ph) &&
+ (at.mt.data_offset != prev_del->data_offset)) {
+ size_t i;
+ size_t key_size;
+ uint32_t key;
+
+ if (get_next_object(&at, ch, 0) != EC_SUCCESS)
+ report_no_payload_failure(NVMEMF_MISSING_OBJECT);
+
+ ctype = ch->container_type;
+
+ switch (ctype) {
+ case NN_OBJ_TPM_RESERVED:
+ key = *((uint8_t *)(ch + 1));
+ key_size = sizeof(uint8_t);
+ break;
+
+ case NN_OBJ_TPM_EVICTABLE:
+ key = *((uint32_t *)(ch + 1));
+ key_size = sizeof(uint32_t);
+ break;
+ default:
+ continue;
+ }
+
+ for (i = 0, po = newobjs->objects; i < newobjs->num_objects;
+ i++, po++) {
+ if ((po->cont_type != ctype) ||
+ ((key_size == 1) && (po->id != key)) ||
+ ((key_size == 4) && (po->handle != key)))
+ continue;
+
+ /*
+ * This indeed is a leftover which needs to be
+ * deleted.
+ */
+ delete_object(&at, ch);
+ }
+ }
+ shared_mem_release(newobjs);
+ if (master_at.mt.data_offset > sizeof(struct nn_page_header)) {
+ top_del.ph = master_at.mt.ph;
+ top_del.data_offset =
+ master_at.mt.data_offset - sizeof(struct nn_container);
+ } else {
+ top_del.ph = list_element_to_ph(master_at.list_index - 1);
+ top_del.data_offset =
+ CONFIG_FLASH_BANK_SIZE - -sizeof(struct nn_container);
+ }
+
+ return finalize_delimiter(page_cursor(&top_del));
+}
+
+/*
+ * This function is called during initialization after the entire flash
+ * contents were scanned, to verify that flash is in a valid state.
+ */
+static enum ec_error_list verify_delimiter(struct nn_container *nc)
+{
+ enum ec_error_list rv;
+ /* Used to read starting at last good delimiter. */
+ struct access_tracker dpt = {};
+
+ if ((master_at.list_index == 0) &&
+ (master_at.mt.data_offset == sizeof(struct nn_page_header))) {
+ /* This must be an init from scratch, no delimiter yet. */
+ if (!master_at.dt.ph)
+ return EC_SUCCESS;
+
+ /* This is bad, will have to wipe out everything. */
+ return EC_ERROR_INVAL;
+ }
+
+ if (nc->container_type_copy == NN_OBJ_TRANSACTION_DEL) {
+ if (nc->container_type == NN_OBJ_OLD_COPY)
+ return EC_SUCCESS;
+ /*
+ * The delimiter is there, but it has not been finalized,
+ * which means that there might be objects in the flash which
+ * were not updated after the last delimiter was written.
+ */
+ return verify_last_section(&master_at.dt, nc);
+ }
+
+ /*
+ * The delimiter is not there, everything above the last verified
+ * delimiter must go.
+ *
+ * First, create a context for retrieving objects starting at the last
+ * valid delimiter, make sure list index is set properly.
+ */
+ dpt.mt = master_at.dt;
+ if (dpt.mt.ph == master_at.mt.ph) {
+ dpt.list_index = master_at.list_index;
+ } else {
+ uint8_t i;
+
+ for (i = 0; i < master_at.list_index; i++)
+ if (list_element_to_ph(i) == dpt.mt.ph)
+ dpt.list_index = i;
+ }
+
+ while ((rv = get_next_object(&dpt, nc, 0)) == EC_SUCCESS) {
+ if (nc->container_type == NN_OBJ_TUPLE)
+ continue;
+ delete_object(&dpt, nc);
+ }
+
+ if (rv == EC_ERROR_INVAL) {
+ /*
+ * There must have been an interruption of the saving process,
+ * let's wipe out flash to the end of the current page and
+ * compact the storage.
+ */
+ size_t remainder_size;
+ const void *p = page_cursor(&master_at.ct);
+
+ remainder_size =
+ CONFIG_FLASH_BANK_SIZE - master_at.ct.data_offset;
+ memset(nc, 0, remainder_size);
+ write_to_flash(p, nc, remainder_size);
+ /* Make sure compaction starts with the new page. */
+ start_new_flash_page(0);
+ compact_nvmem();
+ } else {
+ /* Add delimiter at the very top. */
+ add_final_delimiter();
+ }
+
+ /* Need to re-read the NVMEM cache. */
+ return EC_ERROR_TRY_AGAIN;
+}
+
+/*
+ * At startup iterate over flash contents and move TPM objects into the
+ * appropriate locations in the NVMEM cache.
+ */
+static enum ec_error_list retrieve_nvmem_contents(void)
+{
+ int rv;
+ int tries;
+ struct max_var_container *vc;
+ struct nn_container *nc;
+ uint8_t res_bitmap[(NV_PSEUDO_RESERVE_LAST + 7) / 8];
+
+ /* No saved object will exceed CONFIG_FLASH_BANK_SIZE in size. */
+ nc = get_scratch_buffer(CONFIG_FLASH_BANK_SIZE);
+
+ /* Depending on the state of flash, we might have to do this twice. */
+ for (tries = 0; tries < 2; tries++) {
+ memset(&master_at, 0, sizeof(master_at));
+ memset(nvmem_cache_base(NVMEM_TPM), 0,
+ nvmem_user_sizes[NVMEM_TPM]);
+ memset(res_bitmap, 0, sizeof(res_bitmap));
+ next_evict_obj_base = 0;
+
+ while ((rv = get_next_object(&master_at, nc, 0)) ==
+ EC_SUCCESS) {
+ switch (nc->container_type) {
+ case NN_OBJ_TUPLE:
+ vc = (struct max_var_container *)nc;
+ total_var_space += vc->t_header.key_len +
+ vc->t_header.val_len;
+ break; /* Keep tuples in flash. */
+ case NN_OBJ_TPM_RESERVED:
+ restore_reserved(nc + 1, nc->size, res_bitmap);
+ break;
+
+ case NN_OBJ_TPM_EVICTABLE:
+ restore_object(nc + 1, nc->size);
+ break;
+ default:
+ break;
+ }
+ }
+
+ rv = verify_delimiter(nc);
+
+ if (rv != EC_ERROR_TRY_AGAIN)
+ break;
+ }
+
+ if (rv != EC_SUCCESS) {
+ /*
+ * Something is really messed up, need to wipe out and start
+ * from scratch?
+ */
+ ccprintf("%s:%d FAILURE!\n", __func__, __LINE__);
+ }
+
+ rv = verify_reserved(res_bitmap, nc);
+
+ shared_mem_release(nc);
+
+ return rv;
+}
+
+enum ec_error_list new_nvmem_init(void)
+{
+ enum ec_error_list rv;
+ timestamp_t start, init;
+
+ total_var_space = 0;
+
+ /* Initialize NVMEM indices. */
+ NvEarlyStageFindHandle(0);
+
+ init_page_list();
+
+ start = get_time();
+
+ rv = retrieve_nvmem_contents();
+
+ init = get_time();
+
+ ccprintf("init took %d\n", (uint32_t)(init.val - start.val));
+
+ return rv;
+}
+
+/*
+ * Browse through the flash storage and save all evictable objects' offsets in
+ * the passed in array. This is used to keep track of objects added or deleted
+ * by the TPM library.
+ */
+test_export_static size_t init_object_offsets(uint16_t *offsets, size_t count)
+{
+ size_t num_objects = 0;
+ uint32_t next_obj_base;
+ uint32_t obj_base;
+ void *obj_addr;
+
+ obj_base = s_evictNvStart;
+ obj_addr = (uint8_t *)nvmem_cache_base(NVMEM_TPM) + obj_base;
+ memcpy(&next_obj_base, obj_addr, sizeof(next_obj_base));
+
+ while (next_obj_base && (next_obj_base <= s_evictNvEnd)) {
+ if (num_objects == count) {
+ /* What do we do here?! */
+ ccprintf("Too many objects!\n");
+ break;
+ }
+
+ offsets[num_objects++] =
+ obj_base - s_evictNvStart + sizeof(next_obj_base);
+
+ obj_addr = nvmem_cache_base(NVMEM_TPM) + next_obj_base;
+ obj_base = next_obj_base;
+ memcpy(&next_obj_base, obj_addr, sizeof(next_obj_base));
+ }
+
+ return num_objects;
+}
+
+static enum ec_error_list update_object(const struct access_tracker *at,
+ struct nn_container *ch,
+ void *cached_object, size_t new_size)
+{
+ size_t copy_size = new_size;
+ size_t preserved_size;
+ uint32_t preserved_hash;
+ uint8_t *dst = (uint8_t *)(ch + 1);
+
+ preserved_size = ch->size;
+ preserved_hash = ch->container_hash;
+
+ /*
+ * Need to copy data into the container, skip reserved type if it is a
+ * reserved object.
+ */
+ if (ch->container_type == NN_OBJ_TPM_RESERVED) {
+ dst++;
+ copy_size--;
+ }
+ memcpy(dst, cached_object, copy_size);
+
+ ch->generation++;
+ ch->size = new_size;
+ save_container(ch);
+
+ ch->generation--;
+ ch->size = preserved_size;
+ ch->container_hash = preserved_hash;
+ return delete_object(at, ch);
+}
+
+static enum ec_error_list update_pcr(const struct access_tracker *at,
+ struct nn_container *ch, uint8_t index,
+ uint8_t *cached)
+{
+ uint8_t preserved;
+
+ cached--;
+ preserved = cached[0];
+ cached[0] = index;
+ update_object(at, ch, cached, ch->size);
+ cached[0] = preserved;
+
+ return EC_SUCCESS;
+}
+
+static enum ec_error_list save_pcr(struct nn_container *ch,
+ uint8_t reserved_index, const void *pcr,
+ size_t pcr_size)
+{
+ uint8_t *container_body;
+
+ ch->container_type = ch->container_type_copy = NN_OBJ_TPM_RESERVED;
+ ch->encrypted = 1;
+ ch->size = pcr_size + 1;
+ ch->generation = 0;
+
+ container_body = (uint8_t *)(ch + 1);
+ container_body[0] = reserved_index;
+ memcpy(container_body + 1, pcr, pcr_size);
+
+ return save_container(ch);
+}
+
+static enum ec_error_list maybe_save_pcr(struct nn_container *ch,
+ size_t pcr_index)
+{
+ const STATE_CLEAR_DATA *scd;
+ const struct pcr_descriptor *pcrd;
+ const void *cached;
+ size_t pcr_size;
+
+ pcrd = pcr_arrays + pcr_index / NUM_STATIC_PCR;
+ scd = get_scd();
+
+ pcr_size = pcrd->pcr_size;
+
+ cached = (const uint8_t *)&scd->pcrSave + pcrd->pcr_array_offset +
+ pcr_size * (pcr_index % NUM_STATIC_PCR);
+
+ if (is_empty(cached, pcr_size))
+ return EC_SUCCESS;
+
+ return save_pcr(ch, pcr_index + NV_VIRTUAL_RESERVE_LAST, cached,
+ pcr_size);
+}
+
+/*
+ * The process_XXX functions below are used to check and if necessary add,
+ * update or delete objects from the flash based on the NVMEM cache
+ * contents.
+ */
+static enum ec_error_list process_pcr(const struct access_tracker *at,
+ struct nn_container *ch, uint8_t index,
+ const uint8_t *saved, uint8_t *pcr_bitmap)
+{
+ STATE_CLEAR_DATA *scd;
+ const struct pcr_descriptor *pcrd;
+ size_t pcr_bitmap_index;
+ size_t pcr_index;
+ size_t pcr_size;
+ uint8_t *cached;
+
+ pcr_bitmap_index = index - NV_VIRTUAL_RESERVE_LAST;
+
+ if (pcr_bitmap_index > NUM_OF_PCRS)
+ return EC_ERROR_INVAL;
+
+ pcrd = pcr_arrays + pcr_bitmap_index / NUM_STATIC_PCR;
+ pcr_index = pcr_bitmap_index % NUM_STATIC_PCR;
+
+ pcr_size = pcrd->pcr_size;
+
+ if (pcr_size != (ch->size - 1))
+ return EC_ERROR_INVAL; /* This is an error. */
+
+ /* Find out base address of the cached PCR. */
+ scd = get_scd();
+ cached = (uint8_t *)&scd->pcrSave + pcrd->pcr_array_offset +
+ pcr_size * pcr_index;
+
+ /* Set bitmap bit to indicate that this PCR was looked at. */
+ bitmap_bit_set(pcr_bitmap, pcr_bitmap_index);
+
+ if (memcmp(saved, cached, pcr_size))
+ return update_pcr(at, ch, index, cached);
+
+ return EC_SUCCESS;
+}
+
+static enum ec_error_list process_reserved(const struct access_tracker *at,
+ struct nn_container *ch,
+ uint8_t *pcr_bitmap)
+{
+ NV_RESERVED_ITEM ri;
+ size_t new_size;
+ uint8_t *saved;
+ uint8_t index;
+ void *cached;
+
+ /*
+ * Find out this object's location in the cache (first byte of the
+ * contents is the index of the reserved object.
+ */
+ saved = (uint8_t *)(ch + 1);
+ index = *saved++;
+
+ NvGetReserved(index, &ri);
+
+ if (ri.size) {
+ void *marshaled;
+
+ cached = (uint8_t *)nvmem_cache_base(NVMEM_TPM) + ri.offset;
+
+ /*
+ * For NV_STATE_CLEAR and NV_STATE_RESET cases Let's marshal
+ * cached data to be able to compare it with saved data.
+ */
+ if (index == NV_STATE_CLEAR) {
+ marshaled = ((uint8_t *)(ch + 1)) + ch->size;
+ new_size = marshal_state_clear(cached, marshaled);
+ cached = marshaled;
+ } else if (index == NV_STATE_RESET) {
+ marshaled = ((uint8_t *)(ch + 1)) + ch->size;
+ new_size = marshal_state_reset_data(cached, marshaled);
+ cached = marshaled;
+ } else {
+ new_size = ri.size;
+ }
+
+ if ((new_size == (ch->size - 1)) &&
+ !memcmp(saved, cached, new_size))
+ return EC_SUCCESS;
+
+ return update_object(at, ch, cached, new_size + 1);
+ }
+
+ /* This must be a PCR. */
+ return process_pcr(at, ch, index, saved, pcr_bitmap);
+}
+
+static enum ec_error_list process_object(const struct access_tracker *at,
+ struct nn_container *ch,
+ uint16_t *tpm_object_offsets,
+ size_t *num_objects)
+{
+ size_t i;
+ uint32_t cached_size;
+ uint32_t cached_type;
+ uint32_t flash_type;
+ uint32_t next_obj_base;
+ uint8_t *evict_start;
+ void *pcache;
+
+ evict_start = (uint8_t *)nvmem_cache_base(NVMEM_TPM) + s_evictNvStart;
+ memcpy(&flash_type, ch + 1, sizeof(flash_type));
+ for (i = 0; i < *num_objects; i++) {
+
+ /* Find TPM object in the NVMEM cache. */
+ pcache = evict_start + tpm_object_offsets[i];
+ memcpy(&cached_type, pcache, sizeof(cached_type));
+ if (cached_type == flash_type)
+ break;
+ }
+
+ if (i == *num_objects) {
+ /*
+ * This object is not in the cache any more, delete it from
+ * flash.
+ */
+ return delete_object(at, ch);
+ }
+
+ memcpy(&next_obj_base, (uint8_t *)pcache - sizeof(next_obj_base),
+ sizeof(next_obj_base));
+ cached_size = next_obj_base - s_evictNvStart - tpm_object_offsets[i];
+ if ((cached_size != ch->size) || memcmp(ch + 1, pcache, cached_size)) {
+ /*
+ * Object changed. Let's delete the old copy and save the new
+ * one.
+ */
+ update_object(at, ch, pcache, ch->size);
+ }
+
+ tpm_object_offsets[i] = tpm_object_offsets[*num_objects - 1];
+ *num_objects -= 1;
+
+ return EC_SUCCESS;
+}
+
+static enum ec_error_list save_new_object(uint16_t obj_base, void *buf)
+{
+ size_t obj_size;
+ struct nn_container *ch = buf;
+ uint32_t next_obj_base;
+ void *obj_addr;
+
+ obj_addr = (uint8_t *)nvmem_cache_base(NVMEM_TPM) + obj_base +
+ s_evictNvStart;
+ memcpy(&next_obj_base, obj_addr - sizeof(next_obj_base),
+ sizeof(next_obj_base));
+ obj_size = next_obj_base - obj_base - s_evictNvStart;
+
+ ch->container_type_copy = ch->container_type = NN_OBJ_TPM_EVICTABLE;
+ ch->encrypted = 1;
+ ch->size = obj_size;
+ ch->generation = 0;
+ memcpy(ch + 1, obj_addr, obj_size);
+
+ return save_container(ch);
+}
+
+enum ec_error_list new_nvmem_save(void)
+{
+ const void *fence_ph;
+ size_t i;
+ size_t num_objs;
+ struct nn_container *ch;
+ struct access_tracker at = {};
+ uint16_t fence_offset;
+ /* We don't foresee ever storing this many objects. */
+ uint16_t tpm_object_offsets[MAX_STORED_EVICTABLE_OBJECTS];
+ uint8_t pcr_bitmap[(NUM_STATIC_PCR * ARRAY_SIZE(pcr_arrays) + 7) / 8];
+
+ /* See if compaction is needed. */
+ if (master_at.list_index >= (ARRAY_SIZE(page_list) - 3)) {
+ enum ec_error_list rv;
+
+ rv = compact_nvmem();
+ if (rv != EC_SUCCESS)
+ return rv;
+ }
+
+ fence_ph = master_at.mt.ph;
+ fence_offset = master_at.mt.data_offset;
+
+ num_objs = init_object_offsets(tpm_object_offsets,
+ ARRAY_SIZE(tpm_object_offsets));
+
+ memset(pcr_bitmap, 0, sizeof(pcr_bitmap));
+ del_candidates = get_scratch_buffer(CONFIG_FLASH_BANK_SIZE +
+ sizeof(struct delete_candidates));
+ ch = (void *)(del_candidates + 1);
+ del_candidates->num_candidates = 0;
+
+ while ((fence_ph != at.mt.ph) || (fence_offset != at.mt.data_offset)) {
+ int rv;
+
+ rv = get_next_object(&at, ch, 0);
+
+ if (rv == EC_ERROR_MEMORY_ALLOCATION)
+ break;
+
+ if (rv != EC_SUCCESS) {
+ ccprintf("%s: failed to read flash when saving (%d)!\n",
+ __func__, rv);
+ shared_mem_release(ch);
+ return rv;
+ }
+
+ if (ch->container_type == NN_OBJ_TPM_RESERVED) {
+ process_reserved(&at, ch, pcr_bitmap);
+ continue;
+ }
+
+ if (ch->container_type == NN_OBJ_TPM_EVICTABLE) {
+ process_object(&at, ch, tpm_object_offsets, &num_objs);
+ continue;
+ }
+ }
+
+ /* Now save new objects, if any. */
+ for (i = 0; i < num_objs; i++)
+ save_new_object(tpm_object_offsets[i], ch);
+
+ /* And new pcrs, if any. */
+ for (i = 0; i < NUM_OF_PCRS; i++) {
+ if (bitmap_bit_check(pcr_bitmap, i))
+ continue;
+ maybe_save_pcr(ch, i);
+ }
+
+#if defined(NVMEM_TEST_BUILD)
+ if (failure_mode == TEST_FAIL_WHEN_SAVING) {
+ shared_mem_release(del_candidates);
+ del_candidates = NULL;
+ return EC_SUCCESS;
+ }
+#endif
+ /*
+ * Add a delimiter if there have been new containers added to the
+ * flash.
+ */
+ if (del_candidates->num_candidates ||
+ (fence_offset != master_at.mt.data_offset) ||
+ (fence_ph != master_at.mt.ph)) {
+ const void *del = page_cursor(&master_at.mt);
+
+ add_delimiter();
+
+ if (del_candidates->num_candidates) {
+ /* Now delete objects which need to be deleted. */
+ for (i = 0; i < del_candidates->num_candidates; i++)
+ invalidate_object(
+ del_candidates->candidates[i]);
+ }
+
+#if defined(NVMEM_TEST_BUILD)
+ if (failure_mode == TEST_FAIL_WHEN_INVALIDATING) {
+ shared_mem_release(del_candidates);
+ del_candidates = NULL;
+ return EC_SUCCESS;
+ }
+#endif
+ finalize_delimiter(del);
+ }
+
+ shared_mem_release(del_candidates);
+ del_candidates = NULL;
+
+ return EC_SUCCESS;
+}
+
+/* Caller must free memory allocated by this function! */
+static struct max_var_container *find_var(const uint8_t *key, size_t key_len,
+ struct access_tracker *at)
+{
+ int rv;
+ struct max_var_container *vc;
+
+ vc = get_scratch_buffer(CONFIG_FLASH_BANK_SIZE);
+
+ /*
+ * Let's iterate over all objects there are and look for matching
+ * tuples.
+ */
+ while ((rv = get_next_object(at, &vc->c_header, 0)) == EC_SUCCESS) {
+
+ if (vc->c_header.container_type != NN_OBJ_TUPLE)
+ continue;
+
+ /* Verify consistency, first that the sizes match */
+ if ((vc->t_header.key_len + vc->t_header.val_len +
+ sizeof(vc->t_header)) != vc->c_header.size) {
+ ccprintf("%s: - inconsistent sizes!\n", __func__);
+ /* report error here. */
+ continue;
+ }
+
+ /* Ok, found a tuple, does the key match? */
+ if ((key_len == vc->t_header.key_len) &&
+ !memcmp(key, vc->body, key_len))
+ /* Yes, it does! */
+ return vc;
+ }
+
+ shared_mem_release(vc);
+ return NULL;
+}
+
+const struct tuple *getvar(const uint8_t *key, uint8_t key_len)
+{
+ const struct max_var_container *vc;
+ struct access_tracker at = {};
+
+ if (!key || !key_len)
+ return NULL;
+
+ vc = find_var(key, key_len, &at);
+
+ if (vc)
+ return &vc->t_header;
+
+ return NULL;
+}
+
+void freevar(const struct tuple *var)
+{
+ void *vc;
+
+ if (!var)
+ return;
+
+ vc = (uint8_t *)var - offsetof(struct max_var_container, t_header);
+ shared_mem_release(vc);
+}
+
+static enum ec_error_list save_container(struct nn_container *nc)
+{
+ uint32_t hash;
+ uint32_t salt[4];
+
+ nc->container_hash = 0;
+ app_compute_hash_wrapper(nc, sizeof(*nc) + nc->size, &hash,
+ sizeof(hash));
+ nc->container_hash = hash; /* This will truncate it. */
+
+ /* Skip transactions delimiters. */
+ if (nc->size) {
+ salt[0] = master_at.mt.ph->page_number;
+ salt[1] = master_at.mt.data_offset;
+ salt[2] = nc->container_hash;
+ salt[3] = 0;
+
+ app_cipher(salt, nc + 1, nc + 1, nc->size);
+ }
+
+ return save_object(nc);
+}
+
+int setvar(const uint8_t *key, uint8_t key_len, const uint8_t *val,
+ uint8_t val_len)
+{
+ enum ec_error_list rv;
+ int erase_request;
+ size_t new_var_space;
+ size_t old_var_space;
+ struct max_var_container *vc;
+ struct access_tracker at = {};
+
+ if (!key || !key_len)
+ return EC_ERROR_INVAL;
+
+ new_var_space = key_len + val_len;
+
+ if (new_var_space > MAX_VAR_BODY_SPACE)
+ /* Too much space would be needed. */
+ return EC_ERROR_INVAL;
+
+ erase_request = !val || !val_len;
+
+ /* See if compaction is needed. */
+ if (!erase_request &&
+ (master_at.list_index >= (ARRAY_SIZE(page_list) - 3))) {
+ rv = compact_nvmem();
+ if (rv != EC_SUCCESS)
+ return rv;
+ }
+
+ vc = find_var(key, key_len, &at);
+
+ if (erase_request) {
+ if (!vc)
+ /* Nothing to erase. */
+ return EC_SUCCESS;
+
+ rv = invalidate_object(
+ (struct nn_container *)((uintptr_t)at.ct.ph +
+ at.ct.data_offset));
+
+ if (rv == EC_SUCCESS)
+ total_var_space -=
+ vc->t_header.key_len + vc->t_header.val_len;
+
+ shared_mem_release(vc);
+ return rv;
+ }
+
+ /* Is this variable already there? */
+ if (!vc) {
+ /* No, it is not. Will it fit? */
+ if ((new_var_space + total_var_space) > MAX_VAR_TOTAL_SPACE)
+ /* No, it will not. */
+ return EC_ERROR_OVERFLOW;
+
+ return save_var(key, key_len, val, val_len, vc);
+ }
+
+ /* The variable was found, let's see if the value is being changed. */
+ if (vc->t_header.val_len == val_len &&
+ !memcmp(val, vc->body + key_len, val_len)) {
+ shared_mem_release(vc);
+ return EC_SUCCESS;
+ }
+
+ /* Ok, the variable was found, and is of a different value. */
+ old_var_space = vc->t_header.val_len + vc->t_header.key_len;
+
+ if ((old_var_space < new_var_space) &&
+ ((total_var_space + new_var_space - old_var_space) >
+ MAX_VAR_BODY_SPACE))
+ return EC_ERROR_OVERFLOW;
+
+ /* Save the new instance first with the larger generation number. */
+ vc->c_header.generation++;
+ rv = save_var(key, key_len, val, val_len, vc);
+ shared_mem_release(vc);
+ if (rv == EC_SUCCESS) {
+ rv = invalidate_object(
+ (struct nn_container *)((uintptr_t)at.ct.ph +
+ at.ct.data_offset));
+ if (rv == EC_SUCCESS)
+ total_var_space -= old_var_space;
+ }
+ return rv;
+}
+
+static void dump_contents(const struct nn_container *ch)
+{
+ const uint8_t *buf = (const void *)ch;
+ size_t i;
+ size_t total_size = sizeof(*ch) + ch->size;
+
+ for (i = 0; i < total_size; i++) {
+ if (!(i % 16)) {
+ ccprintf("\n");
+ cflush();
+ }
+ ccprintf(" %02x", buf[i]);
+ }
+ ccprintf("\n");
+}
+
+/*
+ * Clear tpm data from nvmem. First fill up the current top page with erased
+ * objects, then compact the flash storage, removing all TPM related objects.
+ * This would guarantee that all pages where TPM objecs were stored would be
+ * erased.
+ *
+ * TODO(vbendeb): need to seed reserved objects after this is done.
+ */
+int nvmem_erase_tpm_data(void)
+{
+ const uint8_t *key;
+ const uint8_t *val;
+ int rv;
+ struct nn_container *ch;
+ struct access_tracker at = {};
+ uint8_t saved_list_index;
+ uint8_t key_len;
+
+ ch = get_scratch_buffer(CONFIG_FLASH_BANK_SIZE);
+
+ while (get_next_object(&at, ch, 0) == EC_SUCCESS) {
+
+ if ((ch->container_type != NN_OBJ_TPM_RESERVED) &&
+ (ch->container_type != NN_OBJ_TPM_EVICTABLE))
+ continue;
+
+ delete_object(&at, ch);
+ }
+
+ shared_mem_release(ch);
+
+ /*
+ * Now fill up the current flash page with erased objects to make sure
+ * that it would be erased during next compaction. Use dummy key,
+ * value pairs as the erase objects.
+ */
+ saved_list_index = master_at.list_index;
+ key = (const uint8_t *)nvmem_erase_tpm_data;
+ val = (const uint8_t *)nvmem_erase_tpm_data;
+ key_len = MAX_VAR_BODY_SPACE - 255;
+ do {
+ size_t to_go_in_page;
+ uint8_t val_len;
+
+ to_go_in_page =
+ CONFIG_FLASH_BANK_SIZE - master_at.mt.data_offset;
+ if (to_go_in_page >
+ (MAX_VAR_BODY_SPACE +
+ offsetof(struct max_var_container, body) - 1)) {
+ val_len = MAX_VAR_BODY_SPACE - key_len;
+ } else {
+ /*
+ * Let's not write more than we have to get over the
+ * page limit. The minimum size we need is:
+ *
+ * <container header size> + <tuple header size> + 2
+ *
+ * (where key and value are of one byte each).
+ */
+ if (to_go_in_page <
+ (offsetof(struct max_var_container, body) + 2)) {
+ /*
+ * There is very little room left, even key
+ * and value of size of one each is enough to
+ * go over.
+ */
+ key_len = 1;
+ val_len = 1;
+ } else {
+ size_t need_to_cover;
+
+ /* How much space key and value should cover? */
+ need_to_cover =
+ to_go_in_page -
+ offsetof(struct max_var_container,
+ body) + 1;
+ key_len = need_to_cover / 2;
+ val_len = need_to_cover - key_len;
+ }
+ }
+ if (setvar(key, key_len, val, val_len) != EC_SUCCESS)
+ ccprintf("%s: adding var failed!\n", __func__);
+ if (setvar(key, key_len, NULL, 0) != EC_SUCCESS)
+ ccprintf("%s: deleting var failed!\n", __func__);
+
+ } while (master_at.list_index != (saved_list_index + 1));
+
+ rv = compact_nvmem();
+ return rv;
+}
+
+/*
+ * Function which verifes flash contents integrity (and printing objects it
+ * finds, if requested by the caller). All objects' active and deleted alike
+ * integrity is verified by get_next_object().
+ */
+test_export_static enum ec_error_list browse_flash_contents(int print)
+{
+ int active = 0;
+ int count = 0;
+ int rv = EC_SUCCESS;
+ size_t line_len = 0;
+ struct nn_container *ch;
+ struct access_tracker at = {};
+
+ ch = get_scratch_buffer(CONFIG_FLASH_BANK_SIZE);
+
+ while ((rv = get_next_object(&at, ch, 1)) == EC_SUCCESS) {
+ uint8_t ctype = ch->container_type;
+
+ count++;
+
+ if ((ctype != NN_OBJ_OLD_COPY) &&
+ (ctype != NN_OBJ_TRANSACTION_DEL))
+ active++;
+
+ if (print) {
+ char erased;
+
+ if (ctype == NN_OBJ_OLD_COPY)
+ erased = 'x';
+ else
+ erased = ' ';
+
+ if (ch->container_type_copy == NN_OBJ_TPM_RESERVED) {
+ ccprintf("%cR:%02x.%d ", erased,
+ *((uint8_t *)(ch + 1)),
+ ch->generation);
+ } else {
+ uint32_t index;
+ char tag;
+
+ switch (ch->container_type_copy) {
+ case NN_OBJ_TPM_EVICTABLE:
+ tag = 'E';
+ break;
+
+ case NN_OBJ_TUPLE:
+ tag = 'T';
+ break;
+
+ case NN_OBJ_TRANSACTION_DEL:
+ tag = 's'; /* 's' for separator. */
+ break;
+
+ default:
+ tag = '?';
+ break;
+ }
+
+ if (ch->container_type_copy !=
+ NN_OBJ_TRANSACTION_DEL)
+ memcpy(&index, ch + 1, sizeof(index));
+ else
+ index = 0;
+ ccprintf("%c%c:%08x.%d ", erased, tag, index,
+ ch->generation);
+ }
+ if (print > 1) {
+ dump_contents(ch);
+ continue;
+ }
+
+ if (line_len > 70) {
+ ccprintf("\n");
+ cflush();
+ line_len = 0;
+ } else {
+ line_len += 11;
+ }
+ }
+ }
+
+ shared_mem_release(ch);
+
+ if (rv == EC_ERROR_MEMORY_ALLOCATION) {
+ ccprintf("%schecked %d objects, %d active\n", print ? "\n" : "",
+ count, active);
+ rv = EC_SUCCESS;
+ }
+
+ return rv;
+}
+
+static int command_dump_nvmem(int argc, char **argv)
+{
+ nvmem_disable_commits();
+
+ browse_flash_contents(1 + (argc > 1));
+
+ nvmem_enable_commits();
+
+ return 0;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(dump_nvmem, command_dump_nvmem, "", "");
diff --git a/common/nvmem.c b/common/nvmem.c
index de6fba47b5..577318af2f 100644
--- a/common/nvmem.c
+++ b/common/nvmem.c
@@ -8,14 +8,13 @@
#include "dcrypto.h"
#include "flash.h"
#include "nvmem.h"
+#include "new_nvmem.h"
#include "task.h"
#include "timer.h"
#include "util.h"
-#define CPRINTF(format, args...) cprintf(CC_COMMAND, format, ## args)
-#define CPRINTS(format, args...) cprints(CC_COMMAND, format, ## args)
-
-#define NVMEM_NOT_INITIALIZED (-1)
+#define CPRINTF(format, args...) cprintf(CC_COMMAND, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_COMMAND, format, ##args)
/*
* The NVMEM contents are stored in flash memory. At run time there is an SRAM
@@ -103,74 +102,13 @@ static void nvmem_compute_sha(struct nvmem_tag *tag, void *sha_buf)
static int nvmem_save(void)
{
- struct nvmem_partition *part;
- size_t nvmem_offset;
- int dest_partition;
- uint8_t sha_comp[NVMEM_SHA_SIZE];
- int rv = EC_SUCCESS;
-
- if (!DCRYPTO_ladder_is_enabled()) {
- CPRINTF("%s: Key ladder is disabled. Skipping flash write\n",
- __func__);
- goto release_cache;
- }
-
- part = (struct nvmem_partition *)nvmem_cache;
-
- /* Has anything changed in the cache? */
- nvmem_compute_sha(&part->tag, sha_comp);
-
- if (!memcmp(part->tag.sha, sha_comp, sizeof(part->tag.sha))) {
- CPRINTF("%s: Nothing changed, skipping flash write\n",
- __func__);
- goto release_cache;
- }
+ enum ec_error_list rv;
- /* Get flash offset of the partition to save to. */
- dest_partition = (nvmem_act_partition + 1) % NVMEM_NUM_PARTITIONS;
- nvmem_offset = nvmem_base_addr[dest_partition] -
- CONFIG_PROGRAM_MEMORY_BASE;
+ rv = new_nvmem_save();
- /* Erase partition */
- rv = flash_physical_erase(nvmem_offset, NVMEM_PARTITION_SIZE);
- if (rv != EC_SUCCESS) {
- CPRINTF("%s flash erase failed\n", __func__);
- goto release_cache;
- }
-
- part->tag.layout_version = NVMEM_LAYOUT_VERSION;
- part->tag.generation++;
-
- /* Calculate sha of the whole thing. */
- nvmem_compute_sha(&part->tag, part->tag.sha);
-
- /* Encrypt actual payload. */
- if (!app_cipher(part->tag.sha, part->buffer, part->buffer,
- sizeof(part->buffer))) {
- CPRINTF("%s encryption failed\n", __func__);
- rv = EC_ERROR_UNKNOWN;
- goto release_cache;
- }
-
- rv = flash_physical_write(nvmem_offset,
- NVMEM_PARTITION_SIZE,
- nvmem_cache);
- if (rv != EC_SUCCESS) {
- CPRINTF("%s flash write failed\n", __func__);
- goto release_cache;
- }
+ if (rv == EC_SUCCESS)
+ nvmem_act_partition = NVMEM_NOT_INITIALIZED;
- /* Restore payload. */
- if (!app_cipher(part->tag.sha, part->buffer, part->buffer,
- sizeof(part->buffer))) {
- CPRINTF("%s decryption failed\n", __func__);
- rv = EC_ERROR_UNKNOWN;
- goto release_cache;
- }
-
- nvmem_act_partition = dest_partition;
-
- release_cache:
nvmem_mutex.write_in_progress = 0;
nvmem_release_cache();
return rv;
@@ -245,21 +183,6 @@ static void nvmem_release_cache(void)
mutex_unlock(&nvmem_mutex.mtx);
}
-static int nvmem_reinitialize(void)
-{
- nvmem_lock_cache(); /* Unlocked by nvmem_save() below. */
- /*
- * NvMem is not properly initialized. Let's just erase everything and
- * start over, so that at least 1 partition is ready to be used.
- */
- nvmem_act_partition = 0;
-
- memset(nvmem_cache, 0xff, NVMEM_PARTITION_SIZE);
-
- /* Start with generation zero in the current active partition. */
- return nvmem_save();
-}
-
static int nvmem_compare_generation(void)
{
struct nvmem_partition *p_part;
@@ -301,10 +224,10 @@ static int nvmem_find_partition(void)
if (nvmem_partition_read_verify(check_part) == EC_SUCCESS) {
nvmem_act_partition = check_part;
+ ccprintf("%s:%d found legacy partition %d\n", __func__,
+ __LINE__, check_part);
return EC_SUCCESS;
}
- ccprintf("%s:%d partiton %d verification FAILED\n",
- __func__, __LINE__, check_part);
}
/*
@@ -312,14 +235,8 @@ static int nvmem_find_partition(void)
* is valid. Let's reinitialize the NVMEM - there is nothing else we
* can do.
*/
- CPRINTS("%s: No Valid Partition found, will reinitialize!", __func__);
-
- if (nvmem_reinitialize() != EC_SUCCESS) {
- CPRINTS("%s: Reinitialization failed!!");
- return EC_ERROR_UNKNOWN;
- }
-
- return EC_SUCCESS;
+ CPRINTS("%s: No Legacy Partitions found.", __func__);
+ return EC_ERROR_INVALID_CONFIG;
}
static int nvmem_generate_offset_table(void)
@@ -342,8 +259,17 @@ static int nvmem_generate_offset_table(void)
return EC_SUCCESS;
}
-static int nvmem_get_partition_off(int user, uint32_t offset,
- uint32_t len, uint32_t *p_buf_offset)
+
+void *nvmem_cache_base(enum nvmem_users user)
+{
+ if ((user < 0) || (user >= NVMEM_NUM_USERS))
+ return NULL;
+
+ return nvmem_cache + nvmem_user_start_offset[user];
+}
+
+static int nvmem_get_partition_off(int user, uint32_t offset, uint32_t len,
+ uint32_t *p_buf_offset)
{
uint32_t start_offset;
@@ -365,48 +291,6 @@ static int nvmem_get_partition_off(int user, uint32_t offset,
return EC_SUCCESS;
}
-int nvmem_erase_user_data(enum nvmem_users user)
-{
- int part;
- int ret;
- uint32_t user_offset, user_size;
-
- if (user >= NVMEM_NUM_USERS)
- return EC_ERROR_INVAL;
-
- CPRINTS("Erasing NVMEM Flash Partition user: %d", user);
-
- ret = EC_SUCCESS;
-
- /* Find offset within cache. */
- user_offset = nvmem_user_start_offset[user];
- user_size = nvmem_user_sizes[user];
-
- for (part = 0; part < NVMEM_NUM_PARTITIONS; part++) {
- int rv;
-
- /* Lock the cache buffer. */
- nvmem_lock_cache();
- /* Erase the user's data. */
- memset(nvmem_cache + user_offset, 0xFF, user_size);
-
- /*
- * Make sure the contents change between runs of
- * nvmem_save() so that all flash partitions are
- * written with empty contents and different
- * generation numbers.
- */
- ((struct nvmem_partition *)nvmem_cache)->tag.generation = part;
-
- /* Make a best effort to clear each partition. */
- rv = nvmem_save();
- if (rv != EC_SUCCESS)
- ret = rv;
- }
-
- return ret;
-}
-
int nvmem_init(void)
{
int ret;
@@ -417,8 +301,6 @@ int nvmem_init(void)
CPRINTF("%s:%d\n", __func__, __LINE__);
return ret;
}
- /* Initialize error state, assume everything is good */
- nvmem_error_state = EC_SUCCESS;
nvmem_write_error = 0;
/*
@@ -426,24 +308,27 @@ int nvmem_init(void)
* succeeds to bootstrap the nvmem area.
*/
commits_enabled = 1;
- ret = nvmem_find_partition();
+
+ /*
+ * Try discovering legacy partition(s). If even one is present, need
+ * to migrate to the new nvmem storage scheme.
+ */
+ if (nvmem_find_partition() == EC_SUCCESS)
+ ret = new_nvmem_migrate(nvmem_act_partition);
+ else
+ ret = new_nvmem_init();
+
+ nvmem_error_state = ret;
if (ret != EC_SUCCESS) {
- /* Change error state to non-zero */
- nvmem_error_state = ret;
- CPRINTF("%s:%d\n", __func__, __LINE__);
+ CPRINTF("%s:%d error %d!\n", __func__, __LINE__, ret);
return ret;
}
- CPRINTS("Active Nvmem partition set to %d", nvmem_act_partition);
-
return EC_SUCCESS;
}
-int nvmem_get_error_state(void)
-{
- return nvmem_error_state;
-}
+int nvmem_get_error_state(void) { return nvmem_error_state; }
int nvmem_is_different(uint32_t offset, uint32_t size, void *data,
enum nvmem_users user)
diff --git a/common/nvmem_vars.c b/common/nvmem_vars.c
index d90fe37f9e..2b60dbed0c 100644
--- a/common/nvmem_vars.c
+++ b/common/nvmem_vars.c
@@ -7,7 +7,7 @@
#include "common.h"
#include "console.h"
#include "nvmem.h"
-#include "nvmem_vars.h"
+#include "new_nvmem.h"
#include "printf.h"
#include "shared_mem.h"
#include "util.h"
@@ -17,33 +17,14 @@
test_mockable_static uint8_t *rbuf;
-test_mockable_static
-void release_local_copy(void)
+int set_local_copy(void)
{
if (rbuf)
- shared_mem_release(rbuf);
- rbuf = NULL;
-}
-
-test_mockable_static
-int get_local_copy(void)
-{
- int rv;
-
- if (rbuf)
- return EC_SUCCESS;
-
- rv = SHARED_MEM_ACQUIRE_CHECK(CONFIG_FLASH_NVMEM_VARS_USER_SIZE,
- (char **)&rbuf);
+ return EC_ERROR_UNKNOWN;
- if (rv == EC_SUCCESS) {
- rv = nvmem_read(0, CONFIG_FLASH_NVMEM_VARS_USER_SIZE,
- rbuf, CONFIG_FLASH_NVMEM_VARS_USER_NUM);
- if (rv != EC_SUCCESS)
- release_local_copy();
- }
+ rbuf = nvmem_cache_base(NVMEM_CR50);
- return rv;
+ return EC_SUCCESS;
}
/****************************************************************************/
@@ -88,261 +69,42 @@ int get_local_copy(void)
*/
/****************************************************************************/
-/* Helper functions */
-
-/* Return true if the tuple at rbuf+idx matches the key */
-static int match_key_at(uint32_t idx, const uint8_t *key, uint8_t key_len)
-{
- struct tuple *tuple = (struct tuple *)(rbuf + idx);
- uint32_t i, max_len;
- uint8_t diffs;
-
- /* Don't try to look past the 0 at the end of the user region */
- max_len = MIN(key_len, CONFIG_FLASH_NVMEM_VARS_USER_SIZE - idx - 1);
-
- /* Do constant-time comparision, since AP sets key_len to look for */
- diffs = max_len ^ key_len;
- diffs |= tuple->key_len ^ key_len;
- for (i = 0; i < max_len; i++)
- diffs |= tuple->data_[i] ^ key[i];
-
- return !diffs;
-}
-
-/*
- * Find the start of the next tuple in rbuf. Return false if there isn't one.
- * The idx arg tracks where to start looking and where the next tuple was
- * expected to be found.
- */
-static int next_tuple(uint32_t *idx)
-{
- struct tuple *tuple = (struct tuple *)(rbuf + *idx);
-
- /* Not at a valid tuple now, so there aren't any more */
- if (!tuple->key_len)
- return 0;
-
- /* Advance to the next one */
- *idx += sizeof(struct tuple) + tuple->key_len + tuple->val_len;
- tuple = (struct tuple *)(rbuf + *idx);
-
- /* Do we have one or not? */
- return tuple->key_len;
-}
-
-/*
- * Look for the key in rbuf. If a match is found, set the index to the start of
- * the tuple and return true. If the key is not found, set the index to the
- * location where a new tuple should be added (0 if no tuples exist at all,
- * else at the '\0' at the end of the tuples) and return false.
- */
-test_mockable_static
-int getvar_idx(uint32_t *idx, const uint8_t *key, uint8_t key_len)
-{
- uint32_t i = *idx;
-
- do {
- if (match_key_at(i, key, key_len)) {
- *idx = i;
- return 1;
- }
- } while (next_tuple(&i));
-
- *idx = i;
- return 0;
-}
-
-static inline int bogus_blob(const uint8_t *blob, uint8_t blob_len)
-{
- return !blob || !blob_len;
-}
-
-/****************************************************************************/
/* API functions */
-/* This MUST be called first. The internal functions assume valid entries */
-int initvars(void)
+const struct tuple *legacy_getnextvar(const struct tuple *prev_var)
{
- struct tuple *tuple;
- int rv, i, len;
-
- rv = get_local_copy();
- if (rv != EC_SUCCESS)
- return rv;
-
- for (i = len = 0; /* FOREVER */ 1; i += len) {
- /* Zero byte (i.e. key_len == 0) indicates end of tuples. */
- if (rbuf[i] == 0)
- break;
-
- tuple = (struct tuple *)(rbuf + i);
- len = sizeof(struct tuple);
-
- /* Make sure the tuple struct is within bounds. */
- if (i + len > CONFIG_FLASH_NVMEM_VARS_USER_SIZE)
- goto fixit;
-
- /* Empty values are not allowed */
- if (!tuple->val_len)
- goto fixit;
-
- /* See how big the tuple is */
- len += tuple->key_len + tuple->val_len;
+ const struct tuple *var;
+ uintptr_t idx;
- /* Oops, it's off the end (leave one byte for final 0) */
- if (i + len >= CONFIG_FLASH_NVMEM_VARS_USER_SIZE)
- goto fixit;
+ if (!prev_var) {
+ /*
+ * The caller is just starting, let's get the first var, if
+ * any.
+ */
+ if (!rbuf[0])
+ return NULL;
+ return (const struct tuple *)rbuf;
}
- /* Found the end of variables. Now make sure the rest is all 0xff. */
- for (i++ ; i < CONFIG_FLASH_NVMEM_VARS_USER_SIZE; i++)
- if (rbuf[i] != 0xff)
- goto fixit;
+ /* Let's try to get the next one. */
+ idx = (uintptr_t)prev_var;
+ idx += prev_var->key_len + prev_var->val_len + sizeof(struct tuple);
- release_local_copy();
- return EC_SUCCESS;
+ var = (const struct tuple *)idx;
-fixit:
- /* No variables */
- rbuf[0] = 0;
- /* Everything else is 0xff */
- memset(rbuf + 1, 0xff, CONFIG_FLASH_NVMEM_VARS_USER_SIZE - 1);
+ if (var->key_len)
+ return var;
- return writevars();
+ return NULL;
}
-const struct tuple *getvar(const uint8_t *key, uint8_t key_len)
-{
- uint32_t i = 0;
-
- if (bogus_blob(key, key_len))
- return 0;
-
- if (get_local_copy() != EC_SUCCESS)
- return 0;
-
- if (!getvar_idx(&i, key, key_len))
- return 0;
-
- return (const struct tuple *)(rbuf + i);
-}
-
-const uint8_t *tuple_key(const struct tuple *t)
-{
- return t->data_;
-}
+const uint8_t *tuple_key(const struct tuple *t) { return t->data_; }
const uint8_t *tuple_val(const struct tuple *t)
{
return t->data_ + t->key_len;
}
-int setvar(const uint8_t *key, uint8_t key_len,
- const uint8_t *val, uint8_t val_len)
-{
- struct tuple *tuple;
- int rv, i, j;
-
- if (bogus_blob(key, key_len))
- return EC_ERROR_INVAL;
-
- rv = get_local_copy();
- if (rv != EC_SUCCESS)
- return rv;
-
- i = 0;
- if (getvar_idx(&i, key, key_len)) {
- /* Found the match at position i */
- j = i;
- if (next_tuple(&j)) {
- /*
- * Now j is the start of the tuple after ours. Delete
- * our entry by shifting left from there to the end of
- * rbuf, so that it covers ours up.
- *
- * Before:
- * i j
- * <foo,bar><KEY,VAL><hey,splat>0
- *
- * After:
- * i
- * <foo,bar><hey,splat>0...
- */
- memmove(rbuf + i, rbuf + j,
- CONFIG_FLASH_NVMEM_VARS_USER_SIZE - j);
- /* Advance i to point to the end of all tuples */
- while (next_tuple(&i))
- ;
- }
- /* Whether we found a match or not, it's not there now */
- }
- /*
- * Now i is where the new tuple should be written.
- *
- * Either this:
- * i
- * <foo,bar><hey,splat>0
- *
- * or there are no tuples at all and i == 0
- *
- */
-
- /* If there's no value to save, we're done. */
- if (bogus_blob(val, val_len))
- goto done;
-
- /*
- * We'll always write the updated entry at the end of any existing
- * tuples, and we mark the end with an additional 0. Make sure all that
- * will all fit. If it doesn't, we've already removed the previous
- * entry but we still need to mark the end.
- */
- if (i + sizeof(struct tuple) + key_len + val_len + 1 >
- CONFIG_FLASH_NVMEM_VARS_USER_SIZE) {
- rv = EC_ERROR_OVERFLOW;
- goto done;
- }
-
- /* write the tuple */
- tuple = (struct tuple *)(rbuf + i);
- tuple->key_len = key_len;
- tuple->val_len = val_len;
- tuple->flags = 0; /* UNUSED, set to zero */
- memcpy(tuple->data_, key, key_len);
- memcpy(tuple->data_ + key_len, val, val_len);
- /* move past it */
- next_tuple(&i);
-
-done:
- /* mark the end */
- rbuf[i++] = 0;
- /* erase the rest */
- memset(rbuf + i, 0xff, CONFIG_FLASH_NVMEM_VARS_USER_SIZE - i);
-
- return rv;
-}
-
-int writevars(void)
-{
- int rv;
-
- if (!rbuf)
- return EC_SUCCESS;
-
- rv = nvmem_write(0, CONFIG_FLASH_NVMEM_VARS_USER_SIZE,
- rbuf, CONFIG_FLASH_NVMEM_VARS_USER_NUM);
- if (rv != EC_SUCCESS)
- return rv;
-
- rv = nvmem_commit();
- if (rv != EC_SUCCESS)
- return rv;
-
- release_local_copy();
-
- return rv;
-}
-
/****************************************************************************/
#if defined(TEST_BUILD) && !defined(TEST_FUZZ)
#include "console.h"
@@ -382,133 +144,28 @@ static int command_set(int argc, char **argv)
return EC_ERROR_PARAM_COUNT;
if (argc == 2)
- rc = setvar(argv[1], strlen(argv[1]), 0, 0);
+ rc = setvar(argv[1], strlen(argv[1]), 0, 0);
else
- rc = setvar(argv[1], strlen(argv[1]),
- argv[2], strlen(argv[2]));
- if (rc)
- return rc;
+ rc = setvar(argv[1], strlen(argv[1]), argv[2], strlen(argv[2]));
- return writevars();
+ return rc;
}
-DECLARE_CONSOLE_COMMAND(set, command_set,
- "VARIABLE [VALUE]",
+DECLARE_CONSOLE_COMMAND(set, command_set, "VARIABLE [VALUE]",
"Set/clear the value of the specified variable");
static int command_print(int argc, char **argv)
{
- const struct tuple *tuple;
- int rv, i = 0;
-
- rv = get_local_copy();
- if (rv)
- return rv;
-
- tuple = (const struct tuple *)(rbuf + i);
- if (!tuple->key_len)
- return EC_SUCCESS;
-
- do {
- tuple = (const struct tuple *)(rbuf + i);
- print_blob(tuple_key(tuple), tuple->key_len);
- ccprintf("=");
- print_blob(tuple_val(tuple), tuple->val_len);
- ccprintf("\n");
- } while (next_tuple(&i));
-
- return EC_SUCCESS;
+ ccprintf("Print all vars is not yet implemented\n");
+ return EC_ERROR_INVAL;
}
-DECLARE_CONSOLE_COMMAND(print, command_print,
- "",
+DECLARE_CONSOLE_COMMAND(print, command_print, "",
"Print all defined variables");
-static int command_dump(int argc, char **argv)
-{
- int i, rv;
-
- rv = get_local_copy();
- if (rv)
- return rv;
-
- for (i = 0; i < CONFIG_FLASH_NVMEM_VARS_USER_SIZE; i++)
- ccprintf(" %02x", rbuf[i]);
- ccprintf("\n");
- release_local_copy();
-
- return EC_SUCCESS;
-}
-DECLARE_CONSOLE_COMMAND(dump, command_dump,
- "",
- "Dump the variable memory");
-
static int command_clear_nvmem_vars(int argc, char **argv)
{
- int rv;
-
- rv = nvmem_erase_user_data(CONFIG_FLASH_NVMEM_VARS_USER_NUM);
- if (rv)
- ccprintf("Error clearing nvmem vars! (rv: %d)\n", rv);
- else
- ccprintf("NvMem vars cleared successfully.\n");
-
- /*
- * Invalidate the cache buffer since we just erased the backing
- * store.
- */
- writevars();
-
- /*
- * Re-initialize the NvMem vars space so that it's ready for
- * immediate use.
- */
- initvars();
-
- /*
- * TODO(aaboagye): For "V1", this is where you might want to call and
- * reset the defaults.
- */
-
- return rv;
+ ccprintf("Nvmem clear vars has not yet been implemented\n");
+ return EC_ERROR_INVAL;
}
-DECLARE_CONSOLE_COMMAND(clr_nvmem_vars, command_clear_nvmem_vars,
- "",
+DECLARE_CONSOLE_COMMAND(clr_nvmem_vars, command_clear_nvmem_vars, "",
"Clear the NvMem variables.");
-
-static int command_nv_test_var(int argc, char **argv)
-{
- const struct tuple *t;
- uint8_t key;
- uint8_t val;
- int rv;
-
- key = NVMEM_VAR_TEST_VAR;
-
- if (argc > 1) {
- val = (uint8_t)atoi(argv[1]);
- rv = setvar(&key, 1, &val, 1);
- if (rv)
- ccprintf("setvar err %d", rv);
-
- rv = writevars();
- if (rv)
- ccprintf("writevar err %d", rv);
- }
-
- t = getvar(&key, 1);
- if (t) {
- val = *tuple_val(t);
- } else {
- ccprintf("No value set.\n");
- return EC_SUCCESS;
- }
-
- /* Invalidate RAM buffer. */
- writevars();
- ccprintf("test_var: %d\n", val);
-
- return EC_SUCCESS;
-}
-DECLARE_SAFE_CONSOLE_COMMAND(nvtestvar, command_nv_test_var,
- "[0-255]",
- "Get/Set an NvMem test variable.");
#endif
diff --git a/common/panic_output.c b/common/panic_output.c
index e6b48a375d..8d34adaa38 100644
--- a/common/panic_output.c
+++ b/common/panic_output.c
@@ -114,6 +114,7 @@ void panic(const char *msg)
struct panic_data *panic_get_data(void)
{
+ BUILD_ASSERT(sizeof(struct panic_data) <= CONFIG_PANIC_DATA_SIZE);
return pdata_ptr->magic == PANIC_DATA_MAGIC ? pdata_ptr : NULL;
}
diff --git a/common/pinweaver.c b/common/pinweaver.c
index 1777a4c28b..d65e1bad0c 100644
--- a/common/pinweaver.c
+++ b/common/pinweaver.c
@@ -656,6 +656,7 @@ static int load_log_data(struct pw_log_storage_t *log)
{
const struct tuple *ptr;
const struct pw_log_storage_t *view;
+ int rv = EC_SUCCESS;
ptr = getvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1);
if (ptr == NULL)
@@ -663,24 +664,21 @@ static int load_log_data(struct pw_log_storage_t *log)
view = (void *)tuple_val(ptr);
if (ptr->val_len != sizeof(struct pw_log_storage_t))
- return PW_ERR_NV_LENGTH_MISMATCH;
- if (view->storage_version != PW_STORAGE_VERSION)
- return PW_ERR_NV_VERSION_MISMATCH;
+ rv = PW_ERR_NV_LENGTH_MISMATCH;
+ else if (view->storage_version != PW_STORAGE_VERSION)
+ rv = PW_ERR_NV_VERSION_MISMATCH;
+ else
+ memcpy(log, view, ptr->val_len);
- memcpy(log, view, ptr->val_len);
- return EC_SUCCESS;
+ freevar(ptr);
+
+ return rv;
}
int store_log_data(const struct pw_log_storage_t *log)
{
- int ret;
-
- ret = setvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1, (uint8_t *)log,
- sizeof(struct pw_log_storage_t));
- if (ret != EC_SUCCESS)
- return ret;
-
- return writevars();
+ return setvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1, (uint8_t *)log,
+ sizeof(struct pw_log_storage_t));
}
static int load_merkle_tree(struct merkle_tree_t *merkle_tree)
@@ -695,15 +693,19 @@ static int load_merkle_tree(struct merkle_tree_t *merkle_tree)
const struct pw_long_term_storage_t *tree;
ptr = getvar(PW_TREE_VAR, sizeof(PW_TREE_VAR) - 1);
- if (ptr == NULL)
+ if (!ptr)
return PW_ERR_NV_EMPTY;
tree = (void *)tuple_val(ptr);
/* Add storage format updates here. */
- if (ptr->val_len != sizeof(*tree))
+ if (ptr->val_len != sizeof(*tree)) {
+ freevar(ptr);
return PW_ERR_NV_LENGTH_MISMATCH;
- if (tree->storage_version != PW_STORAGE_VERSION)
+ }
+ if (tree->storage_version != PW_STORAGE_VERSION) {
+ freevar(ptr);
return PW_ERR_NV_VERSION_MISMATCH;
+ }
merkle_tree->bits_per_level = tree->bits_per_level;
merkle_tree->height = tree->height;
@@ -711,8 +713,10 @@ static int load_merkle_tree(struct merkle_tree_t *merkle_tree)
tree->key_derivation_nonce,
sizeof(tree->key_derivation_nonce));
ret = derive_keys(merkle_tree);
- if (ret != EC_SUCCESS)
+ if (ret != EC_SUCCESS) {
+ freevar(ptr);
return ret;
+ }
}
/* Handle the root hash. */
@@ -720,15 +724,19 @@ static int load_merkle_tree(struct merkle_tree_t *merkle_tree)
struct pw_log_storage_t *log;
ptr = getvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1);
- if (ptr == NULL)
+ if (!ptr)
return PW_ERR_NV_EMPTY;
log = (void *)tuple_val(ptr);
/* Add storage format updates here. */
- if (ptr->val_len != sizeof(struct pw_log_storage_t))
+ if (ptr->val_len != sizeof(struct pw_log_storage_t)) {
+ freevar(ptr);
return PW_ERR_NV_LENGTH_MISMATCH;
- if (log->storage_version != PW_STORAGE_VERSION)
+ }
+ if (log->storage_version != PW_STORAGE_VERSION) {
+ freevar(ptr);
return PW_ERR_NV_VERSION_MISMATCH;
+ }
memcpy(merkle_tree->root, log->entries[0].root,
sizeof(merkle_tree->root));
@@ -747,15 +755,15 @@ static int load_merkle_tree(struct merkle_tree_t *merkle_tree)
ret = setvar(PW_LOG_VAR0, sizeof(PW_LOG_VAR0) - 1,
(uint8_t *)log,
sizeof(struct pw_log_storage_t));
- if (ret != EC_SUCCESS)
- return ret;
- ret = writevars();
- if (ret != EC_SUCCESS)
+ if (ret != EC_SUCCESS) {
+ freevar(ptr);
return ret;
+ }
}
pw_restart_count = log->restart_count;
}
+ freevar(ptr);
cprints(CC_TASK, "PinWeaver: Loaded Tree. restart_count = %d",
pw_restart_count);
diff --git a/common/shmalloc.c b/common/shmalloc.c
index c1cebaaf7c..8c66a63eb8 100644
--- a/common/shmalloc.c
+++ b/common/shmalloc.c
@@ -310,6 +310,8 @@ int shared_mem_acquire(int size, char **dest_ptr)
int rv;
struct shm_buffer *new_buf;
+ *dest_ptr = NULL;
+
if (in_interrupt_context())
return EC_ERROR_INVAL;
diff --git a/common/system.c b/common/system.c
index d956787e74..53282b278d 100644
--- a/common/system.c
+++ b/common/system.c
@@ -86,16 +86,6 @@ struct jump_data {
/* Jump data (at end of RAM, or preceding panic data) */
static struct jump_data *jdata;
-/*
- * Reset flag descriptions. Must be in same order as bits of RESET_FLAG_
- * constants.
- */
-static const char * const reset_flag_descs[] = {
- "other", "reset-pin", "brownout", "power-on", "watchdog", "soft",
- "hibernate", "rtc-alarm", "wake-pin", "low-battery", "sysjump",
- "hard", "ap-off", "preserved", "usb-resume", "rdd", "rbox",
- "security", "ap-watchdog" };
-
static uint32_t reset_flags;
static int jumped_to_image;
static int disable_jump; /* Disable ALL jumps if system is locked */
@@ -278,6 +268,9 @@ void system_print_reset_flags(void)
{
int count = 0;
int i;
+ static const char * const reset_flag_descs[] = {
+ #include "reset_flag_desc.inc"
+ };
if (!reset_flags) {
CPUTS("unknown");
@@ -292,6 +285,13 @@ void system_print_reset_flags(void)
CPUTS(reset_flag_descs[i]);
}
}
+
+ if (reset_flags >= BIT(i)) {
+ if (count)
+ CPUTS(" ");
+
+ CPUTS("no-desc");
+ }
}
int system_jumped_to_this_image(void)
@@ -554,6 +554,11 @@ static void jump_to_image(uintptr_t init_addr)
usleep(MSEC);
gpio_set_level(GPIO_ENTERING_RW, 0);
+#ifdef CONFIG_USB_PD_ALT_MODE_DFP
+ /* Note: must be before i2c module is locked down */
+ pd_prepare_sysjump();
+#endif
+
#ifdef CONFIG_I2C_MASTER
/* Prepare I2C module for sysjump */
i2c_prepare_sysjump();
@@ -937,7 +942,19 @@ static int handle_pending_reboot(enum ec_reboot_cmd cmd)
return system_run_image_copy(system_get_active_copy());
case EC_REBOOT_COLD:
#ifdef HAS_TASK_PDCMD
- /* Reboot the PD chip as well */
+ /*
+ * Reboot the PD chip(s) as well, but first suspend the ports
+ * if this board has PD tasks running so they don't query the
+ * TCPCs while they reset.
+ */
+#ifdef HAS_TASK_PD_C0
+ {
+ int port;
+
+ for (port = 0; port < CONFIG_USB_PD_PORT_COUNT; port++)
+ pd_set_suspend(port, 1);
+ }
+#endif
board_reset_pd_mcu();
#endif
diff --git a/common/tablet_mode.c b/common/tablet_mode.c
index da2de38ad3..652756b665 100644
--- a/common/tablet_mode.c
+++ b/common/tablet_mode.c
@@ -14,14 +14,21 @@
#define CPRINTS(format, args...) cprints(CC_MOTION_LID, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_MOTION_LID, format, ## args)
-/* 1: in tablet mode. 0: otherwise */
-static int tablet_mode = 1;
+/* 1: in tablet mode; 0: notebook mode; -1: uninitialized */
+static int tablet_mode = -1;
+/* 1: hall sensor is reporting 360 degrees. */
+static int hall_sensor_at_360;
+
+/*
+ * 1: all calls to tablet_set_mode are ignored and tablet_mode if forced to 0
+ * 0: all calls to tablet_set_mode are honored
+ */
static int disabled;
int tablet_get_mode(void)
{
- return tablet_mode;
+ return !!tablet_mode;
}
void tablet_set_mode(int mode)
@@ -34,15 +41,20 @@ void tablet_set_mode(int mode)
return;
}
+ if (hall_sensor_at_360 && !mode) {
+ CPRINTS("Ignoring tablet mode exit while hall sensor active.");
+ return;
+ }
+
tablet_mode = mode;
CPRINTS("tablet mode %sabled", mode ? "en" : "dis");
hook_notify(HOOK_TABLET_MODE_CHANGE);
+#ifdef CONFIG_HOSTCMD_EVENTS
/*
* When tablet mode changes, send an event to ACPI to retrieve
* tablet mode value and send an event to the kernel.
*/
-#ifdef CONFIG_HOSTCMD_EVENTS
host_set_single_event(EC_HOST_EVENT_MODE_CHANGE);
#endif
}
@@ -54,7 +66,9 @@ void tablet_set_mode(int mode)
#endif
static void hall_sensor_interrupt_debounce(void)
{
- int flipped_360_mode = !gpio_get_level(HALL_SENSOR_GPIO_L);
+ hall_sensor_at_360 = IS_ENABLED(CONFIG_HALL_SENSOR_CUSTOM)
+ ? board_sensor_at_360()
+ : !gpio_get_level(HALL_SENSOR_GPIO_L);
/*
* 1. Peripherals are disabled only when lid reaches 360 position (It's
@@ -69,15 +83,11 @@ static void hall_sensor_interrupt_debounce(void)
* driver to clear it when lid goes into laptop zone.
*/
-#ifdef CONFIG_LID_ANGLE
- if (flipped_360_mode)
-#endif /* CONFIG_LID_ANGLE */
- tablet_set_mode(flipped_360_mode);
+ if (!IS_ENABLED(CONFIG_LID_ANGLE) || hall_sensor_at_360)
+ tablet_set_mode(hall_sensor_at_360);
-#ifdef CONFIG_LID_ANGLE_UPDATE
- if (flipped_360_mode)
+ if (IS_ENABLED(CONFIG_LID_ANGLE_UPDATE) && hall_sensor_at_360)
lid_angle_peripheral_enable(0);
-#endif /* CONFIG_LID_ANGLE_UPDATE */
}
DECLARE_DEFERRED(hall_sensor_interrupt_debounce);
@@ -87,7 +97,7 @@ DECLARE_DEFERRED(hall_sensor_interrupt_debounce);
void hall_sensor_isr(enum gpio_signal signal)
{
hook_call_deferred(&hall_sensor_interrupt_debounce_data,
- HALL_SENSOR_DEBOUNCE_US);
+ HALL_SENSOR_DEBOUNCE_US);
}
static void hall_sensor_init(void)
@@ -97,8 +107,10 @@ static void hall_sensor_init(void)
return;
gpio_enable_interrupt(HALL_SENSOR_GPIO_L);
- /* Ensure tablet mode is initialized according to the hardware state
- * so that the cached state reflects reality. */
+ /*
+ * Ensure tablet mode is initialized according to the hardware state
+ * so that the cached state reflects reality.
+ */
hall_sensor_interrupt_debounce();
}
DECLARE_HOOK(HOOK_INIT, hall_sensor_init, HOOK_PRIO_DEFAULT);
diff --git a/common/tpm_registers.c b/common/tpm_registers.c
index 8a7a4c5360..947a3b089a 100644
--- a/common/tpm_registers.c
+++ b/common/tpm_registers.c
@@ -13,7 +13,7 @@
#include "console.h"
#include "extension.h"
#include "link_defs.h"
-#include "nvmem.h"
+#include "new_nvmem.h"
#include "printf.h"
#include "signed_header.h"
#include "sps.h"
@@ -448,7 +448,7 @@ void tpm_register_put(uint32_t regaddr, const uint8_t *data, uint32_t data_size)
}
-void fifo_reg_read(uint8_t *dest, uint32_t data_size)
+static void fifo_reg_read(uint8_t *dest, uint32_t data_size)
{
uint32_t still_in_fifo = tpm_.fifo_write_index -
tpm_.fifo_read_index;
@@ -828,20 +828,11 @@ static void tpm_reset_now(int wipe_first)
if (wipe_first)
/* Now wipe the TPM's nvmem */
- wipe_result = nvmem_erase_user_data(NVMEM_TPM);
+ wipe_result = nvmem_erase_tpm_data();
else
wipe_result = EC_SUCCESS;
/*
- * Clear the TPM library's zero-init data. Note that the linker script
- * includes this file's .bss in the same section, so it will be cleared
- * at the same time.
- */
- memset(&__bss_libtpm2_start, 0,
- (uintptr_t)(&__bss_libtpm2_end) -
- (uintptr_t)(&__bss_libtpm2_start));
-
- /*
* NOTE: If any __initialized variables need reinitializing after
* reset, this is the place to do it.
*/
@@ -853,6 +844,15 @@ static void tpm_reset_now(int wipe_first)
nvmem_enable_commits();
/*
+ * Clear the TPM library's zero-init data. Note that the linker script
+ * includes this file's .bss in the same section, so it will be cleared
+ * at the same time.
+ */
+ memset(&__bss_libtpm2_start, 0,
+ (uintptr_t)(&__bss_libtpm2_end) -
+ (uintptr_t)(&__bss_libtpm2_start));
+
+ /*
* Prevent NVRAM commits until further notice, unless running in
* factory mode.
*/
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index 2782dee5ec..ff9d710e64 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -152,6 +152,11 @@ static const uint8_t vdo_ver[] = {
#define VDO_VER(v) VDM_VER10
#endif
+#ifdef CONFIG_USB_PD_ALT_MODE_DFP
+/* Tracker for which task is waiting on sysjump prep to finish */
+static volatile task_id_t sysjump_task_waiting = TASK_ID_INVALID;
+#endif
+
static struct pd_protocol {
/* current port power role (SOURCE or SINK) */
uint8_t power_role;
@@ -2165,7 +2170,7 @@ int pd_dev_store_rw_hash(int port, uint16_t dev_id, uint32_t *rw_hash,
return 0;
}
-#ifdef CONFIG_POWER_COMMON /* Needed b/c CONFIG_POWER_COMMON is only caller */
+#if defined(CONFIG_POWER_COMMON) || defined(CONFIG_USB_PD_ALT_MODE_DFP)
static void exit_dp_mode(int port)
{
#ifdef CONFIG_USB_PD_ALT_MODE_DFP
@@ -2508,6 +2513,8 @@ static typec_current_t get_typec_current_limit(int polarity, int cc1, int cc2)
charge = 3000;
else if (cc == TYPEC_CC_VOLT_RP_1_5)
charge = 1500;
+ else if (cc == TYPEC_CC_VOLT_RP_DEF)
+ charge = 500;
else
charge = 0;
@@ -2766,6 +2773,12 @@ void pd_task(void *u)
this_state = PD_STATE_SOFT_RESET;
/*
+ * Re-discover any alternate modes we may have been
+ * using with this port partner.
+ */
+ pd[port].flags |= PD_FLAGS_CHECK_IDENTITY;
+
+ /*
* Set the TCPC reset event such that we can set our CC
* terminations, determine polarity, and enable RX so we
* can hear back from our port partner.
@@ -2857,6 +2870,20 @@ void pd_task(void *u)
if (evt & PD_EVENT_POWER_STATE_CHANGE)
handle_new_power_state(port);
#endif
+
+#if defined(CONFIG_USB_PD_ALT_MODE_DFP)
+ if (evt & PD_EVENT_SYSJUMP) {
+ exit_dp_mode(port);
+ /*
+ * If event was set from pd_prepare_sysjump, wake the
+ * task waiting on us to complete.
+ */
+ if (sysjump_task_waiting != TASK_ID_INVALID)
+ task_set_event(sysjump_task_waiting,
+ TASK_EVENT_SYSJUMP_READY, 0);
+ }
+#endif
+
#ifdef CONFIG_USB_PD_DUAL_ROLE
if (evt & PD_EVENT_UPDATE_DUAL_ROLE)
pd_update_dual_role_config(port);
@@ -3466,9 +3493,26 @@ void pd_task(void *u)
PD_STATE_SRC_SWAP_SRC_DISABLE);
break;
case PD_STATE_SRC_SWAP_SRC_DISABLE:
- /* Turn power off */
if (pd[port].last_state != pd[port].task_state) {
+ /* Turn power off */
pd_power_supply_reset(port);
+
+ /*
+ * Switch to Rd and swap roles to sink
+ *
+ * The reason we do this as early as possible is
+ * to help prevent CC disconnection cases where
+ * both partners are applying an Rp. Certain PD
+ * stacks (e.g. qualcomm), reflexively apply
+ * their Rp once VBUS falls beneath
+ * ~3.67V. (b/77827528).
+ */
+ tcpm_set_cc(port, TYPEC_CC_RD);
+ pd_set_power_role(port, PD_ROLE_SINK);
+
+ /* Inform TCPC of power role update. */
+ pd_update_roles(port);
+
set_state_timeout(port,
get_time().val +
PD_POWER_SUPPLY_TURN_OFF_DELAY,
@@ -3486,9 +3530,6 @@ void pd_task(void *u)
PD_STATE_SRC_DISCONNECTED);
break;
}
- /* Switch to Rd and swap roles to sink */
- tcpm_set_cc(port, TYPEC_CC_RD);
- pd_set_power_role(port, PD_ROLE_SINK);
/* Wait for PS_RDY from new source */
set_state_timeout(port,
get_time().val +
@@ -3925,7 +3966,7 @@ void pd_task(void *u)
break;
case PD_STATE_SNK_SWAP_STANDBY:
if (pd[port].last_state != pd[port].task_state) {
- /* Switch to Rp and enable power supply */
+ /* Switch to Rp and enable power supply. */
tcpm_set_cc(port, TYPEC_CC_RP);
if (pd_set_power_supply_ready(port)) {
/* Restore Rd */
@@ -4355,6 +4396,28 @@ DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pd_chipset_shutdown, HOOK_PRIO_DEFAULT);
#endif /* CONFIG_USB_PD_DUAL_ROLE */
+#ifdef CONFIG_USB_PD_ALT_MODE_DFP
+void pd_prepare_sysjump(void)
+{
+ int i;
+
+ /* Exit modes before sysjump so we can cleanly enter again later */
+ for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++) {
+ /*
+ * We can't be in an alternate mode if PD comm is disabled, so
+ * no need to send the event
+ */
+ if (!pd_comm_is_enabled(i))
+ continue;
+
+ sysjump_task_waiting = task_get_current();
+ task_set_event(PD_PORT_TO_TASK_ID(i), PD_EVENT_SYSJUMP, 0);
+ task_wait_event_mask(TASK_EVENT_SYSJUMP_READY, -1);
+ sysjump_task_waiting = TASK_ID_INVALID;
+ }
+}
+#endif
+
#ifdef CONFIG_COMMON_RUNTIME
/*
@@ -5030,6 +5093,7 @@ DECLARE_HOST_COMMAND(EC_CMD_USB_PD_CONTROL,
hc_usb_pd_control,
EC_VER_MASK(0) | EC_VER_MASK(1) | EC_VER_MASK(2));
+#ifdef CONFIG_HOSTCMD_FLASHPD
static int hc_remote_flash(struct host_cmd_handler_args *args)
{
const struct ec_params_usb_pd_fw_update *p = args->params;
@@ -5127,7 +5191,9 @@ static int hc_remote_flash(struct host_cmd_handler_args *args)
DECLARE_HOST_COMMAND(EC_CMD_USB_PD_FW_UPDATE,
hc_remote_flash,
EC_VER_MASK(0));
+#endif /* CONFIG_HOSTCMD_FLASHPD */
+#ifdef CONFIG_HOSTCMD_RWHASHPD
static int hc_remote_rw_hash_entry(struct host_cmd_handler_args *args)
{
int i, idx = 0, found = 0;
@@ -5157,6 +5223,7 @@ static int hc_remote_rw_hash_entry(struct host_cmd_handler_args *args)
DECLARE_HOST_COMMAND(EC_CMD_USB_PD_RW_HASH_ENTRY,
hc_remote_rw_hash_entry,
EC_VER_MASK(0));
+#endif /* CONFIG_HOSTCMD_RWHASHPD */
static int hc_remote_pd_dev_info(struct host_cmd_handler_args *args)
{
@@ -5178,7 +5245,6 @@ static int hc_remote_pd_dev_info(struct host_cmd_handler_args *args)
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
-
DECLARE_HOST_COMMAND(EC_CMD_USB_PD_DEV_INFO,
hc_remote_pd_dev_info,
EC_VER_MASK(0));
diff --git a/common/usb_pd_tcpc.c b/common/usb_pd_tcpc.c
index 83a1569223..2bdadcec7e 100644
--- a/common/usb_pd_tcpc.c
+++ b/common/usb_pd_tcpc.c
@@ -343,11 +343,19 @@ int prepare_message(int port, uint16_t header, uint8_t cnt,
int off, i;
/* 64-bit preamble */
off = pd_write_preamble(port);
+#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD)
+ /* Start Of Packet Prime: 2x Sync-1 + 2x Sync-3 */
+ off = pd_write_sym(port, off, BMC(PD_SYNC1));
+ off = pd_write_sym(port, off, BMC(PD_SYNC1));
+ off = pd_write_sym(port, off, BMC(PD_SYNC3));
+ off = pd_write_sym(port, off, BMC(PD_SYNC3));
+#else
/* Start Of Packet: 3x Sync-1 + 1x Sync-2 */
off = pd_write_sym(port, off, BMC(PD_SYNC1));
off = pd_write_sym(port, off, BMC(PD_SYNC1));
off = pd_write_sym(port, off, BMC(PD_SYNC1));
off = pd_write_sym(port, off, BMC(PD_SYNC2));
+#endif
/* header */
off = encode_short(port, off, header);
@@ -682,6 +690,17 @@ int pd_analyze_rx(int port, uint32_t *payload)
/* Find the Start Of Packet sequence */
while (bit > 0) {
bit = pd_dequeue_bits(port, bit, 20, &val);
+#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD)
+ if (val == PD_SOP_PRIME) {
+ break;
+ } else if (val == PD_SOP) {
+ CPRINTF("SOP\n");
+ return PD_RX_ERR_UNSUPPORTED_SOP;
+ } else if (val == PD_SOP_PRIME_PRIME) {
+ CPRINTF("SOP''\n");
+ return PD_RX_ERR_UNSUPPORTED_SOP;
+ }
+#else /* CONFIG_USB_TYPEC_VPD || CONFIG_USB_TYPEC_CTVPD */
#ifdef CONFIG_USB_PD_DECODE_SOP
if (val == PD_SOP || val == PD_SOP_PRIME ||
val == PD_SOP_PRIME_PRIME)
@@ -696,7 +715,8 @@ int pd_analyze_rx(int port, uint32_t *payload)
CPRINTF("SOP''\n");
return PD_RX_ERR_UNSUPPORTED_SOP;
}
-#endif
+#endif /* CONFIG_USB_PD_DECODE_SOP */
+#endif /* CONFIG_USB_TYPEC_VPD || CONFIG_USB_TYPEC_CTVPD */
}
if (bit < 0) {
#ifdef CONFIG_USB_PD_DECODE_SOP
@@ -872,7 +892,11 @@ int tcpc_run(int port, int evt)
/* outgoing packet ? */
if ((evt & PD_EVENT_TX) && pd[port].rx_enabled) {
switch (pd[port].tx_type) {
+#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD)
+ case TCPC_TX_SOP_PRIME:
+#else
case TCPC_TX_SOP:
+#endif
res = send_validate_message(port,
pd[port].tx_head,
pd[port].tx_data);
@@ -938,7 +962,7 @@ int tcpc_run(int port, int evt)
#endif
}
-#ifndef CONFIG_USB_POWER_DELIVERY
+#if !defined(CONFIG_USB_POWER_DELIVERY) && !defined(CONFIG_USB_SM_FRAMEWORK)
void pd_task(void *u)
{
int port = TASK_ID_TO_PD_PORT(task_get_current());
diff --git a/common/usb_pe_sm.c b/common/usb_pe_sm.c
new file mode 100644
index 0000000000..91f8b587ed
--- /dev/null
+++ b/common/usb_pe_sm.c
@@ -0,0 +1,12 @@
+/* Copyright 2019 The Chromium OS 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 "common.h"
+
+/* Include USB PD Policy Engine State Machine */
+#if defined(CONFIG_USB_TYPEC_VPD) || defined(CONFIG_USB_TYPEC_CTVPD)
+#include "usb_pe_ctvpd_sm.h"
+#else
+#error "A USB PD Policy Engine State Machine must be defined."
+#endif
diff --git a/common/usb_prl_sm.c b/common/usb_prl_sm.c
new file mode 100644
index 0000000000..42b4ee3c24
--- /dev/null
+++ b/common/usb_prl_sm.c
@@ -0,0 +1,2163 @@
+/* Copyright 2019 The Chromium OS 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 "battery.h"
+#include "battery_smart.h"
+#include "board.h"
+#include "charge_manager.h"
+#include "charge_state.h"
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "ec_commands.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "tcpm.h"
+#include "util.h"
+#include "usb_charge.h"
+#include "usb_mux.h"
+#include "usb_pd.h"
+#include "usb_pe_sm.h"
+#include "usb_prl_sm.h"
+#include "usb_tc_sm.h"
+#include "usb_emsg.h"
+#include "usb_sm.h"
+#include "vpd_api.h"
+#include "version.h"
+
+/* Protocol Layer Flags */
+#define PRL_FLAGS_TX_COMPLETE BIT(0)
+#define PRL_FLAGS_START_AMS BIT(1)
+#define PRL_FLAGS_END_AMS BIT(2)
+#define PRL_FLAGS_TX_ERROR BIT(3)
+#define PRL_FLAGS_PE_HARD_RESET BIT(4)
+#define PRL_FLAGS_HARD_RESET_COMPLETE BIT(5)
+#define PRL_FLAGS_PORT_PARTNER_HARD_RESET BIT(6)
+#define PRL_FLAGS_MSG_XMIT BIT(7)
+#define PRL_FLAGS_MSG_RECEIVED BIT(8)
+#define PRL_FLAGS_ABORT BIT(9)
+#define PRL_FLAGS_CHUNKING BIT(10)
+
+/* PD counter definitions */
+#define PD_MESSAGE_ID_COUNT 7
+
+#define RCH_OBJ(port) (SM_OBJ(rch[port]))
+#define TCH_OBJ(port) (SM_OBJ(tch[port]))
+#define PRL_TX_OBJ(port) (SM_OBJ(prl_tx[port]))
+#define PRL_HR_OBJ(port) (SM_OBJ(prl_hr[port]))
+
+#define RCH_TEST_OBJ(port) (SM_OBJ(rch[(port)].obj))
+#define TCH_TEST_OBJ(port) (SM_OBJ(tch[(port)].obj))
+#define PRL_TX_TEST_OBJ(port) (SM_OBJ(prl_tx[(port)].obj))
+#define PRL_HR_TEST_OBJ(port) (SM_OBJ(prl_hr[(port)].obj))
+
+static enum sm_local_state local_state[CONFIG_USB_PD_PORT_COUNT] = {SM_INIT};
+
+/* Chunked Rx State Machine Object */
+static struct rx_chunked {
+ /* struct sm_obj must be first. */
+ struct sm_obj obj;
+ /* state id */
+ enum rch_state_id state_id;
+ /* PRL_FLAGS */
+ uint32_t flags;
+ /* protocol timer */
+ uint64_t chunk_sender_response_timer;
+} rch[CONFIG_USB_PD_PORT_COUNT];
+
+/* Chunked Tx State Machine Object */
+static struct tx_chunked {
+ /* struct sm_obj must be first. */
+ struct sm_obj obj;
+ /* state id */
+ enum tch_state_id state_id;
+ /* state machine flags */
+ uint32_t flags;
+ /* protocol timer */
+ uint64_t chunk_sender_request_timer;
+} tch[CONFIG_USB_PD_PORT_COUNT];
+
+/* Message Reception State Machine Object */
+static struct protocol_layer_rx {
+ /* message ids for all valid port partners */
+ int msg_id[NUM_XMIT_TYPES];
+} prl_rx[CONFIG_USB_PD_PORT_COUNT];
+
+/* Message Transmission State Machine Object */
+static struct protocol_layer_tx {
+ /* struct sm_obj must be first. */
+ struct sm_obj obj;
+ /* state id */
+ enum prl_tx_state_id state_id;
+ /* state machine flags */
+ uint32_t flags;
+ /* protocol timer */
+ uint64_t sink_tx_timer;
+ /* tcpc transmit timeout */
+ uint64_t tcpc_tx_timeout;
+ /* Last SOP* we transmitted to */
+ uint8_t sop;
+ /* message id counters for all 6 port partners */
+ uint32_t msg_id_counter[NUM_XMIT_TYPES];
+ /* message retry counter */
+ uint32_t retry_counter;
+ /* transmit status */
+ int xmit_status;
+} prl_tx[CONFIG_USB_PD_PORT_COUNT];
+
+/* Hard Reset State Machine Object */
+static struct protocol_hard_reset {
+ /* struct sm_obj must be first. */
+ struct sm_obj obj;
+ /* state id */
+ enum prl_hr_state_id state_id;
+ /* state machine flags */
+ uint32_t flags;
+ /* protocol timer */
+ uint64_t hard_reset_complete_timer;
+} prl_hr[CONFIG_USB_PD_PORT_COUNT];
+
+/* Chunking Message Object */
+static struct pd_message {
+ /* message status flags */
+ uint32_t status_flags;
+
+ /* SOP* */
+ enum tcpm_transmit_type xmit_type;
+ /* type of message */
+ uint8_t msg_type;
+ /* extended message */
+ uint8_t ext;
+ /* PD revision */
+ enum pd_rev_type rev;
+ /* Number of 32-bit objects in chk_buf */
+ uint16_t data_objs;
+ /* temp chunk buffer */
+ uint32_t chk_buf[7];
+ uint32_t chunk_number_expected;
+ uint32_t num_bytes_received;
+ uint32_t chunk_number_to_send;
+ uint32_t send_offset;
+} pdmsg[CONFIG_USB_PD_PORT_COUNT];
+
+struct extended_msg emsg[CONFIG_USB_PD_PORT_COUNT];
+
+/* Protocol Layer States */
+/* Common Protocol Layer Message Transmission */
+static void prl_tx_construct_message(int port);
+
+static unsigned int prl_tx_phy_layer_reset(int port, enum signal sig);
+static unsigned int prl_tx_phy_layer_reset_entry(int port);
+static unsigned int prl_tx_phy_layer_reset_run(int port);
+
+static unsigned int prl_tx_wait_for_message_request(int port, enum signal sig);
+static unsigned int prl_tx_wait_for_message_request_entry(int port);
+static unsigned int prl_tx_wait_for_message_request_run(int port);
+
+static unsigned int prl_tx_layer_reset_for_transmit(int port, enum signal sig);
+static unsigned int prl_tx_layer_reset_for_transmit_entry(int port);
+static unsigned int prl_tx_layer_reset_for_transmit_run(int port);
+
+static unsigned int prl_tx_wait_for_phy_response(int port, enum signal sig);
+static unsigned int prl_tx_wait_for_phy_response_entry(int port);
+static unsigned int prl_tx_wait_for_phy_response_run(int port);
+static unsigned int prl_tx_wait_for_phy_response_exit(int port);
+
+static unsigned int prl_tx_src_source_tx(int port, enum signal sig);
+static unsigned int prl_tx_src_source_tx_entry(int port);
+static unsigned int prl_tx_src_source_tx_run(int port);
+
+static unsigned int prl_tx_snk_start_ams(int port, enum signal sig);
+static unsigned int prl_tx_snk_start_ams_entry(int port);
+static unsigned int prl_tx_snk_start_ams_run(int port);
+
+/* Source Protocol Layser Message Transmission */
+static unsigned int prl_tx_src_pending(int port, enum signal sig);
+static unsigned int prl_tx_src_pending_entry(int port);
+static unsigned int prl_tx_src_pending_run(int port);
+
+/* Sink Protocol Layer Message Transmission */
+static unsigned int prl_tx_snk_pending(int port, enum signal sig);
+static unsigned int prl_tx_snk_pending_entry(int port);
+static unsigned int prl_tx_snk_pending_run(int port);
+
+static unsigned int prl_tx_discard_message(int port, enum signal sig);
+static unsigned int prl_tx_discard_message_entry(int port);
+static unsigned int prl_tx_discard_message_run(int port);
+
+/* Protocol Layer Message Reception */
+static unsigned int prl_rx_wait_for_phy_message(int port, int evt);
+
+/* Hard Reset Operation */
+static unsigned int prl_hr_wait_for_request(int port, enum signal sig);
+static unsigned int prl_hr_wait_for_request_entry(int port);
+static unsigned int prl_hr_wait_for_request_run(int port);
+
+static unsigned int prl_hr_reset_layer(int port, enum signal sig);
+static unsigned int prl_hr_reset_layer_entry(int port);
+static unsigned int prl_hr_reset_layer_run(int port);
+
+static unsigned int
+ prl_hr_wait_for_phy_hard_reset_complete(int port, enum signal sig);
+static unsigned int prl_hr_wait_for_phy_hard_reset_complete_entry(int port);
+static unsigned int prl_hr_wait_for_phy_hard_reset_complete_run(int port);
+
+static unsigned int
+ prl_hr_wait_for_pe_hard_reset_complete(int port, enum signal sig);
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_entry(int port);
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_run(int port);
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_exit(int port);
+
+/* Chunked Rx */
+static unsigned int
+ rch_wait_for_message_from_protocol_layer(int port, enum signal sig);
+static unsigned int rch_wait_for_message_from_protocol_layer_entry(int port);
+static unsigned int rch_wait_for_message_from_protocol_layer_run(int port);
+
+static unsigned int rch_processing_extended_message(int port, enum signal sig);
+static unsigned int rch_processing_extended_message_entry(int port);
+static unsigned int rch_processing_extended_message_run(int port);
+
+static unsigned int rch_requesting_chunk(int port, enum signal sig);
+static unsigned int rch_requesting_chunk_entry(int port);
+static unsigned int rch_requesting_chunk_run(int port);
+
+static unsigned int rch_waiting_chunk(int port, enum signal sig);
+static unsigned int rch_waiting_chunk_entry(int port);
+static unsigned int rch_waiting_chunk_run(int port);
+
+static unsigned int rch_report_error(int port, enum signal sig);
+static unsigned int rch_report_error_entry(int port);
+static unsigned int rch_report_error_run(int port);
+
+/* Chunked Tx */
+static unsigned int
+ tch_wait_for_message_request_from_pe(int port, enum signal sig);
+static unsigned int tch_wait_for_message_request_from_pe_entry(int port);
+static unsigned int tch_wait_for_message_request_from_pe_run(int port);
+
+static unsigned int
+ tch_wait_for_transmission_complete(int port, enum signal sig);
+static unsigned int tch_wait_for_transmission_complete_entry(int port);
+static unsigned int tch_wait_for_transmission_complete_run(int port);
+
+static unsigned int tch_construct_chunked_message(int port, enum signal sig);
+static unsigned int tch_construct_chunked_message_entry(int port);
+static unsigned int tch_construct_chunked_message_run(int port);
+
+static unsigned int tch_sending_chunked_message(int port, enum signal sig);
+static unsigned int tch_sending_chunked_message_entry(int port);
+static unsigned int tch_sending_chunked_message_run(int port);
+
+static unsigned int tch_wait_chunk_request(int port, enum signal sig);
+static unsigned int tch_wait_chunk_request_entry(int port);
+static unsigned int tch_wait_chunk_request_run(int port);
+
+static unsigned int tch_message_received(int port, enum signal sig);
+static unsigned int tch_message_received_entry(int port);
+static unsigned int tch_message_received_run(int port);
+
+static unsigned int do_nothing_exit(int port);
+static unsigned int get_super_state(int port);
+
+static const state_sig prl_tx_phy_layer_reset_sig[] = {
+ prl_tx_phy_layer_reset_entry,
+ prl_tx_phy_layer_reset_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_wait_for_message_request_sig[] = {
+ prl_tx_wait_for_message_request_entry,
+ prl_tx_wait_for_message_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_layer_reset_for_transmit_sig[] = {
+ prl_tx_layer_reset_for_transmit_entry,
+ prl_tx_layer_reset_for_transmit_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_wait_for_phy_response_sig[] = {
+ prl_tx_wait_for_phy_response_entry,
+ prl_tx_wait_for_phy_response_run,
+ prl_tx_wait_for_phy_response_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_src_source_tx_sig[] = {
+ prl_tx_src_source_tx_entry,
+ prl_tx_src_source_tx_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_snk_start_ams_sig[] = {
+ prl_tx_snk_start_ams_entry,
+ prl_tx_snk_start_ams_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+/* Source Protocol Layser Message Transmission */
+static const state_sig prl_tx_src_pending_sig[] = {
+ prl_tx_src_pending_entry,
+ prl_tx_src_pending_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+/* Sink Protocol Layer Message Transmission */
+static const state_sig prl_tx_snk_pending_sig[] = {
+ prl_tx_snk_pending_entry,
+ prl_tx_snk_pending_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_tx_discard_message_sig[] = {
+ prl_tx_discard_message_entry,
+ prl_tx_discard_message_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+/* Hard Reset Operation */
+static const state_sig prl_hr_wait_for_request_sig[] = {
+ prl_hr_wait_for_request_entry,
+ prl_hr_wait_for_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_hr_reset_layer_sig[] = {
+ prl_hr_reset_layer_entry,
+ prl_hr_reset_layer_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_hr_wait_for_phy_hard_reset_complete_sig[] = {
+ prl_hr_wait_for_phy_hard_reset_complete_entry,
+ prl_hr_wait_for_phy_hard_reset_complete_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig prl_hr_wait_for_pe_hard_reset_complete_sig[] = {
+ prl_hr_wait_for_pe_hard_reset_complete_entry,
+ prl_hr_wait_for_pe_hard_reset_complete_run,
+ prl_hr_wait_for_pe_hard_reset_complete_exit,
+ get_super_state
+};
+
+/* Chunked Rx */
+static const state_sig rch_wait_for_message_from_protocol_layer_sig[] = {
+ rch_wait_for_message_from_protocol_layer_entry,
+ rch_wait_for_message_from_protocol_layer_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig rch_processing_extended_message_sig[] = {
+ rch_processing_extended_message_entry,
+ rch_processing_extended_message_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig rch_requesting_chunk_sig[] = {
+ rch_requesting_chunk_entry,
+ rch_requesting_chunk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig rch_waiting_chunk_sig[] = {
+ rch_waiting_chunk_entry,
+ rch_waiting_chunk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig rch_report_error_sig[] = {
+ rch_report_error_entry,
+ rch_report_error_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+/* Chunked Tx */
+static const state_sig tch_wait_for_message_request_from_pe_sig[] = {
+ tch_wait_for_message_request_from_pe_entry,
+ tch_wait_for_message_request_from_pe_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_wait_for_transmission_complete_sig[] = {
+ tch_wait_for_transmission_complete_entry,
+ tch_wait_for_transmission_complete_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_construct_chunked_message_sig[] = {
+ tch_construct_chunked_message_entry,
+ tch_construct_chunked_message_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_sending_chunked_message_sig[] = {
+ tch_sending_chunked_message_entry,
+ tch_sending_chunked_message_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_wait_chunk_request_sig[] = {
+ tch_wait_chunk_request_entry,
+ tch_wait_chunk_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tch_message_received_sig[] = {
+ tch_message_received_entry,
+ tch_message_received_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+void pd_transmit_complete(int port, int status)
+{
+ prl_tx[port].xmit_status = status;
+}
+
+void pd_execute_hard_reset(int port)
+{
+ /* Only allow async. function calls when state machine is running */
+ if (local_state[port] != SM_RUN)
+ return;
+
+ prl_hr[port].flags |= PRL_FLAGS_PORT_PARTNER_HARD_RESET;
+ set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer);
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_execute_hard_reset(int port)
+{
+ /* Only allow async. function calls when state machine is running */
+ if (local_state[port] != SM_RUN)
+ return;
+
+ prl_hr[port].flags |= PRL_FLAGS_PE_HARD_RESET;
+ set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer);
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_init(int port)
+{
+ int i;
+
+ prl_tx[port].flags = 0;
+ prl_tx[port].xmit_status = TCPC_TX_UNSET;
+
+ tch[port].flags = 0;
+ rch[port].flags = 0;
+
+ /*
+ * Initialize to highest revision supported. If the port partner
+ * doesn't support this revision, the Protocol Engine will lower
+ * this value to the revision supported by the port partner.
+ */
+ pdmsg[port].rev = PD_REV30;
+ pdmsg[port].status_flags = 0;
+
+ prl_hr[port].flags = 0;
+
+ for (i = 0; i < NUM_XMIT_TYPES; i++) {
+ prl_rx[port].msg_id[i] = -1;
+ prl_tx[port].msg_id_counter[i] = 0;
+ }
+
+ init_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset);
+ init_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ init_state(port, TCH_OBJ(port), tch_wait_for_message_request_from_pe);
+ init_state(port, PRL_HR_OBJ(port), prl_hr_wait_for_request);
+}
+
+enum rch_state_id get_rch_state_id(int port)
+{
+ return rch[port].state_id;
+}
+
+enum tch_state_id get_tch_state_id(int port)
+{
+ return tch[port].state_id;
+}
+
+enum prl_tx_state_id get_prl_tx_state_id(int port)
+{
+ return prl_tx[port].state_id;
+}
+
+enum prl_hr_state_id get_prl_hr_state_id(int port)
+{
+ return prl_hr[port].state_id;
+}
+
+void prl_start_ams(int port)
+{
+ prl_tx[port].flags |= PRL_FLAGS_START_AMS;
+}
+
+void prl_end_ams(int port)
+{
+ prl_tx[port].flags |= PRL_FLAGS_END_AMS;
+}
+
+void prl_hard_reset_complete(int port)
+{
+ prl_hr[port].flags |= PRL_FLAGS_HARD_RESET_COMPLETE;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_send_ctrl_msg(int port,
+ enum tcpm_transmit_type type,
+ enum pd_ctrl_msg_type msg)
+{
+ pdmsg[port].xmit_type = type;
+ pdmsg[port].msg_type = msg;
+ pdmsg[port].ext = 0;
+ emsg[port].len = 0;
+
+ tch[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_send_data_msg(int port,
+ enum tcpm_transmit_type type,
+ enum pd_data_msg_type msg)
+{
+ pdmsg[port].xmit_type = type;
+ pdmsg[port].msg_type = msg;
+ pdmsg[port].ext = 0;
+
+ tch[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_send_ext_data_msg(int port,
+ enum tcpm_transmit_type type,
+ enum pd_ext_msg_type msg)
+{
+ pdmsg[port].xmit_type = type;
+ pdmsg[port].msg_type = msg;
+ pdmsg[port].ext = 1;
+
+ tch[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void prl_reset(int port)
+{
+ local_state[port] = SM_INIT;
+}
+
+void protocol_layer(int port, int evt, int en)
+{
+ switch (local_state[port]) {
+ case SM_INIT:
+ prl_init(port);
+ local_state[port] = SM_RUN;
+ /* fall through */
+ case SM_RUN:
+ /* If disabling, wait until message is sent. */
+ if (!en && tch[port].state_id ==
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE) {
+ /* Disable RX */
+#if defined(CONFIG_USB_TYPEC_CTVPD) || defined(CONFIG_USB_TYPEC_VPD)
+ vpd_rx_enable(0);
+#else
+ tcpm_set_rx_enable(port, 0);
+#endif
+ local_state[port] = SM_PAUSED;
+ break;
+ }
+
+ /* Run Protocol Layer Message Reception */
+ prl_rx_wait_for_phy_message(port, evt);
+
+ /* Run RX Chunked state machine */
+ exe_state(port, RCH_OBJ(port), RUN_SIG);
+
+ /* Run TX Chunked state machine */
+ exe_state(port, TCH_OBJ(port), RUN_SIG);
+
+ /* Run Protocol Layer Message Transmission state machine */
+ exe_state(port, PRL_TX_OBJ(port), RUN_SIG);
+
+ /* Run Protocol Layer Hard Reset state machine */
+ exe_state(port, PRL_HR_OBJ(port), RUN_SIG);
+ break;
+ case SM_PAUSED:
+ if (en)
+ local_state[port] = SM_INIT;
+ break;
+ }
+}
+
+enum sm_local_state prl_get_local_state(int port)
+{
+ return local_state[port];
+}
+
+void prl_set_rev(int port, enum pd_rev_type rev)
+{
+ pdmsg[port].rev = rev;
+}
+
+enum pd_rev_type prl_get_rev(int port)
+{
+ return pdmsg[port].rev;
+}
+
+/* Common Protocol Layer Message Transmission */
+static unsigned int prl_tx_phy_layer_reset(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_phy_layer_reset_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_phy_layer_reset_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_PHY_LAYER_RESET;
+
+#if defined(CONFIG_USB_TYPEC_CTVPD) || defined(CONFIG_USB_TYPEC_VPD)
+ vpd_rx_enable(1);
+#else
+ tcpm_init(port);
+ tcpm_set_rx_enable(port, 1);
+#endif
+
+ return 0;
+}
+
+static unsigned int prl_tx_phy_layer_reset_run(int port)
+{
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_message_request);
+ return 0;
+}
+
+static unsigned int prl_tx_wait_for_message_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_wait_for_message_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_wait_for_message_request_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_WAIT_FOR_MESSAGE_REQUEST;
+
+ /* Reset RetryCounter */
+ prl_tx[port].retry_counter = 0;
+
+ return 0;
+}
+
+static unsigned int prl_tx_wait_for_message_request_run(int port)
+{
+ if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) {
+ prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT;
+ /*
+ * Soft Reset Message Message pending
+ */
+ if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) &&
+ (emsg[port].len == 0)) {
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_layer_reset_for_transmit);
+ }
+ /*
+ * Message pending (except Soft Reset)
+ */
+ else {
+ /* NOTE: PRL_TX_Construct_Message State embedded here */
+ prl_tx_construct_message(port);
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_phy_response);
+ }
+
+ return 0;
+ } else if ((pdmsg[port].rev == PD_REV30) &&
+ (prl_tx[port].flags &
+ (PRL_FLAGS_START_AMS | PRL_FLAGS_END_AMS))) {
+ if (tc_get_power_role(port) == PD_ROLE_SOURCE) {
+ /*
+ * Start of AMS notification received from
+ * Policy Engine
+ */
+ if (prl_tx[port].flags & PRL_FLAGS_START_AMS) {
+ prl_tx[port].flags &= ~PRL_FLAGS_START_AMS;
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_src_source_tx);
+ return 0;
+ }
+ /*
+ * End of AMS notification received from
+ * Policy Engine
+ */
+ else if (prl_tx[port].flags & PRL_FLAGS_END_AMS) {
+ prl_tx[port].flags &= ~PRL_FLAGS_END_AMS;
+ /* Set Rp = SinkTxOk */
+ tcpm_select_rp_value(port, SINK_TX_OK);
+ tcpm_set_cc(port, TYPEC_CC_RP);
+ prl_tx[port].retry_counter = 0;
+ prl_tx[port].flags = 0;
+ }
+ } else {
+ if (prl_tx[port].flags & PRL_FLAGS_START_AMS) {
+ prl_tx[port].flags &= ~PRL_FLAGS_START_AMS;
+ /*
+ * First Message in AMS notification
+ * received from Policy Engine.
+ */
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_snk_start_ams);
+ return 0;
+ }
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+static void increment_msgid_counter(int port)
+{
+ prl_tx[port].msg_id_counter[prl_tx[port].sop] =
+ (prl_tx[port].msg_id_counter[prl_tx[port].sop] + 1) &
+ PD_MESSAGE_ID_COUNT;
+}
+
+/*
+ * PrlTxDiscard
+ */
+static unsigned int prl_tx_discard_message(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_discard_message_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_discard_message_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_DISCARD_MESSAGE;
+
+ /* Increment msgidCounter */
+ increment_msgid_counter(port);
+ set_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset);
+
+ return 0;
+}
+
+static unsigned int prl_tx_discard_message_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/*
+ * PrlTxSrcSourceTx
+ */
+static unsigned int prl_tx_src_source_tx(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_src_source_tx_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_src_source_tx_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_SRC_SOURCE_TX;
+
+ /* Set Rp = SinkTxNG */
+ tcpm_select_rp_value(port, SINK_TX_NG);
+ tcpm_set_cc(port, TYPEC_CC_RP);
+
+ return 0;
+}
+
+static unsigned int prl_tx_src_source_tx_run(int port)
+{
+ if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) {
+ prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT;
+
+ set_state(port, PRL_TX_OBJ(port), prl_tx_src_pending);
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * PrlTxSnkStartAms
+ */
+static unsigned int prl_tx_snk_start_ams(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_snk_start_ams_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_snk_start_ams_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_SNK_START_OF_AMS;
+ return 0;
+}
+
+static unsigned int prl_tx_snk_start_ams_run(int port)
+{
+ if (prl_tx[port].flags & PRL_FLAGS_MSG_XMIT) {
+ prl_tx[port].flags &= ~PRL_FLAGS_MSG_XMIT;
+
+ set_state(port, PRL_TX_OBJ(port), prl_tx_snk_pending);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * PrlTxLayerResetForTransmit
+ */
+static unsigned int prl_tx_layer_reset_for_transmit(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_layer_reset_for_transmit_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_layer_reset_for_transmit_entry(int port)
+{
+ int i;
+
+ prl_tx[port].state_id = PRL_TX_LAYER_RESET_FOR_TRANSMIT;
+
+ /* Reset MessageIdCounters */
+ for (i = 0; i < NUM_XMIT_TYPES; i++)
+ prl_tx[port].msg_id_counter[i] = 0;
+
+ return 0;
+}
+
+static unsigned int prl_tx_layer_reset_for_transmit_run(int port)
+{
+ /* NOTE: PRL_Tx_Construct_Message State embedded here */
+ prl_tx_construct_message(port);
+ set_state(port, PRL_TX_OBJ(port), prl_tx_wait_for_phy_response);
+
+ return 0;
+}
+
+static void prl_tx_construct_message(int port)
+{
+ uint32_t header = PD_HEADER(
+ pdmsg[port].msg_type,
+ tc_get_power_role(port),
+ tc_get_data_role(port),
+ prl_tx[port].msg_id_counter[pdmsg[port].xmit_type],
+ pdmsg[port].data_objs,
+ pdmsg[port].rev,
+ pdmsg[port].ext);
+
+ /* Save SOP* so the correct msg_id_counter can be incremented */
+ prl_tx[port].sop = pdmsg[port].xmit_type;
+
+ /* Pass message to PHY Layer */
+ tcpm_transmit(port, pdmsg[port].xmit_type, header,
+ pdmsg[port].chk_buf);
+}
+
+/*
+ * PrlTxWaitForPhyResponse
+ */
+static unsigned int prl_tx_wait_for_phy_response(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_wait_for_phy_response_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_wait_for_phy_response_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_WAIT_FOR_PHY_RESPONSE;
+
+ prl_tx[port].tcpc_tx_timeout = get_time().val + PD_T_TCPC_TX_TIMEOUT;
+ return 0;
+}
+
+static unsigned int prl_tx_wait_for_phy_response_run(int port)
+{
+ /* Wait until TX is complete */
+
+ /*
+ * NOTE: The TCPC will set xmit_status to TCPC_TX_COMPLETE_DISCARDED
+ * when a GoodCRC containing an incorrect MessageID is received.
+ * This condition satifies the PRL_Tx_Match_MessageID state
+ * requirement.
+ */
+
+ if (get_time().val > prl_tx[port].tcpc_tx_timeout ||
+ prl_tx[port].xmit_status == TCPC_TX_COMPLETE_FAILED ||
+ prl_tx[port].xmit_status == TCPC_TX_COMPLETE_DISCARDED) {
+
+ /* NOTE: PRL_Tx_Check_RetryCounter State embedded here. */
+
+ /* Increment check RetryCounter */
+ prl_tx[port].retry_counter++;
+
+ /*
+ * (RetryCounter > nRetryCount) | Large Extended Message
+ */
+ if (prl_tx[port].retry_counter > N_RETRY_COUNT ||
+ (pdmsg[port].ext &&
+ PD_EXT_HEADER_DATA_SIZE(GET_EXT_HEADER(
+ pdmsg[port].chk_buf[0]) > 26))) {
+
+ /*
+ * NOTE: PRL_Tx_Transmission_Error State embedded
+ * here.
+ */
+
+ /*
+ * State tch_wait_for_transmission_complete will
+ * inform policy engine of error
+ */
+ pdmsg[port].status_flags |= PRL_FLAGS_TX_ERROR;
+
+ /* Increment message id counter */
+ increment_msgid_counter(port);
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_message_request);
+ return 0;
+ }
+
+ /* Try to resend the message. */
+ /* NOTE: PRL_TX_Construct_Message State embedded here. */
+ prl_tx_construct_message(port);
+ return 0;
+ }
+
+ if (prl_tx[port].xmit_status == TCPC_TX_COMPLETE_SUCCESS) {
+
+ /* NOTE: PRL_TX_Message_Sent State embedded here. */
+
+ /* Increment messageId counter */
+ increment_msgid_counter(port);
+ /* Inform Policy Engine Message was sent */
+ pdmsg[port].status_flags |= PRL_FLAGS_TX_COMPLETE;
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_message_request);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int prl_tx_wait_for_phy_response_exit(int port)
+{
+ prl_tx[port].xmit_status = TCPC_TX_UNSET;
+ return 0;
+}
+
+/* Source Protocol Layer Message Transmission */
+/*
+ * PrlTxSrcPending
+ */
+static unsigned int prl_tx_src_pending(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_src_pending_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_src_pending_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_SRC_PENDING;
+
+ /* Start SinkTxTimer */
+ prl_tx[port].sink_tx_timer = get_time().val + PD_T_SINK_TX;
+
+ return 0;
+}
+
+static unsigned int prl_tx_src_pending_run(int port)
+{
+
+ if (get_time().val > prl_tx[port].sink_tx_timer) {
+ /*
+ * Soft Reset Message pending &
+ * SinkTxTimer timeout
+ */
+ if ((emsg[port].len == 0) &&
+ (pdmsg[port].msg_type == PD_CTRL_SOFT_RESET)) {
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_layer_reset_for_transmit);
+ }
+ /* Message pending (except Soft Reset) &
+ * SinkTxTimer timeout
+ */
+ else {
+ prl_tx_construct_message(port);
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_phy_response);
+ }
+
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * PrlTxSnkPending
+ */
+static unsigned int prl_tx_snk_pending(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_tx_snk_pending_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_tx_snk_pending_entry(int port)
+{
+ prl_tx[port].state_id = PRL_TX_SNK_PENDING;
+ return 0;
+}
+
+static unsigned int prl_tx_snk_pending_run(int port)
+{
+ int cc1;
+ int cc2;
+
+ tcpm_get_cc(port, &cc1, &cc2);
+ if (cc1 == TYPEC_CC_VOLT_RP_3_0 || cc2 == TYPEC_CC_VOLT_RP_3_0) {
+ /*
+ * Soft Reset Message Message pending &
+ * Rp = SinkTxOk
+ */
+ if ((pdmsg[port].msg_type == PD_CTRL_SOFT_RESET) &&
+ (emsg[port].len == 0)) {
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_layer_reset_for_transmit);
+ }
+ /*
+ * Message pending (except Soft Reset) &
+ * Rp = SinkTxOk
+ */
+ else {
+ prl_tx_construct_message(port);
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_phy_response);
+ }
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/* Hard Reset Operation */
+
+static unsigned int prl_hr_wait_for_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_hr_wait_for_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_hr_wait_for_request_entry(int port)
+{
+ prl_hr[port].state_id = PRL_HR_WAIT_FOR_REQUEST;
+
+ prl_hr[port].flags = 0;
+ return 0;
+}
+
+static unsigned int prl_hr_wait_for_request_run(int port)
+{
+ if (prl_hr[port].flags & PRL_FLAGS_PE_HARD_RESET ||
+ prl_hr[port].flags & PRL_FLAGS_PORT_PARTNER_HARD_RESET) {
+ set_state(port, PRL_HR_OBJ(port), prl_hr_reset_layer);
+ }
+
+ return 0;
+}
+
+/*
+ * PrlHrResetLayer
+ */
+static unsigned int prl_hr_reset_layer(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_hr_reset_layer_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_hr_reset_layer_entry(int port)
+{
+ int i;
+
+ prl_hr[port].state_id = PRL_HR_RESET_LAYER;
+
+ /* reset messageIDCounters */
+ for (i = 0; i < NUM_XMIT_TYPES; i++)
+ prl_tx[port].msg_id_counter[i] = 0;
+ /*
+ * Protocol Layer message transmission transitions to
+ * PRL_Tx_Wait_For_Message_Request state.
+ */
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_wait_for_message_request);
+
+ return 0;
+}
+
+static unsigned int prl_hr_reset_layer_run(int port)
+{
+ /*
+ * Protocol Layer reset Complete &
+ * Hard Reset was initiated by Policy Engine
+ */
+ if (prl_hr[port].flags & PRL_FLAGS_PE_HARD_RESET) {
+ /* Request PHY to perform a Hard Reset */
+ prl_send_ctrl_msg(port, TCPC_TX_HARD_RESET, 0);
+ set_state(port, PRL_HR_OBJ(port),
+ prl_hr_wait_for_phy_hard_reset_complete);
+ }
+ /*
+ * Protocol Layer reset complete &
+ * Hard Reset was initiated by Port Partner
+ */
+ else {
+ /* Inform Policy Engine of the Hard Reset */
+ pe_got_hard_reset(port);
+ set_state(port, PRL_HR_OBJ(port),
+ prl_hr_wait_for_pe_hard_reset_complete);
+ }
+
+ return 0;
+}
+
+/*
+ * PrlHrWaitForPhyHardResetComplete
+ */
+static unsigned int
+ prl_hr_wait_for_phy_hard_reset_complete(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_hr_wait_for_phy_hard_reset_complete_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_hr_wait_for_phy_hard_reset_complete_entry(int port)
+{
+ prl_hr[port].state_id = PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE;
+
+ /* Start HardResetCompleteTimer */
+ prl_hr[port].hard_reset_complete_timer =
+ get_time().val + PD_T_PS_HARD_RESET;
+
+ return 0;
+}
+
+static unsigned int prl_hr_wait_for_phy_hard_reset_complete_run(int port)
+{
+ /*
+ * Wait for hard reset from PHY
+ * or timeout
+ */
+ if ((pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) ||
+ (get_time().val > prl_hr[port].hard_reset_complete_timer)) {
+ /* PRL_HR_PHY_Hard_Reset_Requested */
+
+ /* Inform Policy Engine Hard Reset was sent */
+ pe_hard_reset_sent(port);
+ set_state(port, PRL_HR_OBJ(port),
+ prl_hr_wait_for_pe_hard_reset_complete);
+
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * PrlHrWaitForPeHardResetComplete
+ */
+static unsigned int
+ prl_hr_wait_for_pe_hard_reset_complete(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*prl_hr_wait_for_pe_hard_reset_complete_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_entry(int port)
+{
+ prl_hr[port].state_id = PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE;
+ return 0;
+}
+
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_run(int port)
+{
+ /*
+ * Wait for Hard Reset complete indication from Policy Engine
+ */
+ if (prl_hr[port].flags & PRL_FLAGS_HARD_RESET_COMPLETE)
+ set_state(port, PRL_HR_OBJ(port), prl_hr_wait_for_request);
+
+ return RUN_SUPER;
+}
+
+static unsigned int prl_hr_wait_for_pe_hard_reset_complete_exit(int port)
+{
+ /* Exit from Hard Reset */
+
+ set_state(port, PRL_TX_OBJ(port), prl_tx_phy_layer_reset);
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ set_state(port, TCH_OBJ(port), tch_wait_for_message_request_from_pe);
+
+ return 0;
+}
+
+static void copy_chunk_to_ext(int port)
+{
+ /* Calculate number of bytes */
+ pdmsg[port].num_bytes_received = (PD_HEADER_CNT(emsg[port].header) * 4);
+
+ /* Copy chunk into extended message */
+ memcpy((uint8_t *)emsg[port].buf, (uint8_t *)pdmsg[port].chk_buf,
+ pdmsg[port].num_bytes_received);
+
+ /* Set extended message length */
+ emsg[port].len = pdmsg[port].num_bytes_received;
+}
+
+/*
+ * Chunked Rx State Machine
+ */
+static unsigned int
+ rch_wait_for_message_from_protocol_layer(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_wait_for_message_from_protocol_layer_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static inline void rch_clear_abort_set_chunking(int port)
+{
+ /* Clear Abort flag */
+ pdmsg[port].status_flags &= ~PRL_FLAGS_ABORT;
+
+ /* All Messages are chunked */
+ rch[port].flags = PRL_FLAGS_CHUNKING;
+}
+
+static unsigned int rch_wait_for_message_from_protocol_layer_entry(int port)
+{
+ rch[port].state_id = RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER;
+ rch_clear_abort_set_chunking(port);
+ return 0;
+}
+
+static unsigned int rch_wait_for_message_from_protocol_layer_run(int port)
+{
+ if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ /*
+ * Are we communicating with a PD3.0 device and is
+ * this an extended message?
+ */
+ if (pdmsg[port].rev == PD_REV30 &&
+ PD_HEADER_EXT(emsg[port].header)) {
+ uint16_t exhdr = GET_EXT_HEADER(*pdmsg[port].chk_buf);
+ uint8_t chunked = PD_EXT_HEADER_CHUNKED(exhdr);
+
+ /*
+ * Received Extended Message &
+ * (Chunking = 1 & Chunked = 1)
+ */
+ if ((rch[port].flags & PRL_FLAGS_CHUNKING) &&
+ chunked) {
+ set_state(port, RCH_OBJ(port),
+ rch_processing_extended_message);
+ return 0;
+ }
+ /*
+ * (Received Extended Message &
+ * (Chunking = 0 & Chunked = 0))
+ */
+ else if (!(rch[port].flags &
+ PRL_FLAGS_CHUNKING) && !chunked) {
+ /* Copy chunk to extended buffer */
+ copy_chunk_to_ext(port);
+ /* Pass Message to Policy Engine */
+ pe_pass_up_message(port);
+ /* Clear Abort flag and set Chunking */
+ rch_clear_abort_set_chunking(port);
+ }
+ /*
+ * Chunked != Chunking
+ */
+ else {
+ set_state(port, RCH_OBJ(port),
+ rch_report_error);
+ return 0;
+ }
+ }
+ /*
+ * Received Non-Extended Message
+ */
+ else if (!PD_HEADER_EXT(emsg[port].header)) {
+ /* Copy chunk to extended buffer */
+ copy_chunk_to_ext(port);
+ /* Pass Message to Policy Engine */
+ pe_pass_up_message(port);
+ /* Clear Abort flag and set Chunking */
+ rch_clear_abort_set_chunking(port);
+ }
+ /*
+ * Received an Extended Message while communicating at a
+ * revision lower than PD3.0
+ */
+ else {
+ set_state(port, RCH_OBJ(port),
+ rch_report_error);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * RchProcessingExtendedMessage
+ */
+static unsigned int rch_processing_extended_message(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_processing_extended_message_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int rch_processing_extended_message_entry(int port)
+{
+ uint32_t header = emsg[port].header;
+ uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]);
+ uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr);
+
+ rch[port].state_id = RCH_PROCESSING_EXTENDED_MESSAGE;
+
+ /*
+ * If first chunk:
+ * Set Chunk_number_expected = 0 and
+ * Num_Bytes_Received = 0
+ */
+ if (chunk_num == 0) {
+ pdmsg[port].chunk_number_expected = 0;
+ pdmsg[port].num_bytes_received = 0;
+ pdmsg[port].msg_type = PD_HEADER_TYPE(header);
+ }
+
+ return 0;
+}
+
+static unsigned int rch_processing_extended_message_run(int port)
+{
+ uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]);
+ uint8_t chunk_num = PD_EXT_HEADER_CHUNK_NUM(exhdr);
+ uint32_t data_size = PD_EXT_HEADER_DATA_SIZE(exhdr);
+ uint32_t byte_num;
+
+ /*
+ * Abort Flag Set
+ */
+ if (pdmsg[port].status_flags & PRL_FLAGS_ABORT) {
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ }
+ /*
+ * If expected Chunk Number:
+ * Append data to Extended_Message_Buffer
+ * Increment Chunk_number_Expected
+ * Adjust Num Bytes Received
+ */
+ else if (chunk_num == pdmsg[port].chunk_number_expected) {
+ byte_num = data_size - pdmsg[port].num_bytes_received;
+
+ if (byte_num > 25)
+ byte_num = 26;
+
+ /* Make sure extended message buffer does not overflow */
+ if (pdmsg[port].num_bytes_received +
+ byte_num > EXTENDED_BUFFER_SIZE) {
+ set_state(port, RCH_OBJ(port), rch_report_error);
+ return 0;
+ }
+
+ /* Append data */
+ /* Add 2 to chk_buf to skip over extended message header */
+ memcpy(((uint8_t *)emsg[port].buf +
+ pdmsg[port].num_bytes_received),
+ (uint8_t *)pdmsg[port].chk_buf + 2, byte_num);
+ /* increment chunk number expected */
+ pdmsg[port].chunk_number_expected++;
+ /* adjust num bytes received */
+ pdmsg[port].num_bytes_received += byte_num;
+
+ /* Was that the last chunk? */
+ if (pdmsg[port].num_bytes_received >= data_size) {
+ emsg[port].len = pdmsg[port].num_bytes_received;
+ /* Pass Message to Policy Engine */
+ pe_pass_up_message(port);
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ }
+ /*
+ * Message not Complete
+ */
+ else
+ set_state(port, RCH_OBJ(port), rch_requesting_chunk);
+ }
+ /*
+ * Unexpected Chunk Number
+ */
+ else
+ set_state(port, RCH_OBJ(port), rch_report_error);
+
+ return 0;
+}
+
+/*
+ * RchRequestingChunk
+ */
+static unsigned int rch_requesting_chunk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_requesting_chunk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int rch_requesting_chunk_entry(int port)
+{
+ rch[port].state_id = RCH_REQUESTING_CHUNK;
+
+ /*
+ * Send Chunk Request to Protocol Layer
+ * with chunk number = Chunk_Number_Expected
+ */
+ pdmsg[port].chk_buf[0] = PD_EXT_HEADER(
+ pdmsg[port].chunk_number_expected,
+ 1, /* Request Chunk */
+ 0 /* Data Size */
+ );
+
+ pdmsg[port].data_objs = 1;
+ pdmsg[port].ext = 1;
+ prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_TX, 0);
+
+ return 0;
+}
+
+static unsigned int rch_requesting_chunk_run(int port)
+{
+ /*
+ * Transmission Error from Protocol Layer or
+ * Message Received From Protocol Layer
+ */
+ if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED ||
+ pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) {
+ /*
+ * Leave PRL_FLAGS_MSG_RECEIVED flag set. It'll be
+ * cleared in rch_report_error state
+ */
+ set_state(port, RCH_OBJ(port), rch_report_error);
+ }
+ /*
+ * Message Transmitted received from Protocol Layer
+ */
+ else if (pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) {
+ pdmsg[port].status_flags &= ~PRL_FLAGS_TX_COMPLETE;
+ set_state(port, RCH_OBJ(port), rch_waiting_chunk);
+ } else
+ return RUN_SUPER;
+
+ return 0;
+}
+
+/*
+ * RchWaitingChunk
+ */
+static unsigned int rch_waiting_chunk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_waiting_chunk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int rch_waiting_chunk_entry(int port)
+{
+ rch[port].state_id = RCH_WAITING_CHUNK;
+
+ /*
+ * Start ChunkSenderResponseTimer
+ */
+ rch[port].chunk_sender_response_timer =
+ get_time().val + PD_T_CHUNK_SENDER_RESPONSE;
+
+ return 0;
+}
+
+static unsigned int rch_waiting_chunk_run(int port)
+{
+ if ((rch[port].flags & PRL_FLAGS_MSG_RECEIVED)) {
+ /*
+ * Leave PRL_FLAGS_MSG_RECEIVED flag set just in case an error
+ * is detected. If an error is detected, PRL_FLAGS_MSG_RECEIVED
+ * will be cleared in rch_report_error state.
+ */
+
+ if (PD_HEADER_EXT(emsg[port].header)) {
+ uint16_t exhdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]);
+ /*
+ * Other Message Received from Protocol Layer
+ */
+ if (PD_EXT_HEADER_REQ_CHUNK(exhdr) ||
+ !PD_EXT_HEADER_CHUNKED(exhdr)) {
+ set_state(port, RCH_OBJ(port),
+ rch_report_error);
+ }
+ /*
+ * Chunk response Received from Protocol Layer
+ */
+ else {
+ /*
+ * No error wad detected, so clear
+ * PRL_FLAGS_MSG_RECEIVED flag.
+ */
+ rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, RCH_OBJ(port),
+ rch_processing_extended_message);
+ }
+
+ return 0;
+ }
+ }
+ /*
+ * ChunkSenderResponseTimer Timeout
+ */
+ else if (get_time().val > rch[port].chunk_sender_response_timer) {
+ set_state(port, RCH_OBJ(port), rch_report_error);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * RchReportError
+ */
+static unsigned int rch_report_error(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*rch_report_error_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int rch_report_error_entry(int port)
+{
+ rch[port].state_id = RCH_REPORT_ERROR;
+
+ /*
+ * If the state was entered because a message was received,
+ * this message is passed to the Policy Engine.
+ */
+ if (rch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ rch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+
+ /* Copy chunk to extended buffer */
+ copy_chunk_to_ext(port);
+ /* Pass Message to Policy Engine */
+ pe_pass_up_message(port);
+ /* Report error */
+ pe_report_error(port, ERR_RCH_MSG_REC);
+ } else {
+ /* Report error */
+ pe_report_error(port, ERR_RCH_CHUNKED);
+ }
+
+ return 0;
+}
+
+static unsigned int rch_report_error_run(int port)
+{
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+
+ return 0;
+}
+
+/*
+ * Chunked Tx State Machine
+ */
+static unsigned int
+ tch_wait_for_message_request_from_pe(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_wait_for_message_request_from_pe_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static inline void tch_clear_abort_set_chunking(int port)
+{
+ /* Clear Abort flag */
+ pdmsg[port].status_flags &= ~PRL_FLAGS_ABORT;
+
+ /* All Messages are chunked */
+ tch[port].flags = PRL_FLAGS_CHUNKING;
+}
+
+static unsigned int tch_wait_for_message_request_from_pe_entry(int port)
+{
+ tch[port].state_id = TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE;
+ tch_clear_abort_set_chunking(port);
+ return 0;
+}
+
+static unsigned int tch_wait_for_message_request_from_pe_run(int port)
+{
+ /*
+ * Any message received and not in state TCH_Wait_Chunk_Request
+ */
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ return 0;
+ } else if (tch[port].flags & PRL_FLAGS_MSG_XMIT) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_XMIT;
+ /*
+ * Rx Chunking State != RCH_Wait_For_Message_From_Protocol_Layer
+ * & Abort Supported
+ *
+ * Discard the Message
+ */
+ if (rch[port].state_id !=
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER) {
+ /* Report Error To Policy Engine */
+ pe_report_error(port, ERR_TCH_XMIT);
+ tch_clear_abort_set_chunking(port);
+ } else {
+ /*
+ * Extended Message Request & Chunking
+ */
+ if ((pdmsg[port].rev == PD_REV30) && pdmsg[port].ext &&
+ (tch[port].flags & PRL_FLAGS_CHUNKING)) {
+ pdmsg[port].send_offset = 0;
+ pdmsg[port].chunk_number_to_send = 0;
+ set_state(port, TCH_OBJ(port),
+ tch_construct_chunked_message);
+ } else
+ /*
+ * Non-Extended Message Request
+ */
+ {
+ /* Make sure buffer doesn't overflow */
+ if (emsg[port].len > BUFFER_SIZE) {
+ /* Report Error To Policy Engine */
+ pe_report_error(port, ERR_TCH_XMIT);
+ tch_clear_abort_set_chunking(port);
+ return 0;
+ }
+
+ /* Copy message to chunked buffer */
+ memset((uint8_t *)pdmsg[port].chk_buf,
+ 0, BUFFER_SIZE);
+ memcpy((uint8_t *)pdmsg[port].chk_buf,
+ (uint8_t *)emsg[port].buf,
+ emsg[port].len);
+ /*
+ * Pad length to 4-byte boundery and
+ * convert to number of 32-bit objects.
+ * Since the value is shifted right by 2,
+ * no need to explicitly clear the lower
+ * 2-bits.
+ */
+ pdmsg[port].data_objs =
+ (emsg[port].len + 3) >> 2;
+ /* Pass Message to Protocol Layer */
+ prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT;
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_transmission_complete);
+ }
+
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+/*
+ * TchWaitForTransmissionComplete
+ */
+static unsigned int
+ tch_wait_for_transmission_complete(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_wait_for_transmission_complete_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_wait_for_transmission_complete_entry(int port)
+{
+ tch[port].state_id = TCH_WAIT_FOR_TRANSMISSION_COMPLETE;
+ return 0;
+}
+
+static unsigned int tch_wait_for_transmission_complete_run(int port)
+{
+ /*
+ * Any message received and not in state TCH_Wait_Chunk_Request
+ */
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ return 0;
+ }
+
+ /*
+ * Inform Policy Engine that Message was sent.
+ */
+ if (pdmsg[port].status_flags & PRL_FLAGS_TX_COMPLETE) {
+ pdmsg[port].status_flags &= ~PRL_FLAGS_TX_COMPLETE;
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+
+ /* Tell PE message was sent */
+ pe_message_sent(port);
+ }
+ /*
+ * Inform Policy Engine of Tx Error
+ */
+ else if (pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) {
+ pdmsg[port].status_flags &= ~PRL_FLAGS_TX_ERROR;
+ /* Tell PE an error occurred */
+ pe_report_error(port, ERR_TCH_XMIT);
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ }
+
+ return 0;
+}
+
+/*
+ * TchConstructChunkedMessage
+ */
+static unsigned int tch_construct_chunked_message(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_construct_chunked_message_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_construct_chunked_message_entry(int port)
+{
+ uint16_t *ext_hdr;
+ uint8_t *data;
+ uint16_t num;
+
+ tch[port].state_id = TCH_CONSTRUCT_CHUNKED_MESSAGE;
+
+ /*
+ * Any message received and not in state TCH_Wait_Chunk_Request
+ */
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ return 0;
+ }
+ /* Prepare to copy chunk into chk_buf */
+
+ ext_hdr = (uint16_t *)pdmsg[port].chk_buf;
+ data = ((uint8_t *)pdmsg[port].chk_buf + 2);
+ num = emsg[port].len - pdmsg[port].send_offset;
+
+ if (num > 26)
+ num = 26;
+
+ /* Set the chunks extended header */
+ *ext_hdr = PD_EXT_HEADER(pdmsg[port].chunk_number_to_send,
+ 0, /* Chunk Request */
+ emsg[port].len);
+
+ /* Copy the message chunk into chk_buf */
+ memset(data, 0, 28);
+ memcpy(data, emsg[port].buf + pdmsg[port].send_offset, num);
+ pdmsg[port].send_offset += num;
+
+ /*
+ * Add in 2 bytes for extended header
+ * pad out to 4-byte boundary
+ * convert to number of 4-byte words
+ * Since the value is shifted right by 2,
+ * no need to explicitly clear the lower
+ * 2-bits.
+ */
+ pdmsg[port].data_objs = (num + 2 + 3) >> 2;
+
+ /* Pass message chunk to Protocol Layer */
+ prl_tx[port].flags |= PRL_FLAGS_MSG_XMIT;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+
+ return 0;
+}
+
+static unsigned int tch_construct_chunked_message_run(int port)
+{
+ if (pdmsg[port].status_flags & PRL_FLAGS_ABORT)
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ else
+ set_state(port, TCH_OBJ(port),
+ tch_sending_chunked_message);
+ return 0;
+}
+
+/*
+ * TchSendingChunkedMessage
+ */
+static unsigned int tch_sending_chunked_message(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_sending_chunked_message_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_sending_chunked_message_entry(int port)
+{
+ tch[port].state_id = TCH_SENDING_CHUNKED_MESSAGE;
+ return 0;
+}
+
+static unsigned int tch_sending_chunked_message_run(int port)
+{
+ /*
+ * Any message received and not in state TCH_Wait_Chunk_Request
+ */
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ return 0;
+ }
+
+ /*
+ * Transmission Error
+ */
+ if (pdmsg[port].status_flags & PRL_FLAGS_TX_ERROR) {
+ pe_report_error(port, ERR_TCH_XMIT);
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ }
+ /*
+ * Message Transmitted from Protocol Layer &
+ * Last Chunk
+ */
+ else if (emsg[port].len == pdmsg[port].send_offset) {
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+
+ /* Tell PE message was sent */
+ pe_message_sent(port);
+ }
+ /*
+ * Message Transmitted from Protocol Layer &
+ * Not Last Chunk
+ */
+ else
+ set_state(port, TCH_OBJ(port), tch_wait_chunk_request);
+
+ return 0;
+}
+
+/*
+ * TchWaitChunkRequest
+ */
+static unsigned int tch_wait_chunk_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_wait_chunk_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_wait_chunk_request_entry(int port)
+{
+ tch[port].state_id = TCH_WAIT_CHUNK_REQUEST;
+
+ /* Increment Chunk Number to Send */
+ pdmsg[port].chunk_number_to_send++;
+ /* Start Chunk Sender Request Timer */
+ tch[port].chunk_sender_request_timer =
+ get_time().val + PD_T_CHUNK_SENDER_REQUEST;
+ return 0;
+}
+
+static unsigned int tch_wait_chunk_request_run(int port)
+{
+ if (tch[port].flags & PRL_FLAGS_MSG_RECEIVED) {
+ tch[port].flags &= ~PRL_FLAGS_MSG_RECEIVED;
+
+ if (PD_HEADER_EXT(emsg[port].header)) {
+ uint16_t exthdr;
+
+ exthdr = GET_EXT_HEADER(pdmsg[port].chk_buf[0]);
+ if (PD_EXT_HEADER_REQ_CHUNK(exthdr)) {
+ /*
+ * Chunk Request Received &
+ * Chunk Number = Chunk Number to Send
+ */
+ if (PD_EXT_HEADER_CHUNK_NUM(exthdr) ==
+ pdmsg[port].chunk_number_to_send) {
+ set_state(port, TCH_OBJ(port),
+ tch_construct_chunked_message);
+ }
+ /*
+ * Chunk Request Received &
+ * Chunk Number != Chunk Number to Send
+ */
+ else {
+ pe_report_error(port, ERR_TCH_CHUNKED);
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ }
+ return 0;
+ }
+ }
+
+ /*
+ * Other message received
+ */
+ set_state(port, TCH_OBJ(port), tch_message_received);
+ }
+ /*
+ * ChunkSenderRequestTimer timeout
+ */
+ else if (get_time().val >=
+ tch[port].chunk_sender_request_timer) {
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+
+ /* Tell PE message was sent */
+ pe_message_sent(port);
+ }
+
+ return 0;
+}
+
+/*
+ * TchMessageReceived
+ */
+static unsigned int tch_message_received(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tch_message_received_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tch_message_received_entry(int port)
+{
+ tch[port].state_id = TCH_MESSAGE_RECEIVED;
+
+ /* Pass message to chunked Rx */
+ rch[port].flags |= PRL_FLAGS_MSG_RECEIVED;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+
+ return 0;
+}
+
+static unsigned int tch_message_received_run(int port)
+{
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+
+ return 0;
+}
+
+/*
+ * Protocol Layer Message Reception State Machine
+ */
+static unsigned int prl_rx_wait_for_phy_message(int port, int evt)
+{
+ uint32_t header;
+ uint8_t type;
+ uint8_t cnt;
+ uint8_t sop;
+ int8_t msid;
+ int ret;
+
+ /* process any potential incoming message */
+ if (tcpm_has_pending_message(port)) {
+ ret = tcpm_dequeue_message(port, pdmsg[port].chk_buf, &header);
+ if (ret == 0) {
+ emsg[port].header = header;
+ type = PD_HEADER_TYPE(header);
+ cnt = PD_HEADER_CNT(header);
+ msid = PD_HEADER_ID(header);
+ sop = PD_HEADER_GET_SOP(header);
+
+ if (cnt == 0 && type == PD_CTRL_SOFT_RESET) {
+ int i;
+
+ for (i = 0; i < NUM_XMIT_TYPES; i++) {
+ /* Clear MessageIdCounter */
+ prl_tx[port].msg_id_counter[i] = 0;
+ /* Clear stored MessageID value */
+ prl_rx[port].msg_id[i] = -1;
+ }
+
+ /* Inform Policy Engine of Soft Reset */
+ pe_got_soft_reset(port);
+
+ /* Soft Reset occurred */
+ set_state(port, PRL_TX_OBJ(port),
+ prl_tx_phy_layer_reset);
+ set_state(port, RCH_OBJ(port),
+ rch_wait_for_message_from_protocol_layer);
+ set_state(port, TCH_OBJ(port),
+ tch_wait_for_message_request_from_pe);
+ }
+
+ /*
+ * Ignore if this is a duplicate message.
+ */
+ if (prl_rx[port].msg_id[sop] != msid) {
+ /*
+ * Discard any pending tx message if this is
+ * not a ping message
+ */
+ if ((pdmsg[port].rev == PD_REV30) &&
+ (cnt == 0) && type != PD_CTRL_PING) {
+ if (prl_tx[port].state_id ==
+ PRL_TX_SRC_PENDING ||
+ prl_tx[port].state_id ==
+ PRL_TX_SNK_PENDING) {
+ set_state(port,
+ PRL_TX_OBJ(port),
+ prl_tx_discard_message);
+ }
+ }
+
+ /* Store Message Id */
+ prl_rx[port].msg_id[sop] = msid;
+
+ /* RTR Chunked Message Router States. */
+ /*
+ * Received Ping from Protocol Layer
+ */
+ if (cnt == 0 && type == PD_CTRL_PING) {
+ /* NOTE: RTR_PING State embedded
+ * here.
+ */
+ emsg[port].len = 0;
+ pe_pass_up_message(port);
+ return 0;
+ }
+ /*
+ * Message (not Ping) Received from
+ * Protocol Layer & Doing Tx Chunks
+ */
+ else if (tch[port].state_id !=
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE) {
+ /* NOTE: RTR_TX_CHUNKS State embedded
+ * here.
+ */
+ /*
+ * Send Message to Tx Chunk
+ * Chunk State Machine
+ */
+ tch[port].flags |=
+ PRL_FLAGS_MSG_RECEIVED;
+ }
+ /*
+ * Message (not Ping) Received from
+ * Protocol Layer & Not Doing Tx Chunks
+ */
+ else {
+ /*
+ * NOTE: RTR_RX_CHUNKS State embedded
+ * here.
+ */
+ /*
+ * Send Message to Rx
+ * Chunk State Machine
+ */
+ rch[port].flags |=
+ PRL_FLAGS_MSG_RECEIVED;
+ }
+
+ task_set_event(PD_PORT_TO_TASK_ID(port),
+ PD_EVENT_SM, 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int do_nothing_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
diff --git a/common/usb_sm.c b/common/usb_sm.c
new file mode 100644
index 0000000000..6d3cacdb3b
--- /dev/null
+++ b/common/usb_sm.c
@@ -0,0 +1,166 @@
+/* Copyright 2019 The Chromium OS 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 "common.h"
+#include "task.h"
+#include "usb_pd.h"
+#include "usb_sm.h"
+#include "util.h"
+#include "console.h"
+
+void init_state(int port, struct sm_obj *obj, sm_state target)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ int i;
+
+ sm_state tmp_super[CONFIG_SM_NESTING_NUM];
+#endif
+
+ obj->last_state = NULL;
+ obj->task_state = target;
+
+#if (CONFIG_SM_NESTING_NUM > 0)
+
+ /* Prepare to execute all entry actions of the target's super states */
+
+ /*
+ * Get targets super state. This will be NULL if the target
+ * has no super state
+ */
+ tmp_super[CONFIG_SM_NESTING_NUM - 1] =
+ (sm_state)(uintptr_t)target(port, SUPER_SIG);
+
+ /* Get all super states of the target */
+ for (i = CONFIG_SM_NESTING_NUM - 1; i > 0; i--) {
+ if (tmp_super[i] != NULL)
+ tmp_super[i - 1] =
+ (sm_state)(uintptr_t)tmp_super[i](port, SUPER_SIG);
+ else
+ tmp_super[i - 1] = NULL;
+ }
+
+ /* Execute all super state entry actions in forward order */
+ for (i = 0; i < CONFIG_SM_NESTING_NUM; i++)
+ if (tmp_super[i] != NULL)
+ tmp_super[i](port, ENTRY_SIG);
+#endif
+
+ /* Now execute the target entry action */
+ target(port, ENTRY_SIG);
+}
+
+int set_state(int port, struct sm_obj *obj, sm_state target)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ int i;
+ int no_execute;
+
+ sm_state tmp_super[CONFIG_SM_NESTING_NUM];
+ sm_state target_super;
+ sm_state last_super;
+ sm_state super;
+
+ /* Execute all exit actions is reverse order */
+
+ /* Get target's super state */
+ target_super = (sm_state)(uintptr_t)target(port, SUPER_SIG);
+ tmp_super[0] = obj->task_state;
+
+ do {
+ /* Execute exit action */
+ tmp_super[0](port, EXIT_SIG);
+
+ /* Get super state */
+ tmp_super[0] =
+ (sm_state)(uintptr_t)tmp_super[0](port, SUPER_SIG);
+ /*
+ * No need to execute a super state's exit action that has
+ * shared ancestry with the target.
+ */
+ super = target_super;
+ while (super != NULL) {
+ if (tmp_super[0] == super) {
+ tmp_super[0] = NULL;
+ break;
+ }
+
+ /* Get target state next super state if it exists */
+ super = (sm_state)(uintptr_t)super(port, SUPER_SIG);
+ }
+ } while (tmp_super[0] != NULL);
+
+ /* All done executing the exit actions */
+#else
+ obj->task_state(port, EXIT_SIG);
+#endif
+ /* update the state variables */
+ obj->last_state = obj->task_state;
+ obj->task_state = target;
+
+#if (CONFIG_SM_NESTING_NUM > 0)
+ /* Prepare to execute all entry actions of the target's super states */
+
+ tmp_super[CONFIG_SM_NESTING_NUM - 1] =
+ (sm_state)(uintptr_t)target(port, SUPER_SIG);
+
+ /* Get all super states of the target */
+ for (i = CONFIG_SM_NESTING_NUM - 1; i > 0; i--) {
+ if (tmp_super[i] != NULL)
+ tmp_super[i - 1] =
+ (sm_state)(uintptr_t)tmp_super[i](port, SUPER_SIG);
+ else
+ tmp_super[i - 1] = NULL;
+ }
+
+ /* Get super state of last state */
+ last_super = (sm_state)(uintptr_t)obj->last_state(port, SUPER_SIG);
+
+ /* Execute all super state entry actions in forward order */
+ for (i = 0; i < CONFIG_SM_NESTING_NUM; i++) {
+ /* No super state */
+ if (tmp_super[i] == NULL)
+ continue;
+
+ /*
+ * We only want to execute the target state's super state entry
+ * action if it doesn't share a super state with the previous
+ * state.
+ */
+ super = last_super;
+ no_execute = 0;
+ while (super != NULL) {
+ if (tmp_super[i] == super) {
+ no_execute = 1;
+ break;
+ }
+
+ /* Get last state's next super state if it exists */
+ super = (sm_state)(uintptr_t)super(port, SUPER_SIG);
+ }
+
+ /* Execute super state's entry */
+ if (!no_execute)
+ tmp_super[i](port, ENTRY_SIG);
+ }
+#endif
+
+ /* Now execute the target entry action */
+ target(port, ENTRY_SIG);
+
+ return 0;
+}
+
+void exe_state(int port, struct sm_obj *obj, enum signal sig)
+{
+#if (CONFIG_SM_NESTING_NUM > 0)
+ sm_state state = obj->task_state;
+
+ do {
+ state = (sm_state)(uintptr_t)state(port, sig);
+ } while (state != NULL);
+#else
+ obj->task_state(port, sig);
+#endif
+}
diff --git a/common/usb_tc_sm.c b/common/usb_tc_sm.c
new file mode 100644
index 0000000000..d9c7992b82
--- /dev/null
+++ b/common/usb_tc_sm.c
@@ -0,0 +1,203 @@
+/* Copyright 2019 The Chromium OS 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 "battery.h"
+#include "battery_smart.h"
+#include "board.h"
+#include "charge_manager.h"
+#include "charge_state.h"
+#include "chipset.h"
+#include "common.h"
+#include "console.h"
+#include "ec_commands.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.h"
+#include "registers.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+#include "usb_charge.h"
+#include "usb_mux.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+#include "usb_prl_sm.h"
+#include "tcpm.h"
+#include "usb_pe_sm.h"
+#include "usb_prl_sm.h"
+#include "usb_sm.h"
+#include "usb_tc_sm.h"
+#include "version.h"
+
+#ifdef CONFIG_COMMON_RUNTIME
+#define CPRINTF(format, args...) cprintf(CC_HOOK, format, ## args)
+#define CPRINTS(format, args...) cprints(CC_HOOK, format, ## args)
+#else /* CONFIG_COMMON_RUNTIME */
+#define CPRINTF(format, args...)
+#define CPRINTS(format, args...)
+#endif
+
+/* Private Function Prototypes */
+
+static inline int cc_is_rp(int cc);
+static inline enum pd_cc_polarity_type get_snk_polarity(int cc1, int cc2);
+static int tc_restart_tcpc(int port);
+static void set_polarity(int port, int polarity);
+
+#ifdef CONFIG_COMMON_RUNTIME
+static const char * const tc_state_names[] = {
+ "Disabled",
+ "Unattached.SNK",
+ "AttachWait.SNK",
+ "Attached.SNK",
+#if !defined(CONFIG_USB_TYPEC_VPD)
+ "ErrorRecovery",
+ "Unattached.SRC",
+ "AttachWait.SRC",
+ "Attached.SRC",
+#endif
+#if !defined(CONFIG_USB_TYPEC_CTVPD) && !defined(CONFIG_USB_TYPEC_VPD)
+ "AudioAccessory",
+ "OrientedDebugAccessory.SRC",
+ "UnorientedDebugAccessory.SRC",
+ "DebugAccessory.SNK",
+ "Try.SRC",
+ "TryWait.SNK",
+ "CTUnattached.SNK",
+ "CTAttached.SNK",
+#endif
+#if defined(CONFIG_USB_TYPEC_CTVPD)
+ "CTTry.SNK",
+ "CTAttached.Unsupported",
+ "CTAttachWait.Unsupported",
+ "CTUnattached.Unsupported",
+ "CTUnattached.VPD",
+ "CTAttachWait.VPD",
+ "CTAttached.VPD",
+ "CTDisabled.VPD",
+ "Try.SNK",
+ "TryWait.SRC"
+#endif
+};
+BUILD_ASSERT(ARRAY_SIZE(tc_state_names) == TC_STATE_COUNT);
+#endif
+
+/* Include USB Type-C State Machine */
+#if defined(CONFIG_USB_TYPEC_CTVPD)
+#include "usb_tc_ctvpd_sm.h"
+#elif defined(CONFIG_USB_TYPEC_VPD)
+#include "usb_tc_vpd_sm.h"
+#else
+#error "A USB Type-C State Machine must be defined."
+#endif
+
+/* Public Functions */
+
+int tc_get_power_role(int port)
+{
+ return tc[port].power_role;
+}
+
+int tc_get_data_role(int port)
+{
+ return tc[port].data_role;
+}
+
+void tc_set_timeout(int port, uint64_t timeout)
+{
+ tc[port].evt_timeout = timeout;
+}
+
+enum typec_state_id get_typec_state_id(int port)
+{
+ return tc[port].state_id;
+}
+
+/* Private Functions */
+
+/**
+ * Returns whether the sink has detected a Rp resistor on the other side.
+ */
+static inline int cc_is_rp(int cc)
+{
+ return (cc == TYPEC_CC_VOLT_RP_DEF) || (cc == TYPEC_CC_VOLT_RP_1_5) ||
+ (cc == TYPEC_CC_VOLT_RP_3_0);
+}
+
+/*
+ * CC values for regular sources and Debug sources (aka DTS)
+ *
+ * Source type Mode of Operation CC1 CC2
+ * ---------------------------------------------
+ * Regular Default USB Power RpUSB Open
+ * Regular USB-C @ 1.5 A Rp1A5 Open
+ * Regular USB-C @ 3 A Rp3A0 Open
+ * DTS Default USB Power Rp3A0 Rp1A5
+ * DTS USB-C @ 1.5 A Rp1A5 RpUSB
+ * DTS USB-C @ 3 A Rp3A0 RpUSB
+ */
+
+/**
+ * Returns the polarity of a Sink.
+ */
+static inline enum pd_cc_polarity_type get_snk_polarity(int cc1, int cc2)
+{
+ /* the following assumes:
+ * TYPEC_CC_VOLT_RP_3_0 > TYPEC_CC_VOLT_RP_1_5
+ * TYPEC_CC_VOLT_RP_1_5 > TYPEC_CC_VOLT_RP_DEF
+ * TYPEC_CC_VOLT_RP_DEF > TYPEC_CC_VOLT_OPEN
+ */
+ return (cc2 > cc1) ? POLARITY_CC2 : POLARITY_CC1;
+}
+
+static int tc_restart_tcpc(int port)
+{
+ return tcpm_init(port);
+}
+
+static void set_polarity(int port, int polarity)
+{
+ tcpm_set_polarity(port, polarity);
+#ifdef CONFIG_USBC_PPC_POLARITY
+ ppc_set_polarity(port, polarity);
+#endif /* defined(CONFIG_USBC_PPC_POLARITY) */
+}
+
+void pd_task(void *u)
+{
+ int port = TASK_ID_TO_PD_PORT(task_get_current());
+
+ tc_state_init(port);
+
+ while (1) {
+ /* wait for next event/packet or timeout expiration */
+ tc[port].evt = task_wait_event(tc[port].evt_timeout);
+
+ /* handle events that affect the state machine as a whole */
+ tc_event_check(port, tc[port].evt);
+
+#ifdef CONFIG_USB_PD_TCPC
+ /*
+ * run port controller task to check CC and/or read incoming
+ * messages
+ */
+ tcpc_run(port, tc[port].evt);
+#endif
+
+#ifdef CONFIG_USB_PE_SM
+ /* run policy engine state machine */
+ policy_engine(port, tc[port].evt, tc[port].pd_enable);
+#endif /* CONFIG_USB_PE_SM */
+
+#ifdef CONFIG_USB_PRL_SM
+ /* run protocol state machine */
+ protocol_layer(port, tc[port].evt, tc[port].pd_enable);
+#endif /* CONFIG_USB_PRL_SM */
+
+ /* run state machine */
+ exe_state(port, TC_OBJ(port), RUN_SIG);
+ }
+}
diff --git a/core/minute-ia/build.mk b/core/minute-ia/build.mk
index a6190663fe..099b9ea736 100644
--- a/core/minute-ia/build.mk
+++ b/core/minute-ia/build.mk
@@ -31,3 +31,6 @@ core-y=cpu.o init.o interrupts.o
core-$(CONFIG_COMMON_PANIC_OUTPUT)+=panic.o
core-$(CONFIG_COMMON_RUNTIME)+=switch.o task.o
core-$(CONFIG_MPU)+=mpu.o
+
+# for 64bit division
+LDFLAGS_EXTRA+=-static-libgcc -lgcc
diff --git a/core/minute-ia/ec.lds.S b/core/minute-ia/ec.lds.S
index 8087f7ce01..fc4383aec2 100644
--- a/core/minute-ia/ec.lds.S
+++ b/core/minute-ia/ec.lds.S
@@ -202,4 +202,8 @@ SECTIONS
def_irq_low = ABSOLUTE(default_int_handler) & 0xFFFF;
def_irq_high = ABSOLUTE(default_int_handler) >> 16;
+
+#ifdef CONFIG_ISH_PM_AONTASK
+ ish_persistent_data_aon = ABSOLUTE(CONFIG_AON_ROM_BASE);
+#endif
}
diff --git a/core/minute-ia/ia_structs.h b/core/minute-ia/ia_structs.h
index 08447cb242..29bbb6c005 100644
--- a/core/minute-ia/ia_structs.h
+++ b/core/minute-ia/ia_structs.h
@@ -8,7 +8,7 @@
#ifndef __ASSEMBLER__
-#include <stdint.h>
+#include "common.h"
/**
diff --git a/core/minute-ia/interrupts.c b/core/minute-ia/interrupts.c
index d41ae836c9..3ce0e2a2a4 100644
--- a/core/minute-ia/interrupts.c
+++ b/core/minute-ia/interrupts.c
@@ -13,6 +13,7 @@
#include "irq_handler.h"
#include "registers.h"
#include "task_defs.h"
+#include "task.h"
#include "util.h"
/* Console output macros */
@@ -26,19 +27,19 @@ extern struct idt_entry __idt[NUM_VECTORS];
/* To count the interrupt nesting depth. Usually it is not nested */
volatile uint32_t __in_isr;
-void write_ioapic_reg(const uint32_t reg, const uint32_t val)
+static void write_ioapic_reg(const uint32_t reg, const uint32_t val)
{
- REG32(IOAPIC_IDX) = (uint8_t)reg;
- REG32(IOAPIC_WDW) = val;
+ IOAPIC_IDX = reg;
+ IOAPIC_WDW = val;
}
-uint32_t read_ioapic_reg(const uint32_t reg)
+static uint32_t read_ioapic_reg(const uint32_t reg)
{
- REG32(IOAPIC_IDX) = (uint8_t)reg;
- return REG32(IOAPIC_WDW);
+ IOAPIC_IDX = reg;
+ return IOAPIC_WDW;
}
-void set_ioapic_redtbl_raw(const unsigned irq, const uint32_t val)
+static void set_ioapic_redtbl_raw(const uint32_t irq, const uint32_t val)
{
const uint32_t redtbl_lo = IOAPIC_IOREDTBL + 2 * irq;
const uint32_t redtbl_hi = redtbl_lo + 1;
@@ -81,10 +82,13 @@ void restore_interrupts(uint64_t irq_map)
{
int i;
+ /* Disable interrupts until everything is unmasked */
+ interrupt_disable();
for (i = 0; i < ISH_MAX_IOAPIC_IRQS; i++) {
if (((uint64_t)0x1 << i) & irq_map)
unmask_interrupt(i);
}
+ interrupt_enable();
}
/*
@@ -143,6 +147,7 @@ static const irq_desc_t system_irqs[] = {
LEVEL_INTR(ISH_HPET_TIMER0_IRQ, ISH_HPET_TIMER0_VEC),
LEVEL_INTR(ISH_HPET_TIMER1_IRQ, ISH_HPET_TIMER1_VEC),
LEVEL_INTR(ISH_DEBUG_UART_IRQ, ISH_DEBUG_UART_VEC),
+ LEVEL_INTR(ISH_FABRIC_IRQ, ISH_FABRIC_VEC),
#ifdef CONFIG_ISH_PM_RESET_PREP
LEVEL_INTR(ISH_RESET_PREP_IRQ, ISH_RESET_PREP_VEC),
#endif
@@ -168,15 +173,19 @@ static const irq_desc_t system_irqs[] = {
* and go directly to the CPU core, so get_current_interrupt_vector
* cannot be used.
*/
-#define DEFINE_EXN_HANDLER(vector) \
- void __keep exception_panic_##vector(void); \
- __attribute__ ((noreturn)) void exception_panic_##vector(void) \
- { \
- __asm__ ( \
- "push $" #vector "\n" \
- "call exception_panic\n"); \
- while (1) \
- continue; \
+#define DEFINE_EXN_HANDLER(vector) \
+ _DEFINE_EXN_HANDLER(vector, exception_panic_##vector)
+#define _DEFINE_EXN_HANDLER(vector, name) \
+ __DEFINE_EXN_HANDLER(vector, name)
+#define __DEFINE_EXN_HANDLER(vector, name) \
+ void __keep name(void); \
+ __attribute__ ((noreturn)) void name(void) \
+ { \
+ __asm__ ( \
+ "push $" #vector "\n" \
+ "call exception_panic\n"); \
+ while (1) \
+ continue; \
}
DEFINE_EXN_HANDLER(0);
@@ -199,6 +208,7 @@ DEFINE_EXN_HANDLER(17);
DEFINE_EXN_HANDLER(18);
DEFINE_EXN_HANDLER(19);
DEFINE_EXN_HANDLER(20);
+_DEFINE_EXN_HANDLER(ISH_WDT_VEC, exception_panic_wdt);
void set_interrupt_gate(uint8_t num, isr_handler_t func, uint8_t flags)
{
@@ -227,11 +237,11 @@ uint32_t get_current_interrupt_vector(void)
uint32_t vec;
/* In service register */
- uint32_t *ioapic_icr_last = (uint32_t *)LAPIC_ISR_REG;
+ volatile uint32_t *ioapic_isr_last = &LAPIC_ISR_LAST_REG;
/* Scan ISRs from highest priority */
- for (i = 7; i >= 0; i--, ioapic_icr_last -= 4) {
- vec = *ioapic_icr_last;
+ for (i = 7; i >= 0; i--, ioapic_isr_last -= 4) {
+ vec = *ioapic_isr_last;
if (vec) {
return (32 * i) + __fls(vec);
}
@@ -271,12 +281,17 @@ DECLARE_DEFERRED(print_lpaic_lvt_error);
* #define VEC_POS(v) ((v) & (32 - 1))
* #define REG_POS(v) (((v) >> 5) << 4)
*/
-static inline unsigned int lapic_get_vector(uint32_t reg_base, uint32_t vector)
+static inline unsigned int lapic_get_vector(volatile uint32_t *reg_base,
+ uint32_t vector)
{
- uint32_t reg_pos = (vector >> 5) << 4;
+ /*
+ * Since we are using array indexing, we need to divide the vec_pos by
+ * sizeof(uint32_t), i.e. shift to the right 2.
+ */
+ uint32_t reg_pos = (vector >> 5) << 2;
uint32_t vec_pos = vector & (32 - 1);
- return REG32(reg_base + reg_pos) & BIT(vec_pos);
+ return reg_base[reg_pos] & BIT(vec_pos);
}
/*
@@ -297,12 +312,12 @@ static inline unsigned int lapic_get_vector(uint32_t reg_base, uint32_t vector)
*/
void handle_lapic_lvt_error(void)
{
- uint32_t esr = REG32(LAPIC_ESR_REG);
+ uint32_t esr = LAPIC_ESR_REG;
uint32_t ioapic_redtbl, vec;
int irq, max_irq_entries;
/* Ack LVT ERROR exception */
- REG32(LAPIC_ESR_REG) = 0;
+ LAPIC_ESR_REG = 0;
/*
* When IOAPIC has more than 1 interrupts in remote IRR state,
@@ -322,9 +337,9 @@ void handle_lapic_lvt_error(void)
/* If pending interrupt is not in LAPIC, clear it. */
if (ioapic_redtbl & IOAPIC_REDTBL_IRR) {
vec = IRQ_TO_VEC(irq);
- if (!lapic_get_vector(LAPIC_IRR_REG, vec)) {
+ if (!lapic_get_vector(&LAPIC_IRR_REG, vec)) {
/* End of interrupt */
- REG32(IOAPIC_EOI_REG) = vec;
+ IOAPIC_EOI_REG = vec;
ioapic_pending_count++;
}
}
@@ -348,6 +363,11 @@ __asm__ (
"movl %esp, %eax\n"
"movl $stack_end, %esp\n"
"push %eax\n"
+#ifdef CONFIG_TASK_PROFILING
+ "push $" STRINGIFY(CONFIG_IRQ_COUNT) "\n"
+ "call task_start_irq_handler\n"
+ "addl $0x04, %esp\n"
+#endif
"call handle_lapic_lvt_error\n"
"pop %esp\n"
"movl $0x00, (0xFEE000B0)\n" /* Set EOI for LAPIC */
@@ -368,6 +388,30 @@ void unhandled_vector(void)
/* This needs to be moved to link_defs.h */
extern const struct irq_data __irq_data[], __irq_data_end[];
+/**
+ * Called from SOFTIRQ_VECTOR when software is trigger an IRQ manually
+ *
+ * If IRQ is out of range, then no routine should be called
+ */
+void call_irq_service_routine(uint32_t irq)
+{
+ const struct irq_data *p = __irq_data;
+
+ /* If just rescheduling a task, we won't have a routine to call */
+ if (irq >= CONFIG_IRQ_COUNT)
+ return;
+
+ for (; p < __irq_data_end; p++) {
+ if (p->irq == irq) {
+ p->routine();
+ break;
+ }
+ }
+
+ if (p == __irq_data_end)
+ CPRINTS("IRQ %d routine not found!", irq);
+}
+
void init_interrupts(void)
{
unsigned entry;
@@ -377,10 +421,14 @@ void init_interrupts(void)
/* Setup gates for IRQs declared by drivers using DECLARE_IRQ */
for (; p < __irq_data_end; p++)
- set_interrupt_gate(IRQ_TO_VEC(p->irq), p->routine, IDT_DESC_FLAGS);
+ set_interrupt_gate(IRQ_TO_VEC(p->irq), p->ioapic_routine,
+ IDT_DESC_FLAGS);
+
+ /* Software generated IRQ */
+ set_interrupt_gate(SOFTIRQ_VECTOR, sw_irq_handler, IDT_DESC_FLAGS);
/* Setup gate for LAPIC_LVT_ERROR vector; clear any remnant error. */
- REG32(LAPIC_ESR_REG) = 0;
+ LAPIC_ESR_REG = 0;
set_interrupt_gate(LAPIC_LVT_ERROR_VECTOR, _lapic_error_handler,
IDT_DESC_FLAGS);
@@ -422,6 +470,16 @@ void init_interrupts(void)
set_interrupt_gate(19, exception_panic_19, IDT_DESC_FLAGS);
set_interrupt_gate(20, exception_panic_20, IDT_DESC_FLAGS);
+ /*
+ * Set up watchdog expiration like a panic, that way we can
+ * use the common panic handling code, and also properly
+ * retrieve EIP.
+ */
+ if (IS_ENABLED(CONFIG_WATCHDOG))
+ set_interrupt_gate(ISH_WDT_VEC,
+ exception_panic_wdt,
+ IDT_DESC_FLAGS);
+
/* Note: At reset, ID field is already set to 0 in APIC ID register */
/* Enable the APIC, mapping the spurious interrupt at the same time. */
diff --git a/core/minute-ia/irq_handler.h b/core/minute-ia/irq_handler.h
index 4df2b14da6..3b33fbb073 100644
--- a/core/minute-ia/irq_handler.h
+++ b/core/minute-ia/irq_handler.h
@@ -11,32 +11,11 @@
#include "registers.h"
#include "task_defs.h"
-#ifdef CONFIG_FPU
-#define save_fpu_ctx "movl "USE_FPU_OFFSET_STR"(%eax), %ebx\n" \
- "test %ebx, %ebx\n" \
- "jz 9f\n" \
- "fnsave "FPU_CTX_OFFSET_STR"(%eax)\n" \
- "9:\n"
-
-#define rstr_fpu_ctx "movl "USE_FPU_OFFSET_STR"(%eax), %ebx\n" \
- "test %ebx, %ebx\n" \
- "jz 9f\n" \
- "frstor "FPU_CTX_OFFSET_STR"(%eax)\n" \
- "9:\n"
-#else
-#define save_fpu_ctx
-#define rstr_fpu_ctx
-#endif
-
-#ifdef CONFIG_TASK_PROFILING
-#define task_start_irq_handler_call "call task_start_irq_handler\n"
-#else
-#define task_start_irq_handler_call
-#endif
-
+asm (".include \"core/minute-ia/irq_handler_common.S\"");
struct irq_data {
void (*routine)(void);
+ void (*ioapic_routine)(void);
int irq;
};
@@ -52,43 +31,28 @@ struct irq_data {
* Note: currently we don't allow nested irq handling
*/
#define DECLARE_IRQ(irq, routine) DECLARE_IRQ_(irq, routine, irq + 32 + 10)
-/* Each irq has a irq_data structure placed in .rodata.irqs section,
- * to be used for dynamically setting up interrupt gates */
+/*
+ * Each irq has a irq_data structure placed in .rodata.irqs section,
+ * to be used for dynamically setting up interrupt gates
+ */
#define DECLARE_IRQ_(irq, routine, vector) \
void __keep routine(void); \
- void IRQ_HANDLER(irq)(void); \
+ void IRQ_HANDLER(irq)(void); \
__asm__ (".section .rodata.irqs\n"); \
const struct irq_data __keep CONCAT4(__irq_, irq, _, routine) \
- __attribute__((section(".rodata.irqs")))= {IRQ_HANDLER(irq), irq};\
+ __attribute__((section(".rodata.irqs"))) = { routine, \
+ IRQ_HANDLER(irq), \
+ irq}; \
__asm__ ( \
".section .text._irq_"#irq"_handler\n" \
"_irq_"#irq"_handler:\n" \
- "pusha\n" \
- ASM_LOCK_PREFIX "addl $1, __in_isr\n" \
- "movl %esp, %eax\n" \
- "movl $stack_end, %esp\n" \
- "push %eax\n" \
- task_start_irq_handler_call \
- "call "#routine"\n" \
- "push $0\n" \
- "push $0\n" \
- "call switch_handler\n" \
- "addl $0x08, %esp\n" \
- "pop %esp\n" \
- "test %eax, %eax\n" \
- "je 1f\n" \
- "movl current_task, %eax\n" \
- save_fpu_ctx \
- "movl %esp, (%eax)\n" \
- "movl next_task, %eax\n" \
- "movl %eax, current_task\n" \
- "movl (%eax), %esp\n" \
- rstr_fpu_ctx \
- "1:\n" \
- "movl $"#vector ", (0xFEC00040)\n" \
- "movl $0x00, (0xFEE000B0)\n" \
- ASM_LOCK_PREFIX "subl $1, __in_isr\n" \
- "popa\n" \
- "iret\n" \
+ "pusha\n" \
+ ASM_LOCK_PREFIX "addl $1, __in_isr\n" \
+ "irq_handler_common $0 $0 $"#irq"\n" \
+ "movl $"#vector ", " STRINGIFY(IOAPIC_EOI_REG_ADDR) "\n" \
+ "movl $0x00, " STRINGIFY(LAPIC_EOI_REG_ADDR) "\n" \
+ ASM_LOCK_PREFIX "subl $1, __in_isr\n" \
+ "popa\n" \
+ "iret\n" \
);
#endif /* __CROS_EC_IRQ_HANDLER_H */
diff --git a/core/minute-ia/irq_handler_common.S b/core/minute-ia/irq_handler_common.S
new file mode 100644
index 0000000000..e07cf26ce1
--- /dev/null
+++ b/core/minute-ia/irq_handler_common.S
@@ -0,0 +1,68 @@
+/* Copyright 2016 The Chromium OS 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 task swtching and interrupt handling
+ */
+
+#include "config.h"
+#include "registers.h"
+#include "task_defs.h"
+
+# desched resched irq are all inputs and should be set to registers or immediate
+# values
+.macro irq_handler_common desched resched irq
+ # __schedule() copies 'resched' to %ecx and 'desched' to %edx before
+ movl %esp, %eax
+ movl $stack_end, %esp # use system stack
+ push %eax # push sp of preempted context
+
+ # Push resched and desched on stack to pass them as function parameters
+ # to switch_handler(desched, resched). After call, we clean up stack
+ # pointer. Note, we do this now before call_irq_service_routine has a
+ # chance to clobber these caller-saved registers.
+ push \resched
+ push \desched
+
+ push \irq
+#ifdef CONFIG_TASK_PROFILING
+ call task_start_irq_handler
+#endif
+ # Leave IRQ on stack for handler
+ call call_irq_service_routine
+ addl $0x04, %esp
+
+ # Call switch_handler(desched, resched).
+ call switch_handler # switch task if needed
+ addl $0x08, %esp
+
+ pop %esp # restore sp of preempted context
+
+ test %eax, %eax # Check if task switch required
+ jz 1f
+
+ movl current_task, %eax
+
+#ifdef CONFIG_FPU
+ movl USE_FPU_OFFSET(%eax), %ecx
+ test %ecx, %ecx
+ jz 2f
+ fnsave FPU_CTX_OFFSET(%eax) # Save current FPU context(current->fp_ctx)
+ 2:
+#endif
+
+ # Save SP of current task and switch to new task
+ movl %esp, (%eax)
+ movl next_task, %eax
+ movl %eax, current_task
+ movl (%eax), %esp
+
+#ifdef CONFIG_FPU
+ movl USE_FPU_OFFSET(%eax), %ecx
+ test %ecx, %ecx
+ jz 1f
+ frstor FPU_CTX_OFFSET(%eax) # Restore next FPU context
+#endif
+
+ 1:
+.endm
diff --git a/core/minute-ia/panic.c b/core/minute-ia/panic.c
index aec6c4016a..0898e4e717 100644
--- a/core/minute-ia/panic.c
+++ b/core/minute-ia/panic.c
@@ -13,14 +13,13 @@
#include "task.h"
#include "timer.h"
#include "util.h"
-#include "watchdog.h"
/*
* This array maps an interrupt vector number to the corresponding
* exception name. See see "Intel 64 and IA-32 Architectures Software
* Developer's Manual", Volume 3A, Section 6.15.
*/
-const static char *PANIC_REASON[] = {
+const static char *panic_reason[] = {
"Divide By Zero",
"Debug Exception",
"NMI Interrupt",
@@ -45,13 +44,16 @@ const static char *PANIC_REASON[] = {
};
/*
- * Print panic data
+ * Print panic data. This may be called either from the report_panic
+ * procedure (below) while handling a panic, or from the panicinfo
+ * console command.
*/
void panic_data_print(const struct panic_data *pdata)
{
- panic_printf("\n========== PANIC ==========\n");
- if (pdata->x86.vector <= 20)
- panic_printf("%s\n", PANIC_REASON[pdata->x86.vector]);
+ if (pdata->x86.vector == ISH_WDT_VEC)
+ panic_printf("Reason: Watchdog Expiration\n");
+ else if (pdata->x86.vector <= 20)
+ panic_printf("Reason: %s\n", panic_reason[pdata->x86.vector]);
else
panic_printf("Interrupt vector number: 0x%08X (unknown)\n",
pdata->x86.vector);
@@ -66,13 +68,6 @@ void panic_data_print(const struct panic_data *pdata)
panic_printf("EDX = 0x%08X\n", pdata->x86.edx);
panic_printf("ESI = 0x%08X\n", pdata->x86.esi);
panic_printf("EDI = 0x%08X\n", pdata->x86.edi);
- panic_printf("\n");
- panic_printf("Resetting system...\n");
- panic_printf("===========================\n");
-}
-
-void __keep report_panic(void)
-{
}
/**
@@ -93,6 +88,14 @@ __attribute__ ((noreturn)) void __keep exception_panic(
uint32_t cs,
uint32_t eflags)
{
+ /*
+ * If a panic were to occur during the reset procedure, we want
+ * to make sure that this panic will certainly cause a hard
+ * reset, rather than aontaskfw reset. Track if paniced once
+ * already.
+ */
+ static int panic_once;
+
register uint32_t eax asm("eax");
register uint32_t ebx asm("ebx");
register uint32_t ecx asm("ecx");
@@ -121,21 +124,28 @@ __attribute__ ((noreturn)) void __keep exception_panic(
PANIC_DATA_PTR->magic = PANIC_DATA_MAGIC;
/* Display the panic and reset */
+ if (panic_once)
+ panic_printf("\nWhile resetting from a panic, another panic"
+ " occurred!");
+
+ panic_printf("\n========== PANIC ==========\n");
panic_data_print(PANIC_DATA_PTR);
- system_reset(SYSTEM_RESET_HARD);
- while (1)
- continue;
-}
+ panic_printf("\n");
+ panic_printf("Resetting system...\n");
+ panic_printf("===========================\n");
-static int command_divzero(int argc, char **argv)
-{
- volatile int x = 0;
+ if (panic_once) {
+ system_reset(SYSTEM_RESET_HARD);
+ } else if (vector == ISH_WDT_VEC) {
+ panic_once = 1;
+ system_reset(SYSTEM_RESET_AP_WATCHDOG);
+ } else {
+ panic_once = 1;
+ system_reset(0);
+ }
- x = 1 / x;
- return EC_SUCCESS;
+ __builtin_unreachable();
}
-DECLARE_CONSOLE_COMMAND(divzero, command_divzero, NULL,
- "Divide by zero to trigger a processor exception");
#ifdef CONFIG_SOFTWARE_PANIC
void software_panic(uint32_t reason, uint32_t info)
diff --git a/core/minute-ia/switch.S b/core/minute-ia/switch.S
index 27aba1a7cc..f8d0be3874 100644
--- a/core/minute-ia/switch.S
+++ b/core/minute-ia/switch.S
@@ -9,11 +9,7 @@
#include "registers.h"
#include "task_defs.h"
-#ifdef CONFIG_TASK_PROFILING
-#define task_start_irq_handler_call call task_start_irq_handler
-#else
-#define task_start_irq_handler_call
-#endif
+#include "irq_handler_common.S"
.text
@@ -23,13 +19,14 @@
.global __task_start
.global __switchto
.global default_int_handler
+.global sw_irq_handler
# Start the task scheduling. Start current_task (hook_task)
# This function is not an ISR but imitates the sequence.
.align 4
.func __task_start
__task_start:
- movl 0x4(%esp), %ebx
+ movl 0x4(%esp), %ecx
movl current_task, %eax
movl (%eax), %esp
#ifdef CONFIG_FPU
@@ -39,7 +36,7 @@ __task_start:
frstor FPU_CTX_OFFSET(%eax)
1:
#endif
- movl $0x1, (%ebx) # first task is ready. set start_called = 1
+ movl $0x1, (%ecx) # first task is ready. set start_called = 1
popa
iret
.endfunc
@@ -64,87 +61,59 @@ default_int_handler:
cmpl $LAPIC_SPURIOUS_INT_VECTOR, %eax
je 1f # No EOI for LAPIC_SPURIOUS_INT_VECTOR
- movl %eax, IOAPIC_EOI_REG # Indicate completion of servicing the
+ movl %eax, IOAPIC_EOI_REG_ADDR # Indicate completion of servicing the
# interrupt to IOAPIC first
- movl $0x00, LAPIC_EOI_REG # Indicate completion of servicing the
+ movl $0x00, LAPIC_EOI_REG_ADDR # Indicate completion of servicing the
# interrupt to LAPIC next
1:
# Ensure we balance the __in_isr counter
ASM_LOCK_PREFIX subl $1, __in_isr
popa
iret
+.endfunc
+
+.align 4
+.func sw_irq_handler
+sw_irq_handler:
+ pusha
+ ASM_LOCK_PREFIX addl $1, __in_isr
+
+ # Call sw irq handler with irq number(%ecx) from task_trigger_irq.
+ # Pass 0 for both desched and resched since we don't need to deschedule
+ # our current task (and idle task can always be rescheduled)
+ irq_handler_common $0 $0 %ecx
+
+ # Indicate completion of servicing the interrupt to LAPIC.
+ # No IOAPIC EOI needed as this is SW triggered.
+ movl $0x00, LAPIC_EOI_REG_ADDR
+ # Decrement ISR counter and restore general purpose registers.
+ ASM_LOCK_PREFIX subl $1, __in_isr
+ popa
+ iret
.endfunc
+
# Switches from one task to another if ready.
# __schedule triggers software interrupt ISH_TS_VECTOR, which is handled by
# __switchto
.align 4
.func __switchto
__switchto:
-
- # Save current task
pusha
ASM_LOCK_PREFIX addl $1, __in_isr
- movl %esp, %eax
- movl $stack_end, %esp # use system stack
- push %eax # push sp of preempted context
-
# __schedule() copies 'resched' to %ecx and 'desched' to %edx before
- # triggering ISH_TS_VECTOR
- #
- # Push %ecx and %edx into stack to pass them as function parameters
- # to switch_handler(desched, resched). After call, we clean up stack
- # pointer. Note, we do this now before task_start_irq has a chance
- # to clobber these caller-saved registers.
- push %ecx
- push %edx
-
- # We don't push anything on the stack for start irq since the
- # parameter is unused.
- task_start_irq_handler_call
-
- # Stack is already set up from previous pushes
- call switch_handler
- addl $0x8, %esp # Clean up stack
- pop %esp # restore sp of preempted context
-
- test %eax, %eax # Check if task switch required
- jz 1f
-
- movl current_task, %eax
-
-#ifdef CONFIG_FPU
- movl USE_FPU_OFFSET(%eax), %ebx
- test %ebx, %ebx
- jz 2f
- fnsave FPU_CTX_OFFSET(%eax) # Save current FPU context(current->fp_ctx)
- 2:
-#endif
-
- # Save SP of current task and switch to new task
- movl %esp, (%eax)
- movl next_task, %eax
- movl %eax, current_task
- movl (%eax), %esp
-
-#ifdef CONFIG_FPU
- movl USE_FPU_OFFSET(%eax), %ebx
- test %ebx, %ebx
- jz 1f
- frstor FPU_CTX_OFFSET(%eax) # Restore next FPU context
-#endif
-
- 1:
+ # triggering ISH_TS_VECTOR.
+ # Call sw_irq with an invalid IRQ so it will skip calling a routine
+ irq_handler_common %edx %ecx $CONFIG_IRQ_COUNT
# Indicate completion of servicing the interrupt to LAPIC.
# No IOAPIC EOI needed as this is SW triggered.
- movl $0x00, LAPIC_EOI_REG
+ movl $0x00, LAPIC_EOI_REG_ADDR
# Decrement ISR counter and restore general purpose registers.
ASM_LOCK_PREFIX subl $1, __in_isr
popa
iret
-
.endfunc
diff --git a/core/minute-ia/task.c b/core/minute-ia/task.c
index c344b55179..18c23f5515 100644
--- a/core/minute-ia/task.c
+++ b/core/minute-ia/task.c
@@ -261,30 +261,27 @@ uint32_t switch_handler(int desched, task_id_t resched)
void __schedule(int desched, int resched)
{
- __asm__ __volatile__ ("int %0"
- :
- : "i" (ISH_TS_VECTOR),
- "d" (desched), "c" (resched)
- );
+ __asm__ __volatile__("int %0"
+ :
+ : "i"(ISH_TS_VECTOR), "d"(desched), "c"(resched));
}
#ifdef CONFIG_TASK_PROFILING
-void __keep task_start_irq_handler(void *unused)
+void __keep task_start_irq_handler(void *data)
{
/*
* Get time before checking depth, in case this handler is
* pre-empted.
*/
uint32_t t = get_time().le.lo;
- uint32_t vector = get_current_interrupt_vector();
- int irq = VEC_TO_IRQ(vector);
+ int irq = (uint32_t)data;
/*
* Track IRQ distribution. No need for atomic add, because an IRQ
* can't pre-empt itself. If less than 0, then the vector did not map
* to an IRQ but was for a synchronous exception instead (TS_VECTOR)
*/
- if (irq > 0 && irq < ARRAY_SIZE(irq_dist))
+ if (irq < CONFIG_IRQ_COUNT)
irq_dist[irq]++;
else
/* Track total number of service calls */
@@ -420,11 +417,20 @@ void task_clear_pending_irq(int irq)
void task_trigger_irq(int irq)
{
- /* Writing to Local APIC Interrupt Command Register (ICR) causes an
- * IPI (Inter-processor interrupt) on the APIC bus. Here we direct the
- * IPI to originating prccessor to generate self-interrupt
+ /* ISR should not be called before the first task is scheduled */
+ if (!task_start_called())
+ return;
+
+ /* we don't allow nested interrupt */
+ if (in_interrupt_context())
+ return;
+
+ /*
+ * "int" instruction accepts vector only as immediate value.
+ * so here, we use one vector(SOFTIRQ_VECTOR) and pass
+ * the address of ISR of irq in ecx register.
*/
- REG32(LAPIC_ICR_REG) = LAPIC_ICR_BITS | IRQ_TO_VEC(irq);
+ __asm__ __volatile__("int %0\n" : : "i"(SOFTIRQ_VECTOR), "c"(irq));
}
void mutex_lock(struct mutex *mtx)
diff --git a/core/minute-ia/task_defs.h b/core/minute-ia/task_defs.h
index 15aca6b8b5..01632392cb 100644
--- a/core/minute-ia/task_defs.h
+++ b/core/minute-ia/task_defs.h
@@ -19,6 +19,10 @@
#define USE_FPU_OFFSET_STR STRINGIFY(USE_FPU_OFFSET) /* "20" */
#define FPU_CTX_OFFSET_STR STRINGIFY(FPU_CTX_OFFSET) /* "24" */
+
+asm (".equ USE_FPU_OFFSET, "USE_FPU_OFFSET_STR);
+asm (".equ FPU_CTX_OFFSET, "FPU_CTX_OFFSET_STR);
+
#endif
#endif /* CONFIG_FPU */
@@ -42,6 +46,7 @@ typedef union {
int __task_start(int *start_called);
void __switchto(void);
+void sw_irq_handler(void);
/* Only the IF bit is set so tasks start with interrupts enabled. */
#define INITIAL_EFLAGS (0x200UL)
diff --git a/driver/accelgyro_bmi160.c b/driver/accelgyro_bmi160.c
index d441ee3c9e..cc8dddece1 100644
--- a/driver/accelgyro_bmi160.c
+++ b/driver/accelgyro_bmi160.c
@@ -835,7 +835,7 @@ static int bmi160_decode_header(struct motion_sensor_t *accel,
vector.flags = 0;
normalize(s, v, *bp);
#ifdef CONFIG_ACCEL_SPOOF_MODE
- if (s->in_spoof_mode)
+ if (s->flags & MOTIONSENSE_FLAG_IN_SPOOF_MODE)
v = s->spoof_xyz;
#endif /* defined(CONFIG_ACCEL_SPOOF_MODE) */
vector.data[X] = v[X];
diff --git a/driver/accelgyro_bmi160.h b/driver/accelgyro_bmi160.h
index db6179be3e..428338b080 100644
--- a/driver/accelgyro_bmi160.h
+++ b/driver/accelgyro_bmi160.h
@@ -180,18 +180,13 @@ enum fifo_header {
/* odr = 100 / (1 << (8 - reg)) ,within limit */
#define BMI160_ODR_0_78HZ 0x01
-#define BMI160_ODR_25HZ 0x06
-#define BMI160_ODR_50HZ 0x07
#define BMI160_ODR_100HZ 0x08
-#define BMI160_ODR_800HZ 0x0b
-#define BMI160_ODR_1600HZ 0x0c
-#define BMI160_ODR_3200HZ 0x0d
#define BMI160_REG_TO_ODR(_regval) \
- ((_regval) < 8 ? 100000 / (1 << (8 - (_regval))) : \
- 100000 * (1 << ((_regval) - 8)))
+ ((_regval) < BMI160_ODR_100HZ ? 100000 / (1 << (8 - (_regval))) : \
+ 100000 * (1 << ((_regval) - 8)))
#define BMI160_ODR_TO_REG(_odr) \
- ((_odr) < 100000 ? (__builtin_clz(100000 / (_odr)) - 23) : \
+ ((_odr) < 100000 ? (__builtin_clz(100000 / (_odr)) - 24) : \
(39 - __builtin_clz((_odr) / 100000)))
#define BMI160_CONF_REG(_sensor) (0x40 + 2 * (_sensor))
diff --git a/driver/accelgyro_lsm6dsm.c b/driver/accelgyro_lsm6dsm.c
index cdf5535c7d..8f7a9a26ae 100644
--- a/driver/accelgyro_lsm6dsm.c
+++ b/driver/accelgyro_lsm6dsm.c
@@ -15,6 +15,7 @@
#include "hwtimer.h"
#include "mag_cal.h"
#include "math_util.h"
+#include "queue.h"
#include "task.h"
#include "timer.h"
@@ -22,8 +23,77 @@
#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args)
#define CPRINTS(format, args...) cprints(CC_ACCEL, format, ## args)
+#define IS_FSTS_EMPTY(s) ((s).len & LSM6DSM_FIFO_EMPTY)
+
#ifdef CONFIG_ACCEL_FIFO
static volatile uint32_t last_interrupt_timestamp;
+
+/**
+ * A queue for holding data from the FIFO as we read it. This will be used to
+ * spread the timestamps if more than one entry is available. The allocated size
+ * is calculated by assuming that the motion sense read loop will never exceed
+ * 20ms. During this time, sensors running full tile (210Hz) will sample ~5
+ * times. At most, this driver supports 3 sensors, leaving us with 15 samples.
+ * Since the queue size needs to be a power of 2, and 16 is too close, 32 was
+ * chosen.
+ */
+static struct queue single_fifo_read_queue = QUEUE_NULL(32,
+ struct ec_response_motion_sensor_data);
+
+/**
+ * Resets the lsm6dsm load fifo sensor states to the given timestamp. This
+ * should be called at the start of the fifo read sequence.
+ *
+ * @param s Pointer to the first sensor in the lsm6dsm (accelerometer).
+ * @param ts The timestamp to use for the interrupt timestamp.
+ */
+static void reset_load_fifo_sensor_state(struct motion_sensor_t *s, uint32_t ts)
+{
+ int i;
+ struct lsm6dsm_data *data = LSM6DSM_GET_DATA(s);
+
+ for (i = 0; i < FIFO_DEV_NUM; i++) {
+ data->load_fifo_sensor_state[i].int_timestamp = ts;
+ data->load_fifo_sensor_state[i].sample_count = 0;
+ }
+}
+
+/**
+ * Gets the dev_fifo enum value for a given sensor.
+ *
+ * @param s Pointer to the sensor in question.
+ * @return the dev_fifo enum value corresponding to the sensor.
+ */
+static inline enum dev_fifo get_fifo_type(const struct motion_sensor_t *s)
+{
+ static enum dev_fifo map[] = {
+ FIFO_DEV_ACCEL,
+ FIFO_DEV_GYRO,
+#ifdef CONFIG_LSM6DSM_SEC_I2C
+ FIFO_DEV_MAG
+#endif /* CONFIG_LSM6DSM_SEC_I2C */
+ };
+ return map[s->type];
+}
+
+/**
+ * Gets the sensor type associated with the dev_fifo enum. This type can be used
+ * to get the sensor number by using it as an offset from the first sensor in
+ * the lsm6dsm (the accelerometer).
+ *
+ * @param fifo_type The dev_fifo enum in question.
+ * @return the type of sensor represented by the fifo type.
+ */
+static inline uint8_t get_sensor_type(enum dev_fifo fifo_type)
+{
+ static uint8_t map[] = {
+ MOTIONSENSE_TYPE_GYRO,
+ MOTIONSENSE_TYPE_ACCEL,
+ MOTIONSENSE_TYPE_MAG,
+ };
+ return map[fifo_type];
+}
+
#endif
/**
@@ -248,16 +318,10 @@ static int fifo_next(struct lsm6dsm_data *private)
* push_fifo_data - Scan data pattern and push upside
*/
static void push_fifo_data(struct motion_sensor_t *accel, uint8_t *fifo,
- uint16_t flen, uint32_t int_ts)
+ uint16_t flen)
{
struct motion_sensor_t *s;
struct lsm6dsm_data *private = LSM6DSM_GET_DATA(accel);
- /* In FIFO sensors are mapped in a different way. */
- uint8_t agm_maps[] = {
- MOTIONSENSE_TYPE_GYRO,
- MOTIONSENSE_TYPE_ACCEL,
- MOTIONSENSE_TYPE_MAG,
- };
while (flen > 0) {
struct ec_response_motion_sensor_data vect;
@@ -273,7 +337,7 @@ static void push_fifo_data(struct motion_sensor_t *accel, uint8_t *fifo,
return;
}
- id = agm_maps[next_fifo];
+ id = get_sensor_type(next_fifo);
if (private->samples_to_discard[id] > 0) {
private->samples_to_discard[id]--;
} else {
@@ -298,7 +362,16 @@ static void push_fifo_data(struct motion_sensor_t *accel, uint8_t *fifo,
vect.flags = 0;
vect.sensor_num = s - motion_sensors;
- motion_sense_fifo_add_data(&vect, s, 3, int_ts);
+ /*
+ * queue_add_units will override old values in case of
+ * an overflow. The sample count should still be
+ * incremented as it might affect the computed sample
+ * rate later on.
+ */
+ queue_add_units(&single_fifo_read_queue, &vect, 1);
+ private->load_fifo_sensor_state[next_fifo]
+ .sample_count++;
+
}
fifo += OUT_XYZ_SIZE;
@@ -306,11 +379,19 @@ static void push_fifo_data(struct motion_sensor_t *accel, uint8_t *fifo,
}
}
-static int load_fifo(struct motion_sensor_t *s, const struct fstatus *fsts)
+static int load_fifo(struct motion_sensor_t *s, const struct fstatus *fsts,
+ uint32_t *last_fifo_read_ts)
{
- uint32_t int_ts = last_interrupt_timestamp;
+ uint32_t interrupt_timestamp = last_interrupt_timestamp;
+ uint32_t fifo_read_start = *last_fifo_read_ts;
int err, left, length;
uint8_t fifo[FIFO_READ_LEN];
+ struct ec_response_motion_sensor_data data;
+ struct load_fifo_sensor_state_t *load_fifo_sensor_state =
+ LSM6DSM_GET_DATA(s)->load_fifo_sensor_state;
+
+ /* Reset the load_fifo_sensor_state so we can start a new read. */
+ reset_load_fifo_sensor_state(s, interrupt_timestamp);
/*
* DIFF[11:0] are number of unread uint16 in FIFO
@@ -338,6 +419,7 @@ static int load_fifo(struct motion_sensor_t *s, const struct fstatus *fsts)
err = st_raw_read_n_noinc(s->port, s->addr,
LSM6DSM_FIFO_DATA_ADDR,
fifo, length);
+ *last_fifo_read_ts = __hw_clock_source_read();
if (err != EC_SUCCESS)
return err;
@@ -348,27 +430,84 @@ static int load_fifo(struct motion_sensor_t *s, const struct fstatus *fsts)
* where we empty the FIFO, and a new IRQ comes in between
* reading the last sample and pushing it into the FIFO.
*/
- push_fifo_data(s, fifo, length, int_ts);
+
+ push_fifo_data(s, fifo, length);
left -= length;
} while (left > 0);
+ /* Compute the window length (ns) between the interrupt and the read. */
+ length = time_until(interrupt_timestamp, fifo_read_start);
+
+ /* Get the event count. */
+ left = queue_count(&single_fifo_read_queue);
+
+ /*
+ * Spread timestamps if we have more than one reading for a given
+ * sensor.
+ */
+ while (left-- > 0) {
+ struct motion_sensor_t *data_sensor;
+ struct load_fifo_sensor_state_t *state;
+
+ /*
+ * Pop an entry off our read queue and get the pointers for the
+ * sensor and the read state.
+ */
+ queue_remove_unit(&single_fifo_read_queue, &data);
+ data_sensor = &motion_sensors[data.sensor_num];
+ state = &load_fifo_sensor_state[get_fifo_type(data_sensor)];
+
+ /*
+ * Push the data to the motion sense fifo and increment the
+ * timestamp in case we have another reading for this sensor
+ * (spreading).
+ */
+ motion_sense_fifo_add_data(&data, data_sensor, 3,
+ state->int_timestamp);
+ state->int_timestamp += MIN(state->sample_rate,
+ length / state->sample_count);
+ }
+
return EC_SUCCESS;
}
+
+static int is_fifo_empty(struct motion_sensor_t *s, struct fstatus *fsts)
+{
+ int res;
+
+ if (s->flags & MOTIONSENSE_FLAG_INT_SIGNAL)
+ return gpio_get_level(s->int_signal);
+ CPRINTS("Interrupt signal not set for %s", s->name);
+ res = st_raw_read_n_noinc(s->port, s->addr,
+ LSM6DSM_FIFO_STS1_ADDR,
+ (int8_t *)fsts, sizeof(*fsts));
+ /* If we failed to read the FIFO size assume empty. */
+ if (res != EC_SUCCESS)
+ return 1;
+ return IS_FSTS_EMPTY(*fsts);
+}
+
#endif /* CONFIG_ACCEL_FIFO */
-/**
- * lsm6dsm_interrupt - interrupt from int1/2 pin of sensor
- */
-void lsm6dsm_interrupt(enum gpio_signal signal)
+static void handle_interrupt_for_fifo(uint32_t ts)
{
#ifdef CONFIG_ACCEL_FIFO
- last_interrupt_timestamp = __hw_clock_source_read();
+ if (time_after(ts, last_interrupt_timestamp))
+ last_interrupt_timestamp = ts;
#endif
task_set_event(TASK_ID_MOTIONSENSE,
CONFIG_ACCEL_LSM6DSM_INT_EVENT, 0);
}
/**
+ * lsm6dsm_interrupt - interrupt from int1/2 pin of sensor
+ */
+void lsm6dsm_interrupt(enum gpio_signal signal)
+{
+ handle_interrupt_for_fifo(__hw_clock_source_read());
+}
+
+/**
* irq_handler - bottom half of the interrupt stack
*/
static int irq_handler(struct motion_sensor_t *s, uint32_t *event)
@@ -382,18 +521,34 @@ static int irq_handler(struct motion_sensor_t *s, uint32_t *event)
#ifdef CONFIG_ACCEL_FIFO
{
struct fstatus fsts;
+ uint32_t last_fifo_read_ts;
+ uint32_t triggering_interrupt_timestamp =
+ last_interrupt_timestamp;
+
/* Read how many data pattern on FIFO to read and pattern. */
ret = st_raw_read_n_noinc(s->port, s->addr,
LSM6DSM_FIFO_STS1_ADDR,
(uint8_t *)&fsts, sizeof(fsts));
if (ret != EC_SUCCESS)
return ret;
+ last_fifo_read_ts = __hw_clock_source_read();
if (fsts.len & (LSM6DSM_FIFO_DATA_OVR | LSM6DSM_FIFO_FULL)) {
CPRINTF("[%T %s FIFO Overrun: %04x]\n",
s->name, fsts.len);
}
- if (!(fsts.len & LSM6DSM_FIFO_EMPTY))
- ret = load_fifo(s, &fsts);
+ if (!IS_FSTS_EMPTY(fsts))
+ ret = load_fifo(s, &fsts, &last_fifo_read_ts);
+
+ /*
+ * Check if FIFO isn't empty and we never got an interrupt.
+ * This can happen if new entries were added to the FIFO after
+ * the count was read, but before the FIFO was cleared out.
+ * In the long term it might be better to use the last
+ * spread timestamp instead.
+ */
+ if (!is_fifo_empty(s, &fsts) &&
+ triggering_interrupt_timestamp == last_interrupt_timestamp)
+ handle_interrupt_for_fifo(last_fifo_read_ts);
}
#endif
return ret;
@@ -536,6 +691,9 @@ int lsm6dsm_set_data_rate(const struct motion_sensor_t *s, int rate, int rnd)
#ifdef CONFIG_ACCEL_FIFO
private->samples_to_discard[s->type] =
LSM6DSM_DISCARD_SAMPLES;
+ private->load_fifo_sensor_state[get_fifo_type(s)].sample_rate =
+ normalized_rate == 0 ? 0 : SECOND * 1000 /
+ normalized_rate;
ret = fifo_enable(accel);
if (ret != EC_SUCCESS)
CPRINTS("Failed to enable FIFO. Error: %d", ret);
diff --git a/driver/accelgyro_lsm6dsm.h b/driver/accelgyro_lsm6dsm.h
index f9a4deff59..3d4e2645f1 100644
--- a/driver/accelgyro_lsm6dsm.h
+++ b/driver/accelgyro_lsm6dsm.h
@@ -291,6 +291,17 @@ struct lsm6dsm_fifo_data {
};
/*
+ * Structure used to maintain the load state per sensor. This will be used to
+ * properly spread values in case we have more than one reading for a given
+ * sensor in a single fifo read pass.
+ */
+struct load_fifo_sensor_state_t {
+ uint32_t int_timestamp;
+ uint8_t sample_count;
+ int sample_rate;
+};
+
+/*
* lsm6dsm_data is used for accel gyro and the sensor connect to a LSM6DSM.
*
* +---- lsm6dsm_data ------------------------------------------------+
@@ -334,7 +345,8 @@ struct lsm6dsm_data {
* initial samples with incorrect values
*/
unsigned int samples_to_discard[FIFO_DEV_NUM];
-#endif
+ struct load_fifo_sensor_state_t load_fifo_sensor_state[FIFO_DEV_NUM];
+#endif /* CONFIG_ACCEL_FIFO */
#if defined(CONFIG_LSM6DSM_SEC_I2C) && defined(CONFIG_MAG_CALIBRATE)
union {
#ifdef CONFIG_MAG_LSM6DSM_BMM150
diff --git a/driver/als_si114x.c b/driver/als_si114x.c
index 17f178080f..be3f090db3 100644
--- a/driver/als_si114x.c
+++ b/driver/als_si114x.c
@@ -295,6 +295,7 @@ static int read(const struct motion_sensor_t *s, intv3_t v)
case SI114X_NOT_READY:
ret = EC_ERROR_NOT_POWERED;
}
+#if 0 /* This code is incorrect https://crbug.com/956569 */
if (ret == EC_ERROR_ACCESS_DENIED &&
s->type == MOTIONSENSE_TYPE_LIGHT) {
timestamp_t ts_now = get_time();
@@ -315,6 +316,7 @@ static int read(const struct motion_sensor_t *s, intv3_t v)
init(s);
}
}
+#endif
return ret;
}
diff --git a/driver/bc12/pi3usb9201.c b/driver/bc12/pi3usb9201.c
index d6e82c3c57..ba4ab9e496 100644
--- a/driver/bc12/pi3usb9201.c
+++ b/driver/bc12/pi3usb9201.c
@@ -37,6 +37,9 @@ struct bc12_status {
int current_limit;
};
+/* Used to store last BC1.2 detection result */
+static enum charge_supplier bc12_supplier[CONFIG_USB_PD_PORT_COUNT];
+
static const struct bc12_status bc12_chg_limits[] = {
[CHG_OTHER] = {CHARGE_SUPPLIER_OTHER, 500},
[CHG_2_4A] = {CHARGE_SUPPLIER_PROPRIETARY, 2400},
@@ -136,6 +139,23 @@ static int pi3usb9201_get_status(int port, int *client, int *host)
return rv;
}
+static void bc12_update_supplier(enum charge_supplier supplier, int port,
+ struct charge_port_info *new_chg)
+{
+ /*
+ * If most recent supplier type is not CHARGE_SUPPLIER_NONE, then the
+ * charge manager table entry for that supplier type needs to be cleared
+ * out.
+ */
+ if (bc12_supplier[port] != CHARGE_SUPPLIER_NONE)
+ charge_manager_update_charge(bc12_supplier[port], port, NULL);
+ /* Now update the current supplier type */
+ bc12_supplier[port] = supplier;
+ /* If new supplier type != NONE, then notify charge manager */
+ if (supplier != CHARGE_SUPPLIER_NONE)
+ charge_manager_update_charge(supplier, port, new_chg);
+}
+
static void bc12_update_charge_manager(int port, int client_status)
{
struct charge_port_info new_chg;
@@ -159,7 +179,7 @@ static void bc12_update_charge_manager(int port, int client_status)
/* bc1.2 is complete and start bit does not auto clear */
pi3usb9201_bc12_detect_ctrl(port, 0);
/* Inform charge manager of new supplier type and current limit */
- charge_manager_update_charge(supplier, port, &new_chg);
+ bc12_update_supplier(supplier, port, &new_chg);
}
static int bc12_detect_start(int port)
@@ -192,8 +212,11 @@ static void bc12_power_down(int port)
pi3usb9201_bc12_detect_ctrl(port, 0);
/* Mask interrupts unitl next bc1.2 detection event */
pi3usb9201_interrupt_mask(port, 1);
- /* Let charge manager know there's no more charge available. */
- charge_manager_update_charge(CHARGE_SUPPLIER_NONE, port, NULL);
+ /*
+ * Let charge manager know there's no more charge available for the
+ * supplier type that was most recently detected.
+ */
+ bc12_update_supplier(CHARGE_SUPPLIER_NONE, port, NULL);
#if defined(CONFIG_POWER_PP5000_CONTROL) && defined(HAS_TASK_CHIPSET)
/* Indicate PP5000_A rail is not required by USB_CHG task. */
power_5v_enable(task_get_current(), 0);
@@ -215,6 +238,14 @@ void usb_charger_task(void *u)
{
int port = (task_get_current() == TASK_ID_USB_CHG_P0 ? 0 : 1);
uint32_t evt;
+ int i;
+
+ /*
+ * Set most recent bc1.2 detection supplier result to
+ * CHARGE_SUPPLIER_NONE for all ports.
+ */
+ for (i = 0; i < CONFIG_USB_PD_PORT_COUNT; i++)
+ bc12_supplier[port] = CHARGE_SUPPLIER_NONE;
/*
* The is no specific initialization required for the pi3usb9201 other
@@ -260,9 +291,9 @@ void usb_charger_task(void *u)
new_chg.voltage = USB_CHARGER_VOLTAGE_MV;
new_chg.current = USB_CHARGER_MIN_CURR_MA;
- charge_manager_update_charge(
- CHARGE_SUPPLIER_OTHER,
- port, &new_chg);
+ /* Save supplier type and notify chg manager */
+ bc12_update_supplier(CHARGE_SUPPLIER_OTHER,
+ port, &new_chg);
CPRINTS("pi3usb9201[p%d]: bc1.2 failed use "
"defaults", port);
}
@@ -279,6 +310,11 @@ void usb_charger_task(void *u)
int rv;
/*
+ * Update the charge manager if bc1.2 client mode is
+ * currently active.
+ */
+ bc12_update_supplier(CHARGE_SUPPLIER_NONE, port, NULL);
+ /*
* If the port is in DFP mode, then need to set mode to
* CDP_HOST which will auto close D+/D- switches.
*/
diff --git a/driver/mag_bmm150.h b/driver/mag_bmm150.h
index 94777e1b61..a504b1a20a 100644
--- a/driver/mag_bmm150.h
+++ b/driver/mag_bmm150.h
@@ -84,10 +84,10 @@
*
* To be safe, declare only 75% of the value.
*/
-#define BMM150_MAG_MAX_FREQ(_preset) (750000000 / \
+#define __BMM150_MAG_MAX_FREQ(_preset) (750000000 / \
(145 * BMM150_REP(_preset, XY) + 500 * BMM150_REP(_preset, Z) + 980))
-#if (BMM150_MAG_MAX_FREQ(SPECIAL) > CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ)
+#if (__BMM150_MAG_MAX_FREQ(SPECIAL) > CONFIG_EC_MAX_SENSOR_FREQ_MILLIHZ)
#error "EC too slow for magnetometer"
#endif
@@ -118,6 +118,18 @@ struct bmm150_private_data {
#define BMM150_CAL(_s) \
(&BMI160_GET_DATA(_s)->compass.cal)
+#ifdef CONFIG_MAG_BMI160_BMM150
+#include "accelgyro_bmi160.h"
+/*
+ * Behind a BMI160, the BMM150 is in forced mode. Be sure to choose a frequency
+ * comptible with BMI160.
+ */
+#define BMM150_MAG_MAX_FREQ(_preset) \
+ BMI160_REG_TO_ODR(BMI160_ODR_TO_REG(__BMM150_MAG_MAX_FREQ(_preset)))
+#else
+#define BMM150_MAG_MAX_FREQ(_preset) __BMM150_MAG_MAX_FREQ(_preset)
+#endif
+
/* Specific initialization of BMM150 when behing BMI160 */
int bmm150_init(const struct motion_sensor_t *s);
diff --git a/driver/mag_lis2mdl.c b/driver/mag_lis2mdl.c
index 99415e9695..4a68c02aa8 100644
--- a/driver/mag_lis2mdl.c
+++ b/driver/mag_lis2mdl.c
@@ -14,6 +14,8 @@
#include "driver/sensorhub_lsm6dsm.h"
#include "driver/accelgyro_lsm6dsm.h"
#include "driver/stm_mems_common.h"
+#include "hwtimer.h"
+#include "mag_cal.h"
#include "task.h"
#ifdef CONFIG_MAG_LSM6DSM_LIS2MDL
@@ -22,6 +24,8 @@
#endif
#endif
+#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args)
+
void lis2mdl_normalize(const struct motion_sensor_t *s,
intv3_t v,
uint8_t *raw)
@@ -88,10 +92,7 @@ static int get_range(const struct motion_sensor_t *s)
static int set_offset(const struct motion_sensor_t *s,
const int16_t *offset, int16_t temp)
{
-
-#ifdef CONFIG_LSM6DSM_SEC_I2C
struct mag_cal_t *cal = LIS2MDL_CAL(s);
-#endif
cal->bias[X] = offset[X];
cal->bias[Y] = offset[Y];
@@ -109,9 +110,7 @@ static int set_offset(const struct motion_sensor_t *s,
static int get_offset(const struct motion_sensor_t *s,
int16_t *offset, int16_t *temp)
{
-#ifdef CONFIG_LSM6DSM_SEC_I2C
struct mag_cal_t *cal = LIS2MDL_CAL(s);
-#endif
intv3_t offset_int;
rotate(cal->bias, *s->rot_standard_ref, offset_int);
@@ -159,7 +158,7 @@ int lis2mdl_thru_lsm6dsm_init(const struct motion_sensor_t *s)
LSM6DSM_MAIN_SENSOR(s),
CONFIG_ACCELGYRO_SEC_ADDR,
LIS2MDL_CFG_REG_A_ADDR,
- LIS2MDL_ODR_100HZ | LIS2MDL_CONT_MODE);
+ LIS2MDL_ODR_50HZ | LIS2MDL_CONT_MODE);
if (ret != EC_SUCCESS)
goto err_unlock;
@@ -180,18 +179,219 @@ err_unlock:
mutex_unlock(s->mutex);
return ret;
}
-#endif /* CONFIG_MAG_LSM6DSM_LIS2MDL */
+
+#else /* END: CONFIG_MAG_LSM6DSM_LIS2MDL */
+
+/**
+ * Checks whether or not data is ready. If the check succeeds EC_SUCCESS will be
+ * returned and the ready target written with the axes that are available, see:
+ * <ul>
+ * <li>LIS2MDL_X_DIRTY</li>
+ * <li>LIS2MDL_Y_DIRTY</li>
+ * <li>LIS2MDL_Z_DIRTY</li>
+ * <li>LIS2MDL_XYZ_DIRTY</li>
+ * </ul>
+ *
+ * @param s Motion sensor pointer
+ * @param[out] ready Writeback pointer to store the result.
+ * @return EC_SUCCESS when the status register was read successfully.
+ */
+static int lis2mdl_is_data_ready(const struct motion_sensor_t *s, int *ready)
+{
+ int ret, tmp;
+
+ ret = st_raw_read8(s->port, s->addr, LIS2MDL_STATUS_REG, &tmp);
+ if (ret != EC_SUCCESS) {
+ *ready = 0;
+ return ret;
+ }
+
+ *ready = tmp & LIS2MDL_XYZ_DIRTY_MASK;
+ return EC_SUCCESS;
+
+}
+
+/**
+ * Read the most recent data from the sensor. If no new data is available,
+ * simply return the last available values.
+ *
+ * @param s Motion sensor pointer
+ * @param v A vector of 3 ints for x, y, z values.
+ * @return EC_SUCCESS when the values were read successfully or no new data was
+ * available.
+ */
+int lis2mdl_read(const struct motion_sensor_t *s, intv3_t v)
+{
+ int ret = EC_SUCCESS, ready = 0;
+ uint8_t raw[OUT_XYZ_SIZE];
+
+ ret = lis2mdl_is_data_ready(s, &ready);
+ if (ret != EC_SUCCESS)
+ return ret;
+
+ /*
+ * If sensor data is not ready, return the previous read data.
+ * Note: return success so that the motion sensor task can read again to
+ * get the latest updated sensor data quickly.
+ */
+ if (!ready) {
+ if (v != s->raw_xyz)
+ memcpy(v, s->raw_xyz, sizeof(intv3_t));
+ return ret;
+ }
+
+ mutex_lock(s->mutex);
+ ret = st_raw_read_n(s->port, s->addr, LIS2MDL_OUT_REG, raw,
+ OUT_XYZ_SIZE);
+ mutex_unlock(s->mutex);
+ if (ret == EC_SUCCESS) {
+ lis2mdl_normalize(s, v, raw);
+ rotate(v, *s->rot_standard_ref, v);
+ }
+ return ret;
+}
+
+/**
+ * Initialize the sensor. This function will verify the who-am-I register
+ */
+int lis2mdl_init(const struct motion_sensor_t *s)
+{
+ int ret = EC_ERROR_UNKNOWN, who_am_i, count = LIS2MDL_STARTUP_MS;
+ struct stprivate_data *data = s->drv_data;
+ struct mag_cal_t *cal = LIS2MDL_CAL(s);
+
+ /* Check who am I value */
+ do {
+ ret = st_raw_read8(s->port, LIS2MDL_ADDR, LIS2MDL_WHO_AM_I_REG,
+ &who_am_i);
+ if (ret != EC_SUCCESS) {
+ /* Make sure we wait for the chip to start up. Sleep 1ms
+ * and try again.
+ */
+ udelay(10);
+ count--;
+ } else {
+ break;
+ }
+ } while (count > 0);
+ if (ret != EC_SUCCESS)
+ return ret;
+ if (who_am_i != LIS2MDL_WHO_AM_I)
+ return EC_ERROR_ACCESS_DENIED;
+
+ mutex_lock(s->mutex);
+
+ /* Reset the sensor */
+ ret = st_raw_write8(s->port, LIS2MDL_ADDR, LIS2MDL_CFG_REG_A_ADDR,
+ LIS2MDL_FLAG_SW_RESET);
+ if (ret != EC_SUCCESS)
+ goto lis2mdl_init_error;
+
+ mutex_unlock(s->mutex);
+
+ if (ret != EC_SUCCESS)
+ return ret;
+
+ init_mag_cal(cal);
+ cal->radius = 0.0f;
+ data->resol = LIS2DSL_RESOLUTION;
+ return sensor_init_done(s);
+
+lis2mdl_init_error:
+ mutex_unlock(s->mutex);
+ return ret;
+}
+
+/**
+ * Set the data rate of the sensor. Use a rate of 0 or below to turn off the
+ * magnetometer. All other values will turn on the sensor in continuous mode.
+ * The rate will be set to the nearest available value:
+ * <ul>
+ * <li>LIS2MDL_ODR_10HZ</li>
+ * <li>LIS2MDL_ODR_20HZ</li>
+ * <li>LIS2MDL_ODR_50HZ</li>
+ * </ul>
+ *
+ * @param s Motion sensor pointer
+ * @param rate Rate (mHz)
+ * @param rnd Flag used to tell whether or not to round up (1) or down (0)
+ * @return EC_SUCCESS when the rate was successfully changed.
+ */
+int lis2mdl_set_data_rate(const struct motion_sensor_t *s, int rate, int rnd)
+{
+ int ret = EC_SUCCESS, normalized_rate = 0;
+ uint8_t reg_val = 0;
+ struct mag_cal_t *cal = LIS2MDL_CAL(s);
+ struct stprivate_data *data = s->drv_data;
+
+ if (rate > 0) {
+ if (rnd)
+ /* Round up */
+ reg_val = rate <= 10000 ? LIS2MDL_ODR_10HZ
+ : rate <= 20000 ? LIS2MDL_ODR_20HZ
+ : LIS2MDL_ODR_50HZ;
+ else
+ /* Round down */
+ reg_val = rate < 20000 ? LIS2MDL_ODR_10HZ
+ : rate < 50000 ? LIS2MDL_ODR_20HZ
+ : LIS2MDL_ODR_50HZ;
+ }
+
+ normalized_rate = rate <= 0 ? 0
+ : reg_val == LIS2MDL_ODR_10HZ ? 10000
+ : reg_val == LIS2MDL_ODR_20HZ ? 20000
+ : 50000;
+
+ /* b/130417518 - verify why skipping init_mag_cal is needed here. */
+ if (normalized_rate == data->base.odr)
+ return ret;
+
+ init_mag_cal(cal);
+
+ if (normalized_rate > 0)
+ cal->batch_size = MAX(
+ MAG_CAL_MIN_BATCH_SIZE,
+ (normalized_rate * 1000) /
+ MAG_CAL_MIN_BATCH_WINDOW_US);
+ else
+ cal->batch_size = 0;
+
+ mutex_lock(s->mutex);
+ if (rate <= 0) {
+ ret = st_raw_write8(s->port, LIS2MDL_ADDR,
+ LIS2MDL_CFG_REG_A_ADDR,
+ LIS2MDL_FLAG_SW_RESET);
+ } else {
+ /* Add continuous and temp compensation flags */
+ reg_val |= LIS2MDL_MODE_CONT | LIS2MDL_FLAG_TEMP_COMPENSATION;
+ ret = st_raw_write8(s->port, LIS2MDL_ADDR,
+ LIS2MDL_CFG_REG_A_ADDR, reg_val);
+ }
+
+ mutex_unlock(s->mutex);
+
+ if (ret == EC_SUCCESS)
+ data->base.odr = normalized_rate;
+
+ return ret;
+}
+
+#endif /* CONFIG_MAG_LIS2MDL */
const struct accelgyro_drv lis2mdl_drv = {
#ifdef CONFIG_MAG_LSM6DSM_LIS2MDL
.init = lis2mdl_thru_lsm6dsm_init,
.read = lis2mdl_thru_lsm6dsm_read,
.set_data_rate = lsm6dsm_set_data_rate,
-#endif
+#else /* CONFIG_MAG_LSM6DSM_LIS2MDL */
+ .init = lis2mdl_init,
+ .read = lis2mdl_read,
+ .set_data_rate = lis2mdl_set_data_rate,
+#endif /* !CONFIG_MAG_LSM6DSM_LIS2MDL */
.set_range = set_range,
.get_range = get_range,
- .get_resolution = st_get_resolution,
.get_data_rate = st_get_data_rate,
+ .get_resolution = st_get_resolution,
.set_offset = set_offset,
.get_offset = get_offset,
};
diff --git a/driver/mag_lis2mdl.h b/driver/mag_lis2mdl.h
index 9d1941de6a..f87b4e1f91 100644
--- a/driver/mag_lis2mdl.h
+++ b/driver/mag_lis2mdl.h
@@ -12,27 +12,41 @@
#include "mag_cal.h"
#include "stm_mems_common.h"
-#define LIS2MDL_I2C_ADDR(__x) (__x << 1)
-
/*
- * 7-bit address is 0011110Xb. Where 'X' is determined
- * by the voltage on the ADDR pin
+ * 8-bit address is 0011110Wb where the last bit represents whether the
+ * operation is a read or a write.
*/
-#define LIS2MDL_ADDR0 LIS2MDL_I2C_ADDR(0x1e)
-#define LIS2MDL_ADDR1 LIS2MDL_I2C_ADDR(0x1f)
+#define LIS2MDL_ADDR 0x3c
+
+#define LIS2MDL_STARTUP_MS 10
/* Registers */
#define LIS2MDL_WHO_AM_I_REG 0x4f
-#define LIS2MDL_WHO_AM_I 0x40
-
#define LIS2MDL_CFG_REG_A_ADDR 0x60
-#define LIS2MDL_SW_RESET 0x20
-#define LIS2MDL_ODR_100HZ 0xc
-#define LIS2MDL_CONT_MODE 0x0
-
+#define LIS2MDL_INT_CTRL_REG 0x63
#define LIS2MDL_STATUS_REG 0x67
#define LIS2MDL_OUT_REG 0x68
+#define LIS2MDL_WHO_AM_I 0x40
+
+#define LIS2MDL_FLAG_TEMP_COMPENSATION 0x80
+#define LIS2MDL_FLAG_REBOOT 0x40
+#define LIS2MDL_FLAG_SW_RESET 0x20
+#define LIS2MDL_FLAG_LOW_POWER 0x10
+#define LIS2MDL_ODR_50HZ 0x08
+#define LIS2MDL_ODR_20HZ 0x04
+#define LIS2MDL_ODR_10HZ 0x00
+#define LIS2MDL_MODE_IDLE 0x03
+#define LIS2MDL_MODE_SINGLE 0x01
+#define LIS2MDL_MODE_CONT 0x00
+#define LIS2MDL_ODR_MODE_MASK 0x8f
+
+#define LIS2MDL_X_DIRTY 0x01
+#define LIS2MDL_Y_DIRTY 0x02
+#define LIS2MDL_Z_DIRTY 0x04
+#define LIS2MDL_XYZ_DIRTY 0x08
+#define LIS2MDL_XYZ_DIRTY_MASK 0x0f
+
#define LIS2DSL_RESOLUTION 16
/*
* Maximum sensor data range (milligauss):
@@ -42,16 +56,27 @@
*/
#define LIS2MDL_RATIO(_in) (((_in) * 24) / 10)
-
struct lis2mdl_private_data {
/* lsm6dsm_data union requires cal be first element */
struct mag_cal_t cal;
+#ifndef CONFIG_LSM6DSM_SEC_I2C
+ struct stprivate_data data;
+#endif
#ifdef CONFIG_MAG_BMI160_LIS2MDL
intv3_t hn; /* last sample for offset compensation */
int hn_valid;
#endif
};
+#ifndef CONFIG_LSM6DSM_SEC_I2C
+#define LIS2MDL_ST_DATA(g) (&((g).data))
+
+#ifdef CONFIG_MAG_CALIBRATE
+#define LIS2MDL_CAL(_s) \
+ (&(DOWNCAST(s->drv_data, struct lis2mdl_private_data, data)->cal))
+#endif
+#endif
+
#define LIS2MDL_ODR_MIN_VAL 10000
#define LIS2MDL_ODR_MAX_VAL 50000
diff --git a/driver/tcpm/anx74xx.c b/driver/tcpm/anx74xx.c
index 248887be28..274e668960 100644
--- a/driver/tcpm/anx74xx.c
+++ b/driver/tcpm/anx74xx.c
@@ -1045,17 +1045,16 @@ static int anx74xx_tcpm_init(int port)
/* Initialize interrupt open-drain */
rv |= tcpc_read(port, ANX74XX_REG_INTP_VCONN_CTRL, &reg);
- if (tcpc_config[port].od == TCPC_ALERT_OPEN_DRAIN)
+ if (tcpc_config[port].flags & TCPC_FLAGS_ALERT_OD)
reg |= ANX74XX_REG_R_INTERRUPT_OPEN_DRAIN;
else
reg &= ~ANX74XX_REG_R_INTERRUPT_OPEN_DRAIN;
rv |= tcpc_write(port, ANX74XX_REG_INTP_VCONN_CTRL, reg);
/* Initialize interrupt polarity */
- rv |= tcpc_write(port, ANX74XX_REG_IRQ_STATUS,
- tcpc_config[port].pol == TCPC_ALERT_ACTIVE_LOW ?
- ANX74XX_REG_IRQ_POL_LOW :
- ANX74XX_REG_IRQ_POL_HIGH);
+ reg = tcpc_config[port].flags & TCPC_FLAGS_ALERT_ACTIVE_HIGH ?
+ ANX74XX_REG_IRQ_POL_HIGH : ANX74XX_REG_IRQ_POL_LOW;
+ rv |= tcpc_write(port, ANX74XX_REG_IRQ_STATUS, reg);
/* unmask interrupts */
rv |= tcpc_read(port, ANX74XX_REG_IRQ_EXT_MASK_1, &reg);
diff --git a/driver/tcpm/tcpci.c b/driver/tcpm/tcpci.c
index 59393a62a9..f9e5db929c 100644
--- a/driver/tcpm/tcpci.c
+++ b/driver/tcpm/tcpci.c
@@ -761,6 +761,13 @@ int tcpci_tcpm_init(int port)
/* Update VBUS status */
tcpc_vbus[port] = power_status &
TCPC_REG_POWER_STATUS_VBUS_PRES ? 1 : 0;
+#if defined(CONFIG_USB_PD_VBUS_DETECT_TCPC) && defined(CONFIG_USB_CHARGER)
+ /*
+ * Set Vbus change now in case the TCPC doesn't send a power status
+ * changed interrupt for it later.
+ */
+ usb_charger_vbus_change(port, tcpc_vbus[port]);
+#endif
error = init_alert_mask(port);
if (error)
return error;
diff --git a/extra/i2c_pseudo/install b/extra/i2c_pseudo/install
index 6d9e783b70..f4fbecedc9 100755
--- a/extra/i2c_pseudo/install
+++ b/extra/i2c_pseudo/install
@@ -1,27 +1,11 @@
#!/bin/sh
#
-# This attempts to build and install the i2c-pseudo Linux kernel module,
-# following the procedure documented in README.
+# This attempts to build and install the i2c-pseudo Linux kernel module.
set -e
-if ! lsmod | grep -q '^i2c_dev\s'; then
- sudo depmod -a
- sudo modprobe -q i2c-dev
-fi
-
-if lsmod | grep -q '^i2c_pseudo\s'; then
- echo "SUCCESS: i2c-dev module already loaded"
- exit
-fi
-
-if sudo modprobe -q i2c-pseudo; then
- echo "SUCCESS: loaded already-installed i2c-pseudo module"
- exit
-fi
-
+make clean
make
-
ret=0
sudo make modules_install || ret="$?"
diff --git a/fuzz/build.mk b/fuzz/build.mk
index fda788fd93..76d7d13f5b 100644
--- a/fuzz/build.mk
+++ b/fuzz/build.mk
@@ -25,7 +25,7 @@ endif
# Does your object file need to link against cstdlib?
# Yes -> use <obj_name>-rw
# Otherwise use <obj_name>-y
-cr50_fuzz-rw = cr50_fuzz.o pinweaver_model.o mem_hash_tree.o
+cr50_fuzz-rw = cr50_fuzz.o pinweaver_model.o mem_hash_tree.o nvmem_tpm2_mock.o
host_command_fuzz-y = host_command_fuzz.o
usb_pd_fuzz-y = usb_pd_fuzz.o
@@ -35,6 +35,9 @@ $(out)/RW/fuzz/pinweaver_model.o: ${CR50_PROTO_HEADERS}
$(out)/RW/fuzz/cr50_fuzz.o: ${CR50_PROTO_HEADERS}
$(out)/RW/fuzz/cr50_fuzz.o: CPPFLAGS+=${LIBPROTOBUF_MUTATOR_CFLAGS}
+TPM2_LIB_ROOT := $(CROS_WORKON_SRCROOT)/src/third_party/tpm2
+$(out)/RW/fuzz/nvmem_tpm2_mock.o: CFLAGS += -I$(TPM2_LIB_ROOT)
+
$(out)/cr50_fuzz.exe: $(out)/cryptoc/libcryptoc.a \
$(out)/gen/fuzz/cr50_fuzz.pb.o \
$(out)/gen/fuzz/pinweaver/pinweaver.pb.o \
diff --git a/fuzz/cr50_fuzz.cc b/fuzz/cr50_fuzz.cc
index e891ee5d85..186700f415 100644
--- a/fuzz/cr50_fuzz.cc
+++ b/fuzz/cr50_fuzz.cc
@@ -71,7 +71,6 @@ void InitializeFuzzerRun() {
memset(__host_flash, 0xff, sizeof(__host_flash));
nvmem_init();
nvmem_enable_commits();
- initvars();
srand(0);
}
diff --git a/fuzz/fuzz_config.h b/fuzz/fuzz_config.h
index 362df69c56..dd2cdc5ae9 100644
--- a/fuzz/fuzz_config.h
+++ b/fuzz/fuzz_config.h
@@ -17,6 +17,7 @@
#define CONFIG_PINWEAVER
#define CONFIG_UPTO_SHA512
#define SHA512_SUPPORT
+#define CONFIG_MALLOC
/******************************************************************************/
/* From chip/g/config_chip.h */
@@ -29,6 +30,11 @@
/******************************************************************************/
/* From board/cr50/board.h */
/* Non-volatile counter storage for U2F */
+#define CONFIG_CRC8
+#define CONFIG_FLASH_ERASED_VALUE32 (-1U)
+#define CONFIG_FLASH_LOG
+#define CONFIG_FLASH_LOG_BASE CONFIG_PROGRAM_MEMORY_BASE
+#define CONFIG_FLASH_LOG_SPACE 0x800
#define CONFIG_FLASH_NVCOUNTER
#define CONFIG_FLASH_NVCTR_SIZE CONFIG_FLASH_BANK_SIZE
#define CONFIG_FLASH_NVCTR_BASE_A (CONFIG_PROGRAM_MEMORY_BASE + \
@@ -41,14 +47,23 @@
#define CONFIG_FLASH_NVMEM_OFFSET_A (CFG_TOP_A_OFF + CONFIG_FLASH_NVCTR_SIZE)
#define CONFIG_FLASH_NVMEM_OFFSET_B (CFG_TOP_B_OFF + CONFIG_FLASH_NVCTR_SIZE)
/* Address of start of Nvmem area */
-#define CONFIG_FLASH_NVMEM_BASE_A (CONFIG_PROGRAM_MEMORY_BASE + \
- CONFIG_FLASH_NVMEM_OFFSET_A)
-#define CONFIG_FLASH_NVMEM_BASE_B (CONFIG_PROGRAM_MEMORY_BASE + \
- CONFIG_FLASH_NVMEM_OFFSET_B)
+#define CONFIG_FLASH_NVMEM_BASE_A \
+ (CONFIG_PROGRAM_MEMORY_BASE + CONFIG_FLASH_NVMEM_OFFSET_A)
+#define CONFIG_FLASH_NVMEM_BASE_B \
+ (CONFIG_PROGRAM_MEMORY_BASE + CONFIG_FLASH_NVMEM_OFFSET_B)
+#define CONFIG_FLASH_NEW_NVMEM_BASE_A \
+ (CONFIG_FLASH_NVMEM_BASE_A + CONFIG_FLASH_BANK_SIZE)
+#define CONFIG_FLASH_NEW_NVMEM_BASE_B \
+ (CONFIG_FLASH_NVMEM_BASE_B + CONFIG_FLASH_BANK_SIZE)
/* Size partition in NvMem */
#define NVMEM_PARTITION_SIZE (CFG_TOP_SIZE - CONFIG_FLASH_NVCTR_SIZE)
/* Size in bytes of NvMem area */
#define CONFIG_FLASH_NVMEM_SIZE (NVMEM_PARTITION_SIZE * NVMEM_NUM_PARTITIONS)
+
+#define NEW_NVMEM_PARTITION_SIZE (NVMEM_PARTITION_SIZE - CONFIG_FLASH_BANK_SIZE)
+#define NEW_NVMEM_TOTAL_PAGES \
+ (2 * NEW_NVMEM_PARTITION_SIZE / CONFIG_FLASH_BANK_SIZE)
+
/* Enable <key, value> variable support. */
#define CONFIG_FLASH_NVMEM_VARS
#define NVMEM_CR50_SIZE 272
diff --git a/fuzz/nvmem_tpm2_mock.c b/fuzz/nvmem_tpm2_mock.c
new file mode 100644
index 0000000000..5fac9674a2
--- /dev/null
+++ b/fuzz/nvmem_tpm2_mock.c
@@ -0,0 +1,228 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* Stuff from tpm2 directory. */
+#define NV_C
+#include "Global.h"
+#undef NV_C
+#include "NV_fp.h"
+#include "tpm_generated.h"
+
+#include "nvmem.h"
+#include "util.h"
+
+#define NVMEM_CR50_SIZE 272
+
+#ifndef TEST_FUZZ
+uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {MOCK_NV_MEMORY_SIZE,
+ NVMEM_CR50_SIZE};
+#endif
+
+uint32_t s_evictNvStart;
+uint32_t s_evictNvEnd;
+
+/* Calculate size of TPM NVMEM. */
+#define MOCK_NV_MEMORY_SIZE \
+ (NVMEM_PARTITION_SIZE - sizeof(struct nvmem_tag) - NVMEM_CR50_SIZE)
+
+/*
+ * Sizes of the reserved objects stored in the TPM NVMEM. Note that the second
+ * last object is in fact a variable size field starting with 4 bytes of size
+ * and then up to 512 bytes of actual index data. The array below assumes that
+ * the full 512 bytes of the index space are used.
+ */
+const uint16_t res_sizes[] = {4, 2, 2, 2, 66, 66, 66, 66, 66, 66,
+ 34, 34, 34, 66, 66, 66, 8, 4, 134, 28,
+ 3, 4, 4, 4, 4, 4, 2, 15, 2, 8,
+ 4, 4, 4, 96, 2844, 424, 516, 8};
+
+static uint16_t res_addrs[ARRAY_SIZE(res_sizes)];
+
+BOOL NvEarlyStageFindHandle(TPM_HANDLE handle)
+{
+ size_t i;
+
+ res_addrs[0] = 0;
+
+ for (i = 1; i < ARRAY_SIZE(res_addrs); i++)
+ res_addrs[i] = res_addrs[i - 1] + res_sizes[i - 1];
+
+ s_evictNvStart = res_addrs[i - 1] + res_sizes[i - 1];
+
+ s_evictNvEnd = MOCK_NV_MEMORY_SIZE;
+ return 0;
+}
+
+void NvGetReserved(UINT32 index, NV_RESERVED_ITEM *ri)
+{
+ if (index < ARRAY_SIZE(res_sizes)) {
+ ri->size = res_sizes[index];
+ ri->offset = res_addrs[index];
+ } else {
+ ri->size = 0;
+ }
+}
+
+UINT16 UINT16_Marshal(UINT16 *source, BYTE **buffer, INT32 *size)
+{
+ uint16_t value;
+
+ if (!size || (*size < sizeof(value)))
+ return 0;
+
+ value = htobe16(*source);
+
+ memcpy(*buffer, &value, sizeof(value));
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return sizeof(value);
+}
+
+UINT16 UINT32_Marshal(UINT32 *source, BYTE **buffer, INT32 *size)
+{
+ uint32_t value;
+
+ if (!size || (*size < sizeof(value)))
+ return 0;
+
+ value = htobe32(*source);
+
+ memcpy(*buffer, &value, sizeof(value));
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return sizeof(value);
+}
+
+UINT16 UINT64_Marshal(UINT64 *source, BYTE **buffer, INT32 *size)
+{
+ uint64_t value;
+
+ if (!size || (*size < sizeof(value)))
+ return 0;
+
+ value = htobe64(*source);
+
+ memcpy(*buffer, &value, sizeof(value));
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return sizeof(value);
+}
+
+UINT16 TPM2B_DIGEST_Marshal(TPM2B_DIGEST *source, BYTE **buffer, INT32 *size)
+{
+ UINT16 total_size;
+ INT32 i;
+ uint8_t *p;
+
+ total_size = UINT16_Marshal(&source->t.size, buffer, size);
+ p = *buffer;
+
+ for (i = 0; (i < source->t.size) && *size; ++i) {
+ *p++ = source->t.buffer[i];
+ *size -= 1;
+ }
+
+ total_size += i;
+ *buffer = p;
+
+ return total_size;
+}
+
+uint16_t TPM2B_AUTH_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size)
+{
+ return TPM2B_DIGEST_Marshal(source, buffer, size);
+}
+
+uint16_t TPM2B_NONCE_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size)
+{
+ return TPM2B_DIGEST_Marshal(source, buffer, size);
+}
+
+TPM_RC UINT16_Unmarshal(UINT16 *target, BYTE **buffer, INT32 *size)
+{
+ uint16_t value;
+
+ if (!size || *size < sizeof(value))
+ return TPM_RC_INSUFFICIENT;
+
+ memcpy(&value, *buffer, sizeof(value));
+ *target = be16toh(value);
+
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return TPM_RC_SUCCESS;
+}
+
+TPM_RC UINT32_Unmarshal(UINT32 *target, BYTE **buffer, INT32 *size)
+{
+ uint32_t value;
+
+ if (!size || *size < sizeof(value))
+ return TPM_RC_INSUFFICIENT;
+
+ memcpy(&value, *buffer, sizeof(value));
+ *target = be32toh(value);
+
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return TPM_RC_SUCCESS;
+}
+
+TPM_RC UINT64_Unmarshal(UINT64 *target, BYTE **buffer, INT32 *size)
+{
+ uint64_t value;
+
+ if (!size || *size < sizeof(value))
+ return TPM_RC_INSUFFICIENT;
+
+ memcpy(&value, *buffer, sizeof(value));
+ *target = be64toh(value);
+
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return TPM_RC_SUCCESS;
+}
+
+TPM_RC TPM2B_DIGEST_Unmarshal(TPM2B_DIGEST *target, BYTE **buffer, INT32 *size)
+{
+ TPM_RC result;
+ INT32 i;
+ uint8_t *p;
+
+ result = UINT16_Unmarshal(&target->t.size, buffer, size);
+
+ if (result != TPM_RC_SUCCESS)
+ return result;
+
+ if (target->t.size == 0)
+ return TPM_RC_SUCCESS;
+
+ if ((target->t.size > sizeof(TPMU_HA)) || (target->t.size > *size))
+ return TPM_RC_SIZE;
+
+ p = *buffer;
+ for (i = 0; i < target->t.size; ++i)
+ target->t.buffer[i] = *p++;
+
+ *buffer = p;
+ *size -= i;
+
+ return TPM_RC_SUCCESS;
+}
+
+TPM_RC TPM2B_AUTH_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size)
+{
+ return TPM2B_DIGEST_Unmarshal(target, buffer, size);
+}
+
+TPM_RC TPM2B_NONCE_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size)
+{
+ return TPM2B_DIGEST_Unmarshal(target, buffer, size);
+}
diff --git a/include/ccd_config.h b/include/ccd_config.h
index 23a6ebfa05..3952cb9bc9 100644
--- a/include/ccd_config.h
+++ b/include/ccd_config.h
@@ -49,6 +49,15 @@ enum ccd_flag {
/* Flags that can be set via ccd_set_flags(); fill from top down */
+ /* Override BATT_PRES_L at boot */
+ CCD_FLAG_OVERRIDE_BATT_AT_BOOT = BIT(20),
+
+ /*
+ * If overriding BATT_PRES_L at boot, set it to what value
+ * (0=disconnect, 1=connected)
+ */
+ CCD_FLAG_OVERRIDE_BATT_STATE_CONNECT = BIT(21),
+
/* Override write protect at boot */
CCD_FLAG_OVERRIDE_WP_AT_BOOT = BIT(22),
@@ -112,6 +121,9 @@ enum ccd_capability {
/* Allow ccd open from usb */
CCD_CAP_OPEN_FROM_USB = 18,
+ /* Override battery presence temporarily or at boot */
+ CCD_CAP_OVERRIDE_BATT_STATE = 19,
+
/* Number of currently defined capabilities */
CCD_CAP_COUNT
};
@@ -173,6 +185,7 @@ struct ccd_capability_info {
{"FlashRead", CCD_CAP_STATE_ALWAYS}, \
{"OpenNoDevMode", CCD_CAP_STATE_OPEN_REQ}, \
{"OpenFromUSB", CCD_CAP_STATE_OPEN_REQ}, \
+ {"OverrideBatt", CCD_CAP_STATE_IF_OPENED}, \
}
#define CCD_STATE_NAMES { "Locked", "Unlocked", "Opened" }
diff --git a/include/common.h b/include/common.h
index 4f1b1f76a4..da8b4b73e2 100644
--- a/include/common.h
+++ b/include/common.h
@@ -180,6 +180,9 @@ enum ec_error_list {
/* something wrong in a HW */
EC_ERROR_HW_INTERNAL = 25,
+ /* Sometimes operation is expected to have to be repeated. */
+ EC_ERROR_TRY_AGAIN = 26,
+
/* Verified boot errors */
EC_ERROR_VBOOT_SIGNATURE = 0x1000, /* 4096 */
EC_ERROR_VBOOT_SIG_MAGIC = 0x1001,
@@ -200,7 +203,7 @@ enum ec_error_list {
/* Module-internal error codes may use this range. */
EC_ERROR_INTERNAL_FIRST = 0x10000,
- EC_ERROR_INTERNAL_LAST = 0x1FFFF
+ EC_ERROR_INTERNAL_LAST = 0x1FFFF
};
/*
@@ -231,4 +234,31 @@ enum ec_error_list {
/* find the most significant bit. Not defined in n == 0. */
#define __fls(n) (31 - __builtin_clz(n))
+/*
+ * Getting something that works in C and CPP for an arg that may or may
+ * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER"
+ * we match on the placeholder define, insert the "0," for arg1 and generate
+ * the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one).
+ * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when
+ * the last step cherry picks the 2nd arg, we get a zero.
+ */
+#define __ARG_PLACEHOLDER_ 0,
+#define config_enabled(cfg) _config_enabled(cfg)
+#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
+#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0, 0)
+#define ___config_enabled(__ignored, val, ...) val
+
+/**
+ * Checks if a config option is defined to an empty value.
+ *
+ * IS_ENABLED(CONFIG_MY_OPTION) will return 1 in the following case:
+ * #define CONFIG_MY_OPTION
+ *
+ * Otherwise if the option has not been defined or defined with a value, it will
+ * return 0.
+ *
+ * @param CONFIG_OPTION
+ */
+#define IS_ENABLED(option) config_enabled(option)
+
#endif /* __CROS_EC_COMMON_H */
diff --git a/include/config.h b/include/config.h
index 4a1277a331..fb9113afe8 100644
--- a/include/config.h
+++ b/include/config.h
@@ -83,6 +83,19 @@
#undef CONFIG_ACCELGYRO_LSM6DSM
#undef CONFIG_ACCELGYRO_LSM6DSO
+/*
+ * Some chips have a portion of memory which will remain powered even
+ * during a reset. This is called Always-On, or AON memory, and
+ * typically has a separate firmware to manage the memory. These
+ * values can be used to configure the RAM layout for Always-On.
+ *
+ * See chip/ish/ for an example implementation.
+ */
+#undef CONFIG_AON_RAM_BASE
+#undef CONFIG_AON_RAM_SIZE
+#undef CONFIG_AON_ROM_BASE
+#undef CONFIG_AON_ROM_SIZE
+
/* Add sensorhub function for LSM6DSM, required if 2nd device attached. */
#undef CONFIG_SENSORHUB_LSM6DSM
@@ -779,6 +792,33 @@
*/
#undef CONFIG_CHARGER_INPUT_CURRENT
+/* Define to use Power Delivery State Machine Framework */
+#undef CONFIG_USB_SM_FRAMEWORK
+
+/*
+ * This is the maximum number of levels in the hierarchical
+ * state machine framework. Set to 0 for a flat state machine.
+ */
+#define CONFIG_SM_NESTING_NUM 3
+
+/*
+ * Define to enable Type-C State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK
+ */
+#define CONFIG_USB_TYPEC_SM
+
+/*
+ * Define to enable Protocol Layer State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK and CONFIG_USB_TYPEC_SM
+ */
+#define CONFIG_USB_PRL_SM
+
+/*
+ * Define to enable Policy Engine State Machine. Must be enabled
+ * with CONFIG_USB_SM_FRAMEWORK and CONFIG_USB_TYPEC_SM
+ */
+#define CONFIG_USB_PE_SM
+
/*
* Board specific maximum input current limit, in mA.
*/
@@ -1027,6 +1067,7 @@
#define CONFIG_CMD_BATTFAKE
#undef CONFIG_CMD_BATT_MFG_ACCESS
#undef CONFIG_CMD_BUTTON
+#define CONFIG_CMD_CBI
#undef CONFIG_CMD_CCD_DISABLE /* 'ccd disable' subcommand */
#define CONFIG_CMD_CHARGER
#undef CONFIG_CMD_CHARGER_ADC_AMON_BMON
@@ -1606,6 +1647,11 @@
/* Address of start of Nvmem area */
#undef CONFIG_FLASH_NVMEM_BASE_A
#undef CONFIG_FLASH_NVMEM_BASE_B
+
+/* Flash offsets for the 'new' (as of 1/2019) nvmem storage scheme. */
+#undef CONFIG_FLASH_NEW_NVMEM_BASE_A
+#undef CONFIG_FLASH_NEW_NVMEM_BASE_B
+
/* Size in bytes of NvMem area */
#undef CONFIG_FLASH_NVMEM_SIZE
@@ -1798,6 +1844,9 @@
*/
#undef CONFIG_GPIO_INIT_POWER_ON_DELAY_MS
+/* Support getting gpio flags. */
+#undef CONFIG_GPIO_GET_EXTENDED
+
/* Do we want to detect the lid angle? */
#undef CONFIG_LID_ANGLE
@@ -1955,7 +2004,13 @@
#undef CONFIG_HOSTCMD_AP_SET_SKUID
/* Command to issue AP reset */
-#undef CONFIG_HOSTCMD_AP_RESET
+#undef CONFIG_HOSTCMD_AP_RESET
+
+/* Flash commands over PD */
+#define CONFIG_HOSTCMD_FLASHPD
+
+/* Set entry in PD MCU's device rw_hash table */
+#define CONFIG_HOSTCMD_RWHASHPD
/* List of host commands whose debug output will be suppressed */
#undef CONFIG_SUPPRESSED_HOST_COMMANDS
@@ -2543,6 +2598,13 @@
/* Support One Time Protection structure */
#undef CONFIG_OTP
+/*
+ * Address to store persistent panic data at. By default, this will be
+ * at the end of RAM, and have a size of sizeof(struct panic_data)
+ */
+#undef CONFIG_PANIC_DATA_BASE
+#undef CONFIG_PANIC_DATA_SIZE
+
/* Support PECI interface to x86 processor */
#undef CONFIG_PECI
@@ -2688,16 +2750,32 @@
#undef CONFIG_PWM_DISPLIGHT
/*
+ * Support keyboard backlight control
+ *
+ * You need to define board_kblight_init unless CONFIG_PWM_KBLIGHT is used.
+ * For example, lm3509 can be registered as a driver in board_kblight_init.
+ */
+#undef CONFIG_KEYBOARD_BACKLIGHT
+
+/*
* Support PWM output to keyboard backlight
*
- * Optionally, lm3509 can be used as a keyboard backlight controller.
- * TODO: Create CONFIG_KEYBOARD_BACKLIGHT to allow lm3509 is used without PWM.
+ * This implies CONFIG_KEYBOARD_BACKLIGHT.
*/
#undef CONFIG_PWM_KBLIGHT
/* Support Real-Time Clock (RTC) */
#undef CONFIG_RTC
+/* Size of each RAM bank in chip, default is CONFIG_RAM_SIZE */
+#undef CONFIG_RAM_BANK_SIZE
+
+/*
+ * Number of RAM banks in chip, default is
+ * CONFIG_RAM_SIZE / CONFIG_RAM_BANK_SIZE
+ */
+#undef CONFIG_RAM_BANKS
+
/* Base address of RAM for the chip */
#undef CONFIG_RAM_BASE
@@ -2989,6 +3067,12 @@
#undef CONFIG_HALL_SENSOR
/*
+ * Board provides board_sensor_at_360 method instead of HALL_SENSOR_GPIO_L as
+ * the means for determining the state of the 360 hall sensor.
+ */
+#undef CONFIG_HALL_SENSOR_CUSTOM
+
+/*
* Add a virtual switch to indicate when detachable device has
* base attached.
*/
@@ -3491,6 +3575,12 @@
/* Use DAC as reference for comparator at 850mV. */
#undef CONFIG_PD_USE_DAC_AS_REF
+/* Type-C VCONN Powered Device */
+#undef CONFIG_USB_TYPEC_VPD
+
+/* Type-C Charge Through VCONN Powered Device */
+#undef CONFIG_USB_TYPEC_CTVPD
+
/* USB Product ID. */
#undef CONFIG_USB_PID
@@ -3789,6 +3879,13 @@
*/
#undef CONFIG_WATCHDOG_HELP
+/*
+ * The maximum number of times that the watchdog timer may reset
+ * before halting the system (or taking some sort of other
+ * chip-dependent corrective action).
+ */
+#define CONFIG_WATCHDOG_MAX_RETRIES 4
+
/* Watchdog period in ms; see also AUX_TIMER_PERIOD_MS */
#define CONFIG_WATCHDOG_PERIOD_MS 1600
@@ -3942,6 +4039,33 @@
#define CONFIG_DATA_RAM_SIZE CONFIG_RAM_SIZE
#endif
+/* Automatic configuration of RAM banks **************************************/
+/* Assume one RAM bank if not specified, auto-compute number of banks */
+#ifndef CONFIG_RAM_BANK_SIZE
+#define CONFIG_RAM_BANK_SIZE CONFIG_RAM_SIZE
+#endif
+
+#ifndef CONFIG_RAM_BANKS
+#define CONFIG_RAM_BANKS (CONFIG_RAM_SIZE / CONFIG_RAM_BANK_SIZE)
+#endif
+
+/******************************************************************************/
+/*
+ * Store panic data at end of memory by default, unless otherwise
+ * configured. This is safe because we don't context switch away from
+ * the panic handler before rebooting, and stacks and data start at
+ * the beginning of RAM.
+ */
+#ifndef CONFIG_PANIC_DATA_SIZE
+#define CONFIG_PANIC_DATA_SIZE sizeof(struct panic_data)
+#endif
+
+#ifndef CONFIG_PANIC_DATA_BASE
+#define CONFIG_PANIC_DATA_BASE (CONFIG_RAM_BASE \
+ + CONFIG_RAM_SIZE \
+ - CONFIG_PANIC_DATA_SIZE)
+#endif
+
/******************************************************************************/
/*
* Set minimum shared memory size, unless it is defined in board file.
diff --git a/include/ec_commands.h b/include/ec_commands.h
index 590ee8202a..aac40efb66 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -1340,7 +1340,7 @@ enum ec_feature_code {
EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37,
/* EC supports audio codec. */
EC_FEATURE_AUDIO_CODEC = 38,
- /* EC Supports SCP. */
+ /* The MCU is a System Companion Processor (SCP). */
EC_FEATURE_SCP = 39,
/* The MCU is an Integrated Sensor Hub */
EC_FEATURE_ISH = 40,
diff --git a/include/flash_log.h b/include/flash_log.h
index 19093ba40b..cfdaf46ce8 100644
--- a/include/flash_log.h
+++ b/include/flash_log.h
@@ -17,6 +17,8 @@ enum flash_event_type {
FE_TPM_I2C_ERROR = 2,
FE_LOG_OVERFLOWS = 3, /* A single byte, overflow counter. */
FE_LOG_LOCKS = 4, /* A single byte, lock failures counter. */
+ FE_LOG_NVMEM = 5, /* NVMEM failure, variable structure. */
+ FE_LOG_TPM_WIPE_ERROR = 6, /* Failed to wipe the TPM */
/*
* Fixed padding value makes it easier to parse log space
@@ -45,6 +47,37 @@ struct flash_log_entry {
uint8_t payload[0]; /* optional additional data payload: 0..63 bytes. */
} __packed;
+/* Payloads for various log events. */
+/* NVMEM failures. */
+enum nvmem_failure_type {
+ NVMEMF_MALLOC = 0,
+ NVMEMF_PH_SIZE_MISMATCH = 1,
+ NVMEMF_READ_UNDERRUN = 2,
+ NVMEMF_INCONSISTENT_FLASH_CONTENTS = 3,
+ NVMEMF_MIGRATION_FAILURE = 4,
+ NVMEMF_LEGACY_ERASE_FAILURE = 5,
+ NVMEMF_EXCESS_DELETE_OBJECTS = 6,
+ NVMEMF_UNEXPECTED_LAST_OBJ = 7,
+ NVMEMF_MISSING_OBJECT = 8,
+ NVMEMF_SECTION_VERIFY = 9,
+ NVMEMF_PRE_ERASE_MISMATCH = 10,
+ NVMEMF_PAGE_LIST_OVERFLOW = 11
+};
+
+/* Not all nvmem failures require payload. */
+struct nvmem_failure_payload {
+ uint8_t failure_type;
+ union {
+ uint16_t size; /* How much memory was requested. */
+ struct {
+ uint16_t ph_offset;
+ uint16_t expected;
+ } ph __packed;
+ uint16_t underrun_size; /* How many bytes short. */
+ uint8_t last_obj_type;
+ } __packed;
+} __packed;
+
/* Returned in the "type" field, when there is no entry available */
#define FLASH_LOG_NO_ENTRY 0xff
#define MAX_FLASH_LOG_PAYLOAD_SIZE ((1 << 6) - 1)
diff --git a/include/gpio.h b/include/gpio.h
index f01cd5147e..c778c5d3a8 100644
--- a/include/gpio.h
+++ b/include/gpio.h
@@ -161,7 +161,11 @@ int gpio_is_implemented(enum gpio_signal signal);
*/
void gpio_set_flags(enum gpio_signal signal, int flags);
-#ifdef CONFIG_CMD_GPIO_EXTENDED
+#if defined(CONFIG_CMD_GPIO_EXTENDED) && !defined(CONFIG_GPIO_GET_EXTENDED)
+#define CONFIG_GPIO_GET_EXTENDED
+#endif
+
+#ifdef CONFIG_GPIO_GET_EXTENDED
/**
* Get the current flags for a signal.
*
diff --git a/include/host_command_heci.h b/include/host_command_heci.h
index af85178e8a..1dcb054ff6 100644
--- a/include/host_command_heci.h
+++ b/include/host_command_heci.h
@@ -7,6 +7,6 @@
#define __HOST_COMMAND_HECI_H
/* send an event message to the ap */
-void heci_send_mkbp_event(void);
+int heci_send_mkbp_event(uint32_t *timestamp);
#endif /* __HOST_COMMAND_HECI_H */
diff --git a/include/link_defs.h b/include/link_defs.h
index 0f83d0173e..4dd0441824 100644
--- a/include/link_defs.h
+++ b/include/link_defs.h
@@ -116,7 +116,7 @@ extern void *__dram_bss_start;
extern void *__dram_bss_end;
/* Helper for special chip-specific memory sections */
-#ifdef CONFIG_CHIP_MEMORY_REGIONS
+#if defined(CONFIG_CHIP_MEMORY_REGIONS) || defined(CONFIG_DRAM_BASE)
#define __SECTION(name) __attribute__((section("." STRINGIFY(name) ".50_auto")))
#define __SECTION_KEEP(name) \
__keep __attribute__((section("." STRINGIFY(name) ".keep.50_auto")))
diff --git a/include/mkbp_event.h b/include/mkbp_event.h
index c451c53673..5bfe7ac505 100644
--- a/include/mkbp_event.h
+++ b/include/mkbp_event.h
@@ -33,7 +33,7 @@ int mkbp_send_event(uint8_t event_type);
*
* This can be used if a board has a custom method.
*/
-void mkbp_set_host_active_via_custom(int active);
+int mkbp_set_host_active_via_custom(int active, uint32_t *timestamp);
/*
* The struct to store the event source definition. The get_data routine is
diff --git a/include/motion_sense.h b/include/motion_sense.h
index 65512c733b..52db4b2fb5 100644
--- a/include/motion_sense.h
+++ b/include/motion_sense.h
@@ -107,6 +107,13 @@ struct motion_data_t {
unsigned int ec_rate;
};
+/*
+ * When set, spoof mode will allow the EC to report arbitrary values for any of
+ * the components.
+ */
+#define MOTIONSENSE_FLAG_IN_SPOOF_MODE BIT(1)
+#define MOTIONSENSE_FLAG_INT_SIGNAL BIT(2)
+
struct motion_sensor_t {
/* RO fields */
uint32_t active_mask;
@@ -115,8 +122,11 @@ struct motion_sensor_t {
enum motionsensor_type type;
enum motionsensor_location location;
const struct accelgyro_drv *drv;
+ /* One mutex per physical chip. */
struct mutex *mutex;
void *drv_data;
+ /* Only valid if flags & MOTIONSENSE_FLAG_INT_SIGNAL is true. */
+ enum gpio_signal int_signal;
/* i2c port */
uint8_t port;
@@ -124,10 +134,9 @@ struct motion_sensor_t {
uint8_t addr;
/*
- * When non-zero, spoof mode will allow the EC to report arbitrary
- * values for any of the components.
+ * Various flags, see MOTIONSENSE_FLAG_*
*/
- uint8_t in_spoof_mode;
+ uint32_t flags;
const mat33_fp_t *rot_standard_ref;
@@ -179,13 +188,17 @@ struct motion_sensor_t {
uint16_t lost;
/*
- * Time since last collection:
- * For sensor with hardware FIFO, time since last sample
- * has move from the hardware FIFO to the FIFO (used if fifo rate != 0).
- * For sensor without FIFO, time since the last event was collect
- * from sensor registers.
+ * For sensors in forced mode the ideal time to collect the next
+ * measurement.
+ *
+ * This is unused with sensors that interrupt the ec like hw fifo chips.
+ */
+ uint32_t next_collection;
+
+ /*
+ * The time in us between collection measurements
*/
- uint32_t last_collection;
+ uint32_t collection_rate;
/* Minimum supported sampling frequency in miliHertz for this sensor */
uint32_t min_frequency;
diff --git a/include/new_nvmem.h b/include/new_nvmem.h
new file mode 100644
index 0000000000..110cfd1667
--- /dev/null
+++ b/include/new_nvmem.h
@@ -0,0 +1,155 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef __TPM2_NVMEM_TEST_NEW_NVMEM_H
+#define __TPM2_NVMEM_TEST_NEW_NVMEM_H
+
+#include "common.h"
+#include "nvmem.h"
+#include "nvmem_vars.h"
+#include "util.h"
+
+#define NVMEM_NOT_INITIALIZED ((unsigned int)-1)
+
+/*
+ * A totally arbitrary byte limit for space occupied by (key, value) pairs in
+ * the flash. This is an improvement compared to the legacy case where there
+ * were just 272 bytes dedicated to the (key, value) pairs storage.
+ */
+#define MAX_VAR_TOTAL_SPACE 1000
+
+/*
+ * Let's be reasonable: we're unlikely to have keys longer than 40 or so
+ * bytes, and leave full 255 bytes for the value. Total data space occupied by
+ * a (key, value) pair is not to exceed the value below.
+ */
+#define MAX_VAR_BODY_SPACE 300
+
+enum nn_object_type {
+ NN_OBJ_OLD_COPY = 0,
+ NN_OBJ_TUPLE = 1,
+ NN_OBJ_TPM_RESERVED = 2,
+ NN_OBJ_TPM_EVICTABLE = 3,
+ NN_OBJ_TRANSACTION_DEL = 4,
+ NN_OBJ_ESCAPE = 5,
+ NN_OBJ_ERASED = 7,
+};
+
+/*
+ * Structure placed at the base of each flash page used for NVMEM storage.
+ *
+ * page_number: allows to arrange pages in order they were added
+ *
+ * data_offset: the offset of the first element in the page (space above
+ * page header and below data_offset could be taken by the
+ * 'tail' of the object stored on the previous page).
+ *
+ * page_hash: is used to verify page header integrity
+ */
+struct nn_page_header {
+ unsigned int page_number : 21;
+ unsigned int data_offset : 11;
+ uint32_t page_hash;
+} __packed;
+
+/*
+ * Index of the 'virtual' last reserved object. RAM index space and max
+ * counter objects stored at fixed location in the NVMEM cache are considered
+ * reserved objects by this NVMEM flash layer.
+ */
+#define NV_VIRTUAL_RESERVE_LAST (NV_RESERVE_LAST + 2)
+
+/*
+ * Container header for all blobs stored in flash.
+ *
+ * container_type: type of object stored in the container. MAKE SURE THIS
+ * FIELD TYPE IS THE FIRST FIELD IN THIS STRUCTURE, it is
+ * supposed to be in the first word of the container so that
+ * the type can be erased when object is deleted.
+ *
+ * container_type_copy: immutable copy of the container_type field, used to
+ * verify contents of deleted objects.
+ *
+ * encrypted: set to 1 if contents are encrypted.
+ *
+ * size: size of the payload, 12 bits allocated, 11 bits would be enough for
+ * this use case.
+ *
+ * generation: a free running counter, used to compare ages of two containers
+ *
+ * container_hash: hash of the ENTIRE container, both header and body
+ * included. This field is set to zero before hash is calculated
+ */
+struct nn_container {
+ unsigned int container_type : 3;
+ unsigned int container_type_copy : 3;
+ unsigned int encrypted : 1;
+ unsigned int size : 11;
+ unsigned int generation : 2;
+ unsigned int container_hash : 12;
+} __packed;
+
+/*
+ * A structure to keep context of accessing to a page, page header and offset
+ * define where the next access would happen.
+ */
+struct page_tracker {
+ const struct nn_page_header *ph;
+ uint16_t data_offset;
+};
+
+/*
+ * Helper structure to keep track of accesses to the flash storage.
+ *
+ * mt: main tracker for read or write accesses.
+ *
+ * ct: keeps track of container fetches, as the location of containers has
+ * special significance: it is both part of the seed used when
+ * encrypting/decryping container contents, and also is necessary to
+ * unwind reading of the container header when the end of storage is
+ * reached and a header of all 0xff is read.
+ *
+ * dt: keeps track of delimiters which is important when assessing flash
+ * contents integrity. If during startup the last item in flash is not a
+ * delimiter, this is an indication of a failed transaction, all data
+ * after the previous delimiter needs to be discarded.
+ *
+ * list_index; index of the current page in the list of pages, useful when
+ * sequential reading and need to get to the next page in the
+ * list.
+ */
+
+struct access_tracker {
+ struct page_tracker mt; /* Main tracker. */
+ struct page_tracker ct; /* Container tracker. */
+ struct page_tracker dt; /* Delimiter tracker.*/
+ uint8_t list_index;
+};
+
+enum ec_error_list new_nvmem_init(void);
+enum ec_error_list new_nvmem_migrate(unsigned int nvmem_act_partition);
+enum ec_error_list new_nvmem_save(void);
+
+enum ec_error_list get_next_object(struct access_tracker *at,
+ struct nn_container *ch,
+ int include_deleted);
+
+#if defined(TEST_BUILD) && !defined(TEST_FUZZ)
+#define NVMEM_TEST_BUILD
+enum ec_error_list browse_flash_contents(int);
+enum ec_error_list compact_nvmem(void);
+extern struct access_tracker master_at;
+extern uint16_t total_var_space;
+int is_uninitialized(const void *p, size_t size);
+size_t init_object_offsets(uint16_t *offsets, size_t count);
+struct nn_page_header *list_element_to_ph(size_t el);
+void *evictable_offs_to_addr(uint16_t offset);
+#endif
+
+/*
+ * Clear tpm data from nvmem.
+ */
+int nvmem_erase_tpm_data(void);
+
+#endif /* ! __TPM2_NVMEM_TEST_NEW_NVMEM_H */
diff --git a/include/nvmem.h b/include/nvmem.h
index 341a7c083d..f48c9cd9c4 100644
--- a/include/nvmem.h
+++ b/include/nvmem.h
@@ -178,15 +178,6 @@ int nvmem_move(uint32_t src_offset, uint32_t dest_offset, uint32_t size,
int nvmem_commit(void);
/*
- * Clear out a user's data across all partitions.
- *
- * @param user: The user who's data should be cleared.
- * @return EC_SUCCESS if the user's data across all partitions was
- * cleared. Error othrwise.
- */
-int nvmem_erase_user_data(enum nvmem_users user);
-
-/*
* Temporarily stopping NVMEM commits could be beneficial. One use case is
* when TPM operations need to be sped up.
*
@@ -206,6 +197,15 @@ void nvmem_disable_commits(void);
int nvmem_enable_commits(void);
/*
+ * Function to retrieve the base address of the nvmem cache of the appropriate
+ * user. After migration there is only one user and one base address, this
+ * function will be eliminated.
+ *
+ * @return pointer to the base address.
+ */
+void *nvmem_cache_base(enum nvmem_users user);
+
+/*
* Clear all NVMEM cache in SRAM.
*/
void nvmem_clear_cache(void);
diff --git a/include/nvmem_vars.h b/include/nvmem_vars.h
index add14345e7..eec1e757a2 100644
--- a/include/nvmem_vars.h
+++ b/include/nvmem_vars.h
@@ -65,12 +65,32 @@ struct tuple {
int initvars(void);
/*
- * Look up a key, return a pointer to the tuple. If the key is not found,
- * return NULL. WARNING: The returned pointer is only valid until the next call
- * to setvar() or writevars(). Use it or lose it.
+ * Look up the key passed in the input tuple and fill the value, if found.
+ *
+ * The val_len field in the passed in tuple indicates how much room is
+ * available, the actual value size could be smaller.
+ *
+ * Return:
+ *
+ * EC_SUCCESS - if the key was found and there was enough room in the passed
+ * in tuple for the value.
+ * EC_ERROR_INVAL - if the key was not found.
+ *
+ * EC_ERROR_MEMORY_ALLOCATION - if the value would not fit into the supplied
+ * tuple.
*/
const struct tuple *getvar(const uint8_t *key, uint8_t key_len);
+/*
+ * Free memory held by the previously read tuple.
+ *
+ * Note that tuple address is not the address to be returned to the heap, so
+ * the user must use this function to free this memory. If var is NULL this
+ * function is a no-op.
+ *
+ */
+void freevar(const struct tuple *var);
+
/* Use these to access the data components of a valid struct tuple pointer */
const uint8_t *tuple_key(const struct tuple *);
const uint8_t *tuple_val(const struct tuple *);
@@ -88,6 +108,15 @@ int setvar(const uint8_t *key, uint8_t key_len,
*/
int writevars(void);
+/*
+ * A fully contained function which does not use any available nvmem_vars
+ * methods, as it is used solely for retrieving vars from legacy storage
+ * format. Runs only during migration.
+ */
+const struct tuple *legacy_getnextvar(const struct tuple *prev_var);
+
+int set_local_copy(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/panic.h b/include/panic.h
index 45fc31583d..f3bccd18a0 100644
--- a/include/panic.h
+++ b/include/panic.h
@@ -89,13 +89,8 @@ enum panic_arch {
PANIC_ARCH_X86 = 3, /* Intel x86 */
};
-/*
- * Panic data goes at the end of RAM. This is safe because we don't context
- * switch away from the panic handler before rebooting, and stacks and data
- * start at the beginning of RAM.
- */
-#define PANIC_DATA_PTR ((struct panic_data *)\
- (CONFIG_RAM_BASE + CONFIG_RAM_SIZE - sizeof(struct panic_data)))
+/* Use PANIC_DATA_PTR to refer to the persistent storage location */
+#define PANIC_DATA_PTR ((struct panic_data *)CONFIG_PANIC_DATA_BASE)
/* Flags for panic_data.flags */
/* panic_data.frame is valid */
diff --git a/include/reset_flag_desc.inc b/include/reset_flag_desc.inc
new file mode 100644
index 0000000000..425332b31b
--- /dev/null
+++ b/include/reset_flag_desc.inc
@@ -0,0 +1,28 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Reset flag descriptions. Must be in same order as bits of RESET_FLAG_
+ * constants.
+ */
+"other",
+"reset-pin",
+"brownout",
+"power-on",
+"watchdog",
+"soft",
+"hibernate",
+"rtc-alarm",
+"wake-pin",
+"low-battery",
+"sysjump",
+"hard",
+"ap-off",
+"preserved",
+"usb-resume",
+"rdd",
+"rbox",
+"security",
+"ap-watchdog",
diff --git a/include/tablet_mode.h b/include/tablet_mode.h
index 5edfb52f20..e4444aca1b 100644
--- a/include/tablet_mode.h
+++ b/include/tablet_mode.h
@@ -28,4 +28,13 @@ void hall_sensor_isr(enum gpio_signal signal);
*/
void hall_sensor_disable(void);
+/*
+ * This must be defined when CONFIG_HALL_SENSOR_CUSTOM is defined. This allows
+ * a board to override the default behavior that determines if the 360 sensor is
+ * active: !gpio_get_level(HALL_SENSOR_GPIO_L).
+ *
+ * Returns 1 if the 360 sensor is active; otherwise 0.
+ */
+int board_sensor_at_360(void);
+
#endif
diff --git a/include/task.h b/include/task.h
index 46a340b898..32e62a8384 100644
--- a/include/task.h
+++ b/include/task.h
@@ -14,7 +14,10 @@
/* Task event bitmasks */
/* Tasks may use the bits in TASK_EVENT_CUSTOM for their own events */
-#define TASK_EVENT_CUSTOM(x) (x & 0x0001ffff)
+#define TASK_EVENT_CUSTOM(x) (x & 0x0000ffff)
+
+/* Used to signal that sysjump preparation has completed */
+#define TASK_EVENT_SYSJUMP_READY BIT(16)
/* Used to signal that IPC layer is available for sending new data */
#define TASK_EVENT_IPC_READY BIT(17)
diff --git a/include/timer.h b/include/timer.h
index 02a50070c1..47fbe7a4bd 100644
--- a/include/timer.h
+++ b/include/timer.h
@@ -134,6 +134,23 @@ void force_time(timestamp_t ts);
void timer_print_info(void);
/**
+ * Returns a free running millisecond clock counter, which matches tpm2
+ * library expectations.
+ */
+clock_t clock(void);
+
+/**
+ * Compute how far to_time is from from_time with rollover taken into account
+ *
+ * Return us until to_time given from_time, if negative then to_time has
+ * passeed from_time.
+ */
+static inline int time_until(uint32_t from_time, uint32_t to_time)
+{
+ return (int32_t)(to_time - from_time);
+}
+
+/**
* Returns the number of microseconds that have elapsed from a start time.
*
* This function is for timing short delays typically of a few milliseconds
@@ -143,27 +160,21 @@ void timer_print_info(void);
* hour. After that, the value returned will wrap.
*
* @param start Start time to compare against
- * @return number of microseconds that have elspsed since that start time
+ * @return number of microseconds that have elapsed since that start time
*/
static inline unsigned time_since32(timestamp_t start)
{
- return get_time().le.lo - start.le.lo;
+ return time_until(start.le.lo, get_time().le.lo);
}
/**
- * Returns a free running millisecond clock counter, which matches tpm2
- * library expectations.
- */
-clock_t clock(void);
-
-/**
* To compare time and deal with rollover
*
* Return true if a is after b.
*/
static inline int time_after(uint32_t a, uint32_t b)
{
- return (int32_t)(b - a) < 0;
+ return time_until(a, b) < 0;
}
#endif /* __CROS_EC_TIMER_H */
diff --git a/include/usb_emsg.h b/include/usb_emsg.h
new file mode 100644
index 0000000000..ffbaa93a0e
--- /dev/null
+++ b/include/usb_emsg.h
@@ -0,0 +1,23 @@
+/* Copyright 2019 The Chromium OS 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 Extended message buffer */
+
+#ifndef __CROS_EC_USB_EBUF_H
+#define __CROS_EC_USB_EBUF_H
+
+#define EXTENDED_BUFFER_SIZE 260
+#define BUFFER_SIZE 28
+
+struct extended_msg {
+ uint32_t header;
+ uint32_t len;
+ uint8_t buf[EXTENDED_BUFFER_SIZE];
+};
+
+/* Defined in usb_prl_sm.c */
+extern struct extended_msg emsg[CONFIG_USB_PD_PORT_COUNT];
+
+#endif /* __CROS_EC_USB_EBUF_H */
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 89202a7ad3..a8f1dab008 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -49,6 +49,8 @@ enum pd_rx_errors {
#define PD_EVENT_DEVICE_ACCESSED (1<<7)
#define PD_EVENT_POWER_STATE_CHANGE (1<<8) /* Chipset power state changed */
#define PD_EVENT_SEND_HARD_RESET (1<<9) /* Issue a Hard Reset. */
+#define PD_EVENT_SM (1<<10) /* PD State machine event */
+#define PD_EVENT_SYSJUMP (1<<11) /* Prepare for sysjump */
/* Ensure TCPC is out of low power mode before handling these events. */
#define PD_EXIT_LOW_POWER_EVENT_MASK \
@@ -168,6 +170,7 @@ enum pd_rx_errors {
#define PD_T_DRP_SNK (40*MSEC) /* toggle time for sink DRP */
#define PD_T_DRP_SRC (30*MSEC) /* toggle time for source DRP */
#define PD_T_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
+#define PD_T_TRY_CC_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
#define PD_T_SINK_ADJ (55*MSEC) /* between PD_T_DEBOUNCE and 60ms */
#define PD_T_SRC_RECOVER (760*MSEC) /* between 660ms and 1000ms */
#define PD_T_SRC_RECOVER_MAX (1000*MSEC) /* 1000ms */
@@ -181,6 +184,10 @@ enum pd_rx_errors {
#define PD_T_TRY_TIMEOUT (550*MSEC) /* between 550ms and 1100ms */
#define PD_T_TRY_WAIT (600*MSEC) /* Max time for TryWait.SNK state */
#define PD_T_SINK_REQUEST (100*MSEC) /* Wait 100ms before next request */
+#define PD_T_PD_DEBOUNCE (15*MSEC) /* between 10ms and 20ms */
+#define PD_T_CHUNK_SENDER_RESPONSE (25*MSEC) /* 25ms */
+#define PD_T_CHUNK_SENDER_REQUEST (25*MSEC) /* 25ms */
+#define PD_T_SWAP_SOURCE_START (25*MSEC) /* Min of 20ms */
/* number of edges and time window to detect CC line is not idle */
#define PD_RX_TRANSITION_COUNT 3
@@ -196,6 +203,11 @@ enum pd_rx_errors {
#define PD_T_VDM_SNDR_RSP (30*MSEC) /* max of 30ms */
#define PD_T_VDM_WAIT_MODE_E (100*MSEC) /* enter/exit the same max */
+/* CTVPD Timers ( USB Type-C ECN Table 4-27 ) */
+#define PD_T_VPDDETACH (20*MSEC) /* max of 20*MSEC */
+#define PD_T_VPDCTDD (4*MSEC) /* max of 4ms */
+#define PD_T_VPDDISABLE (25*MSEC) /* min of 25ms */
+
/* function table for entered mode */
struct amode_fx {
int (*status)(int port, uint32_t *payload);
@@ -391,6 +403,7 @@ struct pd_policy {
#define IDH_PTYPE_PCABLE 3
#define IDH_PTYPE_ACABLE 4
#define IDH_PTYPE_AMA 5
+#define IDH_PTYPE_VPD 6
#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \
((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \
@@ -491,6 +504,34 @@ struct pd_policy {
#define AMA_USBSS_BBONLY 3
/*
+ * VPD VDO
+ * ---------
+ * <31:28> :: HW version
+ * <27:24> :: FW version
+ * <23:21> :: VDO version
+ * <20:17> :: SBZ
+ * <16:15> :: Maximum VBUS Voltage
+ * <14:13> :: SBZ
+ * <12:7> :: VBUS Impedance
+ * <6:1> :: Ground Impedance
+ * <0> :: Charge Through Support
+ */
+#define VDO_VPD(hw, fw, vbus, vbusz, gndz, cts) \
+ (((hw) & 0xf) << 28 | ((fw) & 0xf) << 24 \
+ | ((vbus) & 0x3) << 15 \
+ | ((vbusz) & 0x3f) << 7 \
+ | ((gndz) & 0x3f) << 1 | (cts))
+
+#define VPD_MAX_VBUS_20V 0
+#define VPD_MAX_VBUS_30V 1
+#define VPD_MAX_VBUS_40V 2
+#define VPD_MAX_VBUS_50V 3
+#define VPD_VBUS_IMP(mo) ((mo + 1) >> 1)
+#define VPD_GND_IMP(mo) (mo)
+#define VPD_CTS_SUPPORTED 1
+#define VPD_CTS_NOT_SUPPORTED 0
+
+/*
* SVDM Discover SVIDs request -> response
*
* Request is properly formatted VDM Header with discover SVIDs command.
@@ -787,6 +828,9 @@ enum pd_states {
#define PD_BBRMFLG_DATA_ROLE BIT(2)
#define PD_BBRMFLG_VCONN_ROLE BIT(3)
+/* Initial value for CC debounce variable */
+#define PD_CC_UNSET -1
+
enum pd_cc_states {
PD_CC_NONE,
@@ -912,14 +956,25 @@ enum pd_data_msg_type {
PD_DATA_VENDOR_DEF = 15,
};
+/* CC Polarity type */
+enum pd_cc_polarity_type {
+ POLARITY_CC1,
+ POLARITY_CC2
+};
+
/* Protocol revision */
-#define PD_REV10 0
-#define PD_REV20 1
-#define PD_REV30 2
+enum pd_rev_type {
+ PD_REV10,
+ PD_REV20,
+ PD_REV30
+};
/* Power role */
#define PD_ROLE_SINK 0
#define PD_ROLE_SOURCE 1
+/* Cable plug */
+#define PD_PLUG_DFP_UFP 0
+#define PD_PLUG_CABLE_VPD 1
/* Data role */
#define PD_ROLE_UFP 0
#define PD_ROLE_DFP 1
@@ -998,6 +1053,9 @@ enum pd_data_msg_type {
#define PD_EXT_HEADER_REQ_CHUNK(header) (((header) >> 10) & 1)
#define PD_EXT_HEADER_DATA_SIZE(header) ((header) & 0x1ff)
+/* Used to get extended header from the first 32-bit word of the message */
+#define GET_EXT_HEADER(msg) (msg & 0xffff)
+
/* K-codes for special symbols */
#define PD_SYNC1 0x18
#define PD_SYNC2 0x11
@@ -1921,4 +1979,15 @@ static inline void pd_log_event(uint8_t type, uint8_t size_port,
static inline int pd_vdm_get_log_entry(uint32_t *payload) { return 0; }
#endif /* CONFIG_USB_PD_LOGGING */
+#ifdef CONFIG_USB_PD_ALT_MODE_DFP
+/**
+ * Prepare for a sysjump by exiting any alternate modes, if PD communication is
+ * allowed.
+ *
+ * Note: this call will block until the PD task has finished its exit mode and
+ * re-awoken the calling task.
+ */
+void pd_prepare_sysjump(void);
+#endif
+
#endif /* __CROS_EC_USB_PD_H */
diff --git a/include/usb_pd_tcpm.h b/include/usb_pd_tcpm.h
index d46aef454d..874b231dce 100644
--- a/include/usb_pd_tcpm.h
+++ b/include/usb_pd_tcpm.h
@@ -16,6 +16,9 @@
/* Time to wait for TCPC to complete transmit */
#define PD_T_TCPC_TX_TIMEOUT (100*MSEC)
+/* Number of valid Transmit Types */
+#define NUM_XMIT_TYPES (TCPC_TX_SOP_DEBUG_PRIME_PRIME + 1)
+
/* Detected resistor values of port partner */
enum tcpc_cc_voltage_status {
TYPEC_CC_VOLT_OPEN = 0,
@@ -32,6 +35,7 @@ enum tcpc_cc_pull {
TYPEC_CC_RP = 1,
TYPEC_CC_RD = 2,
TYPEC_CC_OPEN = 3,
+ TYPEC_CC_RA_RD = 4, /* Powered cable with Sink */
};
/* Pull-up values we apply as a SRC to advertise different current limits */
@@ -54,6 +58,7 @@ enum tcpm_transmit_type {
};
enum tcpc_transmit_complete {
+ TCPC_TX_UNSET = -1,
TCPC_TX_COMPLETE_SUCCESS = 0,
TCPC_TX_COMPLETE_DISCARDED = 1,
TCPC_TX_COMPLETE_FAILED = 2,
@@ -260,22 +265,23 @@ struct tcpm_drv {
#endif
};
-enum tcpc_alert_polarity {
- TCPC_ALERT_ACTIVE_LOW,
- TCPC_ALERT_ACTIVE_HIGH,
-};
-
-enum tcpc_alert_open_drain {
- TCPC_ALERT_PUSH_PULL = 0,
- TCPC_ALERT_OPEN_DRAIN,
-};
+/*
+ * Macros for tcpc_config_t flags field.
+ *
+ * Bit 0 --> Polarity for TCPC alert. Set to 1 if alert is active high.
+ * Bit 1 --> Set to 1 if TCPC alert line is open-drain instead of push-pull.
+ * Bit 2 --> Polarity for TCPC reset. Set to 1 if reset line is active high.
+ */
+#define TCPC_FLAGS_ALERT_ACTIVE_HIGH BIT(0)
+#define TCPC_FLAGS_ALERT_OD BIT(1)
+#define TCPC_FLAGS_RESET_ACTIVE_HIGH BIT(2)
struct tcpc_config_t {
int i2c_host_port;
int i2c_slave_addr;
const struct tcpm_drv *drv;
- enum tcpc_alert_polarity pol;
- enum tcpc_alert_open_drain od;
+ /* See TCPC_FLAGS_* above */
+ uint32_t flags;
};
/**
diff --git a/include/usb_pe_ctvpd_sm.h b/include/usb_pe_ctvpd_sm.h
new file mode 100644
index 0000000000..e2b2cde8c4
--- /dev/null
+++ b/include/usb_pe_ctvpd_sm.h
@@ -0,0 +1,239 @@
+/* Copyright 2019 The Chromium OS 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 "common.h"
+#include "console.h"
+#include "task.h"
+#include "util.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+#include "usb_pe_sm.h"
+#include "usb_prl_sm.h"
+#include "usb_tc_sm.h"
+#include "usb_emsg.h"
+#include "usb_sm.h"
+
+/* USB Policy Engine Charge-Through VCONN Powered Device module */
+
+#ifndef __CROS_EC_USB_PE_CTVPD_H
+#define __CROS_EC_USB_PE_CTVPD_H
+
+/* Policy Engine Flags */
+#define PE_FLAGS_MSG_RECEIVED (1 << 0)
+
+enum l_state {
+ PE_INIT,
+ PE_RUN,
+ PE_PAUSED
+};
+
+static enum l_state local_state = PE_INIT;
+
+/*
+ * PE_OBJ is a convenience macro to access struct sm_obj, which
+ * must be the first member of struct policy_engine.
+ */
+#define PE_OBJ(port) (SM_OBJ(pe[port]))
+
+/**
+ * This is the PE Port object that contains information needed to
+ * implement a VCONN and Charge-Through VCONN Powered Device.
+ */
+static struct policy_engine {
+ /*
+ * struct sm_obj must be first. This is the state machine
+ * object that keeps track of the current and last state
+ * of the state machine.
+ */
+ struct sm_obj obj;
+ /* port flags, see PE_FLAGS_* */
+ uint32_t flags;
+} pe[CONFIG_USB_PD_PORT_COUNT];
+
+static unsigned int pe_request(int port, enum signal sig);
+static unsigned int pe_request_entry(int port);
+static unsigned int pe_request_run(int port);
+
+static unsigned int do_nothing_exit(int port);
+static unsigned int get_super_state(int port);
+
+static const state_sig pe_request_sig[] = {
+ pe_request_entry,
+ pe_request_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+void pe_init(int port)
+{
+ pe[port].flags = 0;
+ init_state(port, PE_OBJ(port), pe_request);
+}
+
+void policy_engine(int port, int evt, int en)
+{
+ switch (local_state) {
+ case PE_INIT:
+ pe_init(port);
+ local_state = PE_RUN;
+ /* fall through */
+ case PE_RUN:
+ if (!en) {
+ local_state = PE_PAUSED;
+ break;
+ }
+
+ exe_state(port, PE_OBJ(port), RUN_SIG);
+ break;
+ case PE_PAUSED:
+ if (en)
+ local_state = PE_INIT;
+ break;
+ }
+}
+
+void pe_pass_up_message(int port)
+{
+ pe[port].flags |= PE_FLAGS_MSG_RECEIVED;
+ task_set_event(PD_PORT_TO_TASK_ID(port), PD_EVENT_SM, 0);
+}
+
+void pe_hard_reset_sent(int port)
+{
+ /* Do nothing */
+}
+
+void pe_got_hard_reset(int port)
+{
+ /* Do nothing */
+}
+
+void pe_report_error(int port, enum pe_error e)
+{
+ /* Do nothing */
+}
+
+void pe_got_soft_reset(int port)
+{
+ /* Do nothing */
+}
+
+void pe_message_sent(int port)
+{
+ /* Do nothing */
+}
+
+static unsigned int pe_request(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*pe_request_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int pe_request_entry(int port)
+{
+ return 0;
+}
+
+static unsigned int pe_request_run(int port)
+{
+ uint32_t *payload = (uint32_t *)emsg[port].buf;
+ uint32_t header = emsg[port].header;
+ uint32_t vdo = payload[0];
+
+ if (pe[port].flags & PE_FLAGS_MSG_RECEIVED) {
+ pe[port].flags &= ~PE_FLAGS_MSG_RECEIVED;
+
+ /*
+ * Only support Structured VDM Discovery
+ * Identity message
+ */
+
+ if (PD_HEADER_TYPE(header) != PD_DATA_VENDOR_DEF)
+ return 0;
+
+ if (PD_HEADER_CNT(header) == 0)
+ return 0;
+
+ if (!PD_VDO_SVDM(vdo))
+ return 0;
+
+ if (PD_VDO_CMD(vdo) != CMD_DISCOVER_IDENT)
+ return 0;
+
+#ifdef CONFIG_USB_TYPEC_CTVPD
+ /*
+ * We have a valid DISCOVER IDENTITY message.
+ * Attempt to reset support timer
+ */
+ tc_reset_support_timer(port);
+#endif
+ /* Prepare to send ACK */
+
+ /* VDM Header */
+ payload[0] = VDO(
+ USB_VID_GOOGLE,
+ 1, /* Structured VDM */
+ VDO_SVDM_VERS(1) |
+ VDO_CMDT(CMDT_RSP_ACK) |
+ CMD_DISCOVER_IDENT);
+
+ /* ID Header VDO */
+ payload[1] = VDO_IDH(
+ 0, /* Not a USB Host */
+ 1, /* Capable of being enumerated as USB Device */
+ IDH_PTYPE_VPD,
+ 0, /* Modal Operation Not Supported */
+ USB_VID_GOOGLE);
+
+ /* Cert State VDO */
+ payload[2] = 0;
+
+ /* Product VDO */
+ payload[3] = VDO_PRODUCT(
+ CONFIG_USB_PID,
+ USB_BCD_DEVICE);
+
+ /* VPD VDO */
+ payload[4] = VDO_VPD(
+ VPD_HW_VERSION,
+ VPD_FW_VERSION,
+ VPD_MAX_VBUS_20V,
+ VPD_VBUS_IMP(VPD_VBUS_IMPEDANCE),
+ VPD_GND_IMP(VPD_GND_IMPEDANCE),
+#ifdef CONFIG_USB_TYPEC_CTVPD
+ VPD_CTS_SUPPORTED
+#else
+ VPD_CTS_NOT_SUPPORTED
+#endif
+ );
+
+ /* 20 bytes, 5 data objects */
+ emsg[port].len = 20;
+
+ /* Set to highest revision supported by both ports. */
+ prl_set_rev(port, (PD_HEADER_REV(header) > PD_REV30) ?
+ PD_REV30 : PD_HEADER_REV(header));
+
+ /* Send the ACK */
+ prl_send_data_msg(port, TCPC_TX_SOP_PRIME,
+ PD_DATA_VENDOR_DEF);
+ }
+
+ return 0;
+}
+
+static unsigned int do_nothing_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /* __CROS_EC_USB_PE_CTVPD_H */
diff --git a/include/usb_pe_sm.h b/include/usb_pe_sm.h
new file mode 100644
index 0000000000..a8fd59b08c
--- /dev/null
+++ b/include/usb_pe_sm.h
@@ -0,0 +1,79 @@
+/* Copyright 2019 The Chromium OS 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 Policy Engine module */
+
+#ifndef __CROS_EC_USB_PE_H
+#define __CROS_EC_USB_PE_H
+
+enum pe_error {
+ ERR_RCH_CHUNKED,
+ ERR_RCH_MSG_REC,
+ ERR_TCH_CHUNKED,
+ ERR_TCH_XMIT,
+};
+
+/**
+ * Initialize the Policy Engine State Machine
+ *
+ * @param port USB-C port number
+ */
+void pe_init(int port);
+
+/**
+ * Runs the Policy Engine State Machine
+ *
+ * @param port USB-C port number
+ * @param evt system event, ie: PD_EVENT_RX
+ * @param en 0 to disable the machine, 1 to enable the machine
+ */
+void policy_engine(int port, int evt, int en);
+
+/**
+ * Informs the Policy Engine that a message was successfully sent
+ *
+ * @param port USB-C port number
+ */
+void pe_message_sent(int port);
+
+/**
+ * Informs the Policy Engine of an error.
+ *
+ * @param port USB-C port number
+ * @parm e error
+ */
+void pe_report_error(int port, enum pe_error e);
+
+/**
+ * Informs the Policy Engine that a message has been received
+ *
+ * @param port USB-C port number
+ * @parm e error
+ */
+void pe_pass_up_message(int port);
+
+/**
+ * Informs the Policy Engine that a hard reset was received.
+ *
+ * @param port USB-C port number
+ */
+void pe_got_hard_reset(int port);
+
+/**
+ * Informs the Policy Engine that a soft reset was received.
+ *
+ * @param port USB-C port number
+ */
+void pe_got_soft_reset(int port);
+
+/**
+ * Informs the Policy Engine that a hard reset was sent.
+ *
+ * @param port USB-C port number
+ */
+void pe_hard_reset_sent(int port);
+
+#endif /* __CROS_EC_USB_PE_H */
+
diff --git a/include/usb_prl_sm.h b/include/usb_prl_sm.h
new file mode 100644
index 0000000000..c02725baad
--- /dev/null
+++ b/include/usb_prl_sm.h
@@ -0,0 +1,213 @@
+/* Copyright 2019 The Chromium OS 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 Protocol Layer module */
+
+#ifndef __CROS_EC_USB_PRL_H
+#define __CROS_EC_USB_PRL_H
+#include "common.h"
+#include "usb_pd.h"
+#include "usb_pd_tcpm.h"
+
+enum prl_tx_state_id {
+ PRL_TX_PHY_LAYER_RESET,
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST,
+ PRL_TX_LAYER_RESET_FOR_TRANSMIT,
+ PRL_TX_CONSTRUCT_MESSAGE,
+ PRL_TX_WAIT_FOR_PHY_RESPONSE,
+ PRL_TX_MATCH_MESSAGE_ID,
+ PRL_TX_MESSAGE_SENT,
+ PRL_TX_CHECK_RETRY_COUNTER,
+ PRL_TX_TRANSMISSION_ERROR,
+ PRL_TX_DISCARD_MESSAGE,
+
+ PRL_TX_SRC_SINK_TX,
+ PRL_TX_SRC_SOURCE_TX,
+ PRL_TX_SRC_PENDING,
+
+ PRL_TX_SNK_START_OF_AMS,
+ PRL_TX_SNK_PENDING,
+};
+
+enum prl_hr_state_id {
+ PRL_HR_WAIT_FOR_REQUEST,
+ PRL_HR_RESET_LAYER,
+ PRL_HR_INDICATE_HARD_RESET,
+ PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE,
+ PRL_HR_PHY_HARD_RESET_REQUESTED,
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE,
+ PRL_HR_PE_HARD_RESET_COMPLETE,
+};
+
+enum rch_state_id {
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER,
+ RCH_PASS_UP_MESSAGE,
+ RCH_PROCESSING_EXTENDED_MESSAGE,
+ RCH_REQUESTING_CHUNK,
+ RCH_WAITING_CHUNK,
+ RCH_REPORT_ERROR,
+};
+
+enum tch_state_id {
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE,
+ TCH_PASS_DOWN_MESSAGE,
+ TCH_WAIT_FOR_TRANSMISSION_COMPLETE,
+ TCH_MESSAGE_SENT,
+ TCH_PREPARE_TO_SEND_CHUNKED_MESSAGE,
+ TCH_CONSTRUCT_CHUNKED_MESSAGE,
+ TCH_SENDING_CHUNKED_MESSAGE,
+ TCH_WAIT_CHUNK_REQUEST,
+ TCH_MESSAGE_RECEIVED,
+ TCH_REPORT_ERROR,
+};
+
+/*
+ * Number of times the Protocol Layer will try to transmit a message
+ * before giving up and signaling an error
+ */
+#define N_RETRY_COUNT 2
+
+/**
+ * Initialize the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ */
+void prl_init(int port);
+
+/**
+ * Resets the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ */
+void prl_reset(int port);
+
+/**
+ * Get Chunked Rx State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum rch_state_id get_rch_state_id(int port);
+
+/**
+ * Get Chunked Tx State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum tch_state_id get_tch_state_id(int port);
+
+/**
+ * Get Message Transmission State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum prl_tx_state_id get_prl_tx_state_id(int port);
+
+/**
+ * Get Hard Reset State Machine state id
+ *
+ * @param port USB-C port number
+ * @return id
+ */
+enum prl_hr_state_id get_prl_hr_state_id(int port);
+
+/**
+ * Returns the state of the PRL state machine
+ * @return SM_INIT for initializing
+ * SM_RUN for running
+ * SM_PAUSED for paused
+ */
+enum sm_local_state prl_get_local_state(int port);
+
+/**
+ * Runs the Protocol Layer State Machine
+ *
+ * @param port USB-C port number
+ * @param evt system event, ie: PD_EVENT_RX
+ * @param en 0 to disable the machine, 1 to enable the machine
+ */
+void protocol_layer(int port, int evt, int en);
+
+/**
+ * Set the PD revision
+ *
+ * @param port USB-C port number
+ * @param rev revision
+ */
+void prl_set_rev(int port, enum pd_rev_type rev);
+
+/**
+ * Get the PD revision
+ *
+ * @param port USB-C port number
+ * @return pd rev
+ */
+enum pd_rev_type prl_get_rev(int port);
+
+/**
+ * Sends a PD control message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Control message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_ctrl_msg(int port, enum tcpm_transmit_type type,
+ enum pd_ctrl_msg_type msg);
+
+/**
+ * Sends a PD data message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Data message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_data_msg(int port, enum tcpm_transmit_type type,
+ enum pd_data_msg_type msg);
+
+/**
+ * Sends a PD extended data message
+ *
+ * @param port USB-C port number
+ * @param type Transmit type
+ * @param msg Extended data message type
+ * @return 0 on EC_SUCCESS, else EC_ERROR_BUSY
+ */
+void prl_send_ext_data_msg(int port, enum tcpm_transmit_type type,
+ enum pd_ext_msg_type msg);
+
+/**
+ * Informs the Protocol Layer that a hard reset has completed
+ *
+ * @param port USB-C port number
+ */
+void prl_hard_reset_complete(int port);
+
+/**
+ * Policy Engine calls this function to execute a hard reset.
+ *
+ * @param port USB-C port number
+ */
+void prl_execute_hard_reset(int port);
+
+/**
+ * Informs the Protocol Layer to start an Atomic Message Sequence
+ *
+ * @param port USB-C port number
+ */
+void prl_start_ams(int port);
+
+/**
+ * Informs the Protocol Layer to end an Atomic Message Sequence
+ *
+ * @param port USB-C port number
+ */
+void prl_end_ams(int port);
+
+#endif /* __CROS_EC_USB_PRL_H */
+
diff --git a/include/usb_sm.h b/include/usb_sm.h
new file mode 100644
index 0000000000..15de5f9ef4
--- /dev/null
+++ b/include/usb_sm.h
@@ -0,0 +1,67 @@
+/* Copyright 2019 The Chromium OS 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 State Machine Framework */
+
+#ifndef __CROS_EC_USB_SM_H
+#define __CROS_EC_USB_SM_H
+
+#define SM_OBJ(smo) ((struct sm_obj *)&smo)
+#define SUPER(r, sig, s) ((((r) == 0) || ((sig) == ENTRY_SIG) || \
+ ((sig) == EXIT_SIG)) ? 0 : ((uintptr_t)(s)))
+#define RUN_SUPER 1
+
+/* Local state machine states */
+enum sm_local_state {
+ SM_INIT,
+ SM_RUN,
+ SM_PAUSED
+};
+
+/* State Machine signals */
+enum signal {
+ ENTRY_SIG = 0,
+ RUN_SIG,
+ EXIT_SIG,
+ SUPER_SIG,
+};
+
+typedef unsigned int (*state_sig)(int port);
+typedef unsigned int (*sm_state)(int port, enum signal sig);
+
+struct sm_obj {
+ sm_state task_state;
+ sm_state last_state;
+};
+
+/**
+ * Initialize a State Machine
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param target Initial state of state machine
+ */
+void init_state(int port, struct sm_obj *obj, sm_state target);
+
+/**
+ * Changes a state machines state
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param target State to transition to
+ * @return 0
+ */
+int set_state(int port, struct sm_obj *obj, sm_state target);
+
+/**
+ * Executes a state machine
+ *
+ * @param port USB-C port number
+ * @param obj State machine object
+ * @param sig State machine signal
+ */
+void exe_state(int port, struct sm_obj *obj, enum signal sig);
+
+#endif /* __CROS_EC_USB_SM_H */
diff --git a/include/usb_tc_ctvpd_sm.h b/include/usb_tc_ctvpd_sm.h
new file mode 100644
index 0000000000..3b47d35ffa
--- /dev/null
+++ b/include/usb_tc_ctvpd_sm.h
@@ -0,0 +1,2039 @@
+/* Copyright 2019 The Chromium OS 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 "vpd_api.h"
+
+/* USB Type-C CTVPD module */
+
+#ifndef __CROS_EC_USB_TC_VPD_H
+#define __CROS_EC_USB_TC_VPD_H
+
+/* Type-C Layer Flags */
+#define TC_FLAGS_VCONN_ON (1 << 0)
+
+
+#undef PD_DEFAULT_STATE
+/* Port default state at startup */
+#define PD_DEFAULT_STATE(port) tc_state_unattached_snk
+
+#define TC_OBJ(port) (SM_OBJ(tc[port]))
+#define TC_TEST_OBJ(port) (SM_OBJ(tc[(port)].obj))
+
+#define SUPPORT_TIMER_RESET_INIT 0
+#define SUPPORT_TIMER_RESET_REQUEST 1
+#define SUPPORT_TIMER_RESET_COMPLETE 2
+
+static struct type_c {
+ /* struct sm_obj must be first */
+ struct sm_obj obj;
+ /* state id */
+ enum typec_state_id state_id;
+ /* current port power role (VPD, SOURCE or SINK) */
+ uint8_t power_role;
+ /* current port data role (DFP or UFP) */
+ uint8_t data_role;
+ /* enable power delivery state machines */
+ uint8_t pd_enable;
+ /* event timeout */
+ uint64_t evt_timeout;
+ /* state machine event */
+ int evt;
+ /* port flags, see TC_FLAGS_* */
+ uint32_t flags;
+ /*
+ * Time a charge-through port shall wait before it can determine it
+ * is attached
+ */
+ uint64_t cc_debounce;
+ /* Time a host port shall wait before it can determine it is attached */
+ uint64_t host_cc_debounce;
+ /* Time a Sink port shall wait before it can determine it is detached
+ * due to the potential for USB PD signaling on CC as described in
+ * the state definitions.
+ */
+ uint64_t pd_debounce;
+ /* Maintains state of billboard device */
+ int billboard_presented;
+ /*
+ * Time a port shall wait before it can determine it is
+ * re-attached during the try-wait process.
+ */
+ uint64_t try_wait_debounce;
+ /* charge-through support timer */
+ uint64_t support_timer;
+ /* reset the charge-through support timer */
+ uint8_t support_timer_reset;
+ /* VPD host port cc state */
+ enum pd_cc_states host_cc_state;
+ uint8_t ct_cc;
+ /* The cc state */
+ enum pd_cc_states cc_state;
+ uint64_t next_role_swap;
+} tc[CONFIG_USB_PD_PORT_COUNT];
+
+/* Type-C states */
+static unsigned int tc_state_disabled(int port, enum signal sig);
+static unsigned int tc_state_disabled_entry(int port);
+static unsigned int tc_state_disabled_run(int port);
+static unsigned int tc_state_disabled_exit(int port);
+
+static unsigned int tc_state_error_recovery(int port, enum signal sig);
+static unsigned int tc_state_error_recovery_entry(int port);
+static unsigned int tc_state_error_recovery_run(int port);
+
+static unsigned int tc_state_unattached_snk(int port, enum signal sig);
+static unsigned int tc_state_unattached_snk_entry(int port);
+static unsigned int tc_state_unattached_snk_run(int port);
+
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_snk_entry(int port);
+static unsigned int tc_state_attach_wait_snk_run(int port);
+
+static unsigned int tc_state_attached_snk(int port, enum signal sig);
+static unsigned int tc_state_attached_snk_entry(int port);
+static unsigned int tc_state_attached_snk_run(int port);
+static unsigned int tc_state_attached_snk_exit(int port);
+
+static unsigned int tc_state_try_snk(int port, enum signal sig);
+static unsigned int tc_state_try_snk_entry(int port);
+static unsigned int tc_state_try_snk_run(int port);
+
+static unsigned int tc_state_unattached_src(int port, enum signal sig);
+static unsigned int tc_state_unattached_src_entry(int port);
+static unsigned int tc_state_unattached_src_run(int port);
+
+static unsigned int tc_state_attach_wait_src(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_src_entry(int port);
+static unsigned int tc_state_attach_wait_src_run(int port);
+
+static unsigned int tc_state_try_wait_src(int port, enum signal sig);
+static unsigned int tc_state_try_wait_src_entry(int port);
+static unsigned int tc_state_try_wait_src_run(int port);
+
+static unsigned int tc_state_attached_src(int port, enum signal sig);
+static unsigned int tc_state_attached_src_entry(int port);
+static unsigned int tc_state_attached_src_run(int port);
+
+static unsigned int tc_state_ct_try_snk(int port, enum signal sig);
+static unsigned int tc_state_ct_try_snk_entry(int port);
+static unsigned int tc_state_ct_try_snk_run(int port);
+static unsigned int tc_state_ct_try_snk_exit(int port);
+
+static unsigned int
+ tc_state_ct_attach_wait_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_attach_wait_unsupported_entry(int port);
+static unsigned int tc_state_ct_attach_wait_unsupported_run(int port);
+static unsigned int tc_state_ct_attach_wait_unsupported_exit(int port);
+
+static unsigned int tc_state_ct_attached_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_attached_unsupported_entry(int port);
+static unsigned int tc_state_ct_attached_unsupported_run(int port);
+static unsigned int tc_state_ct_attached_unsupported_exit(int port);
+
+static unsigned int
+ tc_state_ct_unattached_unsupported(int port, enum signal sig);
+static unsigned int tc_state_ct_unattached_unsupported_entry(int port);
+static unsigned int tc_state_ct_unattached_unsupported_run(int port);
+static unsigned int tc_state_ct_unattached_unsupported_exit(int port);
+
+static unsigned int tc_state_ct_unattached_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_unattached_vpd_entry(int port);
+static unsigned int tc_state_ct_unattached_vpd_run(int port);
+static unsigned int tc_state_ct_unattached_vpd_exit(int port);
+
+static unsigned int tc_state_ct_disabled_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_disabled_vpd_entry(int port);
+static unsigned int tc_state_ct_disabled_vpd_run(int port);
+
+static unsigned int tc_state_ct_attached_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_attached_vpd_entry(int port);
+static unsigned int tc_state_ct_attached_vpd_run(int port);
+
+static unsigned int tc_state_ct_attach_wait_vpd(int port, enum signal sig);
+static unsigned int tc_state_ct_attach_wait_vpd_entry(int port);
+static unsigned int tc_state_ct_attach_wait_vpd_run(int port);
+static unsigned int tc_state_ct_attach_wait_vpd_exit(int port);
+
+
+/* Super States */
+static unsigned int tc_state_host_rard_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rard_ct_rd_entry(int port);
+static unsigned int tc_state_host_rard_ct_rd_run(int port);
+
+static unsigned int tc_state_host_open_ct_open(int port, enum signal sig);
+static unsigned int tc_state_host_open_ct_open_entry(int port);
+static unsigned int tc_state_host_open_ct_open_run(int port);
+
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig);
+static unsigned int tc_state_vbus_cc_iso_entry(int port);
+static unsigned int tc_state_vbus_cc_iso_run(int port);
+
+static unsigned int tc_state_host_rp3_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rp3_ct_rd_entry(int port);
+static unsigned int tc_state_host_rp3_ct_rd_run(int port);
+
+static unsigned int tc_state_host_rp3_ct_rpu(int port, enum signal sig);
+static unsigned int tc_state_host_rp3_ct_rpu_entry(int port);
+static unsigned int tc_state_host_rp3_ct_rpu_run(int port);
+
+static unsigned int tc_state_host_rpu_ct_rd(int port, enum signal sig);
+static unsigned int tc_state_host_rpu_ct_rd_entry(int port);
+static unsigned int tc_state_host_rpu_ct_rd_run(int port);
+
+static unsigned int do_nothing_exit(int port);
+static unsigned int get_super_state(int port);
+
+
+static const state_sig tc_state_disabled_sig[] = {
+ tc_state_disabled_entry,
+ tc_state_disabled_run,
+ tc_state_disabled_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_error_recovery_sig[] = {
+ tc_state_error_recovery_entry,
+ tc_state_error_recovery_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_snk_sig[] = {
+ tc_state_unattached_snk_entry,
+ tc_state_unattached_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_snk_sig[] = {
+ tc_state_attach_wait_snk_entry,
+ tc_state_attach_wait_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_snk_sig[] = {
+ tc_state_attached_snk_entry,
+ tc_state_attached_snk_run,
+ tc_state_attached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_try_snk_sig[] = {
+ tc_state_try_snk_entry,
+ tc_state_try_snk_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_src_sig[] = {
+ tc_state_unattached_src_entry,
+ tc_state_unattached_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_src_sig[] = {
+ tc_state_attach_wait_src_entry,
+ tc_state_attach_wait_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_try_wait_src_sig[] = {
+ tc_state_try_wait_src_entry,
+ tc_state_try_wait_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_src_sig[] = {
+ tc_state_attached_src_entry,
+ tc_state_attached_src_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_try_snk_sig[] = {
+ tc_state_ct_try_snk_entry,
+ tc_state_ct_try_snk_run,
+ tc_state_ct_try_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attach_wait_unsupported_sig[] = {
+ tc_state_ct_attach_wait_unsupported_entry,
+ tc_state_ct_attach_wait_unsupported_run,
+ tc_state_ct_attach_wait_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attached_unsupported_sig[] = {
+ tc_state_ct_attached_unsupported_entry,
+ tc_state_ct_attached_unsupported_run,
+ tc_state_ct_attached_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_unattached_unsupported_sig[] = {
+ tc_state_ct_unattached_unsupported_entry,
+ tc_state_ct_unattached_unsupported_run,
+ tc_state_ct_unattached_unsupported_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_unattached_vpd_sig[] = {
+ tc_state_ct_unattached_vpd_entry,
+ tc_state_ct_unattached_vpd_run,
+ tc_state_ct_unattached_vpd_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_disabled_vpd_sig[] = {
+ tc_state_ct_disabled_vpd_entry,
+ tc_state_ct_disabled_vpd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attached_vpd_sig[] = {
+ tc_state_ct_attached_vpd_entry,
+ tc_state_ct_attached_vpd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_ct_attach_wait_vpd_sig[] = {
+ tc_state_ct_attach_wait_vpd_entry,
+ tc_state_ct_attach_wait_vpd_run,
+ tc_state_ct_attach_wait_vpd_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rard_ct_rd_sig[] = {
+ tc_state_host_rard_ct_rd_entry,
+ tc_state_host_rard_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_open_ct_open_sig[] = {
+ tc_state_host_open_ct_open_entry,
+ tc_state_host_open_ct_open_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_vbus_cc_iso_sig[] = {
+ tc_state_vbus_cc_iso_entry,
+ tc_state_vbus_cc_iso_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rp3_ct_rd_sig[] = {
+ tc_state_host_rp3_ct_rd_entry,
+ tc_state_host_rp3_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rp3_ct_rpu_sig[] = {
+ tc_state_host_rp3_ct_rpu_entry,
+ tc_state_host_rp3_ct_rpu_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rpu_ct_rd_sig[] = {
+ tc_state_host_rpu_ct_rd_entry,
+ tc_state_host_rpu_ct_rd_run,
+ do_nothing_exit,
+ get_super_state
+};
+
+void tc_reset_support_timer(int port)
+{
+ tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_REQUEST;
+}
+
+static void tc_state_init(int port)
+{
+ int res = 0;
+ sm_state this_state;
+
+ res = tc_restart_tcpc(port);
+
+ CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready");
+ this_state = res ? tc_state_disabled : PD_DEFAULT_STATE(port);
+
+ init_state(port, TC_OBJ(port), this_state);
+
+ /* Disable pd state machines */
+ tc[port].pd_enable = 0;
+ tc[port].evt_timeout = 10*MSEC;
+ tc[port].power_role = PD_PLUG_CABLE_VPD;
+ tc[port].data_role = 0; /* Reserved for VPD */
+ tc[port].billboard_presented = 0;
+ tc[port].flags = 0;
+}
+
+static void tc_event_check(int port, int evt)
+{
+ /* Do Nothing */
+}
+
+/**
+ * Disabled
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Remove the terminations from Host
+ * Remove the terminations from Charge-Through
+ */
+static unsigned int tc_state_disabled(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_disabled_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_disabled_entry(int port)
+{
+ tc[port].state_id = DISABLED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ return 0;
+}
+
+static unsigned int tc_state_disabled_run(int port)
+{
+ task_wait_event(-1);
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_disabled_exit(int port)
+{
+#ifndef CONFIG_USB_PD_TCPC
+ if (tc_restart_tcpc(port) != 0) {
+ CPRINTS("TCPC p%d restart failed!", port);
+ return 0;
+ }
+#endif
+ CPRINTS("TCPC p%d resumed!", port);
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * ErrorRecovery
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Remove the terminations from Host
+ * Remove the terminations from Charge-Through
+ */
+static unsigned int tc_state_error_recovery(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_error_recovery_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_error_recovery_entry(int port)
+{
+ tc[port].state_id = ERROR_RECOVERY;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ /* Use cc_debounce state variable for error recovery timeout */
+ tc[port].cc_debounce = get_time().val + PD_T_ERROR_RECOVERY;
+ return 0;
+}
+
+static unsigned int tc_state_error_recovery_run(int port)
+{
+ if (get_time().val > tc[port].cc_debounce) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * Unattached.SNK
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_unattached_snk(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_unattached_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_unattached_snk_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SNK;
+ if (tc[port].obj.last_state != tc_state_unattached_src)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].flags &= ~TC_FLAGS_VCONN_ON;
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_snk_run(int port)
+{
+ int host_cc;
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ /*
+ * Transition to AttachWait.SNK when a Source connection is
+ * detected, as indicated by the SNK.Rp state on its Host-side
+ * port’s CC pin.
+ */
+ if (cc_is_rp(host_cc)) {
+ set_state(port, TC_OBJ(port), tc_state_attach_wait_snk);
+ return 0;
+ }
+
+ /* Check Charge-Through CCs for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc_is_rp(cc1) != cc_is_rp(cc2))
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ new_cc_state = PD_CC_NONE;
+
+ /* Debounce Charge-Through CC state */
+ if (tc[port].cc_state != new_cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE;
+ }
+
+ /* If we are here, Host CC must be open */
+
+ /* Wait for Charge-Through CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SRC when the state of the Host-side port’s CC pin is
+ * SNK.Open for tDRP − dcSRC.DRP ∙ tDRP and both of the following
+ * is detected on the Charge-Through port.
+ * 1) SNK.Rp state is detected on exactly one of the CC1 or CC2
+ * pins for at least tCCDebounce
+ * 2) VBUS is detected
+ */
+ if (vpd_is_ct_vbus_present() &&
+ tc[port].cc_state == PD_CC_DFP_ATTACHED) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_src);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * AttachWait.SNK
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attach_wait_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_attach_wait_snk_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (cc_is_rp(host_cc))
+ host_new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ if (host_new_cc_state == PD_CC_DFP_ATTACHED)
+ tc[port].host_cc_debounce = get_time().val +
+ PD_T_CC_DEBOUNCE;
+ else
+ tc[port].host_cc_debounce = get_time().val +
+ PD_T_PD_DEBOUNCE;
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].host_cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Attached.SNK after the state of the Host-side port’s CC pin is
+ * SNK.Rp for at least tCCDebounce and either host-side VCONN or
+ * VBUS is detected.
+ *
+ * Transition to Unattached.SNK when the state of both the CC1 and
+ * CC2 pins is SNK.Open for at least tPDDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED &&
+ (vpd_is_vconn_present() || vpd_is_host_vbus_present()))
+ set_state(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Attached.SNK
+ */
+static unsigned int tc_state_attached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_snk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_snk_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ /*
+ * This state can only be entered from states AttachWait.SNK
+ * and Try.SNK. So the Host port is isolated from the
+ * Charge-Through port. We only need to High-Z the
+ * Charge-Through ports CC1 and CC2 pins.
+ */
+ vpd_ct_set_pull(TYPEC_CC_OPEN, 0);
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ /* Start Charge-Through support timer */
+ tc[port].support_timer_reset = SUPPORT_TIMER_RESET_INIT;
+ tc[port].support_timer = get_time().val + PD_T_AME;
+
+ /* Sample host CC every 2ms */
+ tc_set_timeout(port, 2*MSEC);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Has host vbus and vconn been removed */
+ if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * Reset the Charge-Through Support Timer when it first
+ * receives any USB PD Structured VDM Command it supports,
+ * which is the Discover Identity command. And this is only
+ * done one time.
+ */
+ if (tc[port].support_timer_reset == SUPPORT_TIMER_RESET_REQUEST) {
+ tc[port].support_timer_reset |= SUPPORT_TIMER_RESET_COMPLETE;
+ tc[port].support_timer = get_time().val + PD_T_AME;
+ }
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (cc_is_rp(host_cc))
+ host_new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ tc[port].host_cc_debounce = get_time().val + PD_T_VPDCTDD;
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].host_cc_debounce)
+ return 0;
+
+ if (vpd_is_vconn_present()) {
+ if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) {
+ /* VCONN detected. Remove RA */
+ vpd_host_set_pull(TYPEC_CC_RD, 0);
+ tc[port].flags |= TC_FLAGS_VCONN_ON;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to CTUnattached.VPD if VCONN is present and the state of
+ * its Host-side port’s CC pin is SNK.Open for tVPDCTDD.
+ */
+ if (tc[port].host_cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_unattached_vpd);
+ return 0;
+ }
+ }
+
+ /* Check the Support Timer */
+ if (get_time().val > tc[port].support_timer &&
+ !tc[port].billboard_presented) {
+ /*
+ * Present USB Billboard Device Class interface
+ * indicating that Charge-Through is not supported
+ */
+ tc[port].billboard_presented = 1;
+ vpd_present_billboard(BB_SNK);
+ }
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_exit(int port)
+{
+ /* Reset timeout value to 10ms */
+ tc_set_timeout(port, 10*MSEC);
+ tc[port].billboard_presented = 0;
+ vpd_present_billboard(BB_NONE);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RA_CT_RD
+ */
+static unsigned int tc_state_host_rard_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rard_ct_rd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rard_ct_rd_entry(int port)
+{
+ /* Place Ra on VCONN and Rd on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RA_RD, 0);
+
+ /* Place Rd on Charge-Through CCs */
+ vpd_ct_set_pull(TYPEC_CC_RD, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rard_ct_rd_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * Super State HOST_OPEN_CT_OPEN
+ */
+static unsigned int tc_state_host_open_ct_open(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_open_ct_open_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_open_ct_open_entry(int port)
+{
+ /* Remove the terminations from Host */
+ vpd_host_set_pull(TYPEC_CC_OPEN, 0);
+
+ /* Remove the terminations from Charge-Through */
+ vpd_ct_set_pull(TYPEC_CC_OPEN, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_open_ct_open_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * Super State VBUS_CC_ISO
+ */
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_vbus_cc_iso_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_vbus_cc_iso_entry(int port)
+{
+ /* Isolate the Host-side port from the Charge-Through port */
+ vpd_vbus_pass_en(0);
+
+ /* Remove Charge-Through side port CCs */
+ vpd_ct_cc_sel(CT_OPEN);
+
+ /* Enable mcu communication and cc */
+ vpd_mcu_cc_en(1);
+
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_run(int port)
+{
+ return 0;
+}
+
+/**
+ * Unattached.SRC
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RpUSB on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_unattached_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_unattached_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_unattached_src_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SRC;
+ if (tc[port].obj.last_state != tc_state_unattached_snk)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VBUS */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ /* Make sure it's the Charge-Through Port's VBUS */
+ if (!vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_error_recovery);
+ return 0;
+ }
+
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC;
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_src_run(int port)
+{
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ /*
+ * Transition to AttachWait.SRC when host-side VBUS is
+ * vSafe0V and SRC.Rd state is detected on the Host-side
+ * port’s CC pin.
+ */
+ if (!vpd_is_host_vbus_present() && host_cc == TYPEC_CC_VOLT_RD) {
+ set_state(port, TC_OBJ(port), tc_state_attach_wait_src);
+ return 0;
+ }
+
+ /*
+ * Transition to Unattached.SNK within tDRPTransition or
+ * if Charge-Through VBUS is removed.
+ */
+ if (!vpd_is_ct_vbus_present() ||
+ get_time().val > tc[port].next_role_swap) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * AttachWait.SRC
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RpUSB on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_attach_wait_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attach_wait_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_attach_wait_src_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_src_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (host_cc == TYPEC_CC_VOLT_RD)
+ host_new_cc_state = PD_CC_UFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to Unattached.SNK when the SRC.Open state is detected on the
+ * Host-side port’s CC or if Charge-Through VBUS falls below
+ * vSinkDisconnect. The Charge-Through VCONN-Powered USB Device
+ * shall detect the SRC.Open state within tSRCDisconnect, but
+ * should detect it as quickly as possible.
+ */
+ if (host_new_cc_state == PD_CC_NONE || !vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE;
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Try.SNK when the host-side VBUS is at vSafe0V and the SRC.Rd
+ * state is on the Host-side port’s CC pin for at least tCCDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED &&
+ !vpd_is_host_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_try_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * Attached.SRC
+ */
+static unsigned int tc_state_attached_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_src_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_src_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ /* Connect Charge-Through VBUS to Host VBUS */
+ vpd_vbus_pass_en(1);
+
+ /*
+ * Get power from VBUS. No need to test because
+ * the Host VBUS is connected to the Charge-Through
+ * VBUS
+ */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_src_run(int port)
+{
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK when VBUS falls below vSinkDisconnect or the
+ * Host-side port’s CC pin is SRC.Open. The Charge-Through
+ * VCONNPowered USB Device shall detect the SRC.Open state within
+ * tSRCDisconnect, but should detect it as quickly as possible.
+ */
+ if (!vpd_is_ct_vbus_present() || host_cc == TYPEC_CC_VOLT_OPEN)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RPU_CT_RD
+ */
+static unsigned int tc_state_host_rpu_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rpu_ct_rd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rpu_ct_rd_entry(int port)
+{
+ /* Place RpUSB on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_USB);
+
+ /* Place Rd on Charge-Through CCs */
+ vpd_ct_set_pull(TYPEC_CC_RD, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rpu_ct_rd_run(int port)
+{
+ return RUN_SUPER;
+}
+
+/**
+ * Try.SNK
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_try_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_try_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard_ct_rd);
+}
+
+static unsigned int tc_state_try_snk_entry(int port)
+{
+ tc[port].state_id = TRY_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VBUS */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ /* Make sure it's the Charge-Through Port's VBUS */
+ if (!vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_error_recovery);
+ return 0;
+ }
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ /* Using next_role_swap timer as try_src timer */
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY;
+
+ return 0;
+}
+
+static unsigned int tc_state_try_snk_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /*
+ * Wait for tDRPTry before monitoring the Charge-Through
+ * port’s CC pins for the SNK.Rp
+ */
+ if (get_time().val < tc[port].next_role_swap)
+ return 0;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (cc_is_rp(host_cc))
+ host_new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE;
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall then transition to
+ * Attached.SNK when the SNK.Rp state is detected on the Host-side
+ * port’s CC pin for at least tTryCCDebounce and VBUS or VCONN is
+ * detected on Host-side port.
+ *
+ * Alternatively, the Charge-Through VCONN-Powered USB Device shall
+ * transition to TryWait.SRC if Host-side SNK.Rp state is not detected
+ * for tTryCCDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED &&
+ (vpd_is_host_vbus_present() || vpd_is_vconn_present()))
+ set_state(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_try_wait_src);
+
+ return 0;
+}
+
+/**
+ * TryWait.SRC
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RpUSB on Host CC
+ * Place Rd on Charge-Through CCs
+ */
+static unsigned int tc_state_try_wait_src(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_try_wait_src_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rpu_ct_rd);
+}
+
+static unsigned int tc_state_try_wait_src_entry(int port)
+{
+ tc[port].state_id = TRY_WAIT_SRC;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ tc[port].host_cc_state = PD_CC_UNSET;
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY;
+
+ return 0;
+}
+
+static unsigned int tc_state_try_wait_src_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (host_cc == TYPEC_CC_VOLT_RD)
+ host_new_cc_state = PD_CC_UFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ tc[port].host_cc_debounce =
+ get_time().val + PD_T_TRY_CC_DEBOUNCE;
+ return 0;
+ }
+
+ if (get_time().val > tc[port].host_cc_debounce) {
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to Attached.SRC when host-side VBUS is at vSafe0V and the
+ * SRC.Rd state is detected on the Host-side port’s CC pin for
+ * at least tTryCCDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_UFP_ATTACHED &&
+ !vpd_is_host_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_attached_src);
+ return 0;
+ }
+ }
+
+ if (get_time().val > tc[port].next_role_swap) {
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall transition
+ * to Unattached.SNK after tDRPTry if the Host-side port’s CC
+ * pin is not in the SRC.Rd state.
+ */
+ if (tc[port].host_cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+/**
+ * CTTry.SNK
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Connect Charge-Through Rd
+ * Get power from VCONN
+ */
+static unsigned int tc_state_ct_try_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_try_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_try_snk_entry(int port)
+{
+ tc[port].state_id = CTTRY_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_TRY;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_try_snk_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /*
+ * Wait for tDRPTry before monitoring the Charge-Through
+ * port’s CC pins for the SNK.Rp
+ */
+ if (get_time().val < tc[port].next_role_swap)
+ return 0;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc_is_rp(cc1) || cc_is_rp(cc2))
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ new_cc_state = PD_CC_NONE;
+
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall transition
+ * to Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* Debounce the CT CC state */
+ if (tc[port].cc_state != new_cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_DEBOUNCE;
+ tc[port].try_wait_debounce = get_time().val + PD_T_TRY_WAIT;
+
+ return 0;
+ }
+
+ if (get_time().val > tc[port].cc_debounce) {
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall then
+ * transition to CTAttached.VPD when the SNK.Rp state is
+ * detected on the Charge-Through port’s CC pins for at
+ * least tTryCCDebounce and VBUS is detected on
+ * Charge-Through port.
+ */
+ if (tc[port].cc_state == PD_CC_DFP_ATTACHED &&
+ vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_ct_attached_vpd);
+ return 0;
+ }
+ }
+
+ if (get_time().val > tc[port].try_wait_debounce) {
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to CTAttached.Unsupported if SNK.Rp state is not detected
+ * for tDRPTryWait.
+ */
+ if (tc[port].cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_attached_unsupported);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_try_snk_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 0;
+}
+
+/**
+ * CTAttachWait.Unsupported
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Place RPUSB on Charge-Through CC
+ * Get power from VCONN
+ */
+static unsigned int
+ tc_state_ct_attach_wait_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attach_wait_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_entry(int port)
+{
+ tc[port].state_id = CTATTACH_WAIT_UNSUPPORTED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc1 == TYPEC_CC_VOLT_RD || cc2 == TYPEC_CC_VOLT_RD)
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else if (cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_RA)
+ new_cc_state = PD_CC_AUDIO_ACC;
+ else /* (cc1 == TYPEC_CC_VOLT_OPEN or cc2 == TYPEC_CC_VOLT_OPEN */
+ new_cc_state = PD_CC_NONE;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* Debounce the cc state */
+ if (tc[port].cc_state != new_cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_CC_DEBOUNCE;
+ return 0;
+ }
+
+ /* Wait for CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.VPD when the state of either the Charge-Through
+ * Port’s CC1 or CC2 pin is SRC.Open for at least tCCDebounce.
+ *
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTTry.SNK if the state of at least one of the Charge-Through
+ * port’s CC pins is SRC.Rd, or if the state of both the CC1 and CC2
+ * pins is SRC.Ra. for at least tCCDebounce.
+ */
+ if (new_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ else /* PD_CC_DFP_ATTACHED or PD_CC_AUDIO_ACC */
+ set_state(port, TC_OBJ(port), tc_state_ct_try_snk);
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_unsupported_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 0;
+}
+
+/**
+ * CTAttached.Unsupported
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Place RPUSB on Charge-Through CC
+ * Get power from VCONN
+ */
+static unsigned int tc_state_ct_attached_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attached_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_attached_unsupported_entry(int port)
+{
+ tc[port].state_id = CTATTACHED_UNSUPPORTED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Present Billboard device */
+ vpd_present_billboard(BB_SNK);
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attached_unsupported_run(int port)
+{
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * The Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.VPD when SRC.Open state is detected on both the
+ * Charge-Through port’s CC pins or the SRC.Open state is detected
+ * on one CC pin and SRC.Ra is detected on the other CC pin.
+ */
+ if ((cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_OPEN) ||
+ (cc1 == TYPEC_CC_VOLT_OPEN && cc2 == TYPEC_CC_VOLT_RA) ||
+ (cc1 == TYPEC_CC_VOLT_RA && cc2 == TYPEC_CC_VOLT_OPEN)) {
+ set_state(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_attached_unsupported_exit(int port)
+{
+ vpd_present_billboard(BB_NONE);
+
+ return 0;
+}
+
+/**
+ * CTUnattached.Unsupported
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Place RPUSB on Charge-Through CC
+ * Get power from VCONN
+ */
+static unsigned int
+ tc_state_ct_unattached_unsupported(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_unattached_unsupported_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rpu);
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_entry(int port)
+{
+ tc[port].state_id = CTUNATTACHED_UNSUPPORTED;
+ if (tc[port].obj.last_state != tc_state_ct_unattached_vpd)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].next_role_swap = get_time().val + PD_T_DRP_SRC;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_run(int port)
+{
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTAttachWait.Unsupported when a Sink connection is detected on
+ * the Charge-Through port, as indicated by the SRC.Rd state on at
+ * least one of the Charge-Through port’s CC pins or SRC.Ra state
+ * on both the CC1 and CC2 pins.
+ */
+ if ((cc1 == TYPEC_CC_VOLT_RD || cc2 == TYPEC_CC_VOLT_RD) ||
+ (cc1 == TYPEC_CC_VOLT_RA &&
+ cc2 == TYPEC_CC_VOLT_RA)) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_attach_wait_unsupported);
+ return 0;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.VPD within tDRPTransition after dcSRC.DRP ∙ tDRP.
+ */
+ if (get_time().val > tc[port].next_role_swap) {
+ set_state(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_unattached_unsupported_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 0;
+}
+
+/**
+ * CTUnattached.VPD
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Connect Charge-Through Rd
+ * Get power from VCONN
+ */
+static unsigned int tc_state_ct_unattached_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_unattached_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_unattached_vpd_entry(int port)
+{
+ tc[port].state_id = CTUNATTACHED_VPD;
+ if (tc[port].obj.last_state != tc_state_ct_unattached_unsupported)
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_unattached_vpd_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc_is_rp(cc1) != cc_is_rp(cc2))
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else if (!cc_is_rp(cc1) && !cc_is_rp(cc2))
+ new_cc_state = PD_CC_NONE;
+ else
+ new_cc_state = PD_CC_UNSET;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTAttachWait.VPD when a Source connection is detected on the
+ * Charge-Through port, as indicated by the SNK.Rp state on
+ * exactly one of the Charge-Through port’s CC pins.
+ */
+ if (new_cc_state == PD_CC_DFP_ATTACHED) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_attach_wait_vpd);
+ return 0;
+ }
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * Unattached.SNK if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port),
+ tc_state_unattached_snk);
+ return 0;
+ }
+
+ /* Debounce the cc state */
+ if (new_cc_state != tc[port].cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_DRP_SRC;
+ return 0;
+ }
+
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.Unsupported within tDRPTransition after the state
+ * of both the Charge-Through port’s CC1 and CC2 pins is SNK.Open
+ * for tDRP-dcSRC.DRP ∙ tDRP, or if directed.
+ */
+ if (tc[port].cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_unattached_unsupported);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_unattached_vpd_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ return 0;
+}
+
+/**
+ * CTDisabled.VPD
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Remove the terminations from Host
+ * Remove the terminations from Charge-Through
+ */
+static unsigned int tc_state_ct_disabled_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_disabled_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open_ct_open);
+}
+
+static unsigned int tc_state_ct_disabled_vpd_entry(int port)
+{
+ tc[port].state_id = CTDISABLED_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VBUS */
+ vpd_vconn_pwr_sel_odl(PWR_VBUS);
+
+ tc[port].next_role_swap = get_time().val + PD_T_VPDDISABLE;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_disabled_vpd_run(int port)
+{
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to Unattached.SNK after tVPDDisable.
+ */
+ if (get_time().val > tc[port].next_role_swap)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * CTAttached.VPD
+ */
+static unsigned int tc_state_ct_attached_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attached_vpd_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_ct_attached_vpd_entry(int port)
+{
+ int cc1;
+ int cc2;
+
+ tc[port].state_id = CTATTACHED_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Get power from VCONN */
+ vpd_vconn_pwr_sel_odl(PWR_VCONN);
+
+ /*
+ * Detect which of the Charge-Through port’s CC1 or CC2
+ * pins is connected through the cable
+ */
+ vpd_ct_get_cc(&cc1, &cc2);
+ tc[port].ct_cc = cc_is_rp(cc2) ? CT_CC2 : CT_CC1;
+
+ /*
+ * 1. Remove or reduce any additional capacitance on the
+ * Host-side CC port
+ */
+ vpd_mcu_cc_en(0);
+
+ /*
+ * 2. Disable the Rp termination advertising 3.0 A on the
+ * host port’s CC pin
+ */
+ vpd_host_set_pull(TYPEC_CC_OPEN, 0);
+
+ /*
+ * 3. Passively multiplex the detected Charge-Through port’s
+ * CC pin through to the host port’s CC
+ */
+ vpd_ct_cc_sel(tc[port].ct_cc);
+
+ /*
+ * 4. Disable the Rd on the Charge-Through port’s CC1 and CC2
+ * pins
+ */
+ vpd_ct_set_pull(TYPEC_CC_OPEN, 0);
+
+ /*
+ * 5. Connect the Charge-Through port’s VBUS through to the
+ * host port’s VBUS
+ */
+ vpd_vbus_pass_en(1);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_ct_attached_vpd_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTDisabled.VPD if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_ct_disabled_vpd);
+ return 0;
+ }
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+ if ((tc[port].ct_cc ? cc2 : cc1) == TYPEC_CC_VOLT_OPEN)
+ new_cc_state = PD_CC_NONE;
+ else
+ new_cc_state = PD_CC_DFP_ATTACHED;
+
+ /* Debounce the cc state */
+ if (new_cc_state != tc[port].cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val + PD_T_VPDCTDD;
+ return 0;
+ }
+
+ if (get_time().val < tc[port].pd_debounce)
+ return 0;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTUnattached.VPD when VBUS falls below vSinkDisconnect and the
+ * state of the passed-through CC pin is SNK.Open for tVPDCTDD.
+ */
+ if (tc[port].cc_state == PD_CC_NONE && !vpd_is_ct_vbus_present())
+ set_state(port, TC_OBJ(port), tc_state_ct_unattached_vpd);
+
+ return 0;
+}
+
+/**
+ * CTAttachWait.VPD
+ *
+ * Super State Entry Actions:
+ * Isolate the Host-side port from the Charge-Through port
+ * Enable mcu communication
+ * Place RP3A0 on Host CC
+ * Connect Charge-Through Rd
+ * Get power from VCONN
+ */
+static unsigned int tc_state_ct_attach_wait_vpd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_ct_attach_wait_vpd_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rp3_ct_rd);
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_entry(int port)
+{
+ tc[port].state_id = CTATTACH_WAIT_VPD;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ tc[port].cc_state = PD_CC_UNSET;
+
+ /* Sample CCs every 2ms */
+ tc_set_timeout(port, 2 * MSEC);
+ return 0;
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_run(int port)
+{
+ int new_cc_state;
+ int cc1;
+ int cc2;
+
+ /* Check CT CC for connection */
+ vpd_ct_get_cc(&cc1, &cc2);
+
+ if (cc_is_rp(cc1) != cc_is_rp(cc2))
+ new_cc_state = PD_CC_DFP_ATTACHED;
+ else if (!cc_is_rp(cc1) && !cc_is_rp(cc2))
+ new_cc_state = PD_CC_NONE;
+ else
+ new_cc_state = PD_CC_UNSET;
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTDisabled.VPD if VCONN falls below vVCONNDisconnect.
+ */
+ if (!vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_ct_disabled_vpd);
+ return 0;
+ }
+
+ /* Debounce the cc state */
+ if (new_cc_state != tc[port].cc_state) {
+ tc[port].cc_state = new_cc_state;
+ tc[port].cc_debounce = get_time().val +
+ PD_T_CC_DEBOUNCE;
+ tc[port].pd_debounce = get_time().val +
+ PD_T_PD_DEBOUNCE;
+ return 0;
+ }
+
+ if (get_time().val > tc[port].pd_debounce) {
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition
+ * to CTUnattached.VPD when the state of both the Charge-Through
+ * port’s CC1 and CC2 pins are SNK.Open for at least
+ * tPDDebounce.
+ */
+ if (tc[port].cc_state == PD_CC_NONE) {
+ set_state(port, TC_OBJ(port),
+ tc_state_ct_unattached_vpd);
+ return 0;
+ }
+ }
+
+ if (get_time().val > tc[port].cc_debounce) {
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall transition to
+ * CTAttached.VPD after the state of only one of the
+ * Charge-Through port’s CC1 or CC2 pins is SNK.Rp for at
+ * least tCCDebounce and VBUS on the Charge-Through port is
+ * detected.
+ */
+ if (tc[port].cc_state == PD_CC_DFP_ATTACHED &&
+ vpd_is_ct_vbus_present()) {
+ set_state(port, TC_OBJ(port), tc_state_ct_attached_vpd);
+ return 0;
+ }
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_ct_attach_wait_vpd_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+
+ /* Reset timeout value to 10ms */
+ tc_set_timeout(port, 10 * MSEC);
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RP3_CT_RD
+ */
+static unsigned int tc_state_host_rp3_ct_rd(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rp3_ct_rd_sig[sig])(port);
+
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rp3_ct_rd_entry(int port)
+{
+ /* Place RP3A0 on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0);
+
+ /* Connect Charge-Through Rd */
+ vpd_ct_set_pull(TYPEC_CC_RD, 0);
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall
+ * ensure that it is powered by VCONN
+ */
+
+ /* Make sure vconn is on */
+ if (!vpd_is_vconn_present())
+ set_state(port, TC_OBJ(port), tc_state_error_recovery);
+
+ /* Get power from VCONN */
+ vpd_vconn_pwr_sel_odl(PWR_VCONN);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rp3_ct_rd_run(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State HOST_RP3_CT_RPU
+ */
+static unsigned int tc_state_host_rp3_ct_rpu(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rp3_ct_rpu_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rp3_ct_rpu_entry(int port)
+{
+ /* Place RP3A0 on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RP, TYPEC_RP_3A0);
+
+ /* Place RPUSB on Charge-Through CC */
+ vpd_ct_set_pull(TYPEC_CC_RP, TYPEC_RP_USB);
+
+ /*
+ * A Charge-Through VCONN-Powered USB Device shall
+ * ensure that it is powered by VCONN
+ */
+
+ /* Make sure vconn is on */
+ if (!vpd_is_vconn_present())
+ set_state(port, TC_OBJ(port), tc_state_error_recovery);
+
+ /* Get power from VCONN */
+ vpd_vconn_pwr_sel_odl(PWR_VCONN);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rp3_ct_rpu_run(int port)
+{
+ return 0;
+}
+
+static unsigned int do_nothing_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /* CONFIG_USB_TYPEC_CTVPD */
diff --git a/include/usb_tc_sm.h b/include/usb_tc_sm.h
new file mode 100644
index 0000000000..08e9ebbf54
--- /dev/null
+++ b/include/usb_tc_sm.h
@@ -0,0 +1,91 @@
+/* Copyright 2019 The Chromium OS 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 Type-C module */
+
+#ifndef __CROS_EC_USB_TC_H
+#define __CROS_EC_USB_TC_H
+
+enum typec_state_id {
+ DISABLED,
+ UNATTACHED_SNK,
+ ATTACH_WAIT_SNK,
+ ATTACHED_SNK,
+#if !defined(CONFIG_USB_TYPEC_VPD)
+ ERROR_RECOVERY,
+ UNATTACHED_SRC,
+ ATTACH_WAIT_SRC,
+ ATTACHED_SRC,
+#endif
+#if !defined(CONFIG_USB_TYPEC_CTVPD) && !defined(CONFIG_USB_TYPEC_VPD)
+ AUDIO_ACCESSORY,
+ ORIENTED_DEBUG_ACCESSORY_SRC,
+ UNORIENTED_DEBUG_ACCESSORY_SRC,
+ DEBUG_ACCESSORY_SNK,
+ TRY_SRC,
+ TRY_WAIT_SNK,
+ CTUNATTACHED_SNK,
+ CTATTACHED_SNK,
+#endif
+#if defined(CONFIG_USB_TYPEC_CTVPD)
+ CTTRY_SNK,
+ CTATTACHED_UNSUPPORTED,
+ CTATTACH_WAIT_UNSUPPORTED,
+ CTUNATTACHED_UNSUPPORTED,
+ CTUNATTACHED_VPD,
+ CTATTACH_WAIT_VPD,
+ CTATTACHED_VPD,
+ CTDISABLED_VPD,
+ TRY_SNK,
+ TRY_WAIT_SRC,
+#endif
+ /* Number of states. Not an actual state. */
+ TC_STATE_COUNT,
+};
+
+/**
+ * Get the id of the current Type-C state
+ *
+ * @param port USB-C port number
+ */
+enum typec_state_id get_typec_state_id(int port);
+
+/**
+ * Get current data role
+ *
+ * @param port USB-C port number
+ * @return 0 for ufp, 1 for dfp, 2 for disconnected
+ */
+int tc_get_data_role(int port);
+
+/**
+ * Get current power role
+ *
+ * @param port USB-C port number
+ * @return 0 for sink, 1 for source or vpd
+ */
+int tc_get_power_role(int port);
+
+/**
+ * Set loop timeout value
+ *
+ * @param port USB-C port number
+ * @timeout time in ms
+ */
+void tc_set_timeout(int port, uint64_t timeout);
+
+#ifdef CONFIG_USB_TYPEC_CTVPD
+/**
+ * Resets the charge-through support timer. This can be
+ * called many times but the support timer will only
+ * reset once, while in the Attached.SNK state.
+ *
+ * @param port USB-C port number
+ */
+void tc_reset_support_timer(int port);
+
+#endif /* CONFIG_USB_TYPEC_CTVPD */
+#endif /* __CROS_EC_USB_TC_H */
+
diff --git a/include/usb_tc_vpd_sm.h b/include/usb_tc_vpd_sm.h
new file mode 100644
index 0000000000..c8a4dd48a3
--- /dev/null
+++ b/include/usb_tc_vpd_sm.h
@@ -0,0 +1,486 @@
+/* Copyright 2019 The Chromium OS 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 "vpd_api.h"
+
+/* USB Type-C VCONN Powered Device module */
+
+#ifndef __CROS_EC_USB_TC_VPD_H
+#define __CROS_EC_USB_TC_VPD_H
+
+/* Type-C Layer Flags */
+#define TC_FLAGS_VCONN_ON BIT(0)
+
+#undef PD_DEFAULT_STATE
+/* Port default state at startup */
+#define PD_DEFAULT_STATE(port) tc_state_unattached_snk
+
+/*
+ * TC_OBJ is a convenience macro to access struct sm_obj, which
+ * must be the first member of struct type_c.
+ */
+#define TC_OBJ(port) (SM_OBJ(tc[port]))
+
+/**
+ * This is the Type-C Port object that contains information needed to
+ * implement a VCONN Powered Device.
+ */
+static struct type_c {
+ /*
+ * struct sm_obj must be first. This is the state machine
+ * object that keeps track of the current and last state
+ * of the state machine.
+ */
+ struct sm_obj obj;
+ /* state id */
+ enum typec_state_id state_id;
+ /* current port power role (VPD, SOURCE or SINK) */
+ uint8_t power_role;
+ /* current port data role (DFP or UFP) */
+ uint8_t data_role;
+ /* bool: enable power delivery state machines */
+ uint8_t pd_enable;
+ /* event timeout */
+ uint64_t evt_timeout;
+ /* state machine event */
+ int evt;
+ /* port flags, see TC_FLAGS_* */
+ uint32_t flags;
+ /* Time a port shall wait before it can determine it is attached */
+ uint64_t cc_debounce;
+ /* VPD host port cc state */
+ enum pd_cc_states host_cc_state;
+ uint8_t ct_cc;
+} tc[CONFIG_USB_PD_PORT_COUNT];
+
+/* Type-C states */
+static unsigned int tc_state_disabled(int port, enum signal sig);
+static unsigned int tc_state_disabled_entry(int port);
+static unsigned int tc_state_disabled_run(int port);
+static unsigned int tc_state_disabled_exit(int port);
+
+static unsigned int tc_state_unattached_snk(int port, enum signal sig);
+static unsigned int tc_state_unattached_snk_entry(int port);
+static unsigned int tc_state_unattached_snk_run(int port);
+static unsigned int tc_state_unattached_snk_exit(int port);
+
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig);
+static unsigned int tc_state_attach_wait_snk_entry(int port);
+static unsigned int tc_state_attach_wait_snk_run(int port);
+static unsigned int tc_state_attach_wait_snk_exit(int port);
+
+static unsigned int tc_state_attached_snk(int port, enum signal sig);
+static unsigned int tc_state_attached_snk_entry(int port);
+static unsigned int tc_state_attached_snk_run(int port);
+static unsigned int tc_state_attached_snk_exit(int port);
+
+/* Super States */
+static unsigned int tc_state_host_rard(int port, enum signal sig);
+static unsigned int tc_state_host_rard_entry(int port);
+static unsigned int tc_state_host_rard_run(int port);
+static unsigned int tc_state_host_rard_exit(int port);
+
+static unsigned int tc_state_host_open(int port, enum signal sig);
+static unsigned int tc_state_host_open_entry(int port);
+static unsigned int tc_state_host_open_run(int port);
+static unsigned int tc_state_host_open_exit(int port);
+
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig);
+static unsigned int tc_state_vbus_cc_iso_entry(int port);
+static unsigned int tc_state_vbus_cc_iso_run(int port);
+static unsigned int tc_state_vbus_cc_iso_exit(int port);
+
+static unsigned int get_super_state(int port);
+
+static const state_sig tc_state_disabled_sig[] = {
+ tc_state_disabled_entry,
+ tc_state_disabled_run,
+ tc_state_disabled_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_unattached_snk_sig[] = {
+ tc_state_unattached_snk_entry,
+ tc_state_unattached_snk_run,
+ tc_state_unattached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attach_wait_snk_sig[] = {
+ tc_state_attach_wait_snk_entry,
+ tc_state_attach_wait_snk_run,
+ tc_state_attach_wait_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_attached_snk_sig[] = {
+ tc_state_attached_snk_entry,
+ tc_state_attached_snk_run,
+ tc_state_attached_snk_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_rard_sig[] = {
+ tc_state_host_rard_entry,
+ tc_state_host_rard_run,
+ tc_state_host_rard_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_host_open_sig[] = {
+ tc_state_host_open_entry,
+ tc_state_host_open_run,
+ tc_state_host_open_exit,
+ get_super_state
+};
+
+static const state_sig tc_state_vbus_cc_iso_sig[] = {
+ tc_state_vbus_cc_iso_entry,
+ tc_state_vbus_cc_iso_run,
+ tc_state_vbus_cc_iso_exit,
+ get_super_state
+};
+
+static void tc_state_init(int port)
+{
+ int res = 0;
+ sm_state this_state;
+
+ res = tc_restart_tcpc(port);
+
+ CPRINTS("TCPC p%d init %s", port, res ? "failed" : "ready");
+ this_state = res ? tc_state_disabled : PD_DEFAULT_STATE(port);
+
+ /* Disable TCPC RX until connection is established */
+ tcpm_set_rx_enable(port, 0);
+
+ init_state(port, TC_OBJ(port), this_state);
+
+ /* Disable pd state machines */
+ tc[port].pd_enable = 0;
+ tc[port].evt_timeout = 10*MSEC;
+ tc[port].power_role = PD_PLUG_CABLE_VPD;
+ tc[port].data_role = 0; /* Reserved for VPD */
+ tc[port].flags = 0;
+}
+
+static void tc_event_check(int port, int evt)
+{
+ /* Do Nothing */
+}
+
+/**
+ * Disabled
+ *
+ * Super State Entries:
+ * Enable mcu communication
+ * Remove the terminations from Host CC
+ */
+static unsigned int tc_state_disabled(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_disabled_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_open);
+}
+
+static unsigned int tc_state_disabled_entry(int port)
+{
+ tc[port].state_id = DISABLED;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ return 0;
+}
+
+static unsigned int tc_state_disabled_run(int port)
+{
+ task_wait_event(-1);
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_disabled_exit(int port)
+{
+#ifndef CONFIG_USB_PD_TCPC
+ if (tc_restart_tcpc(port) != 0) {
+ CPRINTS("TCPC p%d restart failed!", port);
+ return 0;
+ }
+#endif
+ CPRINTS("TCPC p%d resumed!", port);
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+/**
+ * Unattached.SNK
+ *
+ * Super State Entry:
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ */
+static unsigned int tc_state_unattached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_unattached_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard);
+}
+
+static unsigned int tc_state_unattached_snk_entry(int port)
+{
+ tc[port].state_id = UNATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ return 0;
+}
+
+static unsigned int tc_state_unattached_snk_run(int port)
+{
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ /*
+ * Transition to AttachWait.SNK when a Source connection is
+ * detected, as indicated by the SNK.Rp state on its Host-side
+ * port’s CC pin.
+ */
+ if (cc_is_rp(host_cc)) {
+ set_state(port, TC_OBJ(port), tc_state_attach_wait_snk);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_unattached_snk_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * AttachedWait.SNK
+ *
+ * Super State Entry:
+ * Enable mcu communication
+ * Place Ra on VCONN and Rd on Host CC
+ */
+static unsigned int tc_state_attach_wait_snk(int port, enum signal sig)
+{
+ int ret = 0;
+
+ ret = (*tc_state_attach_wait_snk_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_host_rard);
+}
+
+static unsigned int tc_state_attach_wait_snk_entry(int port)
+{
+ tc[port].state_id = ATTACH_WAIT_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+ tc[port].host_cc_state = PD_CC_UNSET;
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_run(int port)
+{
+ int host_new_cc_state;
+ int host_cc;
+
+ /* Check Host CC for connection */
+ vpd_host_get_cc(&host_cc);
+
+ if (cc_is_rp(host_cc))
+ host_new_cc_state = PD_CC_DFP_ATTACHED;
+ else
+ host_new_cc_state = PD_CC_NONE;
+
+ /* Debounce the Host CC state */
+ if (tc[port].host_cc_state != host_new_cc_state) {
+ tc[port].host_cc_state = host_new_cc_state;
+ if (host_new_cc_state == PD_CC_DFP_ATTACHED)
+ tc[port].cc_debounce = get_time().val +
+ PD_T_CC_DEBOUNCE;
+ else
+ tc[port].cc_debounce = get_time().val +
+ PD_T_PD_DEBOUNCE;
+
+ return 0;
+ }
+
+ /* Wait for Host CC debounce */
+ if (get_time().val < tc[port].cc_debounce)
+ return 0;
+
+ /*
+ * A VCONN-Powered USB Device shall transition to
+ * Attached.SNK after the state of the Host-side port’s CC pin is
+ * SNK.Rp for at least tCCDebounce and either host-side VCONN or
+ * VBUS is detected.
+ *
+ * Transition to Unattached.SNK when the state of both the CC1 and
+ * CC2 pins is SNK.Open for at least tPDDebounce.
+ */
+ if (tc[port].host_cc_state == PD_CC_DFP_ATTACHED &&
+ (vpd_is_vconn_present() || vpd_is_host_vbus_present()))
+ set_state(port, TC_OBJ(port), tc_state_attached_snk);
+ else if (tc[port].host_cc_state == PD_CC_NONE)
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+
+ return 0;
+}
+
+static unsigned int tc_state_attach_wait_snk_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Attached.SNK
+ */
+static unsigned int tc_state_attached_snk(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_attached_snk_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_attached_snk_entry(int port)
+{
+ tc[port].state_id = ATTACHED_SNK;
+ CPRINTS("C%d: %s", port, tc_state_names[tc[port].state_id]);
+
+ /* Enable PD */
+ tc[port].pd_enable = 1;
+ set_polarity(port, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_run(int port)
+{
+ /* Has host vbus and vconn been removed */
+ if (!vpd_is_host_vbus_present() && !vpd_is_vconn_present()) {
+ set_state(port, TC_OBJ(port), tc_state_unattached_snk);
+ return 0;
+ }
+
+ if (vpd_is_vconn_present()) {
+ if (!(tc[port].flags & TC_FLAGS_VCONN_ON)) {
+ /* VCONN detected. Remove RA */
+ vpd_host_set_pull(TYPEC_CC_RD, 0);
+ tc[port].flags |= TC_FLAGS_VCONN_ON;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int tc_state_attached_snk_exit(int port)
+{
+ /* Disable PD */
+ tc[port].pd_enable = 0;
+ tc[port].flags &= ~TC_FLAGS_VCONN_ON;
+
+ return 0;
+}
+
+/**
+ * Super State HOST_RARD
+ */
+static unsigned int tc_state_host_rard(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_rard_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_rard_entry(int port)
+{
+ /* Place Ra on VCONN and Rd on Host CC */
+ vpd_host_set_pull(TYPEC_CC_RA_RD, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_rard_run(int port)
+{
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_host_rard_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State HOST_OPEN
+ */
+static unsigned int tc_state_host_open(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_host_open_sig[sig])(port);
+ return SUPER(ret, sig, tc_state_vbus_cc_iso);
+}
+
+static unsigned int tc_state_host_open_entry(int port)
+{
+ /* Remove the terminations from Host CC */
+ vpd_host_set_pull(TYPEC_CC_OPEN, 0);
+
+ return 0;
+}
+
+static unsigned int tc_state_host_open_run(int port)
+{
+ return RUN_SUPER;
+}
+
+static unsigned int tc_state_host_open_exit(int port)
+{
+ return 0;
+}
+
+/**
+ * Super State VBUS_CC_ISO
+ */
+static unsigned int tc_state_vbus_cc_iso(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*tc_state_vbus_cc_iso_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int tc_state_vbus_cc_iso_entry(int port)
+{
+ /* Enable mcu communication and cc */
+ vpd_mcu_cc_en(1);
+
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_run(int port)
+{
+ return 0;
+}
+
+static unsigned int tc_state_vbus_cc_iso_exit(int port)
+{
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+#endif /*__CROS_EC_USB_TC_VPD_H */
diff --git a/power/common.c b/power/common.c
index 09501a16ec..13b66f5117 100644
--- a/power/common.c
+++ b/power/common.c
@@ -887,6 +887,7 @@ static int host_command_host_sleep_event(struct host_cmd_handler_args *args)
enum host_sleep_event state = p->sleep_event;
host_sleep_state = state;
+ ctx.sleep_transitions = 0;
switch (state) {
case HOST_SLEEP_EVENT_S3_SUSPEND:
case HOST_SLEEP_EVENT_S0IX_SUSPEND:
diff --git a/test/build.mk b/test/build.mk
index 979fc352fe..b5b6e8bbea 100644
--- a/test/build.mk
+++ b/test/build.mk
@@ -47,7 +47,6 @@ test-list-host += motion_angle_tablet
test-list-host += motion_lid
test-list-host += mutex
test-list-host += nvmem
-test-list-host += nvmem_vars
test-list-host += pingpong
test-list-host += pinweaver
test-list-host += power_button
@@ -66,6 +65,13 @@ test-list-host += timer_dos
test-list-host += usb_pd
test-list-host += usb_pd_giveback
test-list-host += usb_pd_rev30
+test-list-host += usb_sm_framework_h3
+test-list-host += usb_sm_framework_h2
+test-list-host += usb_sm_framework_h1
+test-list-host += usb_sm_framework_h0
+test-list-host += usb_typec_vpd
+test-list-host += usb_typec_ctvpd
+test-list-host += usb_prl
test-list-host += utils
test-list-host += utils_str
test-list-host += vboot
@@ -105,8 +111,7 @@ motion_angle-y=motion_angle.o motion_angle_data_literals.o motion_common.o
motion_angle_tablet-y=motion_angle_tablet.o motion_angle_data_literals_tablet.o motion_common.o
motion_lid-y=motion_lid.o
mutex-y=mutex.o
-nvmem-y=nvmem.o
-nvmem_vars-y=nvmem_vars.o
+nvmem-y=nvmem.o nvmem_tpm2_mock.o
pingpong-y=pingpong.o
pinweaver-y=pinweaver.o
power_button-y=power_button.o
@@ -129,9 +134,21 @@ timer_dos-y=timer_dos.o
usb_pd-y=usb_pd.o
usb_pd_giveback-y=usb_pd.o
usb_pd_rev30-y=usb_pd.o
+usb_sm_framework_h3-y=usb_sm_framework_h3.o
+usb_sm_framework_h2-y=usb_sm_framework_h3.o
+usb_sm_framework_h1-y=usb_sm_framework_h3.o
+usb_sm_framework_h0-y=usb_sm_framework_h3.o
+usb_typec_vpd-y=usb_typec_ctvpd.o vpd_api.o
+usb_typec_ctvpd-y=usb_typec_ctvpd.o vpd_api.o
+usb_prl-y=usb_prl.o
utils-y=utils.o
utils_str-y=utils_str.o
vboot-y=vboot.o
float-y=fp.o
fp-y=fp.o
x25519-y=x25519.o
+
+TPM2_ROOT := $(CROS_WORKON_SRCROOT)/src/third_party/tpm2
+$(out)/RO/common/new_nvmem.o: CFLAGS += -I$(TPM2_ROOT)
+$(out)/RO/test/nvmem.o: CFLAGS += -I$(TPM2_ROOT)
+$(out)/RO/test/nvmem_tpm2_mock.o: CFLAGS += -I$(TPM2_ROOT)
diff --git a/test/kb_mkbp.c b/test/kb_mkbp.c
index 15952e74bb..324b5b3e91 100644
--- a/test/kb_mkbp.c
+++ b/test/kb_mkbp.c
@@ -101,6 +101,46 @@ int verify_key(int c, int r, int pressed)
return 1;
}
+int verify_key_v2(int c, int r, int pressed, int expect_more)
+{
+ struct host_cmd_handler_args args;
+ struct ec_response_get_next_event_v1 event;
+ int i;
+
+ args.version = 2;
+ args.command = EC_CMD_GET_NEXT_EVENT;
+ args.params = NULL;
+ args.params_size = 0;
+ args.response = &event;
+ args.response_max = sizeof(event);
+ args.response_size = 0;
+
+ if (c >= 0 && r >= 0) {
+ ccprintf("Verify %s (%d, %d). Expect %smore.\n",
+ action[pressed], c, r, expect_more ? "" : "no ");
+ set_state(c, r, pressed);
+
+ if (host_command_process(&args) != EC_RES_SUCCESS)
+ return 0;
+
+ if (!!(event.event_type & EC_MKBP_HAS_MORE_EVENTS) !=
+ expect_more) {
+ ccprintf("Incorrect more events!\n");
+ return 0;
+ }
+
+ for (i = 0; i < KEYBOARD_COLS_MAX; ++i)
+ if (event.data.key_matrix[i] != state[i])
+ return 0;
+ } else {
+ ccprintf("Verify no events available\n");
+ if (host_command_process(&args) != EC_RES_UNAVAILABLE)
+ return 0;
+ }
+
+ return 1;
+}
+
int mkbp_config(struct ec_params_mkbp_set_config params)
{
struct host_cmd_handler_args args;
@@ -179,6 +219,24 @@ int single_key_press(void)
return EC_SUCCESS;
}
+int single_key_press_v2(void)
+{
+ keyboard_clear_buffer();
+ clear_state();
+ TEST_ASSERT(press_key(0, 0, 1) == EC_SUCCESS);
+ TEST_ASSERT(FIFO_NOT_EMPTY());
+ TEST_ASSERT(press_key(0, 0, 0) == EC_SUCCESS);
+ TEST_ASSERT(FIFO_NOT_EMPTY());
+
+ clear_state();
+ TEST_ASSERT(verify_key_v2(0, 0, 1, 1));
+ TEST_ASSERT(FIFO_NOT_EMPTY());
+ TEST_ASSERT(verify_key_v2(0, 0, 0, 0));
+ TEST_ASSERT(FIFO_EMPTY());
+
+ return EC_SUCCESS;
+}
+
int test_fifo_size(void)
{
keyboard_clear_buffer();
@@ -236,6 +294,7 @@ void run_test(void)
/* Clear any pending events such as lid open. */
clear_mkbp_events();
RUN_TEST(single_key_press);
+ RUN_TEST(single_key_press_v2);
RUN_TEST(test_fifo_size);
RUN_TEST(test_enable);
RUN_TEST(fifo_underrun);
diff --git a/test/legacy_nvmem_dump.h b/test/legacy_nvmem_dump.h
new file mode 100644
index 0000000000..6816673c23
--- /dev/null
+++ b/test/legacy_nvmem_dump.h
@@ -0,0 +1,1043 @@
+/*
+ * Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * This is a test NVMEM snapshot, it includes a couple of key,value pairs and
+ * a set of TPM reserved and evictable objects, as created after the first
+ * Chrome OS boot on a device.
+ *
+ * This binary dump is placed in a separate file not to free up the test file
+ * using it.
+ */
+ 0x00, 0x65, 0x8e, 0x10, 0x80, 0xca, 0x52, 0x1e, 0x95, 0x81, 0x12, 0x4f,
+ 0x36, 0x78, 0x9a, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x63, 0x72, 0x6f, 0x73, 0x2d, 0x70,
+ 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00, 0xe1, 0xac, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x37, 0x01, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0xbd, 0xfe, 0xff, 0xff, 0x85, 0xfc, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb0, 0xde, 0x01, 0x00, 0xb4, 0xde, 0x01, 0x00,
+ 0x9b, 0x0f, 0x06, 0x00, 0xbc, 0xde, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x5b, 0x15, 0xa1, 0x0a, 0x05, 0x12, 0x58, 0x84, 0xbf, 0xf6, 0xc9, 0xf3,
+ 0xdd, 0xb7, 0x26, 0xce, 0x56, 0x9e, 0x5f, 0x7a, 0xa8, 0xd4, 0x8a, 0x67,
+ 0x5c, 0x26, 0x35, 0x0e, 0xb2, 0x13, 0x2c, 0x79, 0x20, 0x00, 0x26, 0xca,
+ 0x7d, 0xb8, 0x1a, 0x1f, 0x0b, 0x5c, 0x0a, 0xf3, 0xb5, 0xe2, 0x6a, 0xec,
+ 0x1a, 0x0d, 0x90, 0x8b, 0x92, 0x3c, 0x07, 0xb0, 0x41, 0xb0, 0x27, 0x20,
+ 0x88, 0x33, 0xfe, 0x5c, 0xf2, 0x7b, 0x20, 0x00, 0x9e, 0xb7, 0xa2, 0x4c,
+ 0xad, 0x6c, 0xc0, 0x92, 0x92, 0xef, 0xbc, 0x56, 0x65, 0x47, 0xf9, 0x09,
+ 0xd1, 0xc4, 0xbc, 0x36, 0xe8, 0x3a, 0xc2, 0x8a, 0x11, 0x3a, 0xca, 0xe1,
+ 0x66, 0xd7, 0x85, 0x57, 0x20, 0x00, 0x0c, 0x6d, 0xc7, 0x61, 0x92, 0xfc,
+ 0x1b, 0x24, 0x02, 0xc1, 0x92, 0x0e, 0xf4, 0xa1, 0x75, 0xbe, 0xb1, 0x3d,
+ 0x29, 0xfe, 0x1e, 0xe2, 0x65, 0xf5, 0x25, 0xae, 0xaf, 0xfe, 0x73, 0x32,
+ 0x35, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x52, 0x01, 0x7b, 0x53, 0xc5, 0x95, 0xa0, 0x3a, 0x07, 0xd5, 0x62, 0x7f,
+ 0xd3, 0x9c, 0x85, 0xaa, 0xfc, 0x56, 0xa0, 0xfa, 0x3a, 0xe8, 0x17, 0x38,
+ 0xc3, 0x59, 0x65, 0xbe, 0x75, 0x1b, 0xdc, 0xdc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x84, 0xe7, 0x7e, 0x46, 0xfe, 0xbd,
+ 0x10, 0xdd, 0x5b, 0x09, 0xb2, 0xe2, 0xb1, 0x3f, 0xbf, 0x9a, 0xf3, 0xd7,
+ 0xfb, 0xf7, 0x28, 0xbb, 0x24, 0x10, 0xa3, 0xf3, 0x18, 0xa4, 0xa2, 0x16,
+ 0xd5, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x03, 0xff, 0xff, 0xff, 0x0b, 0x00,
+ 0x03, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x03, 0xff, 0xff, 0xff, 0x0d, 0x00,
+ 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8,
+ 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ /* Manually added nonempty pcr. Array 0, index 0 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ /* Manually added nonempty pcr. Array 1, index 1 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ /* Manually added ram index of size 0x20 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x55, 0x55,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x69, 0x17, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00,
+ 0xc0, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x20, 0x04, 0x62, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xef, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x01, 0x30, 0x82, 0x03, 0xeb, 0x30, 0x82, 0x02, 0xd3, 0xa0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x10, 0x71, 0x7b, 0xc1, 0xfb, 0x3b, 0x21, 0xb5,
+ 0xfb, 0x02, 0x21, 0x1f, 0xb7, 0x0a, 0xbc, 0xe4, 0x88, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x30, 0x81, 0x80, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x08, 0x0c, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69,
+ 0x61, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b,
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1b, 0x45, 0x6e,
+ 0x67, 0x69, 0x6e, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e,
+ 0x64, 0x20, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6d, 0x65, 0x6e,
+ 0x74, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x17,
+ 0x43, 0x52, 0x4f, 0x53, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x50, 0x52, 0x44,
+ 0x20, 0x45, 0x4b, 0x20, 0x52, 0x4f, 0x4f, 0x54, 0x20, 0x43, 0x41, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x37, 0x30, 0x36, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x37, 0x30, 0x36,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x5a, 0x30, 0x00, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xf8, 0x26, 0xde, 0x99, 0xfa, 0x25,
+ 0xe1, 0xb6, 0xda, 0xb7, 0x88, 0x74, 0x77, 0x3d, 0x9a, 0x2e, 0xd0, 0xbd,
+ 0xda, 0x68, 0x8b, 0x1b, 0x8c, 0xe7, 0xd1, 0x0b, 0xf4, 0xfe, 0x8e, 0x94,
+ 0x09, 0x43, 0x11, 0x11, 0x46, 0xfc, 0x16, 0xb5, 0x15, 0x67, 0xab, 0x1b,
+ 0x8f, 0x25, 0xa7, 0x28, 0x04, 0xcc, 0xed, 0xf0, 0x5c, 0xbe, 0xa3, 0xfd,
+ 0x85, 0x85, 0x9b, 0xea, 0x6c, 0x61, 0x8b, 0x7e, 0xd2, 0x76, 0x2b, 0x37,
+ 0x87, 0x30, 0xd3, 0x9f, 0x0d, 0xb7, 0x0e, 0x31, 0x39, 0x3b, 0x3a, 0xa3,
+ 0xab, 0xb5, 0x21, 0x15, 0xb2, 0xc6, 0x7e, 0x78, 0xdc, 0x97, 0x53, 0x56,
+ 0x3f, 0xe9, 0xb4, 0x4e, 0xb8, 0xdd, 0x09, 0xa6, 0x37, 0xd3, 0xf7, 0x11,
+ 0xa2, 0x52, 0x50, 0xa6, 0x53, 0x44, 0xce, 0xb3, 0x9c, 0x02, 0xd8, 0x59,
+ 0x04, 0x3b, 0xba, 0x6c, 0xce, 0xf1, 0x6b, 0x33, 0x60, 0x14, 0x6b, 0xa0,
+ 0x3d, 0x2e, 0x66, 0x67, 0x82, 0x4f, 0x13, 0xa1, 0x82, 0xc4, 0x15, 0x08,
+ 0x7e, 0x59, 0xba, 0x84, 0x3e, 0xac, 0x12, 0x42, 0x98, 0x3d, 0x6e, 0xda,
+ 0xa9, 0xc3, 0xd7, 0x45, 0xeb, 0xc8, 0xec, 0x2a, 0x94, 0x9e, 0xc1, 0xf7,
+ 0x71, 0xca, 0x3d, 0xb3, 0xd2, 0x68, 0x2a, 0xc0, 0xbe, 0x9e, 0x2f, 0x26,
+ 0xf3, 0xf9, 0xb9, 0x7f, 0x21, 0x7f, 0x2f, 0x8b, 0x1b, 0x10, 0xb1, 0x09,
+ 0xc8, 0xab, 0xae, 0xda, 0xae, 0x66, 0x07, 0x4a, 0xc0, 0x75, 0x2a, 0x29,
+ 0x74, 0xb2, 0x99, 0xa6, 0x58, 0x84, 0xdc, 0x3e, 0x6b, 0x47, 0x37, 0xcb,
+ 0xb1, 0xc8, 0xcc, 0x81, 0xb3, 0x8b, 0x3a, 0x0c, 0xd4, 0x6a, 0x11, 0x3f,
+ 0x25, 0x17, 0x5d, 0xaf, 0x33, 0x8a, 0x32, 0x9d, 0x93, 0xef, 0xdb, 0x95,
+ 0x60, 0x5a, 0x15, 0xc5, 0x20, 0x7a, 0xec, 0xce, 0xa9, 0x31, 0x70, 0x24,
+ 0xd1, 0x4d, 0x29, 0xed, 0xeb, 0xec, 0xac, 0x53, 0x19, 0xc3, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xdf, 0x30, 0x81, 0xdc, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x00,
+ 0x20, 0x30, 0x51, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04,
+ 0x47, 0x30, 0x45, 0xa4, 0x43, 0x30, 0x41, 0x31, 0x16, 0x30, 0x14, 0x06,
+ 0x05, 0x67, 0x81, 0x05, 0x02, 0x01, 0x0c, 0x0b, 0x69, 0x64, 0x3a, 0x34,
+ 0x37, 0x34, 0x46, 0x34, 0x46, 0x34, 0x37, 0x31, 0x0f, 0x30, 0x0d, 0x06,
+ 0x05, 0x67, 0x81, 0x05, 0x02, 0x02, 0x0c, 0x04, 0x48, 0x31, 0x42, 0x32,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x03, 0x0c,
+ 0x0b, 0x69, 0x64, 0x3a, 0x30, 0x30, 0x31, 0x33, 0x30, 0x30, 0x33, 0x37,
+ 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02,
+ 0x30, 0x00, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0c, 0x30,
+ 0x0a, 0x30, 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x15, 0x39, 0x34, 0xfc, 0x59, 0x19, 0xcd, 0x29, 0x82, 0xf1, 0xf4, 0x7f,
+ 0xad, 0x85, 0xd6, 0x44, 0x69, 0xa1, 0xa1, 0x7b, 0x30, 0x10, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x09, 0x30, 0x07, 0x06, 0x05, 0x67, 0x81, 0x05,
+ 0x08, 0x01, 0x30, 0x21, 0x06, 0x03, 0x55, 0x1d, 0x09, 0x04, 0x1a, 0x30,
+ 0x18, 0x30, 0x16, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x10, 0x31, 0x0d,
+ 0x30, 0x0b, 0x0c, 0x03, 0x32, 0x2e, 0x30, 0x02, 0x01, 0x00, 0x02, 0x01,
+ 0x10, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x00, 0x1a, 0xef,
+ 0x74, 0x00, 0x05, 0xa3, 0x1c, 0x8c, 0xec, 0x0b, 0x6d, 0x67, 0x1b, 0x26,
+ 0x34, 0x62, 0xb3, 0x0c, 0x04, 0x34, 0xf6, 0x8c, 0x60, 0xa3, 0xcc, 0x5a,
+ 0xa7, 0x5f, 0x30, 0xa1, 0x50, 0x13, 0xb5, 0xf2, 0x83, 0x49, 0xfb, 0x35,
+ 0x01, 0x74, 0xde, 0xba, 0x3d, 0xba, 0x81, 0x0c, 0x87, 0x92, 0xbb, 0x20,
+ 0xc8, 0xe3, 0x4e, 0x15, 0xd4, 0x6d, 0xe6, 0x6f, 0xae, 0xca, 0x29, 0xa2,
+ 0xba, 0x5a, 0xac, 0xb9, 0x0c, 0xef, 0x85, 0xce, 0x6c, 0x03, 0xbd, 0x57,
+ 0x41, 0x90, 0x13, 0x68, 0x7b, 0xe0, 0x5e, 0xc6, 0x96, 0xe6, 0xbd, 0x67,
+ 0x62, 0x83, 0x7b, 0xc1, 0xa6, 0xfc, 0x2e, 0xc7, 0x74, 0x54, 0xef, 0x93,
+ 0x28, 0x8b, 0x1f, 0x73, 0x3c, 0xa9, 0x11, 0x6e, 0xac, 0xdf, 0x9e, 0xe2,
+ 0xb5, 0x6d, 0x62, 0x44, 0xc3, 0xa0, 0xf6, 0x82, 0x7b, 0x23, 0x82, 0x73,
+ 0x91, 0x22, 0x11, 0x7b, 0xb8, 0x8a, 0x19, 0x45, 0x2b, 0x99, 0xdc, 0x7a,
+ 0xa1, 0x21, 0xd8, 0x37, 0x51, 0x60, 0xfb, 0x20, 0xab, 0x2d, 0x6c, 0x32,
+ 0x07, 0xb8, 0x15, 0xed, 0x7c, 0x62, 0xe9, 0xc9, 0x1d, 0x50, 0x6c, 0x6c,
+ 0x1b, 0x55, 0xcd, 0x92, 0xb6, 0xc1, 0x9a, 0x3d, 0xac, 0x01, 0xc0, 0x66,
+ 0xca, 0xc1, 0x91, 0x09, 0x50, 0x4d, 0x1a, 0xc0, 0x6f, 0xe3, 0x2d, 0x4b,
+ 0x7f, 0xee, 0xa9, 0xff, 0xeb, 0x0d, 0x5d, 0xe5, 0x64, 0xfd, 0x52, 0x6e,
+ 0x8a, 0x91, 0x2d, 0xd0, 0x13, 0xf5, 0xcd, 0x32, 0xfb, 0xe2, 0xe7, 0x6d,
+ 0xfb, 0x05, 0x6a, 0x2a, 0xec, 0xc8, 0xa6, 0xd3, 0x94, 0xee, 0x89, 0x40,
+ 0x24, 0x39, 0x84, 0x6a, 0xc1, 0xd4, 0x16, 0xdf, 0x0f, 0x79, 0x3b, 0x57,
+ 0x17, 0x4d, 0x2d, 0x58, 0x65, 0x19, 0xce, 0x0c, 0xf4, 0x86, 0x19, 0xa6,
+ 0xfe, 0xd7, 0xac, 0x7c, 0x15, 0x7d, 0x06, 0x08, 0xd2, 0xb6, 0xb9, 0x5e,
+ 0x00, 0x29, 0x1b, 0x00, 0x00, 0x01, 0x00, 0xc0, 0x01, 0x01, 0x00, 0xc0,
+ 0x01, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x20, 0x04, 0x62, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
+ 0x01, 0x30, 0x82, 0x03, 0x20, 0x30, 0x82, 0x02, 0x08, 0xa0, 0x03, 0x02,
+ 0x01, 0x02, 0x02, 0x10, 0x64, 0x2e, 0x3d, 0x44, 0xaa, 0x6d, 0x90, 0x95,
+ 0x42, 0x28, 0x7d, 0x07, 0x5e, 0xe7, 0x68, 0x66, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
+ 0x81, 0x80, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x0c, 0x0a, 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61,
+ 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x47,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x24,
+ 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x1b, 0x45, 0x6e, 0x67,
+ 0x69, 0x6e, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x64,
+ 0x20, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6d, 0x65, 0x6e, 0x74,
+ 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x17, 0x43,
+ 0x52, 0x4f, 0x53, 0x20, 0x54, 0x50, 0x4d, 0x20, 0x50, 0x52, 0x44, 0x20,
+ 0x45, 0x4b, 0x20, 0x52, 0x4f, 0x4f, 0x54, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x38, 0x30, 0x37, 0x30, 0x36, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x37, 0x30, 0x36, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x32, 0x5a, 0x30, 0x00, 0x30, 0x59, 0x30, 0x13,
+ 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
+ 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xad,
+ 0x72, 0x36, 0x6c, 0x76, 0x5e, 0x69, 0xa1, 0xf5, 0xeb, 0xbf, 0x5d, 0x38,
+ 0x1d, 0x76, 0xda, 0xb3, 0x80, 0xf6, 0xa8, 0xf3, 0x2c, 0x42, 0x7e, 0xc8,
+ 0xd7, 0xc9, 0x46, 0xbd, 0xc6, 0x80, 0x7f, 0xd3, 0xf4, 0xac, 0xa5, 0x60,
+ 0xa3, 0xf1, 0x31, 0x6c, 0xdf, 0x87, 0x95, 0x5c, 0xb6, 0xf3, 0x79, 0xff,
+ 0x7b, 0x46, 0x53, 0xec, 0x3f, 0x20, 0xde, 0x59, 0xa5, 0x2d, 0xcb, 0x77,
+ 0x55, 0x89, 0xad, 0xa3, 0x81, 0xdf, 0x30, 0x81, 0xdc, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x00,
+ 0x20, 0x30, 0x51, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, 0x01, 0xff, 0x04,
+ 0x47, 0x30, 0x45, 0xa4, 0x43, 0x30, 0x41, 0x31, 0x16, 0x30, 0x14, 0x06,
+ 0x05, 0x67, 0x81, 0x05, 0x02, 0x01, 0x0c, 0x0b, 0x69, 0x64, 0x3a, 0x34,
+ 0x37, 0x34, 0x46, 0x34, 0x46, 0x34, 0x37, 0x31, 0x0f, 0x30, 0x0d, 0x06,
+ 0x05, 0x67, 0x81, 0x05, 0x02, 0x02, 0x0c, 0x04, 0x48, 0x31, 0x42, 0x32,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x03, 0x0c,
+ 0x0b, 0x69, 0x64, 0x3a, 0x30, 0x30, 0x31, 0x33, 0x30, 0x30, 0x33, 0x37,
+ 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02,
+ 0x30, 0x00, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0c, 0x30,
+ 0x0a, 0x30, 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x15, 0x39, 0x34, 0xfc, 0x59, 0x19, 0xcd, 0x29, 0x82, 0xf1, 0xf4, 0x7f,
+ 0xad, 0x85, 0xd6, 0x44, 0x69, 0xa1, 0xa1, 0x7b, 0x30, 0x10, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x09, 0x30, 0x07, 0x06, 0x05, 0x67, 0x81, 0x05,
+ 0x08, 0x01, 0x30, 0x21, 0x06, 0x03, 0x55, 0x1d, 0x09, 0x04, 0x1a, 0x30,
+ 0x18, 0x30, 0x16, 0x06, 0x05, 0x67, 0x81, 0x05, 0x02, 0x10, 0x31, 0x0d,
+ 0x30, 0x0b, 0x0c, 0x03, 0x32, 0x2e, 0x30, 0x02, 0x01, 0x00, 0x02, 0x01,
+ 0x10, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3a, 0xe0, 0x1e,
+ 0x15, 0xd7, 0x11, 0x97, 0xc6, 0xc3, 0x50, 0x9a, 0x0b, 0x7f, 0x0a, 0x5e,
+ 0x7d, 0xd2, 0xb8, 0x08, 0xc0, 0x98, 0x35, 0x73, 0x6d, 0x85, 0x19, 0x46,
+ 0x76, 0x27, 0xcb, 0xa3, 0x66, 0x7f, 0xc9, 0x26, 0x20, 0x52, 0xb5, 0xe9,
+ 0xf5, 0xa0, 0x7a, 0x1d, 0xb3, 0x78, 0x66, 0x6d, 0x13, 0xcb, 0x82, 0x5b,
+ 0x09, 0xdc, 0xcb, 0x01, 0x0c, 0x3e, 0x34, 0x4a, 0x92, 0x44, 0x30, 0x44,
+ 0xda, 0x49, 0xad, 0x45, 0x17, 0xc3, 0x5b, 0x80, 0x8c, 0xa5, 0x11, 0xc4,
+ 0x07, 0x1a, 0x06, 0x43, 0x9c, 0x57, 0x51, 0x42, 0x35, 0xfd, 0xec, 0x68,
+ 0xe3, 0xe8, 0x3f, 0x5b, 0xb0, 0x01, 0x4f, 0xcb, 0xe0, 0x2a, 0x7f, 0xe6,
+ 0x89, 0xcc, 0xef, 0x59, 0xbb, 0x11, 0xed, 0xcb, 0xe6, 0xc9, 0x55, 0x94,
+ 0xf0, 0x5b, 0xee, 0x33, 0xc9, 0xed, 0x1b, 0x02, 0xeb, 0x90, 0x33, 0x0e,
+ 0xc1, 0x8f, 0x1c, 0x37, 0x3f, 0x9c, 0x59, 0x96, 0xe7, 0x73, 0xb1, 0x90,
+ 0x7a, 0x93, 0x2f, 0x4e, 0x98, 0xff, 0xff, 0xa7, 0xfb, 0x7f, 0xb9, 0x1b,
+ 0x46, 0xb8, 0x64, 0x12, 0x92, 0x74, 0x54, 0xe1, 0x7f, 0x07, 0xfa, 0xc4,
+ 0x2e, 0x53, 0xa8, 0x6a, 0xe5, 0x23, 0x92, 0x9e, 0x39, 0x37, 0xfc, 0x5d,
+ 0x2f, 0x7b, 0x69, 0x84, 0xd3, 0x69, 0x4e, 0xd1, 0x98, 0xda, 0x53, 0x34,
+ 0x80, 0xe6, 0xeb, 0xd5, 0x9a, 0x10, 0x5d, 0xc9, 0x88, 0x2b, 0x13, 0x9c,
+ 0xb9, 0xad, 0x15, 0x4f, 0x3a, 0x6c, 0x0d, 0xd4, 0x50, 0x0b, 0x1d, 0xfb,
+ 0x36, 0x74, 0xf2, 0x3e, 0x32, 0xce, 0x69, 0x5f, 0x67, 0x63, 0x33, 0x0f,
+ 0x7b, 0xc9, 0x5e, 0x68, 0x39, 0xdf, 0xb9, 0xe2, 0x73, 0xd5, 0xd5, 0x40,
+ 0xd8, 0x62, 0x0a, 0x4e, 0x45, 0x6f, 0xc1, 0x92, 0x9c, 0x41, 0x58, 0x21,
+ 0xd4, 0x60, 0x0b, 0xc4, 0x7c, 0x9c, 0xe8, 0x57, 0x5c, 0xed, 0xbc, 0xc1,
+ 0xf4, 0x51, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc3, 0x3c, 0x43,
+ 0xf2, 0xe7, 0x82, 0xf3, 0x5c, 0xec, 0x61, 0x96, 0xef, 0xf5, 0xfb, 0x24,
+ 0x87, 0xaa, 0xf9, 0x5f, 0x3f, 0xf6, 0x5e, 0xdd, 0x0a, 0x59, 0x33, 0xbb,
+ 0xe1, 0xaf, 0x3b, 0xb2, 0x26, 0xfa, 0x1b, 0x00, 0x00, 0x08, 0x10, 0x00,
+ 0x01, 0x08, 0x10, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05,
+ 0x60, 0x00, 0x00, 0x00, 0xe0, 0xd7, 0xdd, 0x01, 0x00, 0xe3, 0xad, 0x05,
+ 0x00, 0xe2, 0xad, 0x05, 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x53, 0x07,
+ 0x00, 0x00, 0x53, 0x07, 0x00, 0x30, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xd7, 0xdd, 0x01, 0x00, 0xd3, 0x3a, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xb0, 0xde, 0x01, 0x00, 0xb4, 0xde, 0x01, 0x00, 0x9b, 0x0f, 0x06,
+ 0x00, 0xbc, 0xde, 0x01, 0x00, 0x40, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x8c, 0xb5, 0x01, 0x00, 0xd7, 0xdd, 0x01,
+ 0x00, 0x1f, 0xbd, 0x05, 0x00, 0x4e, 0xbd, 0x05, 0x00, 0x00, 0x00, 0x00,
+ 0x41, 0x01, 0x00, 0x00, 0x00, 0x30, 0xe4, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x19, 0x53, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x4c, 0x57, 0x52, 0x47, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xa0, 0x1c, 0x00, 0x00, 0x07, 0x10,
+ 0x00, 0x01, 0x07, 0x10, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x4c,
+ 0x05, 0x60, 0x20, 0x00, 0x09, 0x93, 0x3c, 0xce, 0xeb, 0xb4, 0x41, 0x11,
+ 0x18, 0x81, 0x1d, 0xd4, 0x47, 0x78, 0x80, 0x08, 0x88, 0x86, 0x62, 0x2d,
+ 0xd7, 0x79, 0x94, 0x46, 0x62, 0x26, 0x68, 0x8e, 0xee, 0xe6, 0x6a, 0xa1,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xb0, 0xde, 0x01, 0x00, 0xb4, 0xde, 0x01, 0x00, 0x9b, 0x0f,
+ 0x06, 0x00, 0xbc, 0xde, 0x01, 0x00, 0x40, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x8c, 0xb5, 0x01, 0x00, 0xd7, 0xdd,
+ 0x01, 0x00, 0x1f, 0xbd, 0x05, 0x00, 0x4e, 0xbd, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x01, 0x00, 0x00, 0x00, 0x30, 0xe4, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x19, 0x53, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x4f, 0x64, 0x1d, 0x00, 0x00, 0x05, 0x00, 0x80, 0x01,
+ 0x05, 0x00, 0x80, 0x01, 0x0b, 0x00, 0x01, 0x00, 0x04, 0x28, 0x04, 0xb0,
+ 0x00, 0x00, 0x40, 0x00, 0xcd, 0xd4, 0x97, 0x24, 0x1f, 0x26, 0xd9, 0x91,
+ 0xad, 0xb8, 0xd2, 0xcb, 0x46, 0x14, 0x65, 0x72, 0x91, 0x8d, 0x56, 0x2e,
+ 0x7b, 0x3f, 0x74, 0x89, 0x80, 0xc6, 0x18, 0xbf, 0xea, 0x58, 0xe6, 0xf6,
+ 0x54, 0xde, 0x01, 0x00, 0xaa, 0x4c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb0, 0xde, 0x01, 0x00, 0xb4, 0xde, 0x01, 0x00, 0x9b, 0x0f, 0x06, 0x00,
+ 0xbc, 0xde, 0x01, 0x00, 0x40, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0xfe, 0x1b, 0x00, 0x00, 0x05, 0x00, 0x80, 0x01, 0x43, 0xcf, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0xf0, 0xde, 0x01, 0x00, 0x69, 0x01, 0x00, 0x00, 0xb3, 0xd3, 0x05, 0x00,
+ 0xa4, 0xde, 0x01, 0x00, 0xa8, 0xde, 0x01, 0x00, 0xf4, 0xde, 0x01, 0x00,
+ 0xd1, 0x73, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xde, 0x40, 0x00,
+ 0x78, 0x04, 0x01, 0x00, 0x32, 0x4d, 0x50, 0x54, 0x01, 0x00, 0x00, 0x00,
+ 0xe0, 0xa7, 0x0d, 0x1e, 0x0d, 0xe2, 0x5a, 0xb8, 0xd4, 0x5e, 0xb8, 0x2b,
+ 0xcc, 0x99, 0xd2, 0x6a, 0x8f, 0xf0, 0x52, 0x18, 0x72, 0x02, 0x16, 0x8e,
+ 0x83, 0x06, 0xba, 0x01, 0xe4, 0xcc, 0xbf, 0xcf, 0x80, 0x1f, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x81, 0x38, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b,
+ 0x00, 0x03, 0x04, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x80, 0x00, 0x43,
+ 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x9b, 0x0f,
+ 0x10, 0x85, 0x8c, 0x80, 0xfc, 0x15, 0x23, 0xeb, 0x29, 0x04, 0x2d, 0x2c,
+ 0xfc, 0xed, 0xc4, 0x1e, 0x3f, 0xf3, 0xc2, 0x3f, 0x42, 0xd0, 0x5c, 0x8d,
+ 0xe7, 0x3f, 0xc3, 0x17, 0x6d, 0xc5, 0x2c, 0xa8, 0x5f, 0x29, 0x80, 0x82,
+ 0xf2, 0x1f, 0xd1, 0x48, 0xfc, 0x50, 0x96, 0x51, 0x13, 0x5f, 0xbf, 0xa0,
+ 0xdf, 0xb0, 0x16, 0xe2, 0x80, 0x54, 0x11, 0x10, 0x7d, 0x05, 0xf6, 0x16,
+ 0x07, 0xd3, 0xbe, 0x8e, 0x16, 0x6c, 0x83, 0x56, 0xdc, 0xd9, 0x0d, 0x29,
+ 0x42, 0x26, 0x37, 0xa0, 0x5a, 0xa0, 0x47, 0x02, 0x80, 0xfd, 0x2c, 0x8c,
+ 0x23, 0x6c, 0x96, 0x44, 0x9f, 0xd6, 0x7e, 0x33, 0x70, 0xd0, 0xe7, 0x3c,
+ 0x93, 0xf6, 0x4c, 0xf3, 0xf9, 0x6f, 0x5d, 0x40, 0xa0, 0xb0, 0xf5, 0x69,
+ 0xcc, 0x60, 0x19, 0x83, 0x0b, 0xe1, 0xc6, 0xc7, 0x8b, 0xa1, 0x3a, 0x01,
+ 0x16, 0x68, 0xd6, 0x28, 0xf9, 0x19, 0xee, 0x2b, 0x74, 0x9a, 0xba, 0xac,
+ 0x5e, 0x4e, 0x8d, 0x1d, 0x86, 0xef, 0xaf, 0xa4, 0xb4, 0xbd, 0xd4, 0x97,
+ 0x08, 0x6f, 0x20, 0x9b, 0xb3, 0xe8, 0x5e, 0x43, 0x9e, 0xad, 0x05, 0xf8,
+ 0xf0, 0x18, 0x53, 0xaf, 0xda, 0xb7, 0xbc, 0xe1, 0x43, 0xda, 0x03, 0xc0,
+ 0xe4, 0x5a, 0xd1, 0x74, 0xc4, 0x63, 0x2c, 0x22, 0x64, 0x34, 0x61, 0x07,
+ 0x26, 0x3d, 0x8c, 0x91, 0x29, 0xcc, 0x22, 0x5f, 0x14, 0xe9, 0xef, 0x7e,
+ 0xbb, 0x80, 0xf3, 0xed, 0x35, 0xbb, 0xf9, 0xca, 0xa5, 0x53, 0xff, 0x47,
+ 0x9d, 0xc7, 0xf4, 0x1e, 0xfe, 0x24, 0xf1, 0xbd, 0x30, 0x3e, 0x4c, 0xd6,
+ 0x47, 0x2c, 0x00, 0x16, 0x78, 0x61, 0xc4, 0x72, 0xb1, 0x39, 0xe1, 0x5d,
+ 0x18, 0x10, 0xa4, 0x1c, 0x4e, 0x42, 0x27, 0xe4, 0xd2, 0x4b, 0xcb, 0x65,
+ 0x2f, 0x33, 0x69, 0x49, 0xca, 0x2a, 0x89, 0xc7, 0x5d, 0xca, 0x7e, 0xfd,
+ 0xf2, 0x6b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x73, 0x9a, 0x3e, 0x61,
+ 0x92, 0x20, 0x63, 0x87, 0x6e, 0x99, 0xaa, 0x85, 0x7b, 0x24, 0x5e, 0xee,
+ 0xfe, 0x80, 0x4d, 0xaa, 0x66, 0x1e, 0x2b, 0xe6, 0x69, 0xca, 0x1f, 0xfd,
+ 0x41, 0xe8, 0x0e, 0xca, 0x00, 0x80, 0xc5, 0xdf, 0xa6, 0x9d, 0x5a, 0x23,
+ 0xde, 0xc4, 0x38, 0xfe, 0xd0, 0x80, 0x5e, 0x7d, 0xd3, 0x01, 0x07, 0xa3,
+ 0x93, 0x80, 0x19, 0xb8, 0xf4, 0xaa, 0xb1, 0xf4, 0x09, 0x78, 0x91, 0x52,
+ 0x48, 0xb1, 0x2b, 0x1b, 0xab, 0xcc, 0x60, 0x53, 0x20, 0x92, 0x85, 0xc6,
+ 0xf7, 0xff, 0x08, 0xee, 0x68, 0x6c, 0xfd, 0x3e, 0xd6, 0x9d, 0xda, 0x0d,
+ 0x03, 0x0c, 0x7c, 0xe0, 0x2b, 0x5c, 0xd4, 0x35, 0x9b, 0x3d, 0x7d, 0xd4,
+ 0x07, 0x7c, 0x8a, 0xc3, 0x79, 0xc0, 0x4b, 0x52, 0x20, 0x18, 0x2f, 0xfe,
+ 0x19, 0xe6, 0x41, 0xac, 0x31, 0xef, 0x32, 0x8e, 0x47, 0xdf, 0x0b, 0x8d,
+ 0x6b, 0x50, 0xbe, 0x15, 0xa2, 0x17, 0x51, 0xef, 0x5e, 0x87, 0xc3, 0x2e,
+ 0xd8, 0xf8, 0xb7, 0x72, 0xc9, 0x5d, 0xe8, 0x71, 0xef, 0x32, 0x02, 0x5d,
+ 0xc5, 0x49, 0x4e, 0xb7, 0xb8, 0xdc, 0xe3, 0x9d, 0xcb, 0x29, 0x49, 0x90,
+ 0xcf, 0xdf, 0x00, 0x00, 0x00, 0x22, 0x00, 0x0b, 0x96, 0xce, 0xc6, 0x42,
+ 0x3b, 0x6f, 0x58, 0x90, 0xd2, 0x2a, 0xbc, 0xc4, 0x59, 0xa1, 0xfb, 0x63,
+ 0xc4, 0xef, 0x59, 0x2a, 0x2d, 0x92, 0x76, 0xfa, 0xdb, 0xac, 0xf6, 0x7b,
+ 0x77, 0xb7, 0x56, 0x93, 0x81, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x0b,
+ 0x68, 0x0f, 0x54, 0x0a, 0x3f, 0x27, 0xdc, 0x66, 0x76, 0x1a, 0x35, 0x71,
+ 0xe2, 0x5c, 0x08, 0xcf, 0xac, 0x39, 0xea, 0xcc, 0x01, 0x54, 0x4e, 0x48,
+ 0xaa, 0xe1, 0x5c, 0xa5, 0xb7, 0xe1, 0x5b, 0x50, 0x7c, 0x20, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x81, 0x38, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x0b,
+ 0x00, 0x03, 0x04, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x80, 0x00, 0x43,
+ 0x00, 0x10, 0x00, 0x03, 0x00, 0x10, 0x00, 0x20, 0x6c, 0x05, 0x79, 0x5e,
+ 0x36, 0x4a, 0x04, 0x2c, 0x10, 0x8e, 0x50, 0xcc, 0x60, 0x7e, 0x18, 0xa1,
+ 0xe5, 0x4a, 0x96, 0x56, 0xf1, 0x5f, 0x34, 0x95, 0x36, 0xea, 0x00, 0x64,
+ 0xb9, 0xf4, 0x49, 0x91, 0x00, 0x20, 0x7f, 0x8c, 0xad, 0xea, 0x97, 0xba,
+ 0x99, 0x28, 0x93, 0xb4, 0xcc, 0xa7, 0x36, 0xfa, 0x61, 0xd8, 0x78, 0xb6,
+ 0x06, 0xcd, 0xe3, 0x27, 0x99, 0x7b, 0xaa, 0x3e, 0xa9, 0x4e, 0xf2, 0x93,
+ 0x18, 0x06, 0x00, 0x23, 0x00, 0x00, 0x00, 0x20, 0x16, 0xba, 0x08, 0xd2,
+ 0xd8, 0x63, 0x38, 0xba, 0xb9, 0x0b, 0xce, 0x5b, 0x72, 0x9a, 0x88, 0x08,
+ 0x25, 0xb9, 0xbd, 0xab, 0x16, 0x77, 0x43, 0xbb, 0xcd, 0x3d, 0x4d, 0x48,
+ 0xbc, 0x77, 0xf8, 0xd3, 0x00, 0x20, 0x0f, 0xd6, 0x89, 0x31, 0xdc, 0xfa,
+ 0x70, 0x00, 0x6a, 0x4b, 0x59, 0x60, 0x28, 0x01, 0x54, 0x39, 0x4e, 0xa1,
+ 0x97, 0x28, 0xe0, 0x84, 0x92, 0x0e, 0x35, 0xdc, 0xb3, 0x9a, 0x11, 0x08,
+ 0x7c, 0x93, 0x00, 0x00, 0x00, 0x22, 0x00, 0x0b, 0x5d, 0xab, 0xa0, 0x5d,
+ 0x70, 0x5f, 0x6e, 0x78, 0xeb, 0x82, 0x8c, 0xb4, 0x77, 0x45, 0x95, 0x50,
+ 0x6b, 0x7c, 0x97, 0x5d, 0x2d, 0x31, 0x76, 0x0e, 0x81, 0x9e, 0x4b, 0xa3,
+ 0x80, 0xf5, 0xff, 0x86, 0x81, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x0b,
+ 0x85, 0xf4, 0xbe, 0x91, 0xe1, 0x2b, 0x4c, 0x2d, 0xaa, 0x64, 0xb1, 0x90,
+ 0x5f, 0xec, 0x74, 0xea, 0xec, 0x2c, 0x3d, 0x0d, 0xbb, 0x26, 0xc0, 0x49,
+ 0x3d, 0xae, 0xa0, 0xe4, 0x72, 0xfa, 0xba, 0x15, 0x74, 0x22, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b,
+ 0x00, 0x02, 0x04, 0x72, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xed, 0x72, 0x50, 0x56, 0x10, 0xb7,
+ 0xab, 0x52, 0x50, 0x32, 0x4f, 0x82, 0x87, 0x94, 0x83, 0x84, 0x55, 0xeb,
+ 0xda, 0x78, 0x46, 0x34, 0x48, 0xc4, 0xc6, 0x28, 0x47, 0xf4, 0x4c, 0xfd,
+ 0x1f, 0x04, 0xcd, 0xb9, 0x72, 0x6a, 0x49, 0x6c, 0x1c, 0x9b, 0x07, 0x5b,
+ 0xe8, 0xef, 0xaf, 0x0b, 0xeb, 0xb1, 0xcb, 0x24, 0x0e, 0x63, 0x2f, 0x35,
+ 0x3c, 0x79, 0x0c, 0xde, 0xf5, 0xdb, 0x06, 0xe9, 0x68, 0xf4, 0xf0, 0x8e,
+ 0xf1, 0xbf, 0x98, 0x9a, 0xd9, 0x2b, 0x45, 0x85, 0x83, 0xda, 0xd9, 0xf3,
+ 0x6f, 0x1a, 0x24, 0xe8, 0x5d, 0xad, 0xda, 0xa7, 0xfc, 0x03, 0x67, 0xb0,
+ 0xdf, 0x07, 0xbd, 0xe1, 0x1e, 0x7d, 0xa1, 0xea, 0x59, 0xfb, 0xb9, 0x48,
+ 0x2d, 0x45, 0x0c, 0x1b, 0x52, 0x0e, 0xb3, 0xe6, 0xac, 0x4a, 0x91, 0x1c,
+ 0xb1, 0x2f, 0xee, 0xae, 0xf4, 0x0a, 0x79, 0x81, 0x92, 0xab, 0xaa, 0xff,
+ 0x44, 0x0f, 0x8d, 0xe7, 0x30, 0xfd, 0xee, 0x62, 0x0f, 0x92, 0x4d, 0x08,
+ 0xe7, 0xd0, 0xdb, 0x16, 0xf2, 0x27, 0x73, 0x59, 0x18, 0xc5, 0xd9, 0x0d,
+ 0xac, 0xaf, 0xc0, 0xd0, 0xdc, 0xfd, 0x1d, 0xeb, 0x74, 0x33, 0x59, 0xd6,
+ 0x30, 0xe1, 0x29, 0x81, 0xa5, 0xeb, 0x67, 0xc6, 0x32, 0x98, 0x37, 0x12,
+ 0xe8, 0x12, 0x3c, 0x5b, 0xd1, 0xc7, 0x9e, 0x9d, 0x5b, 0xe3, 0x7d, 0x5c,
+ 0xf6, 0x9e, 0x4d, 0xdf, 0x65, 0xe1, 0x95, 0x14, 0xf6, 0xda, 0x94, 0x91,
+ 0x76, 0x91, 0x6b, 0x39, 0x37, 0xf0, 0x72, 0xf9, 0x7f, 0xc1, 0x09, 0x9d,
+ 0x33, 0xf5, 0x26, 0x84, 0xe2, 0xa2, 0x94, 0xc3, 0xad, 0x4a, 0xc2, 0x86,
+ 0xa0, 0x1a, 0xf2, 0x0e, 0xa1, 0x98, 0xb1, 0x9f, 0x50, 0x54, 0xd5, 0xc8,
+ 0x58, 0x11, 0xc3, 0x76, 0x63, 0xc0, 0x49, 0x7b, 0xa5, 0x80, 0x79, 0x75,
+ 0xc0, 0x0c, 0xe9, 0x8a, 0xb0, 0xbe, 0x09, 0x54, 0xfa, 0x19, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xfa, 0x82, 0xd6, 0x21, 0xf1, 0x41,
+ 0x7e, 0xca, 0x35, 0x00, 0x73, 0x63, 0x3f, 0x8d, 0x38, 0xbd, 0x4d, 0xd0,
+ 0x27, 0x0d, 0xe2, 0x66, 0xa5, 0x3c, 0x47, 0x14, 0xe7, 0x36, 0x38, 0x87,
+ 0xe2, 0x5d, 0x6d, 0x2a, 0x19, 0x10, 0xee, 0xfe, 0xf1, 0x6d, 0x6d, 0xb5,
+ 0x2c, 0x2f, 0xe3, 0x8f, 0x9b, 0x5f, 0x73, 0x8b, 0xdd, 0x1b, 0x0d, 0xe8,
+ 0x81, 0x53, 0x79, 0x9b, 0xca, 0x86, 0x03, 0xf9, 0x2d, 0x6e, 0x85, 0x26,
+ 0x14, 0xa7, 0x71, 0xd5, 0xf8, 0xd8, 0x30, 0x27, 0x26, 0x08, 0x30, 0x17,
+ 0xf8, 0xc2, 0x17, 0x6a, 0xb3, 0x7e, 0x98, 0x69, 0x19, 0x94, 0x51, 0x34,
+ 0x94, 0x3c, 0xcf, 0x45, 0x9a, 0x9d, 0x9d, 0xea, 0xcf, 0x04, 0x74, 0x74,
+ 0x79, 0x09, 0x3f, 0xa0, 0xaa, 0x25, 0xd2, 0xda, 0x1f, 0x1a, 0xc6, 0x87,
+ 0xcf, 0x17, 0x1c, 0xfc, 0x1d, 0x59, 0x4e, 0x6d, 0x2f, 0x32, 0x28, 0x39,
+ 0xb0, 0xcd, 0x00, 0x00, 0x00, 0x22, 0x00, 0x0b, 0x92, 0x80, 0x9c, 0x28,
+ 0xa0, 0xc7, 0x75, 0x91, 0x80, 0xc9, 0x86, 0x63, 0xe1, 0xa7, 0x19, 0x33,
+ 0x21, 0xd8, 0x47, 0x8c, 0xac, 0xd4, 0x51, 0x7d, 0x78, 0x31, 0x84, 0x74,
+ 0x49, 0x63, 0x40, 0xba, 0x81, 0x00, 0x00, 0x02, 0x00, 0x22, 0x00, 0x0b,
+ 0xe9, 0x80, 0x8e, 0x73, 0x7d, 0xaa, 0xc3, 0xf8, 0x84, 0xaf, 0x7d, 0x18,
+ 0x10, 0x7c, 0xed, 0xf3, 0x14, 0x72, 0x61, 0x97, 0xb6, 0xd7, 0xbb, 0x3a,
+ 0x47, 0xb0, 0x91, 0xb0, 0xba, 0x7c, 0xbc, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x01, 0x20, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x12, 0x00, 0x76, 0x61, 0x72, 0x31, 0x74, 0x68, 0x69, 0x73, 0x5f,
+ 0x69, 0x73, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f,
+ 0x31, 0x04, 0x12, 0x00, 0x76, 0x61, 0x72, 0x32, 0x74, 0x68, 0x69, 0x73,
+ 0x5f, 0x69, 0x73, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65,
+ 0x5f, 0x32, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
diff --git a/test/motion_angle.c b/test/motion_angle.c
index c69f1dccc2..37157dc0c0 100644
--- a/test/motion_angle.c
+++ b/test/motion_angle.c
@@ -41,7 +41,6 @@ static int test_lid_angle_less180(void)
hook_notify(HOOK_CHIPSET_SHUTDOWN);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S5);
TEST_ASSERT(lid->drv->get_data_rate(lid) == 0);
- TEST_ASSERT(motion_interval == 0);
/* Go to S0 state */
hook_notify(HOOK_CHIPSET_SUSPEND);
@@ -49,7 +48,6 @@ static int test_lid_angle_less180(void)
msleep(1000);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S0);
TEST_ASSERT(lid->drv->get_data_rate(lid) == TEST_LID_FREQUENCY);
- TEST_ASSERT(motion_interval == TEST_LID_EC_RATE);
/* Open lid, testing close to 180 degree. */
gpio_set_level(GPIO_LID_OPEN, 1);
diff --git a/test/motion_angle_tablet.c b/test/motion_angle_tablet.c
index ee241d3172..5f096b1603 100644
--- a/test/motion_angle_tablet.c
+++ b/test/motion_angle_tablet.c
@@ -43,7 +43,6 @@ static int test_lid_angle_less180(void)
hook_notify(HOOK_CHIPSET_SHUTDOWN);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S5);
TEST_ASSERT(lid->drv->get_data_rate(lid) == 0);
- TEST_ASSERT(motion_interval == 0);
/* Go to S0 state */
hook_notify(HOOK_CHIPSET_SUSPEND);
@@ -51,7 +50,6 @@ static int test_lid_angle_less180(void)
msleep(1000);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S0);
TEST_ASSERT(lid->drv->get_data_rate(lid) == TEST_LID_FREQUENCY);
- TEST_ASSERT(motion_interval == TEST_LID_EC_RATE);
/* Open lid, testing close to 180 degree. */
gpio_set_level(GPIO_LID_OPEN, 1);
diff --git a/test/motion_common.h b/test/motion_common.h
index 3adb9f24e5..45d856d9ef 100644
--- a/test/motion_common.h
+++ b/test/motion_common.h
@@ -24,7 +24,6 @@
#define TEST_LID_SAMPLE_SIZE (2 * 3)
extern enum chipset_state_mask sensor_active;
-extern unsigned int motion_interval;
extern struct motion_sensor_t motion_sensors[];
extern const unsigned int motion_sensor_count;
diff --git a/test/motion_lid.c b/test/motion_lid.c
index 6dc5f05831..a8e16f8cd1 100644
--- a/test/motion_lid.c
+++ b/test/motion_lid.c
@@ -21,7 +21,6 @@
#include "util.h"
extern enum chipset_state_mask sensor_active;
-extern unsigned motion_interval;
/*
* Period in us for the motion task period.
@@ -181,7 +180,6 @@ static int test_lid_angle(void)
hook_notify(HOOK_CHIPSET_SHUTDOWN);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S5);
TEST_ASSERT(accel_get_data_rate(lid) == 0);
- TEST_ASSERT(motion_interval == 0);
/* Go to S0 state */
hook_notify(HOOK_CHIPSET_SUSPEND);
@@ -189,7 +187,6 @@ static int test_lid_angle(void)
msleep(1000);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S0);
TEST_ASSERT(accel_get_data_rate(lid) == 119000);
- TEST_ASSERT(motion_interval == TEST_LID_EC_RATE);
/*
* Set the base accelerometer as if it were sitting flat on a desk
diff --git a/test/nvmem.c b/test/nvmem.c
index 99b2d554e9..dc5cde1eee 100644
--- a/test/nvmem.c
+++ b/test/nvmem.c
@@ -5,11 +5,15 @@
* Test Cr-50 Non-Voltatile memory module
*/
+#include "nvmem_test.h"
+
#include "common.h"
#include "console.h"
#include "crc.h"
-#include "nvmem.h"
#include "flash.h"
+#include "new_nvmem.h"
+#include "nvmem.h"
+#include "printf.h"
#include "shared_mem.h"
#include "task.h"
#include "test_util.h"
@@ -19,16 +23,29 @@
#define WRITE_SEGMENT_LEN 200
#define WRITE_READ_SEGMENTS 4
-uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {
- NVMEM_USER_0_SIZE,
- NVMEM_USER_1_SIZE,
- NVMEM_USER_2_SIZE
+enum test_failure_mode failure_mode;
+
+static const uint8_t legacy_nvmem_image[] = {
+#include "legacy_nvmem_dump.h"
};
+BUILD_ASSERT(sizeof(legacy_nvmem_image) == NVMEM_PARTITION_SIZE);
+
static uint8_t write_buffer[NVMEM_PARTITION_SIZE];
-static uint8_t read_buffer[NVMEM_PARTITION_SIZE];
static int flash_write_fail;
-static int lock_test_started;
+
+struct nvmem_test_result {
+ int var_count;
+ int reserved_obj_count;
+ int evictable_obj_count;
+ int deleted_obj_count;
+ int delimiter_count;
+ int unexpected_count;
+ size_t valid_data_size;
+ size_t erased_data_size;
+};
+
+static struct nvmem_test_result test_result;
int app_cipher(const void *salt_p, void *out_p, const void *in_p, size_t size)
{
@@ -50,15 +67,28 @@ void app_compute_hash(uint8_t *p_buf, size_t num_bytes,
uint32_t crc;
uint32_t *p_data;
int n;
+ size_t tail_size;
crc32_init();
- /* Assuming here that buffer is 4 byte aligned and that num_bytes is
- * divisible by 4
- */
+ /* Assuming here that buffer is 4 byte aligned. */
p_data = (uint32_t *)p_buf;
- for (n = 0; n < num_bytes/4; n++)
+ for (n = 0; n < num_bytes / 4; n++)
crc32_hash32(*p_data++);
- crc = crc32_result();
+
+ tail_size = num_bytes % 4;
+ if (tail_size) {
+ uint32_t tail;
+
+ tail = 0;
+ memcpy(&tail, p_data, tail_size);
+ crc32_hash32(tail);
+ }
+
+ /*
+ * Crc32 of 0xffffffff is 0xffffffff. Let's spike the results to avoid
+ * this unfortunate Crc32 property.
+ */
+ crc = crc32_result() ^ 0x55555555;
for (n = 0; n < hash_bytes; n += sizeof(crc)) {
size_t copy_bytes = MIN(sizeof(crc), hash_bytes - n);
@@ -73,652 +103,1263 @@ int flash_pre_op(void)
return flash_write_fail ? EC_ERROR_UNKNOWN : EC_SUCCESS;
}
-static int generate_random_data(int offset, int num_bytes)
+static void dump_nvmem_state(const char *title,
+ const struct nvmem_test_result *tr)
+{
+ ccprintf("\n%s:\n", title);
+ ccprintf("var_count: %d\n", tr->var_count);
+ ccprintf("reserved_obj_count: %d\n", tr->reserved_obj_count);
+ ccprintf("evictable_obj_count: %d\n", tr->evictable_obj_count);
+ ccprintf("deleted_obj_count: %d\n", tr->deleted_obj_count);
+ ccprintf("deimiter_count: %d\n", tr->delimiter_count);
+ ccprintf("unexpected_count: %d\n", tr->unexpected_count);
+ ccprintf("valid_data_size: %d\n", tr->valid_data_size);
+ ccprintf("erased_data_size: %d\n\n", tr->erased_data_size);
+}
+
+static void wipe_out_nvmem_cache(void)
+{
+ memset(nvmem_cache_base(NVMEM_TPM), 0, nvmem_user_sizes[NVMEM_TPM]);
+}
+
+static int prepare_nvmem_contents(void)
+{
+ struct nvmem_tag *tag;
+
+ memcpy(write_buffer, legacy_nvmem_image, sizeof(write_buffer));
+ tag = (struct nvmem_tag *)write_buffer;
+
+ app_compute_hash(tag->padding, NVMEM_PARTITION_SIZE - NVMEM_SHA_SIZE,
+ tag->sha, sizeof(tag->sha));
+ app_cipher(tag->sha, tag + 1, tag + 1,
+ NVMEM_PARTITION_SIZE - sizeof(struct nvmem_tag));
+
+ return flash_physical_write(CONFIG_FLASH_NVMEM_BASE_A -
+ CONFIG_PROGRAM_MEMORY_BASE,
+ sizeof(write_buffer), write_buffer);
+}
+
+static int iterate_over_flash(void)
{
- int m, n, limit;
- uint32_t r_data;
+ enum ec_error_list rv;
+ struct nn_container *ch;
+ struct access_tracker at = {};
+ uint8_t buf[CONFIG_FLASH_BANK_SIZE];
+
+ memset(&test_result, 0, sizeof(test_result));
+ ch = (struct nn_container *)buf;
+
+ while ((rv = get_next_object(&at, ch, 1)) == EC_SUCCESS)
+ switch (ch->container_type) {
+ case NN_OBJ_OLD_COPY:
+ if (ch->container_type_copy == NN_OBJ_TRANSACTION_DEL) {
+ test_result.delimiter_count++;
+ } else {
+ test_result.deleted_obj_count++;
+ test_result.erased_data_size += ch->size;
+ }
+ break;
+
+ case NN_OBJ_TUPLE:
+ test_result.var_count++;
+ test_result.valid_data_size += ch->size;
+ break;
+
+ case NN_OBJ_TPM_RESERVED:
+ test_result.reserved_obj_count++;
+ test_result.valid_data_size += ch->size;
+ break;
+
+ case NN_OBJ_TPM_EVICTABLE:
+ test_result.evictable_obj_count++;
+ test_result.valid_data_size += ch->size;
+ break;
+
+ case NN_OBJ_TRANSACTION_DEL:
+ test_result.delimiter_count++;
+ break;
+ default:
+ test_result.unexpected_count++;
+ break;
+ }
- /* Ensure it will fit in the write buffer */
- TEST_ASSERT((num_bytes + offset) <= NVMEM_PARTITION_SIZE);
- /* Seed random number sequence */
- r_data = prng((uint32_t)clock());
- m = 0;
- while (m < num_bytes) {
- r_data = prng(r_data);
- limit = MIN(4, num_bytes - m);
- /* No byte alignment assumptions */
- for (n = 0; n < limit; n++)
- write_buffer[offset + m + n] = (r_data >> (n*8)) & 0xff;
- m += limit;
+ if (rv != EC_ERROR_MEMORY_ALLOCATION) {
+ ccprintf("\n%s:%d - unexpected return value %d\n", __func__,
+ __LINE__, rv);
+ return rv;
}
- return EC_SUCCESS;
+ /* Verify that there is a delimiter at the top of the flash. */
+ if (at.mt.data_offset > sizeof(*at.mt.ph)) {
+ if ((at.mt.ph == at.dt.ph) &&
+ (((at.mt.data_offset - sizeof(struct nn_container))) ==
+ at.dt.data_offset)) {
+ return EC_SUCCESS;
+ }
+ } else {
+ if ((at.dt.ph == list_element_to_ph(at.list_index)) &&
+ (at.dt.data_offset ==
+ (CONFIG_FLASH_BANK_SIZE - sizeof(struct nn_container)))) {
+ ccprintf("%s:%d edge delimiter case OK\n", __func__,
+ __LINE__);
+ return EC_SUCCESS;
+ }
+ }
+ ccprintf("%s:%d bad delimiter location: ph %p, "
+ "dt.ph %p, offset %d, delim offset %d\n",
+ __func__, __LINE__, at.mt.ph, at.dt.ph, at.mt.data_offset,
+ at.dt.data_offset);
+
+ return EC_ERROR_INVAL;
}
-static int test_write_read(uint32_t offset, uint32_t num_bytes, int user)
+static void *page_to_flash_addr(int page_num)
{
- int ret;
+ uint32_t base_offset = CONFIG_FLASH_NEW_NVMEM_BASE_A;
- /* Generate source data */
- generate_random_data(0, num_bytes);
- /* Write source data to NvMem */
- ret = nvmem_write(offset, num_bytes, write_buffer, user);
- /* Write to flash */
- ret = nvmem_commit();
- if (ret != EC_SUCCESS)
- return ret;
- /* Read from flash */
- nvmem_read(offset, num_bytes, read_buffer, user);
- /* Verify that write to flash was successful */
- TEST_ASSERT_ARRAY_EQ(write_buffer, read_buffer, num_bytes);
+ if (page_num > NEW_NVMEM_TOTAL_PAGES)
+ return NULL;
- return EC_SUCCESS;
+ if (page_num >= (NEW_NVMEM_TOTAL_PAGES / 2)) {
+ page_num -= (NEW_NVMEM_TOTAL_PAGES / 2);
+ base_offset = CONFIG_FLASH_NEW_NVMEM_BASE_B;
+ }
+
+ return (void *)((uintptr_t)base_offset +
+ page_num * CONFIG_FLASH_BANK_SIZE);
}
-static int write_full_buffer(uint32_t size, int user)
+static int post_init_from_scratch(uint8_t flash_value)
{
- uint32_t offset;
- uint32_t len;
- int ret;
+ int i;
+ void *flash_p;
+
+ memset(write_buffer, flash_value, sizeof(write_buffer));
+
+ /* Overwrite nvmem flash space with junk value. */
+ flash_physical_write(
+ CONFIG_FLASH_NEW_NVMEM_BASE_A - CONFIG_PROGRAM_MEMORY_BASE,
+ NEW_FLASH_HALF_NVMEM_SIZE, (const char *)write_buffer);
+ flash_physical_write(
+ CONFIG_FLASH_NEW_NVMEM_BASE_B - CONFIG_PROGRAM_MEMORY_BASE,
+ NEW_FLASH_HALF_NVMEM_SIZE, (const char *)write_buffer);
+
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+ TEST_ASSERT(test_result.var_count == 0);
+ TEST_ASSERT(test_result.reserved_obj_count == 38);
+ TEST_ASSERT(test_result.evictable_obj_count == 0);
+ TEST_ASSERT(test_result.deleted_obj_count == 0);
+ TEST_ASSERT(test_result.unexpected_count == 0);
+ TEST_ASSERT(test_result.valid_data_size == 1088);
+ TEST_ASSERT(total_var_space == 0);
+
+ for (i = 0; i < (NEW_NVMEM_TOTAL_PAGES - 1); i++) {
+ flash_p = page_to_flash_addr(i);
+
+ TEST_ASSERT(!!flash_p);
+ TEST_ASSERT(is_uninitialized(flash_p, CONFIG_FLASH_BANK_SIZE));
+ }
- /* Start at beginning of the user buffer */
- offset = 0;
- do {
- /* User default segment length unless it will exceed */
- len = MIN(WRITE_SEGMENT_LEN, size - offset);
- /* Generate data for tx buffer */
- generate_random_data(offset, len);
- /* Write data to Nvmem cache memory */
- nvmem_write(offset, len, &write_buffer[offset], user);
- /* Write to flash */
- ret = nvmem_commit();
- if (ret != EC_SUCCESS)
- return ret;
- /* Adjust starting offset by segment length */
- offset += len;
- } while (offset < size);
-
- /* Entire flash buffer should be full at this point */
- nvmem_read(0, size, read_buffer, user);
- /* Verify that write to flash was successful */
- TEST_ASSERT_ARRAY_EQ(write_buffer, read_buffer, size);
+ flash_p = page_to_flash_addr(i);
+ TEST_ASSERT(!is_uninitialized(flash_p, CONFIG_FLASH_BANK_SIZE));
return EC_SUCCESS;
}
+/*
+ * The purpose of this test is to check NvMem initialization when NvMem is
+ * completely erased (i.e. following SpiFlash write of program). In this case,
+ * nvmem_init() is expected to create initial flash storage containing
+ * reserved objects only.
+ */
static int test_fully_erased_nvmem(void)
{
- /*
- * The purpose of this test is to check NvMem intialization when NvMem
- * is completely erased (i.e. following SpiFlash write of program). In
- * this configuration, nvmem_init() should be able to detect this case
- * and configure an initial NvMem partition.
- */
- /* Erase full NvMem area */
- flash_physical_erase(CONFIG_FLASH_NVMEM_OFFSET_A,
- NVMEM_PARTITION_SIZE);
- flash_physical_erase(CONFIG_FLASH_NVMEM_OFFSET_B,
- NVMEM_PARTITION_SIZE);
- /* Call NvMem initialization function */
- return nvmem_init();
+ return post_init_from_scratch(0xff);
}
-static int test_configured_nvmem(void)
+/*
+ * The purpose of this test is to check nvmem_init() in the case when no valid
+ * pages exist but flash space is garbled as opposed to be fully erased. In
+ * this case, the initialization is expected to create one new valid page and
+ * erase the rest of the pages.
+ */
+static int test_corrupt_nvmem(void)
{
- /*
- * The purpose of this test is to check nvmem_init() when both
- * partitions are configured and valid.
- */
+ return post_init_from_scratch(0x55);
+}
- /* Call NvMem initialization */
- return nvmem_init();
+static int prepare_new_flash(void)
+{
+ TEST_ASSERT(test_fully_erased_nvmem() == EC_SUCCESS);
+
+ /* Now copy sensible information into the nvmem cache. */
+ memcpy(nvmem_cache_base(NVMEM_TPM),
+ legacy_nvmem_image + sizeof(struct nvmem_tag),
+ nvmem_user_sizes[NVMEM_TPM]);
+
+ dump_nvmem_state("after first save", &test_result);
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+
+ TEST_ASSERT(test_result.deleted_obj_count == 24);
+ TEST_ASSERT(test_result.var_count == 0);
+ TEST_ASSERT(test_result.reserved_obj_count == 40);
+ TEST_ASSERT(test_result.evictable_obj_count == 9);
+ TEST_ASSERT(test_result.unexpected_count == 0);
+ TEST_ASSERT(test_result.valid_data_size == 5128);
+ TEST_ASSERT(test_result.erased_data_size == 698);
+
+ return EC_SUCCESS;
}
-/* Verify that nvmem_erase_user_data only erases the given user's data. */
-static int test_nvmem_erase_user_data(void)
+static int test_nvmem_save(void)
{
- uint32_t write_value;
- uint32_t read_value;
- int i;
+ const char *key = "var1";
+ const char *value = "value of var 1";
+ size_t total_var_size;
+ struct nvmem_test_result old_result;
- nvmem_init();
-
- /* Make sure all partitions have data in them. */
- for (i = 0; i < NVMEM_NUM_PARTITIONS; i++) {
- write_value = i;
- nvmem_write(0, sizeof(write_value), &write_value, NVMEM_USER_0);
- write_value = 2;
- nvmem_write(0, sizeof(write_value), &write_value, NVMEM_USER_1);
- write_value = 3;
- nvmem_write(0, sizeof(write_value), &write_value, NVMEM_USER_2);
- nvmem_commit();
- }
+ TEST_ASSERT(prepare_new_flash() == EC_SUCCESS);
- /* Check that the writes took place. */
- read_value = ~write_value;
- nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_0);
- TEST_ASSERT(read_value == i-1);
- nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_1);
- TEST_ASSERT(read_value == 2);
- nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_2);
- TEST_ASSERT(read_value == 3);
+ /*
+ * Verify that saving without changing the cache does not affect flash
+ * contents.
+ */
+ old_result = test_result;
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
/*
- * nvmem_erase_user_data() is supposed to erase the user's data across
- * all partitions.
+ * Save of unmodified cache does not modify the flash contents and
+ * does not set the delimiter.
*/
- nvmem_erase_user_data(NVMEM_USER_0);
- for (i = 0; i < NVMEM_NUM_PARTITIONS; i++) {
- /* Make sure USER 0's data is (still) gone. */
- nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_0);
- TEST_ASSERT(read_value == 0xffffffff);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+ TEST_ASSERT(!memcmp(&test_result, &old_result, sizeof(test_result)));
- /* Make sure the other users' data has been untouched. */
- nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_1);
- TEST_ASSERT(read_value == 2);
+ wipe_out_nvmem_cache();
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+ TEST_ASSERT(!memcmp(&test_result, &old_result, sizeof(test_result)));
- /*
- * The active partition changes when the contents of the cache
- * changes. Therefore, in order to examine all the paritions,
- * we'll keep modifying one of the user's data.
- */
- nvmem_read(0, sizeof(read_value), &read_value, NVMEM_USER_2);
- TEST_ASSERT(read_value == (3+i));
- write_value = 4 + i;
- nvmem_write(0, sizeof(write_value), &write_value, NVMEM_USER_2);
- nvmem_commit();
- }
+ /*
+ * Total size test variable storage takes in flash (container header
+ * size not included).
+ */
+ total_var_size = strlen(key) + strlen(value) + sizeof(struct tuple);
+
+ /* Verify that we can add a variable to nvmem. */
+ TEST_ASSERT(setvar(key, strlen(key), value, strlen(value)) ==
+ EC_SUCCESS);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+
+ /* Remove changes caused by the new var addition. */
+ test_result.var_count -= 1;
+ test_result.delimiter_count -= 1;
+ test_result.valid_data_size -= total_var_size;
+
+ TEST_ASSERT(memcmp(&test_result, &old_result, sizeof(test_result)) ==
+ 0);
+
+ /* Verify that we can delete a variable from nvmem. */
+ TEST_ASSERT(setvar(key, strlen(key), NULL, 0) == EC_SUCCESS);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+ test_result.deleted_obj_count -= 1;
+ test_result.erased_data_size -= total_var_size;
+ test_result.delimiter_count -= 1;
+ TEST_ASSERT(memcmp(&test_result, &old_result, sizeof(test_result)) ==
+ 0);
return EC_SUCCESS;
}
-static int test_corrupt_nvmem(void)
+static size_t get_free_nvmem_room(void)
{
- uint8_t invalid_value = 0x55;
- int ret;
- struct nvmem_tag *p_part;
- uint8_t *p_data;
+ size_t free_room;
+ size_t free_pages;
+ /* Compaction kicks in when 3 pages or less are left. */
+ const size_t max_pages = NEW_NVMEM_TOTAL_PAGES - 3;
+
+ ccprintf("list index %d, data offset 0x%x\n", master_at.list_index,
+ master_at.mt.data_offset);
+
+ if (master_at.list_index >= max_pages)
+ return 0;
+
+ free_pages = max_pages - master_at.list_index;
+ free_room = (free_pages - 1) * (CONFIG_FLASH_BANK_SIZE -
+ sizeof(struct nn_page_header)) +
+ CONFIG_FLASH_BANK_SIZE - master_at.mt.data_offset;
+ ccprintf("free pages %d, data offset 0x%x\n", free_pages,
+ master_at.mt.data_offset);
+ return free_room;
+}
+
+static int test_nvmem_compaction(void)
+{
+ char value[100]; /* Definitely more than enough. */
+ const char *key = "var 1";
+ int i;
+ size_t key_len;
+ size_t val_len;
+ size_t free_room;
+ size_t real_var_size;
+ size_t var_space;
+ int max_vars;
+ int erased_data_size;
+ const size_t alignment_mask = CONFIG_FLASH_WRITE_SIZE - 1;
+
+ key_len = strlen(key);
+ val_len = snprintf(value, sizeof(value), "variable value is %04d", 0);
+
+ TEST_ASSERT(prepare_new_flash() == EC_SUCCESS);
/*
- * The purpose of this test is to check nvmem_init() in the case when no
- * vailid partition exists (not fully erased and no valid sha). In this
- * case, the initialization create one new valid partition.
+ * Remember how much room was erased before flooding nvmem with erased
+ * values.
*/
+ erased_data_size = test_result.erased_data_size;
- /* Overwrite each partition will all 0s */
- memset(write_buffer, invalid_value, NVMEM_PARTITION_SIZE);
- flash_physical_write(CONFIG_FLASH_NVMEM_OFFSET_A,
- NVMEM_PARTITION_SIZE,
- (const char *)write_buffer);
- flash_physical_write(CONFIG_FLASH_NVMEM_OFFSET_B,
- NVMEM_PARTITION_SIZE,
- (const char *)write_buffer);
+ /* Let's see how much free room there is. */
+ free_room = get_free_nvmem_room();
+ TEST_ASSERT(free_room);
+
+ /* How much room (key, value) pair takes in a container. */
+ real_var_size = val_len + key_len + sizeof(struct tuple);
/*
- * The initialization function will look for a valid partition and if
- * none is found, it will create one, and save it at partition index
- * 1.
+ * See how many vars including containers should be able to fit there.
+ *
+ * First calculate rounded up space a var will take. Apart from the
+ * var itself there will be a container header and a delimiter.
*/
- ret = nvmem_init();
- if (ret)
- return ret;
+ var_space = (real_var_size + 2 * sizeof(struct nn_container) +
+ alignment_mask) & ~alignment_mask;
+
+ max_vars = free_room / var_space;
/*
- * nvmem_init() called on uninitialized flash will create the first
- * valid partition with generation set to 0 at flash partition 1.
- *
- * Check here that partition 1 has a generation number of 0.
+ * And now flood the NVMEM with erased values (each new setvar()
+ * invocation erases the previous instance.
*/
- p_part = (struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_B;
- TEST_ASSERT(p_part->generation == 0);
- p_data = (uint8_t *)p_part + sizeof(struct nvmem_tag);
+ for (i = 0; i <= max_vars; i++) {
+ snprintf(value, sizeof(value), "variable value is %04d", i);
+ TEST_ASSERT(setvar(key, key_len, value, val_len) == EC_SUCCESS);
+ }
- /* Verify that partition 0 is still empty. */
- memset(write_buffer, invalid_value, NVMEM_PARTITION_SIZE);
- p_data = (void *)CONFIG_FLASH_NVMEM_BASE_A;
- TEST_ASSERT_ARRAY_EQ(write_buffer, p_data, NVMEM_PARTITION_SIZE);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+ /* Make sure there was no compaction yet. */
+ TEST_ASSERT(test_result.erased_data_size > erased_data_size);
- /* Now let's write a different value into user NVMEM_CR50 */
- invalid_value ^= ~0;
- TEST_ASSERT(nvmem_write(0, sizeof(invalid_value),
- &invalid_value, NVMEM_USER_0) == EC_SUCCESS);
- TEST_ASSERT(nvmem_commit() == EC_SUCCESS);
+ /* This is how much the erased space grew as a result of flooding. */
+ erased_data_size = test_result.erased_data_size - erased_data_size;
+ TEST_ASSERT(erased_data_size == max_vars * real_var_size);
- /* Verify that partition 1 generation did not change. */
- TEST_ASSERT(p_part->generation == 0);
+ /* This will take it over the compaction limit. */
+ val_len = snprintf(value, sizeof(value), "variable value is %03d", i);
+ TEST_ASSERT(setvar(key, key_len, value, val_len) == EC_SUCCESS);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+ TEST_ASSERT(test_result.erased_data_size < var_space);
+ return EC_SUCCESS;
+}
+
+static int test_configured_nvmem(void)
+{
/*
- * Now verify that partition 0 generation is set to 1;
+ * The purpose of this test is to check how nvmem_init() initializes
+ * from previously saved flash contents.
*/
- p_part = (struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_A;
- TEST_ASSERT(p_part->generation == 1);
+ TEST_ASSERT(prepare_nvmem_contents() == EC_SUCCESS);
- return EC_SUCCESS;
+ /*
+ * This is initialization from legacy flash contents which replaces
+ * legacy flash image with the new format flash image
+ */
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+
+ /* And this is initialization from the new flash layout. */
+ return nvmem_init();
}
-static int test_write_read_sequence(void)
+static uint8_t find_lb(const void *data)
{
- uint32_t offset;
- uint32_t length;
- int user;
- int n;
- int ret;
-
- for (user = 0; user < NVMEM_NUM_USERS; user++) {
- /* Length for each write/read segment */
- length = nvmem_user_sizes[user] / WRITE_READ_SEGMENTS;
- /* Start at beginning of user buffer */
- offset = 0;
- for (n = 0; n < WRITE_READ_SEGMENTS; n++) {
- ret = test_write_read(offset, length, user);
- if (ret != EC_SUCCESS)
- return ret;
- /* Adjust offset by segment length */
- offset += length;
- /* For 1st iteration only, adjust to create stagger */
- if (n == 0)
- offset -= length / 2;
-
- }
- }
- return EC_SUCCESS;
+ return (const uint8_t *)memchr(data, '#', 256) - (const uint8_t *)data;
}
-static int test_write_full_multi(void)
+/*
+ * Helper function, depending on the argument value either writes variables
+ * into nvmem and verifies their presence, or deletes them and verifies that
+ * they indeed disappear.
+ */
+static int var_read_write_delete_helper(int do_write)
{
- int n;
- int ret;
+ size_t i;
+ uint16_t saved_total_var_space;
+ uint32_t coverage_map;
+
+ const struct {
+ uint8_t *key;
+ uint8_t *value;
+ } kv_pairs[] = {
+ /* Use # as the delimiter to allow \0 in keys/values. */
+ {"\0key\00#", "value of key2#"}, {"key1#", "value of key1#"},
+ {"key2#", "value of key2#"}, {"key3#", "value of\0 key3#"},
+ {"ke\04#", "value\0 of\0 key4#"},
+ };
+
+ coverage_map = 0;
+ saved_total_var_space = total_var_space;
/*
- * The purpose of this test is to completely fill each user buffer in
- * NvMem with random data a segment length at a time. The data written
- * to NvMem is saved in write_buffer[] and then can be used to check the
- * NvMem writes were successful by reading and then comparing each user
- * buffer.
+ * Read all vars, one at a time, verifying that they shows up in
+ * getvar results when appropriate but not before.
*/
- for (n = 0; n < NVMEM_NUM_USERS; n++) {
- ret = write_full_buffer(nvmem_user_sizes[n], n);
- if (ret != EC_SUCCESS)
- return ret;
+ for (i = 0; i <= ARRAY_SIZE(kv_pairs); i++) {
+ size_t j;
+ uint8_t key_len;
+ uint8_t val_len;
+ const void *value;
+
+ for (j = 0; j < ARRAY_SIZE(kv_pairs); j++) {
+ const struct tuple *t;
+
+ coverage_map |= 1;
+
+ key_len = find_lb(kv_pairs[j].key);
+ t = getvar(kv_pairs[j].key, key_len);
+
+ if ((j >= i) ^ !do_write) {
+ TEST_ASSERT(t == NULL);
+ continue;
+ }
+
+ coverage_map |= 2;
+
+ TEST_ASSERT(saved_total_var_space == total_var_space);
+
+ /* Confirm that what we found is the right variable. */
+ val_len = find_lb(kv_pairs[j].value);
+
+ TEST_ASSERT(t->key_len == key_len);
+ TEST_ASSERT(t->val_len == val_len);
+ TEST_ASSERT(
+ !memcmp(kv_pairs[j].key, t->data_, key_len));
+ TEST_ASSERT(!memcmp(kv_pairs[j].value,
+ t->data_ + key_len, val_len));
+ freevar(t);
+ }
+
+ if (i == ARRAY_SIZE(kv_pairs)) {
+ coverage_map |= 4;
+ /* All four variables have been processed. */
+ break;
+ }
+
+ val_len = find_lb(kv_pairs[i].value);
+ key_len = find_lb(kv_pairs[i].key);
+ value = kv_pairs[i].value;
+ if (!do_write) {
+
+ coverage_map |= 8;
+
+ saved_total_var_space -= val_len + key_len;
+ /*
+ * Make sure all val_len == 0 and val == NULL
+ * combinations are exercised.
+ */
+ switch (i) {
+ case 0:
+ val_len = 0;
+ coverage_map |= 0x10;
+ break;
+
+ case 1:
+ coverage_map |= 0x20;
+ value = NULL;
+ break;
+ default:
+ coverage_map |= 0x40;
+ val_len = 0;
+ value = NULL;
+ break;
+ }
+ } else {
+ coverage_map |= 0x80;
+ saved_total_var_space += val_len + key_len;
+ }
+ key_len = find_lb(kv_pairs[i].key);
+ TEST_ASSERT(setvar(kv_pairs[i].key, key_len, value, val_len) ==
+ EC_SUCCESS);
+
+ TEST_ASSERT(saved_total_var_space == total_var_space);
}
+
+ if (do_write)
+ TEST_ASSERT(coverage_map == 0x87);
+ else
+ TEST_ASSERT(coverage_map == 0x7f);
+
return EC_SUCCESS;
}
-static int test_write_fail(void)
+static int test_var_read_write_delete(void)
{
- uint32_t offset = 0;
- uint32_t num_bytes = 0x200;
- int ret;
+ TEST_ASSERT(post_init_from_scratch(0xff) == EC_SUCCESS);
- /* Do write/read sequence that's expected to be successful */
- if (test_write_read(offset, num_bytes, NVMEM_USER_0))
- return EC_ERROR_UNKNOWN;
+ ccprintf("\n%s: starting write cycle\n", __func__);
+ TEST_ASSERT(var_read_write_delete_helper(1) == EC_SUCCESS);
- /* Prevent flash erase/write operations */
- flash_write_fail = 1;
- /* Attempt flash write */
- ret = test_write_read(offset, num_bytes, NVMEM_USER_0);
- /* Resume normal operation */
- flash_write_fail = 0;
+ ccprintf("%s: starting delete cycle\n", __func__);
+ TEST_ASSERT(var_read_write_delete_helper(0) == EC_SUCCESS);
+
+ return EC_SUCCESS;
+}
+/* Verify that nvmem_erase_user_data only erases the given user's data. */
+static int test_nvmem_erase_tpm_data(void)
+{
+ TEST_ASSERT(prepare_nvmem_contents() == EC_SUCCESS);
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ browse_flash_contents(1);
+ TEST_ASSERT(nvmem_erase_tpm_data() == EC_SUCCESS);
+ browse_flash_contents(1);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+ TEST_ASSERT(test_result.deleted_obj_count == 0);
+ TEST_ASSERT(test_result.var_count == 3);
+ TEST_ASSERT(test_result.reserved_obj_count == 0);
+ TEST_ASSERT(test_result.evictable_obj_count == 0);
+ TEST_ASSERT(test_result.unexpected_count == 0);
+ TEST_ASSERT(test_result.valid_data_size == 86);
+ TEST_ASSERT(test_result.erased_data_size == 0);
- /* This test is successful if write attempt failed */
- return !ret;
+ return EC_SUCCESS;
}
-static int test_buffer_overflow(void)
+static size_t fill_obj_offsets(uint16_t *offsets, size_t max_objects)
{
- int ret;
- int n;
+ size_t i;
+ size_t obj_count;
- /*
- * The purpose of this test is to check that NvMem writes behave
- * properly in relation to the defined length of each user buffer. A
- * write operation to completely fill the buffer is done first. This
- * should pass. Then the same buffer is written to with one extra byte
- * and this operation is expected to fail.
- */
+ obj_count = init_object_offsets(offsets, max_objects);
- /* Do test for each user buffer */
- for (n = 0; n < NVMEM_NUM_USERS; n++) {
- /* Write full buffer */
- ret = write_full_buffer(nvmem_user_sizes[n], n);
- if (ret != EC_SUCCESS)
- return ret;
- /* Attempt to write full buffer plus 1 extra byte */
- ret = write_full_buffer(nvmem_user_sizes[n] + 1, n);
- if (!ret)
- return EC_ERROR_UNKNOWN;
+ ccprintf("%d objects\n", obj_count);
+ for (i = 0; i < obj_count; i++) {
+ uint32_t *op;
+
+ op = evictable_offs_to_addr(offsets[i]);
+ ccprintf("offs %04x:%08x:%08x:%08x addr %p size %d\n",
+ offsets[i], op[-1], op[0], op[1], op,
+ (uintptr_t)nvmem_cache_base(NVMEM_TPM) + op[-1] -
+ (uintptr_t)op);
}
- /* Test case where user buffer number is valid */
- ret = test_write_read(0, 0x100, NVMEM_USER_0);
- if (ret != EC_SUCCESS)
- return ret;
- /* Attempt same write, but with invalid user number */
- ret = test_write_read(0, 0x100, NVMEM_NUM_USERS);
- if (!ret)
- return ret;
+ return obj_count;
+}
- return EC_SUCCESS;
+static size_t fill_cache_offsets(const void *cache, uint16_t *offsets,
+ size_t max_objects)
+{
+ uint8_t buf[nvmem_user_sizes[NVMEM_TPM]];
+ void *real_cache;
+ size_t num_offsets;
+
+ real_cache = nvmem_cache_base(NVMEM_TPM);
+ memcpy(buf, real_cache, sizeof(buf));
+
+ memcpy(real_cache, cache, sizeof(buf));
+ memset(offsets, 0, sizeof(*offsets) * max_objects);
+ num_offsets = fill_obj_offsets(offsets, max_objects);
+
+ /* Restore the real cache. */
+ memcpy(real_cache, buf, sizeof(buf));
+
+ return num_offsets;
}
+#define MAX_OFFSETS 20
-static int test_move(void)
+static uint32_t get_evict_size(const uint8_t *cache, uint16_t offset)
{
- uint32_t len = 0x100;
- uint32_t nv1_offset;
- uint32_t nv2_offset;
- int user = 0;
- int n;
- int ret;
+ uint32_t next_addr;
+ uint32_t cache_offset;
- /*
- * The purpose of this test is to check that nvmem_move() behaves
- * properly. This test only uses one user buffer as accessing multiple
- * user buffers is tested separately. This test uses writes a set of
- * test data then test move operations with full overlap, half overlap
- * and no overlap. Folliwng these tests, the boundary conditions for
- * move operations are checked for the giver user buffer.
- */
+ cache_offset = s_evictNvStart + offset;
+ memcpy(&next_addr, cache + cache_offset - sizeof(next_addr),
+ sizeof(next_addr));
+
+ return next_addr - cache_offset;
+}
+
+/* Returns zero if the two objects are identical. */
+static int compare_objects(const uint8_t *cache1, uint16_t offset1,
+ const uint8_t *cache2, uint16_t offset2)
+{
+ uint32_t size1;
+ uint32_t size2;
+
+ size1 = get_evict_size(cache1, offset1);
+ size2 = get_evict_size(cache2, offset2);
+
+ if (size1 == size2)
+ return memcmp(cache1 + s_evictNvStart + offset1,
+ cache2 + s_evictNvStart + offset2, size1);
+
+ return 1;
+}
+/*
+ * Compare two instances of NVMEM caches. Reserved spaces should be exactly
+ * the same for the match, but evictable objects could be rearranged due to
+ * compaction, updating, etc.
+ *
+ * For the two cache instances to be considered the same the sets and contents
+ * of the evictable object spaces must also match object to object.
+ */
+static int caches_match(const uint8_t *cache1, const uint8_t *cache2)
+{
+ int failed_count;
+ size_t cache1_offs_count;
+ size_t cache2_offs_count;
+ size_t i;
+ uint16_t cache1_offsets[MAX_OFFSETS];
+ uint16_t cache2_offsets[MAX_OFFSETS];
+
+ for (failed_count = i = 0; i < NV_PSEUDO_RESERVE_LAST; i++) {
+ NV_RESERVED_ITEM ri;
+ struct {
+ uint32_t offset;
+ uint32_t size;
+ } ranges[3];
+ size_t j;
+
+ NvGetReserved(i, &ri);
+
+ ranges[0].offset = ri.offset;
+
+ if (i != NV_STATE_CLEAR) {
+ ranges[0].size = ri.size;
+ ranges[1].size = 0;
+ } else {
+ ranges[0].size = offsetof(STATE_CLEAR_DATA, pcrSave);
+ ranges[1].offset = ranges[0].offset + ranges[0].size;
+ ranges[1].size = sizeof(PCR_SAVE);
+ ranges[2].offset = ranges[1].offset + ranges[1].size;
+ ranges[2].size = sizeof(PCR_AUTHVALUE);
+ }
+
+ for (j = 0; j < ARRAY_SIZE(ranges); j++) {
+
+ uint32_t offset;
+ uint32_t size;
+ uint32_t k;
+
+ size = ranges[j].size;
+ if (!size)
+ break;
+
+ offset = ranges[j].offset;
- nv1_offset = 0;
- for (n = 0; n < 3; n++) {
- /* Generate Test data */
- generate_random_data(nv1_offset, len);
- nv2_offset = nv1_offset + (len / 2) * n;
- /* Write data to Nvmem cache memory */
- nvmem_write(nv1_offset, len, &write_buffer[nv1_offset], user);
- nvmem_commit();
- /* Test move while data is in cache area */
- nvmem_move(nv1_offset, nv2_offset, len, user);
- nvmem_read(nv2_offset, len, read_buffer, user);
- if (memcmp(write_buffer, read_buffer, len))
- return EC_ERROR_UNKNOWN;
- ccprintf("Memmove nv1 = 0x%x, nv2 = 0x%x\n",
- nv1_offset, nv2_offset);
+ if (!memcmp(cache1 + offset, cache2 + offset, size))
+ continue;
+
+ ccprintf("%s:%d failed comparing %d:%d:\n", __func__,
+ __LINE__, i, j);
+ for (k = offset; k < (offset + size); k++)
+ if (cache1[k] != cache2[k])
+ ccprintf(" %3d:%02x", k - offset,
+ cache1[k]);
+ ccprintf("\n");
+ for (k = offset; k < (offset + size); k++)
+ if (cache1[k] != cache2[k])
+ ccprintf(" %3d:%02x", k - offset,
+ cache2[k]);
+ ccprintf("\n");
+
+ failed_count++;
+ }
}
- /* Test invalid buffer offsets */
- /* Destination offset is equal to length of buffer */
- nv1_offset = 0;
- nv2_offset = nvmem_user_sizes[user];
- /* Attempt to move just 1 byte */
- ret = nvmem_move(nv1_offset, nv2_offset, 1, user);
- if (!ret)
- return EC_ERROR_UNKNOWN;
-
- /* Source offset is equal to length of buffer */
- nv1_offset = nvmem_user_sizes[user];
- nv2_offset = 0;
- /* Attempt to move just 1 byte */
- ret = nvmem_move(nv1_offset, nv2_offset, 1, user);
- if (!ret)
- return EC_ERROR_UNKNOWN;
-
- nv1_offset = 0;
- nv2_offset = nvmem_user_sizes[user] - len;
- /* Move data chunk from start to end of buffer */
- ret = nvmem_move(nv1_offset, nv2_offset,
- len, user);
- if (ret)
- return ret;
-
- /* Attempt to move data chunk 1 byte beyond end of user buffer */
- nv1_offset = 0;
- nv2_offset = nvmem_user_sizes[user] - len + 1;
- ret = nvmem_move(nv1_offset, nv2_offset,
- len, user);
- if (!ret)
- return EC_ERROR_UNKNOWN;
- /* nvmem_move returned an error, need to clear internal error state */
- nvmem_commit();
+ TEST_ASSERT(!failed_count);
+
+ cache1_offs_count = fill_cache_offsets(cache1, cache1_offsets,
+ ARRAY_SIZE(cache1_offsets));
+ cache2_offs_count = fill_cache_offsets(cache2, cache2_offsets,
+ ARRAY_SIZE(cache2_offsets));
+
+ TEST_ASSERT(cache1_offs_count == cache2_offs_count);
+
+ for (i = 0; (i < ARRAY_SIZE(cache1_offsets)) && cache2_offs_count;
+ i++) {
+ size_t j;
+
+ for (j = 0; j < cache2_offs_count; j++) {
+ if (compare_objects(cache1, cache1_offsets[i], cache2,
+ cache2_offsets[j]))
+ continue;
+ /* Remove object from the cache2 offsets. */
+ cache2_offsets[j] = cache2_offsets[--cache2_offs_count];
+ break;
+ }
+ }
+
+ TEST_ASSERT(cache2_offs_count == 0);
return EC_SUCCESS;
}
-static int test_is_different(void)
+static int prepare_post_migration_nvmem(void)
{
- uint32_t len = 0x41;
- uint32_t nv1_offset = 0;
- int user = 1;
- int ret;
+ TEST_ASSERT(prepare_nvmem_contents() == EC_SUCCESS);
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ return EC_SUCCESS;
+}
+/*
+ * This test creates various failure conditions related to interrupted nvmem
+ * save operations and verifies that transaction integrity is maintained -
+ * i.e. either all variables get updated,
+ */
+static int test_nvmem_incomplete_transaction(void)
+{
/*
- * The purpose of this test is to verify nv_is_different(). Test data is
- * written to a location in user buffer 1, then a case that's expected
- * to pass along with a case that is expected to fail are checked. Next
- * the same tests are repeated when the NvMem write is followed by a
- * commit operation.
+ * Will be more than enough, we can't store more than 15 objects or so
+ * anyways.
*/
+ uint16_t offsets[MAX_OFFSETS];
+ size_t num_objects;
+ uint8_t buf[nvmem_user_sizes[NVMEM_TPM]];
+
+ TEST_ASSERT(prepare_post_migration_nvmem() == EC_SUCCESS);
+ num_objects = fill_obj_offsets(offsets, ARRAY_SIZE(offsets));
+ TEST_ASSERT(num_objects == 9);
+
+ /* Save cache state before deleting objects. */
+ memcpy(buf, nvmem_cache_base(NVMEM_TPM), sizeof(buf));
+
+ drop_evictable_obj(evictable_offs_to_addr(offsets[4]));
+ drop_evictable_obj(evictable_offs_to_addr(offsets[3]));
+
+ failure_mode = TEST_FAIL_WHEN_SAVING;
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ wipe_out_nvmem_cache();
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+
+ TEST_ASSERT(caches_match(buf, nvmem_cache_base(NVMEM_TPM)) ==
+ EC_SUCCESS);
+ drop_evictable_obj(evictable_offs_to_addr(offsets[4]));
+ drop_evictable_obj(evictable_offs_to_addr(offsets[3]));
+
+ /* Check if failure when invalidating is recovered after restart. */
+ failure_mode = TEST_FAIL_WHEN_INVALIDATING;
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ ccprintf("%s:%d\n", __func__, __LINE__);
+ wipe_out_nvmem_cache();
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ ccprintf("%s:%d\n", __func__, __LINE__);
+ num_objects = fill_obj_offsets(offsets, ARRAY_SIZE(offsets));
+ TEST_ASSERT(num_objects == 7);
- /* Generate test data */
- generate_random_data(nv1_offset, len);
- /* Write to NvMem cache buffer */
- nvmem_write(nv1_offset, len, &write_buffer[nv1_offset], user);
- /* Expected to be the same */
- ret = nvmem_is_different(nv1_offset, len,
- &write_buffer[nv1_offset], user);
- if (ret)
- return EC_ERROR_UNKNOWN;
-
- /* Expected to be different */
- ret = nvmem_is_different(nv1_offset + 1, len,
- &write_buffer[nv1_offset], user);
- if (!ret)
- return EC_ERROR_UNKNOWN;
-
- /* Commit cache buffer and retest */
- nvmem_commit();
- /* Expected to be the same */
- ret = nvmem_is_different(nv1_offset, len,
- &write_buffer[nv1_offset], user);
- if (ret)
- return EC_ERROR_UNKNOWN;
-
- /* Expected to be different */
- write_buffer[nv1_offset] ^= 0xff;
- ret = nvmem_is_different(nv1_offset, len,
- &write_buffer[nv1_offset], user);
- if (!ret)
- return EC_ERROR_UNKNOWN;
+ return EC_SUCCESS;
+}
+
+/*
+ * Verify that interrupted compaction results in a consistent state of the
+ * NVMEM cache.
+ */
+static int test_nvmem_interrupted_compaction(void)
+{
+ uint8_t buf[nvmem_user_sizes[NVMEM_TPM]];
+ uint8_t target_list_index;
+ uint8_t filler = 1;
+ TEST_ASSERT(prepare_post_migration_nvmem() == EC_SUCCESS);
+
+ /* Let's fill up a couple of pages with erased objects. */
+ target_list_index = master_at.list_index + 2;
+
+ do {
+ /*
+ * A few randomly picked reserved objects to modify to create
+ * need for compaction.
+ */
+ const uint8_t objs_to_modify[] = {1, 3, 19, 42};
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(objs_to_modify); i++) {
+ NV_RESERVED_ITEM ri;
+
+ NvGetReserved(i, &ri);
+
+ /* Direct access to the object. */
+ memset((uint8_t *)nvmem_cache_base(NVMEM_TPM) +
+ ri.offset,
+ filler++, ri.size);
+ }
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ } while (master_at.list_index != target_list_index);
+
+ /* Save the state of NVMEM cache. */
+ memcpy(buf, nvmem_cache_base(NVMEM_TPM), sizeof(buf));
+ failure_mode = TEST_FAIL_WHEN_COMPACTING;
+ compact_nvmem();
+ wipe_out_nvmem_cache();
+ ccprintf("%s:%d\n", __func__, __LINE__);
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ TEST_ASSERT(caches_match(buf, nvmem_cache_base(NVMEM_TPM)) ==
+ EC_SUCCESS);
return EC_SUCCESS;
}
int nvmem_first_task(void *unused)
{
- uint32_t offset = 0;
- uint32_t num_bytes = WRITE_SEGMENT_LEN;
- int user = NVMEM_USER_0;
-
- task_wait_event(0);
- /* Generate source data */
- generate_random_data(0, num_bytes);
- nvmem_write(0, num_bytes, &write_buffer[offset], user);
- /* Read from cache memory */
- nvmem_read(0, num_bytes, read_buffer, user);
- /* Verify that write to nvmem was successful */
- TEST_ASSERT_ARRAY_EQ(write_buffer, read_buffer, num_bytes);
- /* Wait here with mutex held by this task */
- task_wait_event(0);
- /* Write to flash which releases nvmem mutex */
- nvmem_commit();
- nvmem_read(0, num_bytes, read_buffer, user);
- /* Verify that write to flash was successful */
- TEST_ASSERT_ARRAY_EQ(write_buffer, read_buffer, num_bytes);
-
return EC_SUCCESS;
}
int nvmem_second_task(void *unused)
{
- uint32_t offset = WRITE_SEGMENT_LEN;
- uint32_t num_bytes = WRITE_SEGMENT_LEN;
- int user = NVMEM_USER_0;
-
- task_wait_event(0);
-
- /* Gen test data and don't overwite test data generated by 1st task */
- generate_random_data(offset, num_bytes);
- /* Write test data at offset 0 nvmem user buffer */
- nvmem_write(0, num_bytes, &write_buffer[offset], user);
- /* Write to flash */
- nvmem_commit();
- /* Read from nvmem */
- nvmem_read(0, num_bytes, read_buffer, user);
- /* Verify that write to nvmem was successful */
- TEST_ASSERT_ARRAY_EQ(&write_buffer[offset], read_buffer, num_bytes);
- /* Clear flag to indicate lock test is complete */
- lock_test_started = 0;
-
return EC_SUCCESS;
}
-static int test_lock(void)
+static void run_test_setup(void)
{
- /*
- * This purpose of this test is to verify the mutex lock portion of the
- * nvmem module. There are two additional tasks utilized. The first task
- * is woken and it creates some test data and does an
- * nvmem_write(). This will cause the mutex to be locked by the 1st
- * task. The 1st task then waits and control is returned to this
- * function and the 2nd task is woken, the 2nd task also attempts to
- * write data to nvmem. The 2nd task should stall waiting for the mutex
- * to be unlocked.
- *
- * When control returns to this function, the 1st task is woken again
- * and the nvmem operation is completed. This will allow the 2nd task to
- * grab the lock and finish its nvmem operation. The test will not
- * complete until the 2nd task finishes the nvmem write. A static global
- * flag is used to let this function know when the 2nd task is complete.
- *
- * Both tasks write to the same location in nvmem so the test will only
- * pass if the 2nd task can't write until the nvmem write in the 1st
- * task is completed.
- */
+ /* Allow Flash erase/writes */
+ flash_write_fail = 0;
+ test_reset();
+}
- /* Set flag for start of test */
- lock_test_started = 1;
- /* Wake first_task */
- task_wake(TASK_ID_NV_1);
- task_wait_event(1000);
- /* Wake second_task. It should stall waiting for mutex */
- task_wake(TASK_ID_NV_2);
- task_wait_event(1000);
- /* Go back to first_task so it can complete its nvmem operation */
- task_wake(TASK_ID_NV_1);
- /* Wait for 2nd task to complete nvmem operation */
- while (lock_test_started)
- task_wait_event(100);
+void nvmem_wipe_cache(void)
+{
+}
- return EC_SUCCESS;
+int DCRYPTO_ladder_is_enabled(void)
+{
+ return 1;
}
-static int test_nvmem_save(void)
+static int test_migration(void)
{
/*
- * The purpose of this test is to verify that if the written value
- * did not change the cache contents there is no actual write
- * happening at the commit time.
+ * This purpose of this test is to verify migration of the 'legacy'
+ * TPM NVMEM format to the new scheme where each element is stored in
+ * flash in its own container.
*/
- int dummy_value;
- int offset = 0x10;
- uint8_t generation_a;
- uint8_t generation_b;
- uint8_t prev_generation;
- uint8_t new_generation;
- const struct nvmem_tag *part_a;
- const struct nvmem_tag *part_b;
- const struct nvmem_tag *new_gen_part;
- const struct nvmem_tag *prev_gen_part;
-
- part_a = (const struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_A;
- part_b = (const struct nvmem_tag *)CONFIG_FLASH_NVMEM_BASE_B;
+ TEST_ASSERT(prepare_nvmem_contents() == EC_SUCCESS);
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ TEST_ASSERT(iterate_over_flash() == EC_SUCCESS);
+ TEST_ASSERT(test_result.var_count == 3);
+ TEST_ASSERT(test_result.reserved_obj_count == 40);
+ TEST_ASSERT(test_result.evictable_obj_count == 9);
+ TEST_ASSERT(test_result.delimiter_count == 1);
+ TEST_ASSERT(test_result.deleted_obj_count == 0);
+ TEST_ASSERT(test_result.unexpected_count == 0);
+ TEST_ASSERT(test_result.valid_data_size == 5214);
+ TEST_ASSERT(total_var_space == 77);
+ /* Container pointer not yet set. */
+ TEST_ASSERT(!master_at.ct.data_offset && !master_at.ct.ph);
+ return EC_SUCCESS;
+}
+
+/*
+ * The purpose of this test is to verify variable storage limits, both per
+ * object and total.
+ */
+static int test_var_boundaries(void)
+{
+ const size_t max_size = 255; /* Key and value must fit in a byte. */
+ const uint8_t *key;
+ const uint8_t *val;
+ size_t key_len;
+ size_t val_len;
+ uint16_t saved_total_var_space;
+ uint32_t coverage_map;
+ uint8_t var_key[10];
+
+ TEST_ASSERT(prepare_new_flash() == EC_SUCCESS);
+ saved_total_var_space = total_var_space;
+ coverage_map = 0;
+
/*
- * Make sure nvmem is initialized and both partitions have been
- * written.
+ * Let's use the legacy NVMEM image as a source of fairly random but
+ * reproducible data.
*/
- nvmem_init();
+ key = legacy_nvmem_image;
+ val = legacy_nvmem_image;
/*
- * Make sure something is changed at offset 0x10 into the second user
- * space.
+ * Test limit of max variable body space, use keys and values of
+ * different sizes, below and above the limit.
*/
- nvmem_read(offset, sizeof(dummy_value), &dummy_value, NVMEM_USER_1);
- dummy_value ^= ~0;
- nvmem_write(0x10, sizeof(dummy_value), &dummy_value, NVMEM_USER_1);
- nvmem_commit();
+ for (key_len = 1; key_len < max_size; key_len += 20) {
+
+ coverage_map |= 1;
+
+ val_len = MIN(max_size, MAX_VAR_BODY_SPACE - key_len);
+ TEST_ASSERT(setvar(key, key_len, val, val_len) == EC_SUCCESS);
+ TEST_ASSERT(total_var_space ==
+ saved_total_var_space + key_len + val_len);
- /* Verify that the two generation values are different. */
- generation_a = part_a->generation;
- generation_b = part_b->generation;
- TEST_ASSERT(generation_a != generation_b);
+ /* Now drop the variable from the storage. */
+ TEST_ASSERT(setvar(key, key_len, NULL, 0) == EC_SUCCESS);
+ TEST_ASSERT(total_var_space == saved_total_var_space);
+
+ /* And if key length allows it, try to write too much. */
+ if (val_len == max_size)
+ continue;
+
+ coverage_map |= 2;
+ /*
+ * Yes, let's try writing one byte too many and see that the
+ * attempt is rejected.
+ */
+ val_len++;
+ TEST_ASSERT(setvar(key, key_len, val, val_len) ==
+ EC_ERROR_INVAL);
+ TEST_ASSERT(total_var_space == saved_total_var_space);
+ }
/*
- * Figure out which one should change next, we are close to the
- * beginnig of the test, no wrap is expected.
+ * Test limit of max total variable space, use keys and values of
+ * different sizes, below and above the limit.
*/
- if (generation_a > generation_b) {
- prev_generation = generation_a;
- new_generation = generation_a + 1;
- new_gen_part = part_b;
- prev_gen_part = part_a;
- } else {
- prev_generation = generation_b;
- new_generation = generation_b + 1;
- new_gen_part = part_a;
- prev_gen_part = part_b;
+ key_len = sizeof(var_key);
+ val_len = 20; /* Anything below 256 would work. */
+ memset(var_key, 'x', key_len);
+
+ while (1) {
+ int rv;
+
+ /*
+ * Change the key so that a new variable is added to the
+ * storage.
+ */
+ rv = setvar(var_key, key_len, val, val_len);
+
+ if (rv == EC_ERROR_OVERFLOW)
+ break;
+
+ coverage_map |= 4;
+ TEST_ASSERT(rv == EC_SUCCESS);
+ var_key[0]++;
+ saved_total_var_space += key_len + val_len;
}
- /* Write a new value, this should trigger generation switch. */
- dummy_value += 1;
- TEST_ASSERT(nvmem_write(0x10, sizeof(dummy_value),
- &dummy_value, NVMEM_USER_1) == EC_SUCCESS);
- TEST_ASSERT(nvmem_commit() == EC_SUCCESS);
+ TEST_ASSERT(saved_total_var_space == total_var_space);
+ TEST_ASSERT(saved_total_var_space <= MAX_VAR_TOTAL_SPACE);
+ TEST_ASSERT((saved_total_var_space + key_len + val_len) >
+ MAX_VAR_TOTAL_SPACE);
- TEST_ASSERT(prev_gen_part->generation == prev_generation);
- TEST_ASSERT(new_gen_part->generation == new_generation);
+ TEST_ASSERT(coverage_map == 7);
+ return EC_SUCCESS;
+}
- /* Write the same value, this should NOT trigger generation switch. */
- TEST_ASSERT(nvmem_write(0x10, sizeof(dummy_value),
- &dummy_value, NVMEM_USER_1) == EC_SUCCESS);
- TEST_ASSERT(nvmem_commit() == EC_SUCCESS);
+static int verify_ram_index_space(size_t verify_size)
+{
+ NV_RESERVED_ITEM ri;
+ size_t i;
+ uint32_t casted_size;
+ uint8_t byte;
+ uint8_t fill_byte = 0x55;
+
+ if (verify_size > RAM_INDEX_SPACE)
+ return EC_ERROR_INVAL;
+
+ NvGetReserved(NV_RAM_INDEX_SPACE, &ri);
+
+ /*
+ * Save the size of the index space, needed on machines where size_t
+ * is a 64 bit value.
+ */
+ casted_size = verify_size;
+
+ /*
+ * Now write index space in the cache, we write the complete space,
+ * but on read back only verify_size bytes are expected to be set.
+ */
+ nvmem_write(ri.offset, sizeof(casted_size), &casted_size, NVMEM_TPM);
+
+ for (i = 0; i < RAM_INDEX_SPACE; i++)
+ nvmem_write(ri.offset + sizeof(casted_size) + i,
+ sizeof(fill_byte), &fill_byte, NVMEM_TPM);
- TEST_ASSERT(prev_gen_part->generation == prev_generation);
- TEST_ASSERT(new_gen_part->generation == new_generation);
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ wipe_out_nvmem_cache();
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+
+ /* Make sure read back size matches. */
+ nvmem_read(ri.offset, sizeof(casted_size), &casted_size, NVMEM_TPM);
+ TEST_ASSERT(casted_size == verify_size);
+
+ /*
+ * Now check spaces which were supposed to be written (up to
+ * verify_size) and left intact.
+ */
+ for (i = 0; i < RAM_INDEX_SPACE; i++) {
+ nvmem_read(ri.offset + sizeof(casted_size) + i, sizeof(byte),
+ &byte, NVMEM_TPM);
+ if (i < verify_size)
+ TEST_ASSERT(byte == fill_byte);
+ else
+ TEST_ASSERT(byte == 0);
+ }
return EC_SUCCESS;
}
-static void run_test_setup(void)
+static int test_tpm_nvmem_modify_reserved_objects(void)
{
- /* Allow Flash erase/writes */
- flash_write_fail = 0;
- test_reset();
+ NV_RESERVED_ITEM ri;
+ /* Some random reserved objects' indices. */
+ const uint8_t res_obj_ids[] = {1, 4, 9, 20};
+ size_t i;
+ static uint8_t cache_copy[12 * 1024];
+ struct nvmem_test_result old_result;
+ uint64_t new_values[ARRAY_SIZE(res_obj_ids)];
+ size_t erased_size;
+
+ TEST_ASSERT(sizeof(cache_copy) >= nvmem_user_sizes[NVMEM_TPM]);
+ TEST_ASSERT(prepare_new_flash() == EC_SUCCESS);
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ iterate_over_flash();
+ old_result = test_result;
+
+ /* Preserve NVMEM cache for future comparison. */
+ memcpy(cache_copy, nvmem_cache_base(NVMEM_TPM),
+ nvmem_user_sizes[NVMEM_TPM]);
+
+ erased_size = 0;
+ /* Modify several reserved objects in the cache. */
+ for (i = 0; i < ARRAY_SIZE(res_obj_ids); i++) {
+ size_t copy_size;
+ uint8_t *addr_in_cache;
+ size_t k;
+
+ NvGetReserved(res_obj_ids[i], &ri);
+ copy_size = MIN(sizeof(new_values[0]), ri.size);
+ addr_in_cache =
+ (uint8_t *)nvmem_cache_base(NVMEM_TPM) + ri.offset;
+
+ /* Prepare a new value for the variable. */
+ memcpy(new_values + i, addr_in_cache, copy_size);
+ for (k = 0; k < copy_size; k++)
+ ((uint8_t *)(new_values + i))[k] ^= 0x55;
+
+ /* Update value in the cache. */
+ memcpy(addr_in_cache, new_values + i, copy_size);
+
+ /* And in the cache copy. */
+ memcpy(cache_copy + ri.offset, new_values + i, copy_size);
+
+ /*
+ * This much will be added to the erased space, object size
+ * plus index size.
+ */
+ erased_size += ri.size + 1;
+ }
+
+ /* Save it into flash. */
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+
+ /* Wipe out the cache to be sure. */
+ wipe_out_nvmem_cache();
+
+ /* Read NVMEM contents from flash. */
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+
+ /* Verify that the cache matches expectations. */
+ TEST_ASSERT(!memcmp(cache_copy, nvmem_cache_base(NVMEM_TPM),
+ nvmem_user_sizes[NVMEM_TPM]));
+
+ iterate_over_flash();
+
+ /* Update previous results with our expectations. */
+ old_result.deleted_obj_count += ARRAY_SIZE(res_obj_ids);
+ old_result.erased_data_size += erased_size;
+ old_result.delimiter_count++;
+
+ TEST_ASSERT(!memcmp(&test_result, &old_result, sizeof(test_result)));
+
+ /* Verify several index space cases. */
+ for (i = 0; i <= RAM_INDEX_SPACE; i += (RAM_INDEX_SPACE / 2))
+ TEST_ASSERT(verify_ram_index_space(i) == EC_SUCCESS);
+
+ return EC_SUCCESS;
}
-void nvmem_wipe_cache(void)
+static int compare_object(uint16_t obj_offset, size_t obj_size, const void *obj)
{
+ uint32_t next_addr;
+
+ memcpy(&next_addr,
+ evictable_offs_to_addr(obj_offset - sizeof(next_addr)),
+ sizeof(next_addr));
+
+ ccprintf("next_addr %x, sum %x size %d\n", next_addr,
+ (s_evictNvStart + obj_offset + obj_size), obj_size);
+ TEST_ASSERT(next_addr == (s_evictNvStart + obj_offset + obj_size));
+
+ if (!memcmp(evictable_offs_to_addr(obj_offset), obj, obj_size))
+ return EC_SUCCESS;
+
+ return EC_ERROR_INVAL;
}
-int DCRYPTO_ladder_is_enabled(void)
+static int test_tpm_nvmem_modify_evictable_objects(void)
{
- return 1;
+ size_t num_objects;
+ uint16_t offsets[MAX_OFFSETS];
+ uint32_t handles[ARRAY_SIZE(offsets)];
+ uint32_t new_evictable_object[30];
+ size_t i;
+ const uint32_t new_obj_handle = 0x100;
+ static uint8_t modified_obj[CONFIG_FLASH_BANK_SIZE];
+ size_t modified_obj_size;
+ uint32_t modified_obj_handle;
+ uint32_t deleted_obj_handle;
+ uint8_t *obj_cache_addr;
+ size_t num_handles;
+ int new_obj_index;
+ int modified_obj_index;
+
+ TEST_ASSERT(prepare_new_flash() == EC_SUCCESS);
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ iterate_over_flash();
+
+ /* Verify that all evictable objects are there. */
+ num_objects = fill_obj_offsets(offsets, ARRAY_SIZE(offsets));
+ TEST_ASSERT(num_objects == 9);
+ num_handles = num_objects;
+
+ /* Save handles of all objects there are. */
+ for (i = 0; i < num_objects; i++) {
+ memcpy(handles + i, evictable_offs_to_addr(offsets[i]),
+ sizeof(handles[i]));
+ ccprintf("obj %d handle %08x\n", i, handles[i]);
+ }
+ /*
+ * Let's modify the object which currently is stored second in the
+ * stack.
+ */
+ modified_obj_size = offsets[3] - offsets[2] - sizeof(uint32_t);
+
+ /* Modify the object and copy modified value into local buffer. */
+ obj_cache_addr = evictable_offs_to_addr(offsets[2]);
+ memcpy(&modified_obj_handle, obj_cache_addr,
+ sizeof(modified_obj_handle));
+
+ for (i = 0; i < modified_obj_size; i++) {
+ uint8_t c;
+
+ c = obj_cache_addr[i];
+
+ if (i >= sizeof(uint32_t)) { /* Preserve the 4 byte handle. */
+ c ^= 0x55;
+ obj_cache_addr[i] = c;
+ }
+ modified_obj[i] = c;
+ }
+
+ /* Save its handle and then drop the object at offset 5. */
+ memcpy(&deleted_obj_handle, evictable_offs_to_addr(offsets[5]),
+ sizeof(deleted_obj_handle));
+ drop_evictable_obj(evictable_offs_to_addr(offsets[5]));
+
+ /* Prepare the new evictable object, first four bytes are the handle. */
+ for (i = 0; i < ARRAY_SIZE(new_evictable_object); i++)
+ new_evictable_object[i] = new_obj_handle + i;
+
+ /* Add it to the cache. */
+ add_evictable_obj(new_evictable_object, sizeof(new_evictable_object));
+
+ /* Save the new cache state in the flash. */
+ TEST_ASSERT(new_nvmem_save() == EC_SUCCESS);
+
+ /* Wipe out NVMEM cache just in case. */
+ wipe_out_nvmem_cache();
+
+ /* Read back from flash into cache. */
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+
+ /* One object removed, one added, the number should have not changed. */
+ TEST_ASSERT(num_objects ==
+ fill_obj_offsets(offsets, ARRAY_SIZE(offsets)));
+
+ new_obj_index = 0;
+ modified_obj_index = 0;
+ for (i = 0; i < num_objects; i++) {
+ uint32_t handle;
+ size_t j;
+
+ memcpy(&handle, evictable_offs_to_addr(offsets[i]),
+ sizeof(handles[i]));
+ ASSERT(handle != deleted_obj_handle);
+
+ if (handle == new_obj_handle)
+ new_obj_index = i;
+ else if (handle == modified_obj_handle)
+ modified_obj_index = i;
+ /*
+ * Remove the found handle from the set of handles which were
+ * there originally.
+ */
+ for (j = 0; j < num_handles; j++)
+ if (handles[j] == handle) {
+ num_handles--;
+ handles[j] = handles[num_handles];
+ break;
+ }
+ }
+
+ /*
+ * Removed object's handle is still in the array, and it should be the
+ * only remaining element.
+ */
+ TEST_ASSERT(num_handles == 1);
+ TEST_ASSERT(handles[0] == deleted_obj_handle);
+ TEST_ASSERT(new_obj_index >= 0); /* New handle was seen in the cache. */
+ TEST_ASSERT(modified_obj_index >=
+ 0); /* Modified object was seen in the cache. */
+
+ TEST_ASSERT(compare_object(offsets[new_obj_index],
+ sizeof(new_evictable_object),
+ new_evictable_object) == EC_SUCCESS);
+ TEST_ASSERT(compare_object(offsets[modified_obj_index],
+ modified_obj_size,
+ modified_obj) == EC_SUCCESS);
+ return EC_SUCCESS;
}
void run_test(void)
{
run_test_setup();
+
+ if (0) {
+ RUN_TEST(test_nvmem_incomplete_transaction);
+ test_print_result();
+ return;
+ }
+ RUN_TEST(test_migration);
RUN_TEST(test_corrupt_nvmem);
RUN_TEST(test_fully_erased_nvmem);
RUN_TEST(test_configured_nvmem);
- RUN_TEST(test_write_read_sequence);
- RUN_TEST(test_write_full_multi);
- RUN_TEST(test_write_fail);
- RUN_TEST(test_buffer_overflow);
- RUN_TEST(test_move);
- RUN_TEST(test_is_different);
- RUN_TEST(test_lock);
- RUN_TEST(test_nvmem_erase_user_data);
RUN_TEST(test_nvmem_save);
+ RUN_TEST(test_var_read_write_delete);
+ RUN_TEST(test_nvmem_compaction);
+ RUN_TEST(test_var_boundaries);
+ RUN_TEST(test_nvmem_erase_tpm_data);
+ RUN_TEST(test_tpm_nvmem_modify_reserved_objects);
+ RUN_TEST(test_tpm_nvmem_modify_evictable_objects);
+ RUN_TEST(test_nvmem_incomplete_transaction);
+ failure_mode = TEST_NO_FAILURE; /* In case the above test failed. */
+ RUN_TEST(test_nvmem_interrupted_compaction);
+ failure_mode = TEST_NO_FAILURE; /* In case the above test failed. */
+
+ /*
+ * more tests to come
+ * RUN_TEST(test_lock);
+ * RUN_TEST(test_malloc_blocking);
+ */
+
test_print_result();
}
diff --git a/test/nvmem_test.h b/test/nvmem_test.h
new file mode 100644
index 0000000000..f8f166dc5e
--- /dev/null
+++ b/test/nvmem_test.h
@@ -0,0 +1,28 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_TEST_NVMEM_TEST_H
+#define __EC_TEST_NVMEM_TEST_H
+
+#define EMBEDDED_MODE 1
+#define NV_C
+#include "Global.h"
+#undef NV_C
+#include "NV_fp.h"
+#include "tpm_generated.h"
+
+enum test_failure_mode {
+ TEST_NO_FAILURE,
+ TEST_FAIL_WHEN_SAVING,
+ TEST_FAIL_WHEN_INVALIDATING,
+ TEST_FAIL_WHEN_COMPACTING
+};
+
+extern enum test_failure_mode failure_mode;
+
+size_t add_evictable_obj(void *obj, size_t obj_size);
+void drop_evictable_obj(void *obj);
+
+#endif /* ! __EC_TEST_NVMEM_TEST_H */
diff --git a/test/nvmem_tpm2_mock.c b/test/nvmem_tpm2_mock.c
new file mode 100644
index 0000000000..070525406d
--- /dev/null
+++ b/test/nvmem_tpm2_mock.c
@@ -0,0 +1,377 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+/* Stuff from tpm2 directory. */
+
+#include "nvmem_test.h"
+
+#include "console.h"
+#include "nvmem.h"
+#include "util.h"
+
+#define NVMEM_CR50_SIZE 272
+
+uint32_t s_evictNvStart;
+uint32_t s_evictNvEnd;
+
+/* Calculate size of TPM NVMEM. */
+#define MOCK_NV_MEMORY_SIZE \
+ (NVMEM_PARTITION_SIZE - sizeof(struct nvmem_tag) - NVMEM_CR50_SIZE)
+
+uint32_t nvmem_user_sizes[NVMEM_NUM_USERS] = {MOCK_NV_MEMORY_SIZE,
+ NVMEM_CR50_SIZE};
+
+/*
+ * Sizes of the reserved objects stored in the TPM NVMEM. Note that the second
+ * last object is in fact a variable size field starting with 4 bytes of size
+ * and then up to 512 bytes of actual index data. The array below assumes that
+ * the full 512 bytes of the index space are used.
+ */
+const uint16_t res_sizes[] = {4, 2, 2, 2, 66, 66, 66, 66, 66, 66,
+ 34, 34, 34, 66, 66, 66, 8, 4, 134, 28,
+ 3, 4, 4, 4, 4, 4, 2, 15, 2, 8,
+ 4, 4, 4, 96, 2844, 424, 516, 8};
+
+static uint16_t res_addrs[ARRAY_SIZE(res_sizes)];
+
+BOOL NvEarlyStageFindHandle(TPM_HANDLE handle)
+{
+ size_t i;
+
+ res_addrs[0] = 0;
+
+ for (i = 1; i < ARRAY_SIZE(res_addrs); i++)
+ res_addrs[i] = res_addrs[i - 1] + res_sizes[i - 1];
+
+ s_evictNvStart = res_addrs[i - 1] + res_sizes[i - 1];
+
+ s_evictNvEnd = MOCK_NV_MEMORY_SIZE;
+ return 0;
+}
+
+void NvGetReserved(UINT32 index, NV_RESERVED_ITEM *ri)
+{
+ uint32_t index_size;
+
+ if (index >= ARRAY_SIZE(res_sizes)) {
+ ri->size = 0;
+ return;
+ }
+
+ ri->offset = res_addrs[index];
+ if (index != NV_RAM_INDEX_SPACE) {
+ ri->size = res_sizes[index];
+ return;
+ }
+
+ memcpy(&index_size, nvmem_cache_base(NVMEM_TPM) + ri->offset,
+ sizeof(index_size));
+
+ if (index_size == ~0)
+ /* Must be starting with empty flash memeory. */
+ index_size = 0;
+
+ ri->size = index_size + sizeof(index_size);
+}
+
+UINT16 UINT16_Marshal(UINT16 *source, BYTE **buffer, INT32 *size)
+{
+ uint16_t value;
+
+ if (!size || (*size < sizeof(value)))
+ return 0;
+
+ value = htobe16(*source);
+
+ memcpy(*buffer, &value, sizeof(value));
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return sizeof(value);
+}
+
+UINT16 UINT32_Marshal(UINT32 *source, BYTE **buffer, INT32 *size)
+{
+ uint32_t value;
+
+ if (!size || (*size < sizeof(value)))
+ return 0;
+
+ value = htobe32(*source);
+
+ memcpy(*buffer, &value, sizeof(value));
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return sizeof(value);
+}
+
+UINT16 UINT64_Marshal(UINT64 *source, BYTE **buffer, INT32 *size)
+{
+ uint64_t value;
+
+ if (!size || (*size < sizeof(value)))
+ return 0;
+
+ value = htobe64(*source);
+
+ memcpy(*buffer, &value, sizeof(value));
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return sizeof(value);
+}
+
+UINT16 TPM2B_DIGEST_Marshal(TPM2B_DIGEST *source, BYTE **buffer, INT32 *size)
+{
+ UINT16 total_size;
+ INT32 i;
+ uint8_t *p;
+
+ total_size = UINT16_Marshal(&source->t.size, buffer, size);
+ p = *buffer;
+
+ for (i = 0; (i < source->t.size) && *size; ++i) {
+ *p++ = source->t.buffer[i];
+ *size -= 1;
+ }
+
+ total_size += i;
+ *buffer = p;
+
+ return total_size;
+}
+
+uint16_t TPM2B_AUTH_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size)
+{
+ return TPM2B_DIGEST_Marshal(source, buffer, size);
+}
+
+uint16_t TPM2B_NONCE_Marshal(TPM2B_AUTH *source, BYTE **buffer, INT32 *size)
+{
+ return TPM2B_DIGEST_Marshal(source, buffer, size);
+}
+
+TPM_RC UINT16_Unmarshal(UINT16 *target, BYTE **buffer, INT32 *size)
+{
+ uint16_t value;
+
+ if (!size || *size < sizeof(value))
+ return TPM_RC_INSUFFICIENT;
+
+ memcpy(&value, *buffer, sizeof(value));
+ *target = be16toh(value);
+
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return TPM_RC_SUCCESS;
+}
+
+TPM_RC UINT32_Unmarshal(UINT32 *target, BYTE **buffer, INT32 *size)
+{
+ uint32_t value;
+
+ if (!size || *size < sizeof(value))
+ return TPM_RC_INSUFFICIENT;
+
+ memcpy(&value, *buffer, sizeof(value));
+ *target = be32toh(value);
+
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return TPM_RC_SUCCESS;
+}
+
+TPM_RC UINT64_Unmarshal(UINT64 *target, BYTE **buffer, INT32 *size)
+{
+ uint64_t value;
+
+ if (!size || *size < sizeof(value))
+ return TPM_RC_INSUFFICIENT;
+
+ memcpy(&value, *buffer, sizeof(value));
+ *target = be64toh(value);
+
+ *buffer += sizeof(value);
+ *size -= sizeof(value);
+
+ return TPM_RC_SUCCESS;
+}
+
+TPM_RC TPM2B_DIGEST_Unmarshal(TPM2B_DIGEST *target, BYTE **buffer, INT32 *size)
+{
+ TPM_RC result;
+ INT32 i;
+ uint8_t *p;
+
+ result = UINT16_Unmarshal(&target->t.size, buffer, size);
+
+ if (result != TPM_RC_SUCCESS)
+ return result;
+
+ if (target->t.size == 0)
+ return TPM_RC_SUCCESS;
+
+ if ((target->t.size > sizeof(TPMU_HA)) || (target->t.size > *size))
+ return TPM_RC_SIZE;
+
+ p = *buffer;
+ for (i = 0; i < target->t.size; ++i)
+ target->t.buffer[i] = *p++;
+
+ *buffer = p;
+ *size -= i;
+
+ return TPM_RC_SUCCESS;
+}
+
+TPM_RC TPM2B_AUTH_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size)
+{
+ return TPM2B_DIGEST_Unmarshal(target, buffer, size);
+}
+
+TPM_RC TPM2B_NONCE_Unmarshal(TPM2B_AUTH *target, BYTE **buffer, INT32 *size)
+{
+ return TPM2B_DIGEST_Unmarshal(target, buffer, size);
+}
+
+#define ITER_INIT (~0)
+
+static void *get_cache_addr(size_t offset)
+{
+ return (void *)(((uintptr_t)nvmem_cache_base(NVMEM_TPM)) + offset);
+}
+
+static void read_from_cache(size_t offset, size_t size, void *dest)
+{
+ nvmem_read(offset, size, dest, NVMEM_TPM);
+}
+
+static void write_to_cache(size_t offset, size_t size, void *src)
+{
+ nvmem_write(offset, size, src, NVMEM_TPM);
+}
+
+/* Copies of the appropriate functions from NV.c in TPM2 library. */
+static uint32_t nv_next(uint32_t *iter)
+{
+ uint32_t currentIter;
+
+ if (*iter == ITER_INIT)
+ *iter = s_evictNvStart;
+
+ if ((*iter + sizeof(uint32_t) > s_evictNvEnd) || !*iter)
+ return 0;
+
+ currentIter = *iter;
+ read_from_cache(*iter, sizeof(uint32_t), iter);
+ if (!*iter || (*iter == ITER_INIT))
+ return 0;
+
+ return currentIter + sizeof(uint32_t);
+}
+
+static uint32_t nv_get_end(void)
+{
+ uint32_t iter = ITER_INIT;
+ uint32_t endAddr = s_evictNvStart;
+ uint32_t currentAddr;
+
+ while ((currentAddr = nv_next(&iter)) != 0)
+ endAddr = currentAddr;
+
+ if (endAddr != s_evictNvStart) {
+ /* Read offset. */
+ endAddr -= sizeof(uint32_t);
+ read_from_cache(endAddr, sizeof(uint32_t), &endAddr);
+ }
+ return endAddr;
+}
+
+size_t add_evictable_obj(void *obj, size_t obj_size)
+{
+ uint32_t end_addr;
+ uint32_t next_addr;
+ uint32_t list_end = 0;
+
+ end_addr = nv_get_end();
+
+ next_addr = end_addr + sizeof(uint32_t) + obj_size;
+
+ if (next_addr >= s_evictNvEnd) {
+ ccprintf("%s: could not fit %d bytes!\n", __func__, obj_size);
+ return 0;
+ }
+
+ /* Write next pointer */
+ write_to_cache(end_addr, sizeof(uint32_t), &next_addr);
+ /* Write entity data. */
+ write_to_cache(end_addr + sizeof(uint32_t), obj_size, obj);
+
+ /* Write the end of list if it fits. */
+ if (next_addr + sizeof(uint32_t) <= s_evictNvEnd)
+ write_to_cache(next_addr, sizeof(list_end), &list_end);
+
+ return obj_size;
+}
+
+/*
+ * It is the responsibility of the caller to pass the proper address of an
+ * object in the cache.
+ */
+void drop_evictable_obj(void *obj)
+{
+ uint32_t next_addr;
+ uint32_t list_end = 0;
+ uint32_t obj_addr;
+
+ obj_addr = (uintptr_t)obj - (uintptr_t)nvmem_cache_base(NVMEM_TPM);
+ read_from_cache(obj_addr - sizeof(next_addr), sizeof(next_addr),
+ &next_addr);
+ ccprintf("%s:%d dropping obj at cache addr %x, offset %x, addr %p next "
+ "addr %x aka %x (off s_evictNvStart)\n",
+ __func__, __LINE__, obj_addr - s_evictNvStart, obj_addr, obj,
+ next_addr, next_addr - s_evictNvStart);
+
+ /*
+ * Now, to make it easier to add objects behind the current one, let's
+ * pretend there is no more objects.
+ */
+ write_to_cache(obj_addr - sizeof(next_addr), sizeof(list_end),
+ &list_end);
+
+ if (!next_addr || (next_addr == s_evictNvEnd))
+ return;
+
+ /*
+ * Iterate over objects starting with next_addr, copying them into
+ * obj_addr.
+ */
+ obj_addr = next_addr;
+ while (1) {
+ uint32_t next_next_addr;
+ uint32_t next_obj_size;
+
+ read_from_cache(next_addr, sizeof(next_next_addr),
+ &next_next_addr);
+
+ if (!next_next_addr || (next_next_addr == s_evictNvEnd))
+ return;
+
+ next_obj_size = next_next_addr - obj_addr - sizeof(uint32_t);
+ add_evictable_obj(
+ (void *)((uintptr_t)nvmem_cache_base(NVMEM_TPM) +
+ next_addr + sizeof(uint32_t)),
+ next_obj_size);
+ next_addr = next_next_addr;
+ obj_addr += next_obj_size + sizeof(next_obj_size);
+ }
+}
+
+void *evictable_offs_to_addr(uint16_t offset)
+{
+ return (void *)((uintptr_t)get_cache_addr(s_evictNvStart) + offset);
+}
diff --git a/test/nvmem_vars.c b/test/nvmem_vars.c
deleted file mode 100644
index 99e059215e..0000000000
--- a/test/nvmem_vars.c
+++ /dev/null
@@ -1,538 +0,0 @@
-/* Copyright 2016 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Test of the key=val variable implementation (set, get, delete, etc).
- */
-
-#include "common.h"
-#include "compile_time_macros.h"
-#include "nvmem.h"
-#include "nvmem_vars.h"
-#include "printf.h"
-#include "shared_mem.h"
-#include "test_util.h"
-
-/* Declare the user regions (see test_config.h) */
-uint32_t nvmem_user_sizes[] = {
- CONFIG_FLASH_NVMEM_VARS_USER_SIZE,
-};
-BUILD_ASSERT(ARRAY_SIZE(nvmem_user_sizes) == NVMEM_NUM_USERS);
-
-/****************************************************************************/
-/* Mock the flash storage */
-
-static uint8_t ram_buffer[CONFIG_FLASH_NVMEM_VARS_USER_SIZE];
-static uint8_t flash_buffer[CONFIG_FLASH_NVMEM_VARS_USER_SIZE];
-
-extern char *rbuf;
-
-/* Internal functions exported for test */
-void release_local_copy(void)
-{
- rbuf = NULL;
-}
-
-int get_local_copy(void)
-{
- if (!rbuf) {
- memcpy(ram_buffer, flash_buffer, sizeof(ram_buffer));
- rbuf = (char *)ram_buffer;
- }
- return EC_SUCCESS;
-}
-
-int nvmem_read(uint32_t startOffset, uint32_t size,
- void *data_, enum nvmem_users user)
-{
- /* Our mocks make some assumptions */
- if (startOffset != 0 ||
- size > CONFIG_FLASH_NVMEM_VARS_USER_SIZE ||
- user != CONFIG_FLASH_NVMEM_VARS_USER_NUM)
- return EC_ERROR_UNIMPLEMENTED;
-
- if (!data_)
- return EC_ERROR_INVAL;
-
- memcpy(data_, flash_buffer, size);
-
- return EC_SUCCESS;
-}
-
-int nvmem_write(uint32_t startOffset, uint32_t size,
- void *data_, enum nvmem_users user)
-{
- /* Our mocks make some assumptions */
- if (startOffset != 0 ||
- size > CONFIG_FLASH_NVMEM_VARS_USER_SIZE ||
- user != CONFIG_FLASH_NVMEM_VARS_USER_NUM)
- return EC_ERROR_UNIMPLEMENTED;
-
- if (!data_)
- return EC_ERROR_INVAL;
-
- memcpy(ram_buffer, data_, size);
-
- return EC_SUCCESS;
-}
-
-int nvmem_commit(void)
-{
- memcpy(flash_buffer, ram_buffer, CONFIG_FLASH_NVMEM_VARS_USER_SIZE);
- return EC_SUCCESS;
-}
-
-int nvmem_erase_user_data(enum nvmem_users user)
-{
- memset(ram_buffer, 0xff, sizeof(ram_buffer));
- memset(flash_buffer, 0xff, sizeof(flash_buffer));
- return EC_SUCCESS;
-}
-
-/****************************************************************************/
-/* Helper routines */
-
-static void erase_flash(void)
-{
- /* Invalidate the RAM cache */
- release_local_copy();
-
- /* Zero flash */
- memset(flash_buffer, 0xff, sizeof(flash_buffer));
-}
-
-/* Erase flash, then copy data_ over it */
-static void load_flash(const uint8_t *data_, size_t data_len)
-{
- erase_flash();
- memcpy(flash_buffer, data_, data_len);
-}
-
-/* Return true if flash matches data_, and is followed by 0xff to the end */
-static int verify_flash(const uint8_t *data_, size_t data_len)
-{
- size_t i;
-
- /* mismatch means false */
- if (memcmp(flash_buffer, data_, data_len))
- return 0;
-
- for (i = data_len;
- i < CONFIG_FLASH_NVMEM_VARS_USER_SIZE - data_len;
- i++)
- if (flash_buffer[i] != 0xff)
- return 0;
- return 1;
-}
-
-/*
- * Treating both as strings, save the <key, value> pair.
- */
-int str_setvar(const char *key, const char *val)
-{
- /* Only for tests, so assume the length will fit */
- uint8_t key_len, val_len;
-
- key_len = strlen(key);
- val_len = val ? strlen(val) : 0;
-
- return setvar(key, key_len, val, val_len);
-}
-
-/*
- * Treating both as strings, lookup the key and compare the result with the
- * expected value. Return true if they match.
- */
-static int str_matches(const char *key, const char *expected_val)
-{
- const struct tuple *t = getvar(key, strlen(key));
- uint8_t expected_len;
-
- if (!expected_val && !t)
- return 1;
-
- if (expected_val && !t)
- return 0;
-
- if (!expected_val && t)
- return 0;
-
- expected_len = strlen(expected_val);
- return !memcmp(tuple_val(t), expected_val, expected_len);
-}
-
-/****************************************************************************/
-/* Tests */
-
-static int check_init(void)
-{
- /* Valid entries */
- const uint8_t good[] = { 0x01, 0x01, 0x00, 'A', 'a',
- 0x01, 0x01, 0x00, 'B', 'b',
- 0x00 };
-
- /* Empty variables are 0x00, followed by all 0xff */
- const uint8_t empty[] = { 0x00 };
-
- /*
- * This is parsed as though there's only one variable, but it's wrong
- * because the rest of the storage isn't 0xff.
- */
- const uint8_t bad_key[] = { 0x01, 0x01, 0x00, 'A', 'a',
- 0x00, 0x01, 0x00, 'B', 'b',
- 0x00 };
-
- /* Zero-length variables are not allowed */
- const uint8_t bad_val[] = { 0x01, 0x01, 0x00, 'A', 'a',
- 0x01, 0x00, 0x00, 'B', 'b',
- 0x00 };
-
- /* The next constants use magic numbers based on on the region size */
- BUILD_ASSERT(CONFIG_FLASH_NVMEM_VARS_USER_SIZE == 600);
-
- /* This is one byte too large */
- const uint8_t too_big[] = { [0] = 0xff, [1] = 0xff, /* 0 - 512 */
- [513] = 0x01, [514] = 0x53, /* 513 - 599 */
- [599] = 0x00 };
-
- /* This should just barely fit */
- const uint8_t just_right[] = { [0] = 0xff, [1] = 0xff, /* 0-512 */
- [513] = 0x01, [514] = 0x52, /* 513-598 */
- [599] = 0x00 };
-
- /* No end marker */
- const uint8_t not_right[] = { [0] = 0xff, [1] = 0xff, /* 0-512 */
- [513] = 0x01, [514] = 0x52, /* 513-598 */
- [599] = 0xff };
-
- load_flash(good, sizeof(good));
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(good, sizeof(good)));
-
- load_flash(empty, sizeof(empty));
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(empty, sizeof(empty)));
-
- /* All 0xff quickly runs off the end of the storage */
- erase_flash();
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(empty, sizeof(empty)));
-
- load_flash(bad_key, sizeof(bad_key));
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(empty, sizeof(empty)));
-
- load_flash(bad_val, sizeof(bad_val));
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(empty, sizeof(empty)));
-
- load_flash(too_big, sizeof(too_big));
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(empty, sizeof(empty)));
-
- load_flash(just_right, sizeof(just_right));
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(just_right, sizeof(just_right)));
-
- load_flash(not_right, sizeof(not_right));
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(empty, sizeof(empty)));
-
- return EC_SUCCESS;
-}
-
-static int simple_search(void)
-{
- const uint8_t preload[] = {
- 0x02, 0x02, 0x00, 'h', 'o', 'y', 'o',
- 0x02, 0x4, 0x00, 'y', 'o', 'h', 'o', 'y', 'o',
- 0x02, 0x06, 0x00, 'm', 'o', 'y', 'o', 'h', 'o', 'y', 'o',
- 0x00 };
-
- load_flash(preload, sizeof(preload));
- TEST_ASSERT(initvars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(preload, sizeof(preload)));
-
- TEST_ASSERT(str_matches("no", 0));
- TEST_ASSERT(str_matches("ho", "yo"));
- TEST_ASSERT(str_matches("yo", "hoyo"));
- TEST_ASSERT(str_matches("mo", "yohoyo"));
-
- return EC_SUCCESS;
-}
-
-static int simple_write(void)
-{
- const uint8_t after_one[] = {
- 0x02, 0x02, 0x00, 'h', 'o', 'y', 'o',
- 0x00 };
-
- const uint8_t after_two[] = {
- 0x02, 0x02, 0x00, 'h', 'o', 'y', 'o',
- 0x02, 0x4, 0x00, 'y', 'o', 'h', 'o', 'y', 'o',
- 0x00 };
-
- const uint8_t after_three[] = {
- 0x02, 0x02, 0x00, 'h', 'o', 'y', 'o',
- 0x02, 0x4, 0x00, 'y', 'o', 'h', 'o', 'y', 'o',
- 0x02, 0x06, 0x00, 'm', 'o', 'y', 'o', 'h', 'o', 'y', 'o',
- 0x00 };
-
- erase_flash();
- TEST_ASSERT(initvars() == EC_SUCCESS);
-
- TEST_ASSERT(setvar("ho", 2, "yo", 2) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(after_one, sizeof(after_one)));
-
- TEST_ASSERT(setvar("yo", 2, "hoyo", 4) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(after_two, sizeof(after_two)));
-
- TEST_ASSERT(setvar("mo", 2, "yohoyo", 6) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(after_three, sizeof(after_three)));
-
- return EC_SUCCESS;
-}
-
-static int simple_delete(void)
-{
- const char start_with[] = {
- 0x01, 0x05, 0x00, 'A', 'a', 'a', 'a', 'a', 'a',
- 0x02, 0x03, 0x00, 'B', 'B', 'b', 'b', 'b',
- 0x03, 0x06, 0x00, 'C', 'C', 'C', 'x', 'y', 'z', 'p', 'd', 'q',
- 0x01, 0x03, 0x00, 'M', 'm', '0', 'm',
- 0x04, 0x01, 0x00, 'N', 'N', 'N', 'N', 'n',
- 0x00 };
-
- const char after_one[] = {
- 0x02, 0x03, 0x00, 'B', 'B', 'b', 'b', 'b',
- 0x03, 0x06, 0x00, 'C', 'C', 'C', 'x', 'y', 'z', 'p', 'd', 'q',
- 0x01, 0x03, 0x00, 'M', 'm', '0', 'm',
- 0x04, 0x01, 0x00, 'N', 'N', 'N', 'N', 'n',
- 0x00 };
-
- const char after_two[] = {
- 0x02, 0x03, 0x00, 'B', 'B', 'b', 'b', 'b',
- 0x03, 0x06, 0x00, 'C', 'C', 'C', 'x', 'y', 'z', 'p', 'd', 'q',
- 0x01, 0x03, 0x00, 'M', 'm', '0', 'm',
- 0x00 };
-
- const char after_three[] = {
- 0x02, 0x03, 0x00, 'B', 'B', 'b', 'b', 'b',
- 0x01, 0x03, 0x00, 'M', 'm', '0', 'm',
- 0x00 };
-
- const char empty[] = { 0x00 };
-
- erase_flash();
- TEST_ASSERT(initvars() == EC_SUCCESS);
-
- TEST_ASSERT(setvar("A", 1, "aaaaa", 5) == EC_SUCCESS);
- TEST_ASSERT(setvar("BB", 2, "bbb", 3) == EC_SUCCESS);
- TEST_ASSERT(setvar("CCC", 3, "xyzpdq", 6) == EC_SUCCESS);
- TEST_ASSERT(setvar("M", 1, "m0m", 3) == EC_SUCCESS);
- TEST_ASSERT(setvar("NNNN", 4, "n", 1) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(start_with, sizeof(start_with)));
-
- /* Zap first variable by setting var_len to 0 */
- TEST_ASSERT(setvar("A", 1, "yohoyo", 0) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(after_one, sizeof(after_one)));
-
- /* Zap last variable by passing null pointer */
- TEST_ASSERT(setvar("NNNN", 4, 0, 3) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(after_two, sizeof(after_two)));
-
- /* Ensure that zapping nonexistant variable does nothing */
- TEST_ASSERT(setvar("XXX", 3, 0, 0) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(after_two, sizeof(after_two)));
-
- /* Zap variable in the middle */
- TEST_ASSERT(setvar("CCC", 3, 0, 0) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(after_three, sizeof(after_three)));
-
- /* Zap the rest */
- TEST_ASSERT(setvar("BB", 2, 0, 0) == EC_SUCCESS);
- TEST_ASSERT(setvar("M", 1, 0, 0) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(empty, sizeof(empty)));
-
- /* Zapping a nonexistant variable still does nothing */
- TEST_ASSERT(setvar("XXX", 3, 0, 0) == EC_SUCCESS);
- TEST_ASSERT(writevars() == EC_SUCCESS);
- TEST_ASSERT(verify_flash(empty, sizeof(empty)));
-
- return EC_SUCCESS;
-}
-
-static int complex_write(void)
-{
- erase_flash();
- TEST_ASSERT(initvars() == EC_SUCCESS);
-
- /* Do a bunch of writes and erases */
- str_setvar("ho", "aa");
- str_setvar("zo", "nn");
- str_setvar("yo", "CCCCCCCC");
- str_setvar("zooo", "yyyyyyy");
- str_setvar("yo", "AA");
- str_setvar("ho", 0);
- str_setvar("yi", "BBB");
- str_setvar("yi", "AA");
- str_setvar("hixx", 0);
- str_setvar("yo", "BBB");
- str_setvar("zo", "");
- str_setvar("hi", "bbb");
- str_setvar("ho", "cccccc");
- str_setvar("yo", "");
- str_setvar("zo", "ggggg");
-
- /* What do we expect to find? */
- TEST_ASSERT(str_matches("hi", "bbb"));
- TEST_ASSERT(str_matches("hixx", 0));
- TEST_ASSERT(str_matches("ho", "cccccc"));
- TEST_ASSERT(str_matches("yi", "AA"));
- TEST_ASSERT(str_matches("yo", 0));
- TEST_ASSERT(str_matches("zo", "ggggg"));
- TEST_ASSERT(str_matches("zooo", "yyyyyyy"));
-
- return EC_SUCCESS;
-}
-
-static int weird_keys(void)
-{
- uint8_t keyA[255];
- uint8_t keyB[255];
- const char *valA = "this is A";
- const char *valB = "THIS IS b";
- int i;
- const struct tuple *t;
-
- erase_flash();
- TEST_ASSERT(initvars() == EC_SUCCESS);
-
- for (i = 0; i < 255; i++) {
- keyA[i] = i;
- keyB[i] = 255 - i;
- }
-
- TEST_ASSERT(setvar(keyA, sizeof(keyA),
- valA, strlen(valA)) == EC_SUCCESS);
-
- TEST_ASSERT(setvar(keyB, sizeof(keyB),
- valB, strlen(valB)) == EC_SUCCESS);
-
- TEST_ASSERT(writevars() == EC_SUCCESS);
-
- t = getvar(keyA, sizeof(keyA));
- TEST_ASSERT(t);
- TEST_ASSERT(t->val_len == strlen(valA));
- TEST_ASSERT(memcmp(tuple_val(t), valA, strlen(valA)) == 0);
-
- t = getvar(keyB, sizeof(keyB));
- TEST_ASSERT(t);
- TEST_ASSERT(t->val_len == strlen(valB));
- TEST_ASSERT(memcmp(tuple_val(t), valB, strlen(valB)) == 0);
-
- return EC_SUCCESS;
-}
-
-static int weird_values(void)
-{
- const char *keyA = "this is A";
- const char *keyB = "THIS IS b";
- char valA[255];
- char valB[255];
- int i;
- const struct tuple *t;
-
- erase_flash();
- TEST_ASSERT(initvars() == EC_SUCCESS);
-
- for (i = 0; i < 255; i++) {
- valA[i] = i;
- valB[i] = 255 - i;
- }
-
- TEST_ASSERT(setvar(keyA, strlen(keyA),
- valA, sizeof(valA)) == EC_SUCCESS);
- TEST_ASSERT(str_setvar("c", "CcC") == EC_SUCCESS);
- TEST_ASSERT(setvar(keyB, strlen(keyB),
- valB, sizeof(valB)) == EC_SUCCESS);
- TEST_ASSERT(str_setvar("d", "dDd") == EC_SUCCESS);
-
- TEST_ASSERT(writevars() == EC_SUCCESS);
-
- t = getvar(keyA, strlen(keyA));
- TEST_ASSERT(t);
- TEST_ASSERT(memcmp(tuple_val(t), valA, sizeof(valA)) == 0);
-
- t = getvar(keyB, strlen(keyB));
- TEST_ASSERT(t);
- TEST_ASSERT(memcmp(tuple_val(t), valB, sizeof(valB)) == 0);
-
- TEST_ASSERT(str_matches("c", "CcC"));
- TEST_ASSERT(str_matches("d", "dDd"));
-
- return EC_SUCCESS;
-}
-
-static int fill_it_up(void)
-{
- int i, n;
- char key[20];
-
- erase_flash();
- TEST_ASSERT(initvars() == EC_SUCCESS);
-
- /*
- * Some magic numbers here, because we want to use up 10 bytes at a
- * time and end up with exactly 9 free bytes left.
- */
- TEST_ASSERT(CONFIG_FLASH_NVMEM_VARS_USER_SIZE % 10 == 0);
- n = CONFIG_FLASH_NVMEM_VARS_USER_SIZE / 10;
- TEST_ASSERT(n < 1000);
-
- /* Fill up the storage */
- for (i = 0; i < n - 1; i++) {
- /* 3-byte header, 5-char key, 2-char val, == 10 chars */
- snprintf(key, sizeof(key), "kk%03d", i);
- TEST_ASSERT(setvar(key, 5, "aa", 2) == EC_SUCCESS);
- }
-
- /*
- * Should be nine bytes left in rbuf (because we need one more '\0' at
- * the end). This won't fit.
- */
- TEST_ASSERT(setvar("kk999", 5, "aa", 2) == EC_ERROR_OVERFLOW);
- /* But this will. */
- TEST_ASSERT(setvar("kk999", 5, "a", 1) == EC_SUCCESS);
- /* And this, because it replaces a previous entry */
- TEST_ASSERT(setvar("kk000", 5, "bc", 2) == EC_SUCCESS);
- /* But this still won't fit */
- TEST_ASSERT(setvar("kk999", 5, "de", 2) == EC_ERROR_OVERFLOW);
-
- return EC_SUCCESS;
-}
-
-void run_test(void)
-{
- test_reset();
-
- RUN_TEST(check_init);
- RUN_TEST(simple_write);
- RUN_TEST(simple_search);
- RUN_TEST(simple_delete);
- RUN_TEST(complex_write);
- RUN_TEST(weird_keys);
- RUN_TEST(weird_values);
- RUN_TEST(fill_it_up);
-
- test_print_result();
-}
diff --git a/test/pinweaver.c b/test/pinweaver.c
index 71f8f0387d..093443e49e 100644
--- a/test/pinweaver.c
+++ b/test/pinweaver.c
@@ -16,6 +16,8 @@
#include "test_util.h"
+#include <stdlib.h>
+
struct pw_test_data_t {
union {
struct pw_request_t request;
@@ -152,13 +154,10 @@ const uint8_t DEFAULT_PCR_DIGEST[] = {
/* Config Variables and defines for Mocks.
*/
-struct tuple MOCK_pw_tuple;
struct pw_long_term_storage_t MOCK_pw_long_term_storage;
struct pw_log_storage_t MOCK_pw_log_storage;
int MOCK_getvar_ret = EC_SUCCESS;
int MOCK_setvar_ret = EC_SUCCESS;
-int MOCK_writevars_ret = EC_SUCCESS;
-void *MOCK_tuple_val_ret;
const uint8_t *MOCK_rand_bytes_src;
size_t MOCK_rand_bytes_offset;
@@ -342,7 +341,6 @@ static void setup_storage(int num_operations)
{
MOCK_getvar_ret = EC_SUCCESS;
MOCK_setvar_ret = EC_SUCCESS;
- MOCK_writevars_ret = EC_SUCCESS;
memset(&MOCK_pw_long_term_storage, 0,
sizeof(MOCK_pw_long_term_storage));
@@ -470,7 +468,6 @@ static void setup_reset_tree_defaults(struct merkle_tree_t *merkle_tree,
MOCK_rand_bytes_len = sizeof(EMPTY_TREE.key_derivation_nonce);
MOCK_appkey_derive_fail = EC_SUCCESS;
MOCK_setvar_ret = EC_SUCCESS;
- MOCK_writevars_ret = EC_SUCCESS;
}
static void setup_insert_leaf_defaults(struct merkle_tree_t *merkle_tree,
@@ -513,7 +510,6 @@ static void setup_insert_leaf_defaults(struct merkle_tree_t *merkle_tree,
MOCK_hmac = DEFAULT_HMAC;
MOCK_aes_fail = 0;
MOCK_setvar_ret = EC_SUCCESS;
- MOCK_writevars_ret = EC_SUCCESS;
}
static void setup_remove_leaf_defaults(struct merkle_tree_t *merkle_tree,
@@ -540,7 +536,6 @@ static void setup_remove_leaf_defaults(struct merkle_tree_t *merkle_tree,
setup_default_empty_path(request->data.remove_leaf.path_hashes);
MOCK_setvar_ret = EC_SUCCESS;
- MOCK_writevars_ret = EC_SUCCESS;
}
static void setup_try_auth_defaults_with_leaf(
@@ -598,7 +593,6 @@ static void setup_try_auth_defaults_with_leaf(
MOCK_hash_update_cb = auth_hash_update_cb;
MOCK_aes_fail = 0;
MOCK_setvar_ret = EC_SUCCESS;
- MOCK_writevars_ret = EC_SUCCESS;
}
static void setup_try_auth_defaults(struct merkle_tree_t *merkle_tree,
@@ -645,7 +639,6 @@ static void setup_reset_auth_defaults(struct merkle_tree_t *merkle_tree,
MOCK_hmac = EMPTY_HMAC; /* Gets overwritten by auth_hash_update_cb. */
MOCK_aes_fail = 0;
MOCK_setvar_ret = EC_SUCCESS;
- MOCK_writevars_ret = EC_SUCCESS;
}
static void setup_get_log_defaults(struct merkle_tree_t *merkle_tree,
@@ -802,33 +795,57 @@ uint8_t get_current_pcr_digest(const uint8_t bitmask[2],
*/
const struct tuple *getvar(const uint8_t *key, uint8_t key_len)
{
+ struct tuple *var = NULL;
+ size_t i;
+
+ const struct {
+ size_t key_len;
+ const void *key;
+ size_t val_size;
+ const void *val;
+ } vars[] = {
+ {sizeof(PW_TREE_VAR) - 1, PW_TREE_VAR,
+ sizeof(MOCK_pw_long_term_storage), &MOCK_pw_long_term_storage},
+ {sizeof(PW_LOG_VAR0) - 1, PW_LOG_VAR0,
+ sizeof(MOCK_pw_log_storage), &MOCK_pw_log_storage},
+ };
+
+ if (!key || !key_len)
+ return NULL;
+
if (MOCK_getvar_ret != EC_SUCCESS)
return NULL;
- MOCK_pw_tuple.flags = 0;
- MOCK_pw_tuple.key_len = key_len;
+ for (i = 0; i < ARRAY_SIZE(vars); i++) {
+ if ((key_len != vars[i].key_len) ||
+ memcmp(key, vars[i].key, key_len)) {
+ continue;
+ }
+ var = malloc(sizeof(struct tuple) + key_len + vars[i].val_size);
+ var->flags = 0;
+ var->val_len = vars[i].val_size;
+ memcpy(var->data_ + var->key_len, vars[i].val, var->val_len);
+ break;
+ }
- if (key_len == (sizeof(PW_TREE_VAR) - 1) &&
- memcmp(key, PW_TREE_VAR, (sizeof(PW_TREE_VAR) - 1)) == 0) {
- MOCK_pw_tuple.val_len = sizeof(MOCK_pw_long_term_storage);
- MOCK_tuple_val_ret = &MOCK_pw_long_term_storage;
- return &MOCK_pw_tuple;
- } else if (key_len == (sizeof(PW_LOG_VAR0) - 1) &&
- memcmp(key, PW_LOG_VAR0, (sizeof(PW_LOG_VAR0) - 1)) == 0) {
- MOCK_pw_tuple.val_len = sizeof(struct pw_log_storage_t);
- MOCK_tuple_val_ret = &MOCK_pw_log_storage;
- return &MOCK_pw_tuple;
- } else
- return NULL;
+ return var;
}
+void freevar(const struct tuple *var)
+{
+ if (!var)
+ return;
+
+ /* This typecast is OK because we know that 'var' came from malloc. */
+ free((void *)var);
+}
const uint8_t *tuple_val(const struct tuple *tpl)
{
- return MOCK_tuple_val_ret;
+ return tpl->data_ + tpl->key_len;
}
-int setvar(const uint8_t *key, uint8_t key_len,
- const uint8_t *val, uint8_t val_len)
+int setvar(const uint8_t *key, uint8_t key_len, const uint8_t *val,
+ uint8_t val_len)
{
if (MOCK_setvar_ret != EC_SUCCESS)
return MOCK_setvar_ret;
@@ -847,11 +864,6 @@ int setvar(const uint8_t *key, uint8_t key_len,
return EC_ERROR_UNKNOWN;
}
-int writevars(void)
-{
- return MOCK_writevars_ret;
-}
-
/******************************************************************************/
/* Mock implementations of TRNG functionality.
*/
@@ -1214,7 +1226,7 @@ static int handle_reset_tree_nv_fail(void)
setup_reset_tree_defaults(&merkle_tree, &buf.request);
- MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH;
+ MOCK_setvar_ret = PW_ERR_NV_LENGTH_MISMATCH;
TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
PW_ERR_NV_LENGTH_MISMATCH);
@@ -1364,7 +1376,7 @@ static int handle_insert_leaf_nv_fail(void)
setup_insert_leaf_defaults(&merkle_tree, &buf.request);
- MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH;
+ MOCK_setvar_ret = PW_ERR_NV_LENGTH_MISMATCH;
TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
PW_ERR_NV_LENGTH_MISMATCH);
@@ -1535,7 +1547,7 @@ static int handle_remove_leaf_nv_fail(void)
setup_remove_leaf_defaults(&merkle_tree, &buf.request);
- MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH;
+ MOCK_setvar_ret = PW_ERR_NV_LENGTH_MISMATCH;
TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
PW_ERR_NV_LENGTH_MISMATCH);
@@ -1789,7 +1801,7 @@ static int handle_try_auth_nv_fail(void)
force_restart_count(0);
force_time((timestamp_t){.val = 65 * SECOND});
- MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH;
+ MOCK_setvar_ret = PW_ERR_NV_LENGTH_MISMATCH;
TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
PW_ERR_NV_LENGTH_MISMATCH);
@@ -2136,7 +2148,7 @@ static int handle_reset_auth_nv_fail(void)
setup_reset_auth_defaults(&merkle_tree, &buf.request);
- MOCK_writevars_ret = PW_ERR_NV_LENGTH_MISMATCH;
+ MOCK_setvar_ret = PW_ERR_NV_LENGTH_MISMATCH;
TEST_RET_EQ(test_handle_short_msg(&merkle_tree, &buf, merkle_tree.root),
PW_ERR_NV_LENGTH_MISMATCH);
diff --git a/test/test_config.h b/test/test_config.h
index 22f497bcc0..afb2350681 100644
--- a/test/test_config.h
+++ b/test/test_config.h
@@ -217,6 +217,93 @@ int ncp15wb_calculate_temp(uint16_t adc);
#define CONFIG_ALS_LIGHTBAR_DIMMING 0
#endif
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+#define CONFIG_USB_PD_PORT_COUNT 1
+#undef CONFIG_USB_PRL_SM
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 3
+#define CONFIG_USB_SM_FRAMEWORK
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H2)
+#define CONFIG_USB_PD_PORT_COUNT 1
+#undef CONFIG_USB_PRL_SM
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 2
+#define CONFIG_USB_SM_FRAMEWORK
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H1)
+#define CONFIG_USB_PD_PORT_COUNT 1
+#undef CONFIG_USB_PRL_SM
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 1
+#define CONFIG_USB_SM_FRAMEWORK
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H0)
+#define CONFIG_USB_PD_PORT_COUNT 1
+#undef CONFIG_USB_PRL_SM
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 0
+#define CONFIG_USB_SM_FRAMEWORK
+#endif
+
+#if defined(TEST_USB_PRL)
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 3
+#define CONFIG_USB_PD_PORT_COUNT 2
+#define CONFIG_USB_SM_FRAMEWORK
+#undef CONFIG_USB_PE_SM
+#undef CONFIG_USB_TYPEC_SM
+#define CONFIG_USB_PRL_SM
+#define CONFIG_USB_PD_TCPC
+#define CONFIG_USB_PD_TCPM_STUB
+#define CONFIG_USB_POWER_DELIVERY
+#define CONFIG_SHA256
+#define CONFIG_SW_CRC
+#endif
+
+#if defined(TEST_USB_TYPEC_VPD) || defined(TEST_USB_TYPEC_CTVPD)
+#if defined(TEST_USB_TYPEC_VPD)
+#define CONFIG_USB_TYPEC_VPD
+#else
+#define CONFIG_USB_TYPEC_CTVPD
+#endif
+#undef CONFIG_SM_NESTING_NUM
+#define CONFIG_SM_NESTING_NUM 3
+
+#define CONFIG_USB_PID 0x5036
+#define VPD_HW_VERSION 0x0001
+#define VPD_FW_VERSION 0x0001
+#define USB_BCD_DEVICE 0
+
+/* Vbus impedance in milliohms */
+#define VPD_VBUS_IMPEDANCE 65
+
+/* GND impedance in milliohms */
+#define VPD_GND_IMPEDANCE 33
+
+#define CONFIG_USB_PD_PORT_COUNT 1
+#define CONFIG_USB_SM_FRAMEWORK
+#define CONFIG_USB_PE_SM
+#define CONFIG_USB_PRL_SM
+#define CONFIG_USB_TYPEC_SM
+#define CONFIG_USB_PD_TCPC
+#define CONFIG_USB_PD_TCPM_STUB
+#define CONFIG_USB_POWER_DELIVERY
+#define CONFIG_SHA256
+#define CONFIG_SW_CRC
+#endif /* TEST_USB_TYPEC_VPD or TEST_USB_TYPEC_CTVPD */
+
#if defined(TEST_USB_PD) || defined(TEST_USB_PD_GIVEBACK) || \
defined(TEST_USB_PD_REV30)
#define CONFIG_USB_POWER_DELIVERY
@@ -257,53 +344,37 @@ int ncp15wb_calculate_temp(uint16_t adc);
#define CONFIG_USB_PD_PORT_COUNT 2
#endif
-#ifdef TEST_NVMEM
+#if defined(TEST_NVMEM) || defined(TEST_NVMEM_VARS)
+#define CONFIG_CRC8
+#define CONFIG_FLASH_ERASED_VALUE32 (-1U)
+#define CONFIG_FLASH_LOG
+#define CONFIG_FLASH_LOG_BASE CONFIG_PROGRAM_MEMORY_BASE
+#define CONFIG_FLASH_LOG_SPACE 0x800
#define CONFIG_FLASH_NVMEM
-#define CONFIG_FLASH_NVMEM_OFFSET_A 0x1000
-#define CONFIG_FLASH_NVMEM_OFFSET_B 0x4000
-#define CONFIG_FLASH_NVMEM_BASE_A (CONFIG_PROGRAM_MEMORY_BASE + \
- CONFIG_FLASH_NVMEM_OFFSET_A)
-#define CONFIG_FLASH_NVMEM_BASE_B (CONFIG_PROGRAM_MEMORY_BASE + \
- CONFIG_FLASH_NVMEM_OFFSET_B)
-#define CONFIG_FLASH_NVMEM_SIZE 0x4000
+#define CONFIG_FLASH_NVMEM_OFFSET_A 0x3d000
+#define CONFIG_FLASH_NVMEM_OFFSET_B 0x7d000
+#define CONFIG_FLASH_NVMEM_BASE_A \
+ (CONFIG_PROGRAM_MEMORY_BASE + CONFIG_FLASH_NVMEM_OFFSET_A)
+#define CONFIG_FLASH_NVMEM_BASE_B \
+ (CONFIG_PROGRAM_MEMORY_BASE + CONFIG_FLASH_NVMEM_OFFSET_B)
+#define CONFIG_FLASH_NEW_NVMEM_BASE_A (CONFIG_FLASH_NVMEM_BASE_A + 0x800)
+#define CONFIG_FLASH_NEW_NVMEM_BASE_B (CONFIG_FLASH_NVMEM_BASE_B + 0x800)
+#define CONFIG_MALLOC
+/* This is legacy NVMEM partition size. */
+#define NVMEM_PARTITION_SIZE 0x3000
+#define NEW_FLASH_HALF_NVMEM_SIZE \
+ (NVMEM_PARTITION_SIZE - CONFIG_FLASH_BANK_SIZE)
+#define NEW_NVMEM_PARTITION_SIZE (NVMEM_PARTITION_SIZE - CONFIG_FLASH_BANK_SIZE)
+#define NEW_NVMEM_TOTAL_PAGES \
+ (2 * NEW_NVMEM_PARTITION_SIZE / CONFIG_FLASH_BANK_SIZE)
#define CONFIG_SW_CRC
-
-#define NVMEM_PARTITION_SIZE \
- (CONFIG_FLASH_NVMEM_SIZE / NVMEM_NUM_PARTITIONS)
-/* User buffer definitions for test purposes */
-#define NVMEM_USER_2_SIZE 0x201
-#define NVMEM_USER_1_SIZE 0x402
-#define NVMEM_USER_0_SIZE (NVMEM_PARTITION_SIZE - \
- NVMEM_USER_2_SIZE - NVMEM_USER_1_SIZE - \
- sizeof(struct nvmem_tag))
+#define CONFIG_FLASH_NVMEM_VARS
#ifndef __ASSEMBLER__
-enum nvmem_users {
- NVMEM_USER_0,
- NVMEM_USER_1,
- NVMEM_USER_2,
- NVMEM_NUM_USERS
-};
+enum nvmem_users { NVMEM_TPM = 0, NVMEM_CR50, NVMEM_NUM_USERS };
#endif
#endif
-#ifdef TEST_NVMEM_VARS
-#define NVMEM_PARTITION_SIZE 0x3000
-#define CONFIG_FLASH_NVMEM_VARS
-#ifndef __ASSEMBLER__
-/* Define the user region numbers */
-enum nvmem_users {
- CONFIG_FLASH_NVMEM_VARS_USER_NUM,
- NVMEM_NUM_USERS
-};
-/* Define a test var. */
-enum nvmem_vars {
- NVMEM_VAR_TEST_VAR,
-};
-#endif
-#define CONFIG_FLASH_NVMEM_VARS_USER_SIZE 600
-#endif /* TEST_NVMEM_VARS */
-
#ifdef TEST_PINWEAVER
#define CONFIG_DCRYPTO_MOCK
#define CONFIG_PINWEAVER
diff --git a/test/usb_pd_test_util.h b/test/usb_pd_test_util.h
index 561033d8fa..02fae22b41 100644
--- a/test/usb_pd_test_util.h
+++ b/test/usb_pd_test_util.h
@@ -13,6 +13,7 @@ void pd_test_rx_set_preamble(int port, int has_preamble);
void pd_test_rx_msg_append_bits(int port, uint32_t bits, int nb);
void pd_test_rx_msg_append_kcode(int port, uint8_t kcode);
void pd_test_rx_msg_append_sop(int port);
+void pd_test_rx_msg_append_sop_prime(int port);
void pd_test_rx_msg_append_eop(int port);
void pd_test_rx_msg_append_last_edge(int port);
void pd_test_rx_msg_append_4b(int port, uint8_t val);
@@ -23,6 +24,7 @@ void pd_simulate_rx(int port);
/* Verify Tx message */
int pd_test_tx_msg_verify_kcode(int port, uint8_t kcode);
int pd_test_tx_msg_verify_sop(int port);
+int pd_test_tx_msg_verify_sop_prime(int port);
int pd_test_tx_msg_verify_eop(int port);
int pd_test_tx_msg_verify_4b5b(int port, uint8_t b4);
int pd_test_tx_msg_verify_short(int port, uint16_t val);
diff --git a/test/usb_prl.c b/test/usb_prl.c
new file mode 100644
index 0000000000..ee15143986
--- /dev/null
+++ b/test/usb_prl.c
@@ -0,0 +1,1306 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test USB Protocol Layer module.
+ */
+#include "common.h"
+#include "crc.h"
+#include "task.h"
+#include "test_util.h"
+#include "timer.h"
+#include "tcpm.h"
+#include "usb_emsg.h"
+#include "usb_pe_sm.h"
+#include "usb_pd.h"
+#include "usb_pd_test_util.h"
+#include "usb_prl_sm.h"
+#include "util.h"
+
+#define PORT0 0
+#define PORT1 1
+
+static uint32_t test_data[] = {
+ 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f,
+ 0x10111213, 0x14151617, 0x1819a0b0, 0xc0d0e0f0,
+ 0x20212223, 0x24252627, 0x28292a2b, 0x2c2d2e2f,
+ 0x30313233, 0x34353637, 0x38393a3b, 0x3c3d3e3f,
+ 0x40414243, 0x44454647, 0x48494a4b, 0x4c4d4e4f,
+ 0x50515253, 0x54555657, 0x58595a5b, 0x5c5d5e5f,
+ 0x60616263, 0x64656667, 0x68696a6b, 0x6c6d6e6f,
+ 0x70717273, 0x74757677, 0x78797a7b, 0x7c7d7e7f,
+ 0x80818283, 0x84858687, 0x88898a8b, 0x8c8d8e8f,
+ 0x90919293, 0x94959697, 0x98999a9b, 0x9c9d9e9f,
+ 0xa0a1a2a3, 0xa4a5a6a7, 0xa8a9aaab, 0xacadaeaf,
+ 0xb0b1b2b3, 0xb4b5b6b7, 0xb8b9babb, 0xbcbdbebf,
+ 0xc0c1c2c3, 0xc4c5c6c7, 0xc8c9cacb, 0xcccdcecf,
+ 0xd0d1d2d3, 0xd4d5d6d7, 0xd8d9dadb, 0xdcdddedf,
+ 0xe0e1e2e3, 0xe4e5e6e7, 0xe8e9eaeb, 0xecedeeef,
+ 0xf0f1f2f3, 0xf4f5f6f7, 0xf8f9fafb, 0xfcfdfeff,
+ 0x11223344
+};
+
+static struct pd_prl {
+ int rev;
+ int pd_enable;
+ int power_role;
+ int data_role;
+ int msg_tx_id;
+ int msg_rx_id;
+
+ int mock_pe_message_sent;
+ int mock_pe_error;
+ int mock_pe_hard_reset_sent;
+ int mock_pe_got_hard_reset;
+ int mock_pe_pass_up_message;
+ int mock_got_soft_reset;
+} pd_port[CONFIG_USB_PD_PORT_COUNT];
+
+static void init_port(int port, int rev)
+{
+ pd_port[port].rev = rev;
+ pd_port[port].pd_enable = 0;
+ pd_port[port].power_role = PD_ROLE_SINK;
+ pd_port[port].data_role = PD_ROLE_UFP;
+ pd_port[port].msg_tx_id = 0;
+ pd_port[port].msg_rx_id = 0;
+ tcpm_init(port);
+ tcpm_set_polarity(port, 0);
+ tcpm_set_rx_enable(port, 0);
+}
+
+void inc_tx_id(int port)
+{
+ pd_port[port].msg_tx_id = (pd_port[port].msg_tx_id + 1) & 7;
+}
+
+void inc_rx_id(int port)
+{
+ pd_port[port].msg_rx_id = (pd_port[port].msg_rx_id + 1) % 7;
+}
+
+static int verify_goodcrc(int port, int role, int id)
+{
+ return pd_test_tx_msg_verify_sop(port) &&
+ pd_test_tx_msg_verify_short(port, PD_HEADER(PD_CTRL_GOOD_CRC,
+ role, role, id, 0, 0, 0)) &&
+ pd_test_tx_msg_verify_crc(port) &&
+ pd_test_tx_msg_verify_eop(port);
+}
+
+static void simulate_rx_msg(int port, uint16_t header, int cnt,
+ const uint32_t *data)
+{
+ int i;
+
+ pd_test_rx_set_preamble(port, 1);
+ pd_test_rx_msg_append_sop(port);
+ pd_test_rx_msg_append_short(port, header);
+
+ crc32_init();
+ crc32_hash16(header);
+
+ for (i = 0; i < cnt; ++i) {
+ pd_test_rx_msg_append_word(port, data[i]);
+ crc32_hash32(data[i]);
+ }
+
+ pd_test_rx_msg_append_word(port, crc32_result());
+
+ pd_test_rx_msg_append_eop(port);
+ pd_test_rx_msg_append_last_edge(port);
+
+ pd_simulate_rx(port);
+}
+
+static void simulate_goodcrc(int port, int role, int id)
+{
+ simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0,
+ pd_port[port].rev, 0), 0, NULL);
+}
+
+static void cycle_through_state_machine(int port, uint32_t num, uint32_t time)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(time);
+ }
+}
+
+static int simulate_request_chunk(int port, enum pd_data_msg_type msg_type,
+ int chunk_num, int len)
+{
+ uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_rx_id,
+ 1, pd_port[port].rev, 1);
+ uint32_t msg = PD_EXT_HEADER(chunk_num, 1, len);
+
+ simulate_rx_msg(port, header, 1, (const uint32_t *)&msg);
+ task_wait_event(30 * MSEC);
+
+ if (!verify_goodcrc(port, pd_port[port].data_role,
+ pd_port[port].msg_rx_id))
+ return 0;
+
+ return 1;
+}
+
+static int simulate_receive_ctrl_msg(int port, enum pd_ctrl_msg_type msg_type)
+{
+ uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ simulate_rx_msg(port, header, 0, NULL);
+ task_wait_event(30 * MSEC);
+
+ if (!verify_goodcrc(port, pd_port[port].data_role,
+ pd_port[port].msg_rx_id))
+ return 0;
+
+ return 1;
+}
+
+static int verify_data_reception(int port, uint16_t header, int len)
+{
+ int i;
+ int cnt = (len + 3) & ~3;
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ if (pd_port[port].mock_pe_error >= 0)
+ return 0;
+
+ if (!pd_port[port].mock_pe_pass_up_message)
+ return 0;
+
+ if (emsg[port].header != header)
+ return 0;
+
+ if (emsg[port].len != cnt)
+ return 0;
+
+ for (i = 0; i < cnt; i++) {
+ if (i < len) {
+ if (emsg[port].buf[i] !=
+ *((unsigned char *)test_data + i))
+ return 0;
+ } else {
+ if (emsg[port].buf[i] != 0)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int verify_chunk_data_reception(int port, uint16_t header, int len)
+{
+ int i;
+ uint8_t *td = (uint8_t *)test_data;
+
+ if (pd_port[port].mock_got_soft_reset)
+ return 0;
+
+ if (!pd_port[port].mock_pe_pass_up_message)
+ return 0;
+
+ if (pd_port[port].mock_pe_error >= 0)
+ return 0;
+
+ if (emsg[port].len != len)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ if (emsg[port].buf[i] != td[i])
+ return 0;
+
+ return 1;
+}
+
+static int simulate_receive_data(int port, enum pd_data_msg_type msg_type,
+ int len)
+{
+ int i;
+ int nw = (len + 3) >> 2;
+ uint8_t td[28];
+ uint16_t header = PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_rx_id,
+ nw, pd_port[port].rev, 0);
+
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_pass_up_message = 0;
+ emsg[port].header = 0;
+ emsg[port].len = 0;
+ memset(emsg[port].buf, 0, 260);
+
+ for (i = 0; i < 28; i++) {
+ if (i < len)
+ td[i] = *((uint8_t *)test_data + i);
+ else
+ td[i] = 0;
+ }
+
+ simulate_rx_msg(port, header, nw, (uint32_t *)td);
+ task_wait_event(30 * MSEC);
+
+ if (!verify_goodcrc(port, pd_port[port].data_role,
+ pd_port[port].msg_rx_id))
+ return 0;
+
+ inc_rx_id(port);
+
+ return verify_data_reception(port, header, len);
+}
+
+static int simulate_receive_extended_data(int port,
+ enum pd_data_msg_type msg_type, int len)
+{
+ int i;
+ int j;
+ int byte_len;
+ int nw;
+ int dsize;
+ uint8_t td[28];
+ int chunk_num = 0;
+ int data_offset = 0;
+ uint8_t *expected_data = (uint8_t *)test_data;
+ uint16_t header;
+ int req_timeout;
+
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_pass_up_message = 0;
+ emsg[port].header = 0;
+ emsg[port].len = 0;
+ memset(emsg[port].buf, 0, 260);
+
+ dsize = len;
+
+ cycle_through_state_machine(port, 2, 40 * MSEC);
+
+ for (j = 0; j < 10; j++) {
+ byte_len = len;
+ if (byte_len > 26)
+ byte_len = 26;
+
+ len -= 26;
+
+ memset(td, 0, 28);
+ *(uint16_t *)td = PD_EXT_HEADER(chunk_num, 0, dsize);
+
+ for (i = 0; i < byte_len; i++)
+ td[i + 2] = *(expected_data + data_offset++);
+
+ nw = (byte_len + 2 + 3) >> 2;
+ header = PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_rx_id,
+ nw, pd_port[port].rev, 1);
+
+ cycle_through_state_machine(port, 2, 40 * MSEC);
+
+ if (pd_port[port].mock_pe_error >= 0)
+ return 0;
+
+ if (pd_port[port].mock_pe_pass_up_message)
+ return 0;
+
+ if (emsg[port].len != 0)
+ return 0;
+
+ simulate_rx_msg(port, header, nw, (uint32_t *)td);
+ task_wait_event(40 * MSEC);
+
+ if (!verify_goodcrc(port, pd_port[port].data_role,
+ pd_port[port].msg_rx_id))
+ return 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40);
+ inc_rx_id(port);
+
+ /*
+ * If no more data, do expected to get a chunk request
+ */
+ if (len <= 0)
+ break;
+
+ /*
+ * Wait for request chunk message
+ */
+ req_timeout = 0;
+ while (get_rch_state_id(port) != RCH_REQUESTING_CHUNK &&
+ req_timeout < 5) {
+ req_timeout++;
+ msleep(2);
+ }
+
+ chunk_num++;
+
+ /* Test Request next chunk packet */
+ if (!pd_test_tx_msg_verify_sop(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_short(port,
+ PD_HEADER(msg_type,
+ pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_tx_id,
+ 1, pd_port[port].rev, 1)))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_word(port,
+ PD_EXT_HEADER(chunk_num, 1, 0)))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_crc(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_eop(port))
+ return 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Request next chunk packet was good. Send GoodCRC */
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+ inc_tx_id(port);
+ }
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(20 * MSEC);
+
+ return verify_chunk_data_reception(port, header, dsize);
+}
+
+static int verify_ctrl_msg_transmission(int port,
+ enum pd_ctrl_msg_type msg_type)
+{
+ if (!pd_test_tx_msg_verify_sop(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_short(port,
+ PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_tx_id, 0,
+ pd_port[port].rev, 0)))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_crc(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_eop(port))
+ return 0;
+
+ return 1;
+}
+
+static int simulate_send_ctrl_msg_request_from_pe(int port,
+ enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type)
+{
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_message_sent = 0;
+ prl_send_ctrl_msg(port, type, msg_type);
+ task_wait_event(40 * MSEC);
+
+ if (msg_type == PD_CTRL_SOFT_RESET)
+ cycle_through_state_machine(port, 1, 20 * MSEC);
+
+ return verify_ctrl_msg_transmission(port, msg_type);
+}
+
+static int verify_data_msg_transmission(int port,
+ enum pd_data_msg_type msg_type, int len)
+{
+ int i;
+ int num_words = (len + 3) >> 2;
+ int data_obj_in_bytes;
+ uint32_t td;
+
+ if (!pd_test_tx_msg_verify_sop(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_short(port,
+ PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role, pd_port[port].msg_tx_id,
+ num_words, pd_port[port].rev, 0)))
+ return 0;
+
+ for (i = 0; i < num_words; i++) {
+ td = test_data[i];
+ data_obj_in_bytes = (i + 1) * 4;
+ if (data_obj_in_bytes > len) {
+ switch (data_obj_in_bytes - len) {
+ case 1:
+ td &= 0x00ffffff;
+ break;
+ case 2:
+ td &= 0x0000ffff;
+ break;
+ case 3:
+ td &= 0x000000ff;
+ break;
+ }
+ }
+
+ if (!pd_test_tx_msg_verify_word(port, td))
+ return 0;
+ }
+
+ if (!pd_test_tx_msg_verify_crc(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_eop(port))
+ return 0;
+
+ return 1;
+}
+
+static int simulate_send_data_msg_request_from_pe(int port,
+ enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type, int len)
+{
+ int i;
+ uint8_t *buf = emsg[port].buf;
+ uint8_t *td = (uint8_t *)test_data;
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_message_sent = 0;
+
+ for (i = 0; i < len; i++)
+ buf[i] = td[i];
+
+ emsg[port].len = len;
+
+ prl_send_data_msg(port, type, msg_type);
+ task_wait_event(30 * MSEC);
+
+ return verify_data_msg_transmission(port, msg_type, len);
+}
+
+static int verify_extended_data_msg_transmission(int port,
+ enum pd_data_msg_type msg_type, int len)
+{
+ int i;
+ int j;
+ int nw;
+ int byte_len;
+ int dsize;
+ uint32_t td;
+ uint8_t *expected_data = (uint8_t *)&test_data;
+ int data_offset = 0;
+ int chunk_number_to_send = 0;
+
+ dsize = len;
+
+ for (j = 0; j < 10; j++) {
+ byte_len = len;
+ if (byte_len > 26)
+ byte_len = 26;
+
+ nw = (byte_len + 2 + 3) >> 2;
+
+ if (!pd_test_tx_msg_verify_sop(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_short(port,
+ PD_HEADER(msg_type, pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_tx_id,
+ nw, pd_port[port].rev, 1)))
+ return 0;
+ td = PD_EXT_HEADER(chunk_number_to_send, 0, dsize);
+ td |= *(expected_data + data_offset++) << 16;
+ td |= *(expected_data + data_offset++) << 24;
+
+ if (byte_len == 1)
+ td &= 0x00ffffff;
+
+ if (!pd_test_tx_msg_verify_word(port, td))
+ return 0;
+
+ byte_len -= 2;
+
+ if (byte_len > 0) {
+ nw = (byte_len + 3) >> 2;
+ for (i = 0; i < nw; i++) {
+ td = *(expected_data + data_offset++) << 0;
+ td |= *(expected_data + data_offset++) << 8;
+ td |= *(expected_data + data_offset++) << 16;
+ td |= *(expected_data + data_offset++) << 24;
+
+ switch (byte_len) {
+ case 3:
+ td &= 0x00ffffff;
+ break;
+ case 2:
+ td &= 0x0000ffff;
+ break;
+ case 1:
+ td &= 0x000000ff;
+ break;
+ }
+
+ if (!pd_test_tx_msg_verify_word(port, td))
+ return 0;
+ byte_len -= 4;
+ }
+ }
+
+ if (!pd_test_tx_msg_verify_crc(port))
+ return 0;
+
+ if (!pd_test_tx_msg_verify_eop(port))
+ return 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(10 * MSEC);
+
+ /* Send GoodCRC */
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ len -= 26;
+ if (len <= 0)
+ break;
+
+ chunk_number_to_send++;
+ cycle_through_state_machine(port, 4, 10 * MSEC);
+ if (!simulate_request_chunk(port, msg_type,
+ chunk_number_to_send, dsize))
+ return 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+ }
+
+ return 1;
+}
+
+static int simulate_send_extended_data_msg(int port,
+ enum tcpm_transmit_type type, enum pd_ctrl_msg_type msg_type,
+ int len)
+{
+ int i;
+ uint8_t *buf = emsg[port].buf;
+ uint8_t *td = (uint8_t *)test_data;
+
+ memset(buf, 0, 260);
+ emsg[port].len = len;
+
+ /* don't overflow buffer */
+ if (len > 260)
+ len = 260;
+
+ for (i = 0; i < len; i++)
+ buf[i] = td[i];
+
+ prl_send_ext_data_msg(port, type, msg_type);
+ task_wait_event(30 * MSEC);
+
+ return verify_extended_data_msg_transmission(port, msg_type,
+ len);
+}
+
+static void enable_prl(int port, int en)
+{
+ tcpm_set_rx_enable(port, en);
+
+ pd_port[port].pd_enable = en;
+ pd_port[port].msg_tx_id = 0;
+ pd_port[port].msg_rx_id = 0;
+
+ /* Init PRL */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(10 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(10 * MSEC);
+
+ prl_set_rev(port, pd_port[port].rev);
+}
+
+int tc_get_power_role(int port)
+{
+ return pd_port[port].power_role;
+}
+
+int tc_get_data_role(int port)
+{
+ return pd_port[port].data_role;
+}
+
+void pe_report_error(int port, enum pe_error e)
+{
+ pd_port[port].mock_pe_error = e;
+}
+
+void pe_got_hard_reset(int port)
+{
+ pd_port[port].mock_pe_got_hard_reset = 1;
+}
+
+void pe_pass_up_message(int port)
+{
+ pd_port[port].mock_pe_pass_up_message = 1;
+}
+
+void pe_message_sent(int port)
+{
+ pd_port[port].mock_pe_message_sent = 1;
+}
+
+void pe_hard_reset_sent(int port)
+{
+ pd_port[port].mock_pe_hard_reset_sent = 1;
+}
+
+void pe_got_soft_reset(int port)
+{
+ pd_port[port].mock_got_soft_reset = 1;
+}
+
+static int test_initial_states(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+ TEST_ASSERT(get_tch_state_id(port) ==
+ TCH_WAIT_FOR_MESSAGE_REQUEST_FROM_PE);
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_REQUEST);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_ctrl_msg(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Control message transmission and tx_id increment
+ */
+ for (i = 0; i < 10; i++) {
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_CTRL_ACCEPT));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ inc_tx_id(port);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_ctrl_msg_with_retry_and_fail(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Control message transmission fail with retry
+ */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_CTRL_ACCEPT));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+
+ /* Do not increment tx_id so phy layer will not transmit message */
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ pd_port[port].mock_pe_message_sent = 0;
+ prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+ task_wait_event(30 * MSEC);
+
+ for (i = 0; i < N_RETRY_COUNT + 1; i++) {
+ cycle_through_state_machine(port, 10, 10 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(PD_T_TCPC_TX_TIMEOUT);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent == 0);
+ if (i == N_RETRY_COUNT)
+ TEST_ASSERT(pd_port[port].mock_pe_error ==
+ ERR_TCH_XMIT);
+ else
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_ctrl_msg_with_retry_and_success(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Control message transmission fail with retry
+ */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_message_sent = 0;
+
+ prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+ task_wait_event(40 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+
+ /* Do not increment tx_id. */
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ pd_port[port].mock_pe_message_sent = 0;
+ prl_send_ctrl_msg(port, TCPC_TX_SOP, PD_CTRL_ACCEPT);
+ task_wait_event(30 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ for (i = 0; i < N_RETRY_COUNT + 1; i++) {
+ if (i == N_RETRY_COUNT)
+ inc_tx_id(port);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+
+ cycle_through_state_machine(port, 8, 10 * MSEC);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(PD_T_TCPC_TX_TIMEOUT);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ if (i == N_RETRY_COUNT)
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ else
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent == 0);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_data_msg(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Sending data message with 1 to 28 bytes
+ */
+ for (i = 1; i <= 28; i++) {
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_data_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_DATA_SOURCE_CAP, i));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ inc_tx_id(port);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_data_msg_to_much_data(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Send data message with more than 28-bytes, should fail
+ */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ /* Try to send 29-bytes */
+ TEST_ASSERT(!simulate_send_data_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_DATA_SOURCE_CAP, 29));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(!pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error = ERR_TCH_XMIT);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_extended_data_msg(void)
+{
+ int i;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Sending extended data message with 29 to 260 bytes
+ */
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+
+ for (i = 29; i <= 260; i++) {
+ pd_port[port].mock_pe_message_sent = 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_extended_data_msg(
+ port, TCPC_TX_SOP, PD_EXT_MANUFACTURER_INFO, i));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ }
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_receive_soft_reset_msg(void)
+{
+ int port = PORT0;
+ int expected_header = PD_HEADER(PD_CTRL_SOFT_RESET,
+ pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Receiving Soft Reset
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_pass_up_message = 0;
+
+ TEST_ASSERT(simulate_receive_ctrl_msg(port, PD_CTRL_SOFT_RESET));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ TEST_ASSERT(pd_port[port].mock_pe_pass_up_message);
+ TEST_ASSERT(expected_header == emsg[port].header);
+ TEST_ASSERT(emsg[port].len == 0);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_receive_control_msg(void)
+{
+ int port = PORT0;
+ int expected_header = PD_HEADER(PD_CTRL_DR_SWAP,
+ pd_port[port].power_role,
+ pd_port[port].data_role,
+ pd_port[port].msg_rx_id,
+ 0, pd_port[port].rev, 0);
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Receiving a control message
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+
+ pd_port[port].mock_got_soft_reset = 0;
+ pd_port[port].mock_pe_error = -1;
+ pd_port[port].mock_pe_pass_up_message = 0;
+
+ TEST_ASSERT(simulate_receive_ctrl_msg(port, PD_CTRL_DR_SWAP));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+ TEST_ASSERT(pd_port[port].mock_pe_pass_up_message);
+ TEST_ASSERT(expected_header == emsg[port].header);
+ TEST_ASSERT(emsg[port].len == 0);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_receive_data_msg(void)
+{
+ int port = PORT0;
+ int i;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Receiving data message with 1 to 28 bytes
+ */
+
+ for (i = 1; i <= 28; i++) {
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+ TEST_ASSERT(simulate_receive_data(port,
+ PD_DATA_BATTERY_STATUS, i));
+ }
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_receive_extended_data_msg(void)
+{
+ int len;
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Receiving extended data message with 29 to 260 bytes
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_rch_state_id(port) ==
+ RCH_WAIT_FOR_MESSAGE_FROM_PROTOCOL_LAYER);
+
+ for (len = 29; len <= 260; len++)
+ TEST_ASSERT(simulate_receive_extended_data(port,
+ PD_DATA_BATTERY_STATUS, len));
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_send_soft_reset_msg(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Send soft reset
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ TEST_ASSERT(simulate_send_ctrl_msg_request_from_pe(port,
+ TCPC_TX_SOP, PD_CTRL_SOFT_RESET));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ simulate_goodcrc(port, pd_port[port].power_role,
+ pd_port[port].msg_tx_id);
+ inc_tx_id(port);
+
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_LAYER_RESET_FOR_TRANSMIT);
+
+ cycle_through_state_machine(port, 3, 10 * MSEC);
+
+ TEST_ASSERT(!pd_port[port].mock_got_soft_reset);
+ TEST_ASSERT(pd_port[port].mock_pe_message_sent);
+ TEST_ASSERT(pd_port[port].mock_pe_error < 0);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_pe_execute_hard_reset_msg(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ pd_port[port].mock_pe_hard_reset_sent = 0;
+
+ /*
+ * TEST: Policy Engine initiated hard reset
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST);
+
+ /* Simulate receiving hard reset from policy engine */
+ prl_execute_hard_reset(port);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_RESET_LAYER);
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ cycle_through_state_machine(port, 1, 10 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_PHY_HARD_RESET_COMPLETE);
+
+ cycle_through_state_machine(port, 2, PD_T_PS_HARD_RESET);
+ TEST_ASSERT(pd_port[port].mock_pe_hard_reset_sent);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE);
+
+ /* Simulate policy engine indicating that it is done hard reset */
+ prl_hard_reset_complete(port);
+
+ cycle_through_state_machine(port, 1, 10 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+static int test_phy_execute_hard_reset_msg(void)
+{
+ int port = PORT0;
+
+ enable_prl(port, 1);
+
+ /*
+ * TEST: Port partner initiated hard reset
+ */
+
+ pd_port[port].mock_pe_got_hard_reset = 0;
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST);
+
+ /* Simulate receiving hard reset from port partner */
+ pd_execute_hard_reset(port);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_RESET_LAYER);
+ TEST_ASSERT(get_prl_tx_state_id(port) ==
+ PRL_TX_WAIT_FOR_MESSAGE_REQUEST);
+
+ cycle_through_state_machine(port, 1, 10 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE);
+
+ cycle_through_state_machine(port, 2, PD_T_PS_HARD_RESET);
+ TEST_ASSERT(pd_port[port].mock_pe_got_hard_reset);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) ==
+ PRL_HR_WAIT_FOR_PE_HARD_RESET_COMPLETE);
+
+ /* Simulate policy engine indicating that it is done hard reset */
+ prl_hard_reset_complete(port);
+
+ cycle_through_state_machine(port, 1, 10 * MSEC);
+
+ TEST_ASSERT(get_prl_hr_state_id(port) == PRL_HR_WAIT_FOR_REQUEST);
+
+ enable_prl(port, 0);
+
+ return EC_SUCCESS;
+}
+
+int pd_task(void *u)
+{
+ int port = PORT0;
+ int evt;
+
+ while (1) {
+ evt = task_wait_event(-1);
+
+ tcpc_run(port, evt);
+ protocol_layer(port, evt, pd_port[port].pd_enable);
+ }
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+
+ /* Test PD 2.0 Protocol */
+ init_port(PORT0, PD_REV20);
+ RUN_TEST(test_initial_states);
+ RUN_TEST(test_send_ctrl_msg);
+ RUN_TEST(test_send_ctrl_msg_with_retry_and_fail);
+ RUN_TEST(test_send_ctrl_msg_with_retry_and_success);
+ RUN_TEST(test_send_data_msg);
+ RUN_TEST(test_send_data_msg_to_much_data);
+ RUN_TEST(test_receive_control_msg);
+ RUN_TEST(test_receive_data_msg);
+ RUN_TEST(test_receive_soft_reset_msg);
+ RUN_TEST(test_send_soft_reset_msg);
+ RUN_TEST(test_pe_execute_hard_reset_msg);
+ RUN_TEST(test_phy_execute_hard_reset_msg);
+
+ /* TODO(shurst): More PD 2.0 Tests */
+
+ /* Test PD 3.0 Protocol */
+ init_port(PORT0, PD_REV30);
+ RUN_TEST(test_initial_states);
+ RUN_TEST(test_send_ctrl_msg);
+ RUN_TEST(test_send_ctrl_msg_with_retry_and_fail);
+ RUN_TEST(test_send_ctrl_msg_with_retry_and_success);
+ RUN_TEST(test_send_data_msg);
+ RUN_TEST(test_send_data_msg_to_much_data);
+ RUN_TEST(test_send_extended_data_msg);
+ RUN_TEST(test_receive_control_msg);
+ RUN_TEST(test_receive_data_msg);
+ RUN_TEST(test_receive_extended_data_msg);
+ RUN_TEST(test_receive_soft_reset_msg);
+ RUN_TEST(test_send_soft_reset_msg);
+ RUN_TEST(test_pe_execute_hard_reset_msg);
+ RUN_TEST(test_phy_execute_hard_reset_msg);
+
+ /* TODO(shurst): More PD 3.0 Tests */
+
+ test_print_result();
+}
+
diff --git a/test/usb_prl.tasklist b/test/usb_prl.tasklist
new file mode 100644
index 0000000000..88eca7d9b7
--- /dev/null
+++ b/test/usb_prl.tasklist
@@ -0,0 +1,19 @@
+/* Copyright (c) 2019 The Chromium OS 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_TEST(n, r, d, s) 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
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST \
+ TASK_TEST(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE) \
+ TASK_TEST(PD_C1, pd_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/test/usb_sm_framework_h0.tasklist b/test/usb_sm_framework_h0.tasklist
new file mode 120000
index 0000000000..b55922b1ee
--- /dev/null
+++ b/test/usb_sm_framework_h0.tasklist
@@ -0,0 +1 @@
+usb_sm_framework_h3.tasklist \ No newline at end of file
diff --git a/test/usb_sm_framework_h1.tasklist b/test/usb_sm_framework_h1.tasklist
new file mode 120000
index 0000000000..b55922b1ee
--- /dev/null
+++ b/test/usb_sm_framework_h1.tasklist
@@ -0,0 +1 @@
+usb_sm_framework_h3.tasklist \ No newline at end of file
diff --git a/test/usb_sm_framework_h2.tasklist b/test/usb_sm_framework_h2.tasklist
new file mode 120000
index 0000000000..b55922b1ee
--- /dev/null
+++ b/test/usb_sm_framework_h2.tasklist
@@ -0,0 +1 @@
+usb_sm_framework_h3.tasklist \ No newline at end of file
diff --git a/test/usb_sm_framework_h3.c b/test/usb_sm_framework_h3.c
new file mode 100644
index 0000000000..db88338387
--- /dev/null
+++ b/test/usb_sm_framework_h3.c
@@ -0,0 +1,1219 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test USB Type-C VPD and CTVPD module.
+ */
+#include "common.h"
+#include "task.h"
+#include "test_util.h"
+#include "timer.h"
+#include "usb_pd.h"
+#include "usb_sm.h"
+#include "usb_tc_sm.h"
+#include "util.h"
+#include "usb_pd_test_util.h"
+#include "vpd_api.h"
+
+/*
+ * Test State Hierarchy
+ * SM_TEST_A4 transitions to SM_TEST_B4
+ * SM_TEST_B4 transitions to SM_TEST_B5
+ * SM_TEST_B5 transitions to SM_TEST_B6
+ * SM_TEST_B6 transitions to SM_TEST_C
+ * SM_TEST_C transitions to SM_TEST_A7
+ * SM_TEST_A7 transitions to SM_TEST_A6
+ * SM_TEST_A6 transitions to SM_TEST_A5
+ * SM_TEST_A5 transitions to SM_TEST_A4
+ *
+ * --------------------------- ---------------------------
+ * | SM_TEST_SUPER_A1 | | SM_TEST_SUPER_B1 |
+ * | ----------------------- | | ----------------------- |
+ * | | SM_TEST_SUPER_A2 | | | | SM_TEST_SUPER_B2 | |
+ * | | ------------------- | | | | ------------------- | |
+ * | | |SM_TEST_SUPER_A3 | | | | | |SM_TEST_SUPER_B3 | | |
+ * | | | | | | | | | | | |
+ * | | | ------------- | | | | | | ------------- | | |
+ * | | | | SM_TEST_A4|------------------>| SM_TEST_B4| | | |
+ * | | | ------------- | | | | | | ------------- | | |
+ * | | | ^ | | | | | |--------|--------| | |
+ * | | | | | | | | | | | |
+ * | | | -------------- | | | | | \/ | |
+ * | | | | SM_TEST_A5 | | | | | | -------------- | |
+ * | | | -------------- | | | | | | SM_TEST_B5 | | |
+ * | | |--------^--------| | | | | -------------- | |
+ * | | | | | | | | | |
+ * | | -------------- | | | -----------|----------- |
+ * | | | SM_TEST_A6 | | | | \/ |
+ * | | -------------- | | | -------------- |
+ * | |----------^----------| | | | SM_TEST_B6 | |
+ * | | | | -------------- |
+ * | -------------- | |--------/----------------|
+ * | | SM_TEST_A7 | | /
+ * | -------------- | /
+ * |------------------^------| /
+ * \ /
+ * \ \/
+ * -------------
+ * | SM_TEST_C |
+ * -------------
+ *
+ * test_hierarchy_0: Tests a flat state machine without super states
+ * test_hierarchy_1: Tests a hierarchical state machine with 1 super state
+ * test_hierarchy_2: Tests a hierarchical state machine with 2 super states
+ * test_hierarchy_3: Tests a hierarchical state machine with 3 super states
+ *
+ */
+
+#define SEQUENCE_SIZE 55
+
+enum state_id {
+ ENTER_A1 = 1,
+ RUN_A1,
+ EXIT_A1,
+ ENTER_A2,
+ RUN_A2,
+ EXIT_A2,
+ ENTER_A3,
+ RUN_A3,
+ EXIT_A3,
+ ENTER_A4,
+ RUN_A4,
+ EXIT_A4,
+ ENTER_A5,
+ RUN_A5,
+ EXIT_A5,
+ ENTER_A6,
+ RUN_A6,
+ EXIT_A6,
+ ENTER_A7,
+ RUN_A7,
+ EXIT_A7,
+ ENTER_B1,
+ RUN_B1,
+ EXIT_B1,
+ ENTER_B2,
+ RUN_B2,
+ EXIT_B2,
+ ENTER_B3,
+ RUN_B3,
+ EXIT_B3,
+ ENTER_B4,
+ RUN_B4,
+ EXIT_B4,
+ ENTER_B5,
+ RUN_B5,
+ EXIT_B5,
+ ENTER_B6,
+ RUN_B6,
+ EXIT_B6,
+ ENTER_C,
+ RUN_C,
+ EXIT_C,
+};
+
+#define PORT0 0
+#define TSM_OBJ(port) (SM_OBJ(sm[port]))
+
+struct sm_ {
+ /* struct sm_obj must be first */
+ struct sm_obj obj;
+ int sv_tmp;
+ int idx;
+ int seq[55];
+} sm[1];
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+static unsigned int sm_test_super_A1(int port, enum signal sig);
+static unsigned int sm_test_super_A1_entry(int port);
+static unsigned int sm_test_super_A1_run(int port);
+static unsigned int sm_test_super_A1_exit(int port);
+
+static unsigned int sm_test_super_B1(int port, enum signal sig);
+static unsigned int sm_test_super_B1_entry(int port);
+static unsigned int sm_test_super_B1_run(int port);
+static unsigned int sm_test_super_B1_exit(int port);
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+static unsigned int sm_test_super_A2(int port, enum signal sig);
+static unsigned int sm_test_super_A2_entry(int port);
+static unsigned int sm_test_super_A2_run(int port);
+static unsigned int sm_test_super_A2_exit(int port);
+
+static unsigned int sm_test_super_B2(int port, enum signal sig);
+static unsigned int sm_test_super_B2_entry(int port);
+static unsigned int sm_test_super_B2_run(int port);
+static unsigned int sm_test_super_B2_exit(int port);
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+static unsigned int sm_test_super_A3(int port, enum signal sig);
+static unsigned int sm_test_super_A3_entry(int port);
+static unsigned int sm_test_super_A3_run(int port);
+static unsigned int sm_test_super_A3_exit(int port);
+
+static unsigned int sm_test_super_B3(int port, enum signal sig);
+static unsigned int sm_test_super_B3_entry(int port);
+static unsigned int sm_test_super_B3_run(int port);
+static unsigned int sm_test_super_B3_exit(int port);
+#endif
+
+static unsigned int sm_test_A4(int port, enum signal sig);
+static unsigned int sm_test_A4_entry(int port);
+static unsigned int sm_test_A4_run(int port);
+static unsigned int sm_test_A4_exit(int port);
+
+static unsigned int sm_test_A5(int port, enum signal sig);
+static unsigned int sm_test_A5_entry(int port);
+static unsigned int sm_test_A5_run(int port);
+static unsigned int sm_test_A5_exit(int port);
+
+static unsigned int sm_test_A6(int port, enum signal sig);
+static unsigned int sm_test_A6_entry(int port);
+static unsigned int sm_test_A6_run(int port);
+static unsigned int sm_test_A6_exit(int port);
+
+static unsigned int sm_test_A7(int port, enum signal sig);
+static unsigned int sm_test_A7_entry(int port);
+static unsigned int sm_test_A7_run(int port);
+static unsigned int sm_test_A7_exit(int port);
+
+static unsigned int sm_test_B4(int port, enum signal sig);
+static unsigned int sm_test_B4_entry(int port);
+static unsigned int sm_test_B4_run(int port);
+static unsigned int sm_test_B4_exit(int port);
+
+static unsigned int sm_test_B5(int port, enum signal sig);
+static unsigned int sm_test_B5_entry(int port);
+static unsigned int sm_test_B5_run(int port);
+static unsigned int sm_test_B5_exit(int port);
+
+static unsigned int sm_test_B6(int port, enum signal sig);
+static unsigned int sm_test_B6_entry(int port);
+static unsigned int sm_test_B6_run(int port);
+static unsigned int sm_test_B6_exit(int port);
+
+static unsigned int sm_test_C(int port, enum signal sig);
+static unsigned int sm_test_C_entry(int port);
+static unsigned int sm_test_C_run(int port);
+static unsigned int sm_test_C_exit(int port);
+
+static unsigned int get_super_state(int port);
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+static const state_sig sm_test_super_A1_sig[] = {
+ sm_test_super_A1_entry,
+ sm_test_super_A1_run,
+ sm_test_super_A1_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_super_B1_sig[] = {
+ sm_test_super_B1_entry,
+ sm_test_super_B1_run,
+ sm_test_super_B1_exit,
+ get_super_state
+};
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+static const state_sig sm_test_super_A2_sig[] = {
+ sm_test_super_A2_entry,
+ sm_test_super_A2_run,
+ sm_test_super_A2_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_super_B2_sig[] = {
+ sm_test_super_B2_entry,
+ sm_test_super_B2_run,
+ sm_test_super_B2_exit,
+ get_super_state
+};
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+static const state_sig sm_test_super_A3_sig[] = {
+ sm_test_super_A3_entry,
+ sm_test_super_A3_run,
+ sm_test_super_A3_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_super_B3_sig[] = {
+ sm_test_super_B3_entry,
+ sm_test_super_B3_run,
+ sm_test_super_B3_exit,
+ get_super_state
+};
+#endif
+
+static const state_sig sm_test_A4_sig[] = {
+ sm_test_A4_entry,
+ sm_test_A4_run,
+ sm_test_A4_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_A5_sig[] = {
+ sm_test_A5_entry,
+ sm_test_A5_run,
+ sm_test_A5_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_A6_sig[] = {
+ sm_test_A6_entry,
+ sm_test_A6_run,
+ sm_test_A6_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_A7_sig[] = {
+ sm_test_A7_entry,
+ sm_test_A7_run,
+ sm_test_A7_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_B4_sig[] = {
+ sm_test_B4_entry,
+ sm_test_B4_run,
+ sm_test_B4_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_B5_sig[] = {
+ sm_test_B5_entry,
+ sm_test_B5_run,
+ sm_test_B5_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_B6_sig[] = {
+ sm_test_B6_entry,
+ sm_test_B6_run,
+ sm_test_B6_exit,
+ get_super_state
+};
+
+static const state_sig sm_test_C_sig[] = {
+ sm_test_C_entry,
+ sm_test_C_run,
+ sm_test_C_exit,
+ get_super_state
+};
+
+static void clear_seq(int port)
+{
+ int i;
+
+ sm[port].idx = 0;
+
+ for (i = 0; i < 8; i++)
+ sm[port].seq[i] = 0;
+}
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+static unsigned int sm_test_super_A1(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_A1_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int sm_test_super_A1_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_A1;
+ return 0;
+}
+
+static unsigned int sm_test_super_A1_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_A1;
+ return 0;
+}
+
+static unsigned int sm_test_super_A1_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A1;
+ return 0;
+}
+
+static unsigned int sm_test_super_B1(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_B1_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int sm_test_super_B1_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_B1;
+ return 0;
+}
+
+static unsigned int sm_test_super_B1_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_B1;
+ return 0;
+}
+
+static unsigned int sm_test_super_B1_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B1;
+ return 0;
+}
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+static unsigned int sm_test_super_A2(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_A2_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ return SUPER(ret, sig, sm_test_super_A1);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_super_A2_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_A2;
+ return 0;
+}
+
+static unsigned int sm_test_super_A2_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_A2;
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_super_A2_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A2;
+ return 0;
+}
+
+static unsigned int sm_test_super_B2(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_B2_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ return SUPER(ret, sig, sm_test_super_B1);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_super_B2_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_B2;
+ return 0;
+}
+
+static unsigned int sm_test_super_B2_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_B2;
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_super_B2_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B2;
+ return 0;
+}
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+static unsigned int sm_test_super_A3(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_A3_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return SUPER(ret, sig, sm_test_super_A2);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_super_A3_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_A3;
+ return 0;
+}
+
+static unsigned int sm_test_super_A3_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_A3;
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return RUN_SUPER;
+#else
+ return 0;
+#endif
+}
+
+static unsigned int sm_test_super_A3_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A3;
+ return 0;
+}
+
+static unsigned int sm_test_super_B3(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_super_B3_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return SUPER(ret, sig, sm_test_super_B2);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_super_B3_entry(int port)
+{
+ sm[port].seq[sm[port].idx++] = ENTER_B3;
+ return 0;
+}
+
+static unsigned int sm_test_super_B3_run(int port)
+{
+ sm[port].seq[sm[port].idx++] = RUN_B3;
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return RUN_SUPER;
+#else
+ return 0;
+#endif
+}
+
+static unsigned int sm_test_super_B3_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B3;
+ return 0;
+}
+#endif
+
+
+static unsigned int sm_test_A4(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_A4_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+ return SUPER(ret, sig, sm_test_super_A3);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_A4_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_A4;
+ return 0;
+}
+
+static unsigned int sm_test_A4_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_A4;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_B4);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_A4_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A4;
+ return 0;
+}
+
+static unsigned int sm_test_A5(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_A5_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+ return SUPER(ret, sig, sm_test_super_A3);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_A5_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_A5;
+ return 0;
+}
+
+static unsigned int sm_test_A5_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_A5;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_A4);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_A5_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A5;
+ return 0;
+}
+
+static unsigned int sm_test_A6(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_A6_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return SUPER(ret, sig, sm_test_super_A2);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_A6_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_A6;
+ return 0;
+}
+
+static unsigned int sm_test_A6_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_A6;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_A5);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_A6_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A6;
+ return 0;
+}
+
+static unsigned int sm_test_A7(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_A7_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ return SUPER(ret, sig, sm_test_super_A1);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_A7_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_A7;
+ return 0;
+}
+
+static unsigned int sm_test_A7_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_A7;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_A6);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_A7_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_A7;
+ return 0;
+}
+
+static unsigned int sm_test_B4(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_B4_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2) || \
+ defined(TEST_USB_SM_FRAMEWORK_H1)
+ return SUPER(ret, sig, sm_test_super_B3);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_B4_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_B4;
+ return 0;
+}
+
+static unsigned int sm_test_B4_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].seq[sm[port].idx++] = RUN_B4;
+ sm[port].sv_tmp = 1;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_B5);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_B4_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B4;
+ return 0;
+}
+
+static unsigned int sm_test_B5(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_B5_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3) || defined(TEST_USB_SM_FRAMEWORK_H2)
+ return SUPER(ret, sig, sm_test_super_B2);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_B5_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_B5;
+ return 0;
+}
+
+static unsigned int sm_test_B5_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_B5;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_B6);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_B5_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B5;
+ return 0;
+}
+
+static unsigned int sm_test_B6(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_B6_sig[sig])(port);
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ return SUPER(ret, sig, sm_test_super_B1);
+#else
+ return SUPER(ret, sig, 0);
+#endif
+}
+
+static unsigned int sm_test_B6_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_B6;
+ return 0;
+}
+
+static unsigned int sm_test_B6_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].sv_tmp = 1;
+ sm[port].seq[sm[port].idx++] = RUN_B6;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_C);
+ return 0;
+ }
+
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_B6_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_B6;
+ return 0;
+}
+
+static unsigned int get_super_state(int port)
+{
+ return RUN_SUPER;
+}
+
+static unsigned int sm_test_C(int port, enum signal sig)
+{
+ int ret;
+
+ ret = (*sm_test_C_sig[sig])(port);
+ return SUPER(ret, sig, 0);
+}
+
+static unsigned int sm_test_C_entry(int port)
+{
+ sm[port].sv_tmp = 0;
+ sm[port].seq[sm[port].idx++] = ENTER_C;
+ return 0;
+}
+
+static unsigned int sm_test_C_run(int port)
+{
+ if (sm[port].sv_tmp == 0) {
+ sm[port].seq[sm[port].idx++] = RUN_C;
+ sm[port].sv_tmp = 1;
+ } else {
+ set_state(port, TSM_OBJ(port), sm_test_A7);
+ }
+
+ return 0;
+}
+
+static unsigned int sm_test_C_exit(int port)
+{
+ sm[port].seq[sm[port].idx++] = EXIT_C;
+ return 0;
+}
+
+#if defined(TEST_USB_SM_FRAMEWORK_H0)
+static int test_hierarchy_0(void)
+{
+ int port = PORT0;
+ int i;
+
+ clear_seq(port);
+ init_state(port, TSM_OBJ(port), sm_test_A4);
+
+ for (i = 0; i < 17; i++) {
+ task_wake(TASK_ID_TEST);
+ task_wait_event(5 * MSEC);
+ }
+
+ /* i == 0 */
+ TEST_ASSERT(sm[port].seq[0] == ENTER_A4);
+
+ /* i == 1 */
+ TEST_ASSERT(sm[port].seq[1] == RUN_A4);
+
+ /* i == 2 */
+ TEST_ASSERT(sm[port].seq[2] == EXIT_A4);
+ TEST_ASSERT(sm[port].seq[3] == ENTER_B4);
+
+ /* i == 3 */
+ TEST_ASSERT(sm[port].seq[4] == RUN_B4);
+
+ /* i == 4 */
+ TEST_ASSERT(sm[port].seq[5] == EXIT_B4);
+ TEST_ASSERT(sm[port].seq[6] == ENTER_B5);
+
+ /* i == 5 */
+ TEST_ASSERT(sm[port].seq[7] == RUN_B5);
+
+ /* i == 6 */
+ TEST_ASSERT(sm[port].seq[8] == EXIT_B5);
+ TEST_ASSERT(sm[port].seq[9] == ENTER_B6);
+
+ /* i == 7 */
+ TEST_ASSERT(sm[port].seq[10] == RUN_B6);
+
+ /* i == 8 */
+ TEST_ASSERT(sm[port].seq[11] == EXIT_B6);
+ TEST_ASSERT(sm[port].seq[12] == ENTER_C);
+
+ /* i == 9 */
+ TEST_ASSERT(sm[port].seq[13] == RUN_C);
+
+ /* i == 10 */
+ TEST_ASSERT(sm[port].seq[14] == EXIT_C);
+ TEST_ASSERT(sm[port].seq[15] == ENTER_A7);
+
+ /* i == 11 */
+ TEST_ASSERT(sm[port].seq[16] == RUN_A7);
+
+ /* i == 12 */
+ TEST_ASSERT(sm[port].seq[17] == EXIT_A7);
+ TEST_ASSERT(sm[port].seq[18] == ENTER_A6);
+
+ /* i == 13 */
+ TEST_ASSERT(sm[port].seq[19] == RUN_A6);
+
+ /* i == 14 */
+ TEST_ASSERT(sm[port].seq[20] == EXIT_A6);
+ TEST_ASSERT(sm[port].seq[21] == ENTER_A5);
+
+ /* i == 15 */
+ TEST_ASSERT(sm[port].seq[22] == RUN_A5);
+
+ /* i == 16 */
+ TEST_ASSERT(sm[port].seq[23] == EXIT_A5);
+ TEST_ASSERT(sm[port].seq[24] == ENTER_A4);
+
+ for (i = 25; i < SEQUENCE_SIZE; i++)
+ TEST_ASSERT(sm[port].seq[i] == 0);
+
+ return EC_SUCCESS;
+}
+#endif
+
+
+#if defined(TEST_USB_SM_FRAMEWORK_H1)
+static int test_hierarchy_1(void)
+{
+ int port = PORT0;
+ int i;
+
+ clear_seq(port);
+ init_state(port, TSM_OBJ(port), sm_test_A4);
+
+ for (i = 0; i < 17; i++) {
+ task_wake(TASK_ID_TEST);
+ task_wait_event(5 * MSEC);
+ }
+
+ /* i == 0 */
+ TEST_ASSERT(sm[port].seq[0] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[1] == ENTER_A4);
+
+ /* i == 1 */
+ TEST_ASSERT(sm[port].seq[2] == RUN_A4);
+ TEST_ASSERT(sm[port].seq[3] == RUN_A3);
+
+ /* i == 2 */
+ TEST_ASSERT(sm[port].seq[4] == EXIT_A4);
+ TEST_ASSERT(sm[port].seq[5] == EXIT_A3);
+ TEST_ASSERT(sm[port].seq[6] == ENTER_B3);
+ TEST_ASSERT(sm[port].seq[7] == ENTER_B4);
+
+ /* i == 3 */
+ TEST_ASSERT(sm[port].seq[8] == RUN_B4);
+ TEST_ASSERT(sm[port].seq[9] == RUN_B3);
+
+ /* i == 4 */
+ TEST_ASSERT(sm[port].seq[10] == EXIT_B4);
+ TEST_ASSERT(sm[port].seq[11] == EXIT_B3);
+ TEST_ASSERT(sm[port].seq[12] == ENTER_B5);
+
+ /* i == 5 */
+ TEST_ASSERT(sm[port].seq[13] == RUN_B5);
+
+ /* i == 6 */
+ TEST_ASSERT(sm[port].seq[14] == EXIT_B5);
+ TEST_ASSERT(sm[port].seq[15] == ENTER_B6);
+
+ /* i == 7 */
+ TEST_ASSERT(sm[port].seq[16] == RUN_B6);
+
+ /* i == 8 */
+ TEST_ASSERT(sm[port].seq[17] == EXIT_B6);
+ TEST_ASSERT(sm[port].seq[18] == ENTER_C);
+
+ /* i == 9 */
+ TEST_ASSERT(sm[port].seq[19] == RUN_C);
+
+ /* i == 10 */
+ TEST_ASSERT(sm[port].seq[20] == EXIT_C);
+ TEST_ASSERT(sm[port].seq[21] == ENTER_A7);
+
+ /* i == 11 */
+ TEST_ASSERT(sm[port].seq[22] == RUN_A7);
+
+ /* i == 12 */
+ TEST_ASSERT(sm[port].seq[23] == EXIT_A7);
+ TEST_ASSERT(sm[port].seq[24] == ENTER_A6);
+
+ /* i == 13 */
+ TEST_ASSERT(sm[port].seq[25] == RUN_A6);
+
+ /* i == 14 */
+ TEST_ASSERT(sm[port].seq[26] == EXIT_A6);
+ TEST_ASSERT(sm[port].seq[27] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[28] == ENTER_A5);
+
+ /* i == 15 */
+ TEST_ASSERT(sm[port].seq[29] == RUN_A5);
+ TEST_ASSERT(sm[port].seq[30] == RUN_A3);
+
+ /* i == 16 */
+ TEST_ASSERT(sm[port].seq[31] == EXIT_A5);
+ TEST_ASSERT(sm[port].seq[32] == ENTER_A4);
+
+ for (i = 33; i < SEQUENCE_SIZE; i++)
+ TEST_ASSERT(sm[port].seq[i] == 0);
+
+ return EC_SUCCESS;
+}
+#endif
+
+
+#if defined(TEST_USB_SM_FRAMEWORK_H2)
+static int test_hierarchy_2(void)
+{
+
+ int port = PORT0;
+ int i;
+
+ clear_seq(port);
+ init_state(port, TSM_OBJ(port), sm_test_A4);
+
+ for (i = 0; i < 17; i++) {
+ task_wake(TASK_ID_TEST);
+ task_wait_event(5 * MSEC);
+ }
+
+ /* i == 0 */
+ TEST_ASSERT(sm[port].seq[0] == ENTER_A2);
+ TEST_ASSERT(sm[port].seq[1] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[2] == ENTER_A4);
+
+ /* i == 1 */
+ TEST_ASSERT(sm[port].seq[3] == RUN_A4);
+ TEST_ASSERT(sm[port].seq[4] == RUN_A3);
+ TEST_ASSERT(sm[port].seq[5] == RUN_A2);
+
+ /* i == 2 */
+ TEST_ASSERT(sm[port].seq[6] == EXIT_A4);
+ TEST_ASSERT(sm[port].seq[7] == EXIT_A3);
+ TEST_ASSERT(sm[port].seq[8] == EXIT_A2);
+ TEST_ASSERT(sm[port].seq[9] == ENTER_B2);
+ TEST_ASSERT(sm[port].seq[10] == ENTER_B3);
+ TEST_ASSERT(sm[port].seq[11] == ENTER_B4);
+
+ /* i == 3 */
+ TEST_ASSERT(sm[port].seq[12] == RUN_B4);
+ TEST_ASSERT(sm[port].seq[13] == RUN_B3);
+ TEST_ASSERT(sm[port].seq[14] == RUN_B2);
+
+ /* i == 4 */
+ TEST_ASSERT(sm[port].seq[15] == EXIT_B4);
+ TEST_ASSERT(sm[port].seq[16] == EXIT_B3);
+ TEST_ASSERT(sm[port].seq[17] == ENTER_B5);
+
+ /* i == 5 */
+ TEST_ASSERT(sm[port].seq[18] == RUN_B5);
+ TEST_ASSERT(sm[port].seq[19] == RUN_B2);
+
+ /* i == 6 */
+ TEST_ASSERT(sm[port].seq[20] == EXIT_B5);
+ TEST_ASSERT(sm[port].seq[21] == EXIT_B2);
+ TEST_ASSERT(sm[port].seq[22] == ENTER_B6);
+
+ /* i == 7 */
+ TEST_ASSERT(sm[port].seq[23] == RUN_B6);
+
+ /* i == 8 */
+ TEST_ASSERT(sm[port].seq[24] == EXIT_B6);
+ TEST_ASSERT(sm[port].seq[25] == ENTER_C);
+
+ /* i == 9 */
+ TEST_ASSERT(sm[port].seq[26] == RUN_C);
+
+ /* i == 10 */
+ TEST_ASSERT(sm[port].seq[27] == EXIT_C);
+ TEST_ASSERT(sm[port].seq[28] == ENTER_A7);
+
+ /* i == 11 */
+ TEST_ASSERT(sm[port].seq[29] == RUN_A7);
+
+ /* i == 12 */
+ TEST_ASSERT(sm[port].seq[30] == EXIT_A7);
+ TEST_ASSERT(sm[port].seq[31] == ENTER_A2);
+ TEST_ASSERT(sm[port].seq[32] == ENTER_A6);
+
+ /* i == 13 */
+ TEST_ASSERT(sm[port].seq[33] == RUN_A6);
+ TEST_ASSERT(sm[port].seq[34] == RUN_A2);
+
+ /* i == 14 */
+ TEST_ASSERT(sm[port].seq[35] == EXIT_A6);
+ TEST_ASSERT(sm[port].seq[36] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[37] == ENTER_A5);
+
+ /* i == 15 */
+ TEST_ASSERT(sm[port].seq[38] == RUN_A5);
+ TEST_ASSERT(sm[port].seq[39] == RUN_A3);
+ TEST_ASSERT(sm[port].seq[40] == RUN_A2);
+
+ /* i == 16 */
+ TEST_ASSERT(sm[port].seq[41] == EXIT_A5);
+ TEST_ASSERT(sm[port].seq[42] == ENTER_A4);
+
+ for (i = 43; i < SEQUENCE_SIZE; i++)
+ TEST_ASSERT(sm[port].seq[i] == 0);
+
+ return EC_SUCCESS;
+}
+#endif
+
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+static int test_hierarchy_3(void)
+{
+
+ int port = PORT0;
+ int i;
+
+ clear_seq(port);
+ init_state(port, TSM_OBJ(port), sm_test_A4);
+
+ for (i = 0; i < 17; i++) {
+ task_wake(TASK_ID_TEST);
+ task_wait_event(5 * MSEC);
+ }
+
+ /* i == 0 */
+ TEST_ASSERT(sm[port].seq[0] == ENTER_A1);
+ TEST_ASSERT(sm[port].seq[1] == ENTER_A2);
+ TEST_ASSERT(sm[port].seq[2] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[3] == ENTER_A4);
+
+ /* i == 1 */
+ TEST_ASSERT(sm[port].seq[4] == RUN_A4);
+ TEST_ASSERT(sm[port].seq[5] == RUN_A3);
+ TEST_ASSERT(sm[port].seq[6] == RUN_A2);
+ TEST_ASSERT(sm[port].seq[7] == RUN_A1);
+
+ /* i == 2 */
+ TEST_ASSERT(sm[port].seq[8] == EXIT_A4);
+ TEST_ASSERT(sm[port].seq[9] == EXIT_A3);
+ TEST_ASSERT(sm[port].seq[10] == EXIT_A2);
+ TEST_ASSERT(sm[port].seq[11] == EXIT_A1);
+ TEST_ASSERT(sm[port].seq[12] == ENTER_B1);
+ TEST_ASSERT(sm[port].seq[13] == ENTER_B2);
+ TEST_ASSERT(sm[port].seq[14] == ENTER_B3);
+ TEST_ASSERT(sm[port].seq[15] == ENTER_B4);
+
+ /* i == 3 */
+ TEST_ASSERT(sm[port].seq[16] == RUN_B4);
+ TEST_ASSERT(sm[port].seq[17] == RUN_B3);
+ TEST_ASSERT(sm[port].seq[18] == RUN_B2);
+ TEST_ASSERT(sm[port].seq[19] == RUN_B1);
+
+ /* i == 4 */
+ TEST_ASSERT(sm[port].seq[20] == EXIT_B4);
+ TEST_ASSERT(sm[port].seq[21] == EXIT_B3);
+ TEST_ASSERT(sm[port].seq[22] == ENTER_B5);
+
+ /* i == 5 */
+ TEST_ASSERT(sm[port].seq[23] == RUN_B5);
+ TEST_ASSERT(sm[port].seq[24] == RUN_B2);
+ TEST_ASSERT(sm[port].seq[25] == RUN_B1);
+
+ /* i == 6 */
+ TEST_ASSERT(sm[port].seq[26] == EXIT_B5);
+ TEST_ASSERT(sm[port].seq[27] == EXIT_B2);
+ TEST_ASSERT(sm[port].seq[28] == ENTER_B6);
+
+ /* i == 7 */
+ TEST_ASSERT(sm[port].seq[29] == RUN_B6);
+ TEST_ASSERT(sm[port].seq[30] == RUN_B1);
+
+ /* i == 8 */
+ TEST_ASSERT(sm[port].seq[31] == EXIT_B6);
+ TEST_ASSERT(sm[port].seq[32] == EXIT_B1);
+ TEST_ASSERT(sm[port].seq[33] == ENTER_C);
+
+ /* i == 9 */
+ TEST_ASSERT(sm[port].seq[34] == RUN_C);
+
+ /* i == 10 */
+ TEST_ASSERT(sm[port].seq[35] == EXIT_C);
+ TEST_ASSERT(sm[port].seq[36] == ENTER_A1);
+ TEST_ASSERT(sm[port].seq[37] == ENTER_A7);
+
+ /* i == 11 */
+ TEST_ASSERT(sm[port].seq[38] == RUN_A7);
+ TEST_ASSERT(sm[port].seq[39] == RUN_A1);
+
+ /* i == 12 */
+ TEST_ASSERT(sm[port].seq[40] == EXIT_A7);
+ TEST_ASSERT(sm[port].seq[41] == ENTER_A2);
+ TEST_ASSERT(sm[port].seq[42] == ENTER_A6);
+
+ /* i == 13 */
+ TEST_ASSERT(sm[port].seq[43] == RUN_A6);
+ TEST_ASSERT(sm[port].seq[44] == RUN_A2);
+ TEST_ASSERT(sm[port].seq[45] == RUN_A1);
+
+ /* i == 14 */
+ TEST_ASSERT(sm[port].seq[46] == EXIT_A6);
+ TEST_ASSERT(sm[port].seq[47] == ENTER_A3);
+ TEST_ASSERT(sm[port].seq[48] == ENTER_A5);
+
+ /* i == 15 */
+ TEST_ASSERT(sm[port].seq[49] == RUN_A5);
+ TEST_ASSERT(sm[port].seq[50] == RUN_A3);
+ TEST_ASSERT(sm[port].seq[51] == RUN_A2);
+ TEST_ASSERT(sm[port].seq[52] == RUN_A1);
+
+ /* i == 16 */
+ TEST_ASSERT(sm[port].seq[53] == EXIT_A5);
+ TEST_ASSERT(sm[port].seq[54] == ENTER_A4);
+
+ return EC_SUCCESS;
+}
+#endif
+
+int test_task(void *u)
+{
+ int port = PORT0;
+
+ while (1) {
+ /* wait for next event/packet or timeout expiration */
+ task_wait_event(-1);
+ /* run state machine */
+ exe_state(port, TSM_OBJ(port), RUN_SIG);
+ }
+
+ return EC_SUCCESS;
+}
+
+void run_test(void)
+{
+ test_reset();
+#if defined(TEST_USB_SM_FRAMEWORK_H3)
+ RUN_TEST(test_hierarchy_3);
+#elif defined(TEST_USB_SM_FRAMEWORK_H2)
+ RUN_TEST(test_hierarchy_2);
+#elif defined(TEST_USB_SM_FRAMEWORK_H1)
+ RUN_TEST(test_hierarchy_1);
+#else
+ RUN_TEST(test_hierarchy_0);
+#endif
+ test_print_result();
+}
diff --git a/test/usb_sm_framework_h3.tasklist b/test/usb_sm_framework_h3.tasklist
new file mode 100644
index 0000000000..d1b4e6e2ca
--- /dev/null
+++ b/test/usb_sm_framework_h3.tasklist
@@ -0,0 +1,18 @@
+/* Copyright (c) 2019 The Chromium OS 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_TEST(n, r, d, s) 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
+ * 's' is the stack size in bytes; must be a multiple of 8
+ */
+#define CONFIG_TEST_TASK_LIST \
+ TASK_TEST(TEST, test_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/test/usb_typec_ctvpd.c b/test/usb_typec_ctvpd.c
new file mode 100644
index 0000000000..19fbfa1b8a
--- /dev/null
+++ b/test/usb_typec_ctvpd.c
@@ -0,0 +1,1488 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test USB Type-C VPD and CTVPD module.
+ */
+#include "common.h"
+#include "crc.h"
+#include "task.h"
+#include "test_util.h"
+#include "timer.h"
+#include "usb_pd.h"
+#include "usb_sm.h"
+#include "usb_tc_sm.h"
+#include "util.h"
+#include "usb_pd_tcpm.h"
+#include "usb_pd_test_util.h"
+#include "vpd_api.h"
+
+#define PORT0 0
+
+enum cc_type {CC1, CC2};
+enum vbus_type {VBUS_0 = 0, VBUS_5 = 5000};
+enum vconn_type {VCONN_0 = 0, VCONN_3 = 3000, VCONN_5 = 5000};
+enum snk_con_voltage_type {SRC_CON_DEF, SRC_CON_1_5, SRC_CON_3_0};
+
+struct pd_port_t {
+ int host_mode;
+ int has_vbus;
+ int msg_tx_id;
+ int msg_rx_id;
+ int polarity;
+ int partner_role; /* -1 for none */
+ int partner_polarity;
+ int rev;
+} pd_port[CONFIG_USB_PD_PORT_COUNT];
+
+uint64_t wait_for_state_change(int port, uint64_t timeout)
+{
+ uint64_t start;
+ uint64_t wait;
+ uint32_t state = get_typec_state_id(port);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+
+ wait = get_time().val + timeout;
+ start = get_time().val;
+ while (get_typec_state_id(port) == state && get_time().val < wait)
+ task_wait_event(5 * MSEC);
+
+ return get_time().val - start;
+}
+
+#if defined(TEST_USB_TYPEC_CTVPD)
+static int ct_connect_sink(enum cc_type cc, enum snk_con_voltage_type v)
+{
+ int ret;
+
+ switch (v) {
+ case SRC_CON_DEF:
+ ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_DEF_RD_THRESH_MV) :
+ mock_set_cc1_rp3a0_rd_l(PD_SRC_DEF_RD_THRESH_MV);
+ break;
+ case SRC_CON_1_5:
+ ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_1_5_RD_THRESH_MV) :
+ mock_set_cc1_rp3a0_rd_l(PD_SRC_1_5_RD_THRESH_MV);
+ break;
+ case SRC_CON_3_0:
+ ret = (cc) ? mock_set_cc2_rp3a0_rd_l(PD_SRC_3_0_RD_THRESH_MV) :
+ mock_set_cc1_rp3a0_rd_l(PD_SRC_3_0_RD_THRESH_MV);
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int ct_disconnect_sink(void)
+{
+ int r1;
+ int r2;
+
+ r1 = mock_set_cc1_rp3a0_rd_l(PD_SRC_DEF_VNC_MV);
+ r2 = mock_set_cc2_rp3a0_rd_l(PD_SRC_DEF_VNC_MV);
+
+ return r1 & r2;
+}
+
+static int ct_connect_source(enum cc_type cc, enum vbus_type vbus)
+{
+ mock_set_ct_vbus(vbus);
+ return (cc) ? mock_set_cc2_rpusb_odh(PD_SNK_VA_MV) :
+ mock_set_cc1_rpusb_odh(PD_SNK_VA_MV);
+}
+
+static int ct_disconnect_source(void)
+{
+ int r1;
+ int r2;
+
+ mock_set_ct_vbus(VBUS_0);
+ r1 = mock_set_cc1_rpusb_odh(0);
+ r2 = mock_set_cc2_rpusb_odh(0);
+
+ return r1 & r2;
+}
+#endif
+
+static void host_disconnect_source(void)
+{
+ mock_set_host_vbus(VBUS_0);
+ mock_set_host_cc_source_voltage(0);
+ mock_set_host_cc_sink_voltage(0);
+}
+
+static void host_connect_source(enum vbus_type vbus)
+{
+ mock_set_host_vbus(vbus);
+ mock_set_host_cc_source_voltage(PD_SNK_VA_MV);
+}
+
+#if defined(TEST_USB_TYPEC_CTVPD)
+static void host_connect_sink(enum snk_con_voltage_type v)
+{
+ switch (v) {
+ case SRC_CON_DEF:
+ mock_set_host_cc_sink_voltage(PD_SRC_DEF_RD_THRESH_MV);
+ break;
+ case SRC_CON_1_5:
+ mock_set_host_cc_sink_voltage(PD_SRC_1_5_RD_THRESH_MV);
+ break;
+ case SRC_CON_3_0:
+ mock_set_host_cc_sink_voltage(PD_SRC_3_0_RD_THRESH_MV);
+ break;
+ }
+}
+#endif
+
+static void init_port(int port)
+{
+ pd_port[port].polarity = 0;
+ pd_port[port].rev = PD_REV30;
+ pd_port[port].msg_tx_id = 0;
+ pd_port[port].msg_rx_id = 0;
+}
+
+static int check_host_ra_rd(void)
+{
+ /* Make sure CC_RP3A0_RD_L is configured as GPO */
+ if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_GPO)
+ return 0;
+
+ /* Make sure CC_RP3A0_RD_L is asserted low */
+ if (mock_get_cc_rp3a0_rd_l() != 0)
+ return 0;
+
+ /* Make sure VPDMCU_CC_EN is enabled */
+ if (mock_get_mcu_cc_en() != 1)
+ return 0;
+
+ /* Make sure CC_VPDMCU is configured as ADC */
+ if (mock_get_cfg_cc_vpdmcu() != PIN_ADC)
+ return 0;
+
+ /* Make sure CC_DB_EN_OD is HZ */
+ if (mock_get_cc_db_en_od() != GPO_HZ)
+ return 0;
+
+ return 1;
+}
+
+static int check_host_rd(void)
+{
+ /* Make sure CC_RP3A0_RD_L is configured as GPO */
+ if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_GPO)
+ return 0;
+
+ /* Make sure CC_RP3A0_RD_L is asserted low */
+ if (mock_get_cc_rp3a0_rd_l() != 0)
+ return 0;
+
+ /* Make sure VPDMCU_CC_EN is enabled */
+ if (mock_get_mcu_cc_en() != 1)
+ return 0;
+
+ /* Make sure CC_VPDMCU is configured as ADC */
+ if (mock_get_cfg_cc_vpdmcu() != PIN_ADC)
+ return 0;
+
+ /* Make sure CC_DB_EN_OD is LOW */
+ if (mock_get_cc_db_en_od() != GPO_LOW)
+ return 0;
+
+ return 1;
+}
+
+#if defined(TEST_USB_TYPEC_CTVPD)
+static int check_host_rp3a0(void)
+{
+ /* Make sure CC_RP3A0_RD_L is asserted high */
+ if (mock_get_cc_rp3a0_rd_l() != 1)
+ return 0;
+
+ return 1;
+}
+
+static int check_host_rpusb(void)
+{
+ /* Make sure CC_RPUSB_ODH is asserted high */
+ if (mock_get_cc_rpusb_odh() != 1)
+ return 0;
+
+ /* Make sure CC_RP3A0_RD_L is configured as comparator */
+ if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_CMP)
+ return 0;
+
+ return 1;
+}
+
+static int check_host_cc_open(void)
+{
+ /* Make sure CC_RPUSB_ODH is hi-z */
+ if (mock_get_cc_rpusb_odh() != GPO_HZ)
+ return 0;
+
+ /* Make sure CC_RP3A0_RD_L is set to comparitor */
+ if (mock_get_cfg_cc_rp3a0_rd_l() != PIN_CMP)
+ return 0;
+
+ /* Make sure cc_db_en_od is set low */
+ if (mock_get_cc_db_en_od() != GPO_LOW)
+ return 0;
+
+ return 1;
+}
+
+static int check_ct_ccs_hz(void)
+{
+ return (mock_get_ct_rd() == GPO_HIGH);
+}
+
+static int check_ct_ccs_rd(void)
+{
+ return (mock_get_ct_rd() == GPO_LOW);
+}
+
+static int check_ct_ccs_cc1_rpusb(void)
+{
+ return (mock_get_ct_cc1_rpusb() == 1);
+}
+#endif
+
+void inc_tx_id(int port)
+{
+ pd_port[port].msg_tx_id = (pd_port[port].msg_tx_id + 1) % 7;
+}
+
+void inc_rx_id(int port)
+{
+ pd_port[port].msg_rx_id = (pd_port[port].msg_rx_id + 1) % 7;
+}
+
+static int verify_goodcrc(int port, int role, int id)
+{
+ return pd_test_tx_msg_verify_sop_prime(port) &&
+ pd_test_tx_msg_verify_short(port, PD_HEADER(PD_CTRL_GOOD_CRC,
+ role, role, id, 0, 0, 0)) &&
+ pd_test_tx_msg_verify_crc(port) &&
+ pd_test_tx_msg_verify_eop(port);
+}
+
+static void simulate_rx_msg(int port, uint16_t header, int cnt,
+ const uint32_t *data)
+{
+ int i;
+
+ pd_test_rx_set_preamble(port, 1);
+ pd_test_rx_msg_append_sop_prime(port);
+ pd_test_rx_msg_append_short(port, header);
+
+ crc32_init();
+ crc32_hash16(header);
+
+ for (i = 0; i < cnt; ++i) {
+ pd_test_rx_msg_append_word(port, data[i]);
+ crc32_hash32(data[i]);
+ }
+
+ pd_test_rx_msg_append_word(port, crc32_result());
+
+ pd_test_rx_msg_append_eop(port);
+ pd_test_rx_msg_append_last_edge(port);
+
+ pd_simulate_rx(port);
+}
+
+static void simulate_goodcrc(int port, int role, int id)
+{
+ simulate_rx_msg(port, PD_HEADER(PD_CTRL_GOOD_CRC, role, role, id, 0,
+ pd_port[port].rev, 0), 0, NULL);
+}
+
+static void simulate_discovery_identity(int port)
+{
+ uint16_t header = PD_HEADER(PD_DATA_VENDOR_DEF, PD_ROLE_SOURCE,
+ 0, pd_port[port].msg_rx_id,
+ 1, pd_port[port].rev, 0);
+ uint32_t msg = VDO(USB_SID_PD,
+ 1, /* Structured VDM */
+ VDO_SVDM_VERS(1) |
+ VDO_CMDT(CMDT_INIT) |
+ CMD_DISCOVER_IDENT);
+
+ simulate_rx_msg(port, header, 1, (const uint32_t *)&msg);
+}
+
+static int test_vpd_host_src_detection(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * TEST:
+ * Host is configured properly and start state is UNATTACHED_SNK
+ */
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host PORT Source Connection Detected
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ /*
+ * TEST:
+ * Host CC debounce in ATTACH_WAIT_SNK state
+ */
+
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(5 * MSEC);
+
+ /*
+ * TEST:
+ * Host CC debounce in ATTACH_WAIT_SNK state
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_0);
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(50 * MSEC);
+
+ /*
+ * TEST:
+ * Host Port Connection Removed
+ */
+ host_disconnect_source();
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_vpd_host_src_detection_vbus(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * TEST:
+ * Host is configured properly and start state is UNATTACHED_SNK
+ */
+
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host Port Source Connection Detected
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ /*
+ * TEST:
+ * Host Port Source Detected for tCCDebounce and Host Port VBUS
+ * Detected.
+ */
+
+ host_connect_source(VBUS_5);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host Port VBUS Removed
+ */
+
+ host_connect_source(VBUS_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_vpd_host_src_detection_vconn(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * TEST:
+ * Host is configured properly and start state is UNATTACHED_SNK
+ */
+
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * TEST:
+ * Host Source Connection Detected
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ /*
+ * TEST:
+ * Host Port Source Detected for tCCDebounce and VCONN Detected
+ */
+
+ host_connect_source(VBUS_0);
+ mock_set_vconn(VCONN_3);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /* VCONN was detected. Make sure RA is removed */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+ TEST_ASSERT(check_host_rd());
+
+ /*
+ * TEST:
+ * Host Port VCONN Removed
+ */
+
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ host_disconnect_source();
+
+ return EC_SUCCESS;
+}
+
+static int test_vpd_host_src_detection_message_reception(void)
+{
+ int port = PORT0;
+ uint32_t expected_vdm_header = VDO(USB_VID_GOOGLE,
+ 1, /* Structured VDM */
+ VDO_SVDM_VERS(1) |
+ VDO_CMDT(CMDT_RSP_ACK) |
+ CMD_DISCOVER_IDENT);
+ uint32_t expected_vdo_id_header = VDO_IDH(
+ 0, /* Not a USB Host */
+ 1, /* Capable of being enumerated as USB Device */
+ IDH_PTYPE_VPD,
+ 0, /* Modal Operation Not Supported */
+ USB_VID_GOOGLE);
+ uint32_t expected_vdo_cert = 0;
+ uint32_t expected_vdo_product = VDO_PRODUCT(
+ CONFIG_USB_PID,
+ USB_BCD_DEVICE);
+ uint32_t expected_vdo_vpd = VDO_VPD(
+ VPD_HW_VERSION,
+ VPD_FW_VERSION,
+ VPD_MAX_VBUS_20V,
+ VPD_VBUS_IMP(VPD_VBUS_IMPEDANCE),
+ VPD_GND_IMP(VPD_GND_IMPEDANCE),
+#ifdef CONFIG_USB_TYPEC_CTVPD
+ VPD_CTS_SUPPORTED
+#else
+ VPD_CTS_NOT_SUPPORTED
+#endif
+ );
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * TEST:
+ * Host is configured properly and start state is UNATTACHED_SNK
+ */
+
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * Transition to ATTACHED_SNK
+ */
+
+ host_connect_source(VBUS_5);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 20 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /* Run state machines to enable rx monitoring */
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /*
+ * TEST:
+ * Reception of Discovery Identity message
+ */
+
+ simulate_discovery_identity(port);
+ task_wait_event(30 * MSEC);
+
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_rx_id(port);
+
+ /* Test Discover Identity Ack */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop_prime(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_VENDOR_DEF, PD_PLUG_CABLE_VPD, 0,
+ pd_port[port].msg_tx_id, 5, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdm_header));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_id_header));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_cert));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_product));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_vpd));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+
+ /* Ack was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(30 * MSEC);
+ inc_tx_id(port);
+
+ /*
+ * TEST:
+ * Host Port VBUS Removed
+ */
+
+ host_connect_source(VBUS_0);
+
+ wait_for_state_change(port, 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ host_disconnect_source();
+
+ return EC_SUCCESS;
+}
+
+#if defined(TEST_USB_TYPEC_CTVPD)
+static int test_ctvpd_behavior_case1(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 1: The following tests the behavior when a DRP is connected to a
+ * Charge-Through VCONN-Powered USB Device (abbreviated CTVPD),
+ * with no Power Source attached to the ChargeThrough port on
+ * the CTVPD.
+ */
+
+ /* 1. DRP and CTVPD are both in the unattached state */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ /*
+ * a. DRP alternates between Unattached.SRC and Unattached.SNK
+ *
+ * b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
+ * pins and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(check_ct_ccs_rd());
+
+ /*
+ * 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
+ * Attached.SRC
+ *
+ * a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
+ * is in Unattached.SNK and DRP enters AttachWait.SRC
+ * b. DRP in AttachWait.SRC detects that pull down on CC persists for
+ * tCCDebounce, enters Attached.SRC and turns on VBUS and VCONN
+ */
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ /*
+ * 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
+ * AttachWait.SNK.
+ *
+ * a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
+ * enters AttachWait.SNK
+ * b. CTVPD in AttachWait.SNK detects that pull up on the Host-side
+ * port’s CC persists for tCCDebounce, VCONN present and enters
+ * Attached.SNK
+ * c. CTVPD present a high-impedance to ground (above zOPEN) on its
+ * Charge-Through port’s CC1 and CC2 pins
+ */
+ wait_for_state_change(port, 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+ TEST_ASSERT(check_ct_ccs_hz());
+
+ /*
+ * 4. While DRP and CTVPD are in their respective attached states, DRP
+ * discovers the ChargeThrough CTVPD and transitions to
+ * CTUnattached.SNK
+ *
+ * a. DRP (as Source) queries the device identity via USB PD
+ * (Device Identity Command) on SOP’.
+ * b. CTVPD responds on SOP’, advertising that it is a
+ * Charge-Through VCONN-Powered USB Device
+ * c. DRP (as Source) removes VBUS
+ * d. DRP (as Source) changes its Rp to a Rd
+ * e. DRP (as Sink) continues to provide VCONN and enters
+ * CTUnattached.SNK
+ */
+ host_disconnect_source();
+
+ /*
+ * 5. CTVPD transitions to CTUnattached.VPD
+ *
+ * a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
+ * CC pin and enters CTUnattached.VPD
+ * b. CTVPD changes its host-side Rd to a Rp advertising 3.0 A
+ * c. CTVPD isolates itself from VBUS
+ * d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
+ */
+ wait_for_state_change(port, 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ /*
+ * 6. While the CTVPD in CTUnattached.VPD state and the DRP in
+ * CTUnattached.SNK state:
+ *
+ * a. CTVPD monitors Charge-Though CC pins for a source or sink;
+ * when a Power Source attach is detected, enters
+ * CTAttachWait.VPD; when a sink is detected, enters
+ * CTAttachWait.Unsupported
+ * b. CTVPD monitors VCONN for Host detach and when detected, enters
+ * Unattached.SNK
+ * c. DRP monitors VBUS and CC for CTVPD detach for tVPDDetach and
+ * when detected, enters Unattached.SNK
+ * d. DRP monitors VBUS for Power Source attach and when detected,
+ * enters CTAttached.SNK
+ */
+ /* Attach Power Source */
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_0));
+
+ wait_for_state_change(port, 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_VPD);
+
+ /* Remove Power Source */
+ TEST_ASSERT(ct_disconnect_source());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ /* Attach Sink */
+ TEST_ASSERT(ct_connect_sink(CC1, SRC_CON_DEF));
+
+ wait_for_state_change(port, PD_T_DRP_SNK);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_UNSUPPORTED);
+
+ /* Remove VCONN (Host detach) */
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case2(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 2: The following tests the behavior when a Power Source is
+ * connected to a Charge-Through VCONN-Powered USB Device
+ * (abbreviated CTVPD), with a Host already attached to the
+ * Host-Side port on the CTVPD.
+ */
+
+ /*
+ * 1. DRP is in CTUnattached.SNK state, CTVPD in CTUnattached.VPD, and
+ * Power Source in the unattached state
+ *
+ * a. CTVPD has applied Rd on the Charge-Through port’s CC1 and CC2
+ * pins and Rp termination advertising 3.0 A on the Host-side
+ * port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+
+ /* Remove Host CC */
+ mock_set_host_cc_source_voltage(0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_rp3a0());
+
+ /*
+ * 2. Power Source transitions from Unattached.SRC to Attached.SRC
+ * through AttachWait.SRC.
+ *
+ * a. Power Source detects the CC pull-down of the CTVPD and enters
+ * AttachWait.SRC
+ * b. Power Source in AttachWait.SRC detects that pull down on CC
+ * persists for tCCDebounce, enters Attached.SRC and turns on
+ * VBUS
+ */
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ /*
+ * 3. CTVPD transitions from CTUnattached.VPD through CTAttachWait.VPD
+ * to CTAttached.VPD
+ *
+ * a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
+ * pins, and transitions to CTAttachWait.VPD
+ * b. CTVPD finishes any active USB PD communication on SOP’ and
+ * ceases to respond to SOP’ queries
+ * c. CTVPD in CTAttachWait.VPD detects that the pull up on
+ * Charge-Through CC pin persists for tCCDebounce, detects VBUS
+ * and enters CTAttached.VPD
+ * d. CTVPD connects the active Charge-Through CC pin to the
+ * Host-side port’s CC pin
+ * e. CTVPD disables its Rp termination advertising 3.0 A on the
+ * Host-side port’s CC pin
+ * f. CTVPD disables its Rd on the Charge-Through CC pins
+ * g. CTVPD connects VBUS from the Charge-Through side to the Host
+ * side
+ */
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_VPD);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACHED_VPD);
+ TEST_ASSERT(moch_get_ct_cl_sel() == CT_CC2);
+ TEST_ASSERT(check_host_cc_open());
+ TEST_ASSERT(check_ct_ccs_hz());
+ TEST_ASSERT(mock_get_vbus_pass_en());
+
+ /*
+ * 4. DRP (as Sink) transitions to CTAttached.SNK
+ * a. DRP (as Sink) detects VBUS, monitors vRd for available current
+ * and enter CTAttached.SNK
+ */
+
+ /*
+ * 5. While the devices are all in their respective attached states:
+ * a. CTVPD monitors VCONN for DRP detach and when detected,
+ * enters CTDisabled.VPD
+ * b. CTVPD monitors VBUS and CC for Power Source detach and when
+ * detected, enters CTUnattached.VPD within tVPDCTDD
+ * c. DRP (as Sink) monitors VBUS for Charge-Through Power Source
+ * detach and when detected, enters CTUnattached.SNK
+ * d. DRP (as Sink) monitors VBUS and CC for CTVPD detach and when
+ * detected, enters Unattached.SNK (and resumes toggling between
+ * Unattached.SNK and Unattached.SRC)
+ * e. Power Source monitors CC for CTVPD detach and when detected,
+ * enters Unattached.SRC
+ */
+ mock_set_vconn(VCONN_0);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTDISABLED_VPD);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case3(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 3: The following describes the behavior when a Power Source is
+ * connected to a ChargeThrough VCONN-Powered USB Device
+ * (abbreviated CTVPD), with no Host attached to the Host-side
+ * port on the CTVPD.
+ */
+
+ /*
+ * 1. CTVPD and Power Source are both in the unattached state
+ * a. CTVPD has applied Rd on the Charge-Through port’s CC1 and CC2
+ * pins and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_ra_rd());
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ /*
+ * 2. Power Source transitions from Unattached.SRC to Attached.SRC
+ * through AttachWait.SRC.
+ *
+ * a. Power Source detects the CC pull-down of the CTVPD and enters
+ * AttachWait.SRC
+ * b. Power Source in AttachWait.SRC detects that pull down on CC
+ * persists for tCCDebounce, enters Attached.SRC and turns on
+ * VBUS
+ */
+
+ /* 3. CTVPD alternates between Unattached.SNk and Unattached.SRC
+ *
+ * a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
+ * pins, detects VBUS for tCCDebounce and starts alternating
+ * between Unattached.SRC and Unattached.SNK
+ */
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SRC);
+
+ /*
+ * 4. While the CTVPD alternates between Unattached.SRC and
+ * Unattached.SNK state and the Power Source in Attached.SRC state:
+ *
+ * a. CTVPD monitors the Host-side port’s CC pin for device attach
+ * and when detected, enters AttachWait.SRC
+ * b. CTVPD monitors VBUS for Power Source detach and when detected,
+ * enters Unattached.SNK
+ * c. Power Source monitors CC for CTVPD detach and when detected,
+ * enters Unattached.SRC
+ */
+
+ /* Attached host side device */
+ host_connect_sink(SRC_CON_DEF);
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SRC);
+
+ /* Remove VBUS */
+ TEST_ASSERT(ct_disconnect_source());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case4(void)
+{
+ int port = PORT0;
+ uint32_t expected_vdm_header = VDO(USB_VID_GOOGLE,
+ 1, /* Structured VDM */
+ VDO_SVDM_VERS(1) |
+ VDO_CMDT(CMDT_RSP_ACK) |
+ CMD_DISCOVER_IDENT);
+ uint32_t expected_vdo_id_header = VDO_IDH(
+ 0, /* Not a USB Host */
+ 1, /* Capable of being enumerated as USB Device */
+ IDH_PTYPE_VPD,
+ 0, /* Modal Operation Not Supported */
+ USB_VID_GOOGLE);
+ uint32_t expected_vdo_cert = 0;
+ uint32_t expected_vdo_product = VDO_PRODUCT(
+ CONFIG_USB_PID,
+ USB_BCD_DEVICE);
+ uint32_t expected_vdo_vpd = VDO_VPD(
+ VPD_HW_VERSION,
+ VPD_FW_VERSION,
+ VPD_MAX_VBUS_20V,
+ VPD_VBUS_IMP(VPD_VBUS_IMPEDANCE),
+ VPD_GND_IMP(VPD_GND_IMPEDANCE),
+ VPD_CTS_SUPPORTED
+ );
+
+ init_port(port);
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 4: The following describes the behavior when a DRP is connected
+ * to a Charge-Through VCONN-Powered USB Device
+ * (abbreviated CTVPD), with a Power Source already attached to
+ * the Charge-Through side on the CTVPD.
+ */
+
+ /*
+ * 1. DRP, CTVPD and Sink are all in the unattached state
+ *
+ * a. DRP alternates between Unattached.SRC and Unattached.SNK
+ * b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
+ * pins and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_ra_rd());
+
+ /*
+ * 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
+ * Attached.SRC
+ *
+ * a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
+ * is in Unattached.SNK and DRP enters AttachWait.SRC
+ * b. DRP in AttachWait.SRC detects that pull down on CC persists
+ * for tCCDebounce, enters Attached.SRC and turns on VBUS and
+ * VCONN
+ */
+
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ /*
+ * 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
+ * AttachWait.SNK.
+ *
+ * a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
+ * enters AttachWait.SNK
+ * b. CTVPD in AttachWait.SNK detects that pull up on the
+ * Host-side port’s CC persists for tCCDebounce, VCONN present
+ * and enters Attached.SNK
+ * c. CTVPD present a high-impedance to ground (above zOPEN) on its
+ * Charge-Through port’s CC1 and CC2 pins
+ */
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+ TEST_ASSERT(check_ct_ccs_hz());
+
+ /*
+ * 4. While DRP and CTVPD are in their respective attached states, DRP
+ * discovers the ChargeThrough CTVPD and transitions to
+ * CTUnattached.SNK
+ *
+ * a. DRP (as Source) queries the device identity via USB PD
+ * (Discover Identity Command) on SOP’.
+ * b. CTVPD responds on SOP’, advertising that it is a
+ * Charge-Through VCONN-Powered USB Device
+ * c. DRP (as Source) removes VBUS
+ * d. DRP (as Source) changes its Rp to a Rd
+ * e. DRP (as Sink) continues to provide VCONN and enters
+ * CTUnattached.SNK
+ */
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ simulate_discovery_identity(port);
+ task_wait_event(40 * MSEC);
+
+ TEST_ASSERT(verify_goodcrc(port,
+ PD_ROLE_SINK, pd_port[port].msg_rx_id));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+ inc_rx_id(port);
+
+ /* Test Discover Identity Ack */
+ TEST_ASSERT(pd_test_tx_msg_verify_sop_prime(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_short(port,
+ PD_HEADER(PD_DATA_VENDOR_DEF, PD_PLUG_CABLE_VPD, 0,
+ pd_port[port].msg_tx_id, 5, pd_port[port].rev, 0)));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdm_header));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_id_header));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_cert));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_product));
+ TEST_ASSERT(pd_test_tx_msg_verify_word(port, expected_vdo_vpd));
+ TEST_ASSERT(pd_test_tx_msg_verify_crc(port));
+ TEST_ASSERT(pd_test_tx_msg_verify_eop(port));
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /* Ack was good. Send GoodCRC */
+ simulate_goodcrc(port, PD_ROLE_SOURCE, pd_port[port].msg_tx_id);
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+ inc_tx_id(port);
+
+ /*
+ * 5. CTVPD transitions to CTUnattached.VPD
+ *
+ * a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
+ * CC pin and enters CTUnattached.VPD
+ * b. CTVPD changes its host-side Rd to a Rp termination advertising
+ * 3.0 A
+ * c. CTVPD isolates itself from VBUS
+ * d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
+ */
+ host_disconnect_source();
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_rp3a0());
+
+ /*
+ * 6. CTVPD alternates between CTUnattached.VPD and
+ * CTUnattached.Unsupported
+ */
+ wait_for_state_change(port, PD_T_DRP_SRC + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_DRP_SRC + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_VPD);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case5(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 5: The following describes the behavior when a Power Source is
+ * connected to a ChargeThrough VCONN-Powered USB Device
+ * (abbreviated CTVPD), with a DRP (with dead battery) attached
+ * to the Host-side port on the CTVPD.
+ */
+
+ /*
+ * 1. DRP, CTVPD and Power Source are all in the unattached state
+ *
+ * a. DRP apply dead battery Rd
+ * b. CTVPD apply Rd on the Charge-Through port’s CC1 and CC2 pins
+ * and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_ra_rd());
+
+ /*
+ * 2. Power Source transitions from Unattached.SRC to Attached.SRC
+ * through AttachWait.SRC.
+ *
+ * a. Power Source detects the CC pull-down of the CTVPD and enters
+ * AttachWait.SRC
+ * b. Power Source in AttachWait.SRC detects that pull down on CC
+ * persists for tCCDebounce, enters Attached.SRC and enable VBUS
+ */
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_5));
+
+ /*
+ * 3. CTVPD alternates between Unattached.SNK and Unattached.SRC
+ *
+ * a. CTVPD detects the Source’s Rp on one of its Charge-Through CC
+ * pins, detects VBUS for tCCDebounce and starts alternating
+ * between Unattached.SRC and Unattached.SNK
+ */
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SRC);
+
+ /* Connect Host With Dead Battery */
+ host_connect_sink(SRC_CON_DEF);
+
+ /*
+ * 4. CTVPD transitions from Unattached.SRC to Try.SNK through
+ * AttachWait.SRC
+ *
+ * a. CTVPD in Unattached.SRC detects the CC pull-down of DRP which
+ * is in Unattached.SNK and CTVPD enters AttachWait.SRC
+ * b. CTVPD in AttachWait.SRC detects that pull down on CC persists
+ * for tCCDebounce and enters Try.SNK
+ * c. CTVPD disables Rp termination advertising Default USB Power on
+ * the Host-side port’s CC
+ * d. CTVPD enables Rd on the Host-side port’s CC
+ */
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SRC);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 10 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == TRY_SNK);
+ TEST_ASSERT(check_host_ra_rd());
+
+ /* 5. DRP in dead battery condition remains in Unattached.SNK */
+
+ /*
+ * 6. CTVPD transitions from Try.SNK to Attached.SRC through
+ * TryWait.SRC
+ *
+ * a. CTVPD didn’t detect the CC pull-up of the DRP for
+ * tTryDebounce after tDRPTry and enters TryWait.SRC
+ * b. CTVPD disables Rd on the Host-side port’s CC
+ * c. CTVPD enables Rp termination advertising Default USB Power on
+ * the Host-side port’s CC
+ * d. CTVPD detects the CC pull-down of the DRP for tTryCCDebounce
+ * and enters Attached.SRC
+ * e. CTVPD connects VBUS from the Charge-Through side to the Host
+ * side
+ */
+ wait_for_state_change(port, PD_T_TRY_CC_DEBOUNCE +
+ PD_T_DRP_TRY + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == TRY_WAIT_SRC);
+ TEST_ASSERT(check_host_rpusb());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SRC);
+ TEST_ASSERT(mock_get_vbus_pass_en());
+
+ /*
+ * 7. DRP transitions from Unattached.SNK to Attached.SNK through
+ * AttachWait.SNK
+ *
+ * a. DRP in Unattached.SNK detects the CC pull-up of CTVPD which is
+ * in Attached.SRC and DRP enters AttachWait.SNK
+ * b. DRP in AttachWait.SNK detects that pull up on CC persists for
+ * tCCDebounce, VBUS present and enters Attached.SNK
+ */
+
+ /*
+ * 8. While the devices are all in their respective attached states:
+ * a. CTVPD monitors the Host-side port’s CC pin for device attach
+ * and when detected, enters Unattached.SNK
+ * b. CTVPD monitors VBUS for Power Source detach and when detected,
+ * enters Unattached.SNK
+ * c. Power Source monitors CC for CTVPD detach and when detected,
+ * enters Unattached.SRC
+ * d. DRP monitors VBUS for CTVPD detach and when detected, enters
+ * Unattached.SNK
+ * e. Additionally, the DRP may query the identity of the cable via
+ * USB PD on SOP’ when it has sufficient battery power and when
+ * a Charge-Through VPD is identified enters TryWait.SRC if
+ * implemented, or enters Unattached.SRC if TryWait.SRC is not
+ * supported
+ */
+ TEST_ASSERT(ct_connect_source(CC2, VBUS_0));
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+
+ return EC_SUCCESS;
+}
+
+static int test_ctvpd_behavior_case6(void)
+{
+ int port = PORT0;
+
+ mock_set_vconn(VCONN_0);
+ host_disconnect_source();
+ TEST_ASSERT(ct_disconnect_source());
+ TEST_ASSERT(ct_disconnect_sink());
+
+ task_wake(PD_PORT_TO_TASK_ID(port));
+ task_wait_event(40 * MSEC);
+
+ /*
+ * CASE 6: The following describes the behavior when a DRP is connected
+ * to a Charge-Through VCONN-Powered USB Device
+ * (abbreviated CTVPD) and a Sink is attached to the
+ * Charge-Through port on the CTVPD.
+ */
+
+ /*
+ * 1. DRP, CTVPD and Sink are all in the unattached state
+ *
+ * a. DRP alternates between Unattached.SRC and Unattached.SNK
+ * b. CTVPD has applied Rd on its Charge-Through port’s CC1 and CC2
+ * pins and Rd on the Host-side port’s CC pin
+ */
+ TEST_ASSERT(get_typec_state_id(port) == UNATTACHED_SNK);
+ TEST_ASSERT(check_ct_ccs_rd());
+ TEST_ASSERT(check_host_ra_rd());
+
+ /*
+ * 2. DRP transitions from Unattached.SRC to AttachWait.SRC to
+ * Attached.SRC
+ *
+ * a. DRP in Unattached.SRC detects the CC pull-down of CTVPD which
+ * is in Unattached.SNK and DRP enters AttachWait.SRC
+ * b. DRP in AttachWait.SRC detects that pull down on CC persists
+ * for tCCDebounce, enters Attached.SRC and turns on VBUS and
+ * VCONN
+ */
+ host_connect_source(VBUS_5);
+ mock_set_vconn(VCONN_3);
+
+ /*
+ * 3. CTVPD transitions from Unattached.SNK to Attached.SNK through
+ * AttachWait.SNK.
+ *
+ * a. CTVPD detects the host-side CC pull-up of the DRP and CTVPD
+ * enters AttachWait.SNK
+ * b. CTVPD in AttachWait.SNK detects that pull up on the Host-side
+ * port’s CC persists for tCCDebounce, VCONN present and enters
+ * Attached.SNK
+ * c. CTVPD present a high-impedance to ground (above zOPEN) on its
+ * Charge-Through port’s CC1 and CC2 pins
+ */
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACH_WAIT_SNK);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == ATTACHED_SNK);
+ TEST_ASSERT(check_ct_ccs_hz());
+
+ /*
+ * 4. While DRP and CTVPD are in their respective attached states, DRP
+ * discovers the ChargeThrough CTVPD and transitions to
+ * CTUnattached.SNK
+ *
+ * a. DRP (as Source) queries the device identity via USB PD
+ * (Discover Identity Command) on SOP’.
+ * b. CTVPD responds on SOP’, advertising that it is a
+ * Charge-Through VCONN-Powered USB Device
+ * c. DRP (as Source) removes VBUS
+ * d. DRP (as Source) changes its Rp to a Rd
+ * e. DRP (as Sink) continues to provide VCONN and enters
+ * CTUnattached.SNK
+ */
+
+ host_disconnect_source();
+ host_connect_sink(SRC_CON_DEF);
+
+ /*
+ * 5. CTVPD transitions to CTUnattached.VPD
+ *
+ * a. CTVPD detects VBUS removal, VCONN presence, the low Host-side
+ * CC pin and enters CTUnattached.VPD
+ * b. CTVPD changes its host-side Rd to a Rp termination advertising
+ * 3.0 A
+ * c. CTVPD isolates itself from VBUS
+ * d. CTVPD apply Rd on its Charge-Through port’s CC1 and CC2 pins
+ */
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+ TEST_ASSERT(check_host_rp3a0());
+ TEST_ASSERT(mock_get_vbus_pass_en() == 0);
+ TEST_ASSERT(check_ct_ccs_rd());
+
+ /*
+ * 6. CTVPD alternates between CTUnattached.VPD and
+ * CTUnattached.Unsupported
+ *
+ * a. CTVPD detects SRC.open on its Charge-Through CC pins and
+ * starts alternating between CTUnattached.VPD and
+ * CTUnattached.Unsupported
+ */
+ wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ wait_for_state_change(port, PD_T_DRP_SNK + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_UNSUPPORTED);
+
+ /*
+ * 7. CTVPD transitions from CTUnattached.Unsupported to CTTry.SNK
+ * through CTAttachWait.Unsupported
+ *
+ * a. CTVPD in CTUnattached.Unsupported detects the CC pull-down of
+ * the Sink which is in Unattached.SNK and CTVPD enters
+ * CTAttachWait.Unsupported
+ * b. CTVPD in CTAttachWait.Unsupported detects that pull down on CC
+ * persists for tCCDebounce and enters CTTry.SNK
+ * c. CTVPD disables Rp termination advertising Default USB Power on
+ * the ChargeThrough port’s CC pins
+ * d. CTVPD enables Rd on the Charge-Through port’s CC pins
+ */
+ TEST_ASSERT(ct_connect_sink(CC1, SRC_CON_DEF));
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACH_WAIT_UNSUPPORTED);
+
+ wait_for_state_change(port, PD_T_CC_DEBOUNCE + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTTRY_SNK);
+ TEST_ASSERT(check_ct_ccs_rd());
+
+ /*
+ * 8. CTVPD transitions from CTTry.SNK to CTAttached.Unsupported
+ *
+ * a. CTVPD didn’t detect the CC pull-up of the potential Source
+ * for tDRPTryWait after tDRPTry and enters
+ * CTAttached.Unsupported
+ */
+
+ wait_for_state_change(port, PD_T_DRP_TRY + PD_T_TRY_WAIT + 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTATTACHED_UNSUPPORTED);
+
+ /*
+ * 9. While the CTVPD in CTAttached.Unsupported state, the DRP in
+ * CTUnattached.SNK state and the Sink in Unattached.SNK state:
+ *
+ * a. CTVPD disables the Rd termination on the Charge-Through
+ * port’s CC pins and applies Rp termination advertising
+ * Default USB Power
+ * b. CTVPD exposes a USB Billboard Device Class to the DRP
+ * indicating that it is connected to an unsupported device on
+ * its Charge Through port
+ * c. CTVPD monitors Charge-Though CC pins for Sink detach and when
+ * detected, enters CTUnattached.VPD
+ * d. CTVPD monitors VCONN for Host detach and when detected, enters
+ * Unattached.SNK
+ * e. DRP monitors CC for CTVPD detach for tVPDDetach and when
+ * detected, enters Unattached.SNK
+ * f. DRP monitors VBUS for CTVPD Charge-Through source attach and,
+ * when detected, enters CTAttached.SNK
+ */
+
+ TEST_ASSERT(check_ct_ccs_cc1_rpusb());
+ TEST_ASSERT(mock_get_present_billboard() == BB_SNK);
+
+ TEST_ASSERT(ct_disconnect_sink());
+
+ wait_for_state_change(port, 40 * MSEC);
+
+ TEST_ASSERT(get_typec_state_id(port) == CTUNATTACHED_VPD);
+
+ return EC_SUCCESS;
+}
+#endif
+
+void run_test(void)
+{
+ test_reset();
+
+ init_port(PORT0);
+
+ /* VPD and CTVPD tests */
+ RUN_TEST(test_vpd_host_src_detection);
+ RUN_TEST(test_vpd_host_src_detection_vbus);
+ RUN_TEST(test_vpd_host_src_detection_vconn);
+ RUN_TEST(test_vpd_host_src_detection_message_reception);
+
+ /* CTVPD only tests */
+#if defined(TEST_USB_TYPEC_CTVPD)
+ /* DRP to VCONN-Powered USB Device (CTVPD) Behavior Tests */
+ RUN_TEST(test_ctvpd_behavior_case1);
+ RUN_TEST(test_ctvpd_behavior_case2);
+ RUN_TEST(test_ctvpd_behavior_case3);
+ RUN_TEST(test_ctvpd_behavior_case4);
+ RUN_TEST(test_ctvpd_behavior_case5);
+ RUN_TEST(test_ctvpd_behavior_case6);
+#endif
+ test_print_result();
+}
+
diff --git a/test/nvmem_vars.tasklist b/test/usb_typec_ctvpd.tasklist
index cc500f5e8f..96ce0f08eb 100644
--- a/test/nvmem_vars.tasklist
+++ b/test/usb_typec_ctvpd.tasklist
@@ -1,4 +1,4 @@
-/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2019 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
@@ -14,4 +14,5 @@
* 'd' in an opaque parameter passed to the routine at startup
* 's' is the stack size in bytes; must be a multiple of 8
*/
-#define CONFIG_TEST_TASK_LIST /* No test task */
+#define CONFIG_TEST_TASK_LIST \
+ TASK_TEST(PD_C0, pd_task, NULL, LARGER_TASK_STACK_SIZE)
diff --git a/test/usb_typec_vpd.tasklist b/test/usb_typec_vpd.tasklist
new file mode 120000
index 0000000000..3e39415ded
--- /dev/null
+++ b/test/usb_typec_vpd.tasklist
@@ -0,0 +1 @@
+usb_typec_ctvpd.tasklist \ No newline at end of file
diff --git a/test/vpd_api.c b/test/vpd_api.c
new file mode 100644
index 0000000000..960c0c664b
--- /dev/null
+++ b/test/vpd_api.c
@@ -0,0 +1,586 @@
+/* Copyright 2019 The Chromium OS 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 "registers.h"
+#include "vpd_api.h"
+#include "driver/tcpm/tcpm.h"
+#include "console.h"
+/*
+ * Polarity based on 'DFP Perspective' (see table USB Type-C Cable and Connector
+ * Specification)
+ *
+ * CC1 CC2 STATE POSITION
+ * ----------------------------------------
+ * open open NC N/A
+ * Rd open UFP attached 1
+ * open Rd UFP attached 2
+ * open Ra pwr cable no UFP N/A
+ * Ra open pwr cable no UFP N/A
+ * Rd Ra pwr cable & UFP 1
+ * Ra Rd pwr cable & UFP 2
+ * Rd Rd dbg accessory N/A
+ * Ra Ra audio accessory N/A
+ *
+ * Note, V(Rd) > V(Ra)
+ */
+#ifndef PD_SRC_RD_THRESHOLD
+#define PD_SRC_RD_THRESHOLD PD_SRC_DEF_RD_THRESH_MV
+#endif
+#ifndef PD_SRC_VNC
+#define PD_SRC_VNC PD_SRC_DEF_VNC_MV
+#endif
+
+#ifndef CC_RA
+#define CC_RA(port, cc, sel) (cc < pd_src_rd_threshold[ct_cc_rp_value])
+#endif
+#define CC_RD(cc) ((cc >= PD_SRC_RD_THRESHOLD) && (cc < PD_SRC_VNC))
+#ifndef CC_NC
+#define CC_NC(port, cc, sel) (cc >= PD_SRC_VNC)
+#endif
+
+/*
+ * Polarity based on 'UFP Perspective'.
+ *
+ * CC1 CC2 STATE POSITION
+ * ----------------------------------------
+ * open open NC N/A
+ * Rp open DFP attached 1
+ * open Rp DFP attached 2
+ * Rp Rp Accessory attached N/A
+ */
+#ifndef PD_SNK_VA
+#define PD_SNK_VA PD_SNK_VA_MV
+#endif
+
+#define CC_RP(cc) (cc >= PD_SNK_VA)
+
+/* Mock Board State */
+static enum vpd_pwr mock_vconn_pwr_sel_odl;
+static enum vpd_gpo mock_cc1_cc2_rd_l;
+static enum vpd_gpo mock_cc_db_en_od;
+static enum vpd_gpo mock_cc_rpusb_odh;
+static enum vpd_cc mock_ct_cl_sel;
+static int mock_mcu_cc_en;
+static enum vpd_billboard mock_present_billboard;
+static int mock_red_led;
+static int mock_green_led;
+static int mock_vbus_pass_en;
+
+static int mock_read_host_vbus;
+static int mock_read_ct_vbus;
+static int mock_read_vconn;
+
+static struct mock_pin mock_cc2_rpusb_odh;
+static struct mock_pin mock_cc2_rp3a0_rd_l;
+static struct mock_pin mock_cc1_rpusb_odh;
+static struct mock_pin mock_cc1_rp3a0_rd_l;
+static struct mock_pin mock_cc_vpdmcu;
+static struct mock_pin mock_cc_rp3a0_rd_l;
+
+/* Charge-Through pull up/down enabled */
+static int ct_cc_pull;
+/* Charge-Through pull up value */
+static int ct_cc_rp_value;
+
+/* Charge-Through pull up/down enabled */
+static int host_cc_pull;
+/* Charge-Through pull up value */
+static int host_cc_rp_value;
+
+/* Voltage thresholds for Ra attach in normal SRC mode */
+static int pd_src_rd_threshold[TYPEC_RP_RESERVED] = {
+ PD_SRC_DEF_RD_THRESH_MV,
+ PD_SRC_1_5_RD_THRESH_MV,
+ PD_SRC_3_0_RD_THRESH_MV,
+};
+
+enum vpd_pwr mock_get_vconn_pwr_source(void)
+{
+ return mock_vconn_pwr_sel_odl;
+}
+
+int mock_get_ct_cc1_rpusb(void)
+{
+ return mock_cc1_rpusb_odh.value;
+}
+
+int mock_get_ct_cc2_rpusb(void)
+{
+ return mock_cc2_rpusb_odh.value;
+}
+
+enum vpd_gpo mock_get_ct_rd(void)
+{
+ return mock_cc1_cc2_rd_l;
+}
+
+enum vpd_gpo mock_get_cc_rpusb_odh(void)
+{
+ return mock_cc_rpusb_odh;
+}
+
+enum vpd_gpo mock_get_cc_db_en_od(void)
+{
+ return mock_cc_db_en_od;
+}
+
+enum vpd_cc moch_get_ct_cl_sel(void)
+{
+ return mock_ct_cl_sel;
+}
+
+int mock_get_mcu_cc_en(void)
+{
+ return mock_mcu_cc_en;
+}
+
+enum vpd_billboard mock_get_present_billboard(void)
+{
+ return mock_present_billboard;
+}
+
+int mock_get_red_led(void)
+{
+ return mock_red_led;
+}
+
+int mock_get_green_led(void)
+{
+ return mock_green_led;
+}
+
+int mock_get_vbus_pass_en(void)
+{
+ return mock_vbus_pass_en;
+}
+
+void mock_set_host_cc_sink_voltage(int v)
+{
+ mock_cc_vpdmcu.value = v;
+}
+
+void mock_set_host_cc_source_voltage(int v)
+{
+ mock_cc_vpdmcu.value2 = v;
+}
+
+void mock_set_host_vbus(int v)
+{
+ mock_read_host_vbus = v;
+}
+
+void mock_set_ct_vbus(int v)
+{
+ mock_read_ct_vbus = v;
+}
+
+void mock_set_vconn(int v)
+{
+ mock_read_vconn = v;
+}
+
+int mock_get_cfg_cc2_rpusb_odh(void)
+{
+ return mock_cc2_rpusb_odh.cfg;
+}
+
+int mock_set_cc2_rpusb_odh(int v)
+{
+ if (mock_cc2_rpusb_odh.cfg == PIN_ADC) {
+ mock_cc2_rpusb_odh.value = v;
+ return 1;
+ }
+ return 0;
+}
+
+int mock_get_cfg_cc2_rp3a0_rd_l(void)
+{
+ return mock_cc2_rp3a0_rd_l.cfg;
+}
+
+int mock_set_cc2_rp3a0_rd_l(int v)
+{
+ if (mock_cc2_rp3a0_rd_l.cfg == PIN_ADC) {
+ mock_cc2_rp3a0_rd_l.value = v;
+ return 1;
+ }
+
+ return 0;
+}
+
+int mock_get_cc1_rpusb_odh(void)
+{
+ return mock_cc1_rpusb_odh.cfg;
+}
+
+int mock_set_cc1_rpusb_odh(int v)
+{
+ if (mock_cc1_rpusb_odh.cfg == PIN_ADC) {
+ mock_cc1_rpusb_odh.value = v;
+ return 1;
+ }
+
+ return 0;
+}
+
+int mock_get_cfg_cc_vpdmcu(void)
+{
+ return mock_cc_vpdmcu.cfg;
+}
+
+enum vpd_pin mock_get_cfg_cc_rp3a0_rd_l(void)
+{
+ return mock_cc_rp3a0_rd_l.cfg;
+}
+
+int mock_get_cc_rp3a0_rd_l(void)
+{
+ return mock_cc_rp3a0_rd_l.value;
+}
+
+int mock_get_cfg_cc1_rp3a0_rd_l(void)
+{
+ return mock_cc1_rp3a0_rd_l.cfg;
+}
+
+int mock_set_cc1_rp3a0_rd_l(int v)
+{
+ if (mock_cc1_rp3a0_rd_l.cfg == PIN_ADC) {
+ mock_cc1_rp3a0_rd_l.value = v;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Convert CC voltage to CC status */
+static int vpd_cc_voltage_to_status(int cc_volt, int cc_pull)
+{
+ /* If we have a pull-up, then we are source, check for Rd. */
+ if (cc_pull == TYPEC_CC_RP) {
+ if (CC_NC(0, cc_volt, 0))
+ return TYPEC_CC_VOLT_OPEN;
+ else if (CC_RA(0, cc_volt, 0))
+ return TYPEC_CC_VOLT_RA;
+ else
+ return TYPEC_CC_VOLT_RD;
+ /* If we have a pull-down, then we are sink, check for Rp. */
+ } else if (cc_pull == TYPEC_CC_RD || cc_pull == TYPEC_CC_RA_RD) {
+ if (cc_volt >= TYPE_C_SRC_3000_THRESHOLD)
+ return TYPEC_CC_VOLT_RP_3_0;
+ else if (cc_volt >= TYPE_C_SRC_1500_THRESHOLD)
+ return TYPEC_CC_VOLT_RP_1_5;
+ else if (CC_RP(cc_volt))
+ return TYPEC_CC_VOLT_RP_DEF;
+ else
+ return TYPEC_CC_VOLT_OPEN;
+ } else {
+ /* If we are open, then always return 0 */
+ return 0;
+ }
+}
+
+void vpd_ct_set_pull(int pull, int rp_value)
+{
+ ct_cc_pull = pull;
+
+ switch (pull) {
+ case TYPEC_CC_RP:
+ ct_cc_rp_value = rp_value;
+ vpd_cc1_cc2_db_en_l(GPO_HIGH);
+ switch (rp_value) {
+ case TYPEC_RP_USB:
+ vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc1_rpusb_odh(PIN_GPO, 1);
+ vpd_config_cc2_rpusb_odh(PIN_GPO, 1);
+ break;
+ case TYPEC_RP_3A0:
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_GPO, 1);
+ vpd_config_cc2_rp3a0_rd_l(PIN_GPO, 1);
+ break;
+ }
+ break;
+ case TYPEC_CC_RD:
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_cc1_cc2_db_en_l(GPO_LOW);
+ break;
+ case TYPEC_CC_OPEN:
+ vpd_cc1_cc2_db_en_l(GPO_HIGH);
+ vpd_config_cc1_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc2_rpusb_odh(PIN_ADC, 0);
+ vpd_config_cc1_rp3a0_rd_l(PIN_ADC, 0);
+ vpd_config_cc2_rp3a0_rd_l(PIN_ADC, 0);
+ break;
+ }
+}
+
+void vpd_ct_get_cc(int *cc1, int *cc2)
+{
+ int cc1_v;
+ int cc2_v;
+
+ switch (ct_cc_pull) {
+ case TYPEC_CC_RP:
+ switch (ct_cc_rp_value) {
+ case TYPEC_RP_USB:
+ cc1_v = mock_cc1_rp3a0_rd_l.value;
+ cc2_v = mock_cc2_rp3a0_rd_l.value;
+ break;
+ case TYPEC_RP_3A0:
+ cc1_v = mock_cc1_rpusb_odh.value;
+ cc2_v = mock_cc2_rpusb_odh.value;
+ break;
+ }
+
+ if (!cc1_v && !cc2_v) {
+ cc1_v = PD_SRC_VNC;
+ cc2_v = PD_SRC_VNC;
+ }
+ break;
+ case TYPEC_CC_RD:
+ cc1_v = mock_cc1_rpusb_odh.value;
+ cc2_v = mock_cc2_rpusb_odh.value;
+ break;
+ case TYPEC_CC_OPEN:
+ *cc1 = 0;
+ *cc2 = 0;
+ return;
+ }
+
+ *cc1 = vpd_cc_voltage_to_status(cc1_v, ct_cc_pull);
+ *cc2 = vpd_cc_voltage_to_status(cc2_v, ct_cc_pull);
+}
+
+void vpd_host_set_pull(int pull, int rp_value)
+{
+ host_cc_pull = pull;
+
+ switch (pull) {
+ case TYPEC_CC_RP:
+ vpd_cc_db_en_od(GPO_LOW);
+ host_cc_rp_value = rp_value;
+ switch (rp_value) {
+ case TYPEC_RP_USB:
+ vpd_config_cc_rp3a0_rd_l(PIN_CMP, 0);
+ vpd_cc_rpusb_odh(GPO_HIGH);
+ break;
+ case TYPEC_RP_3A0:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 1);
+ break;
+ }
+ break;
+ case TYPEC_CC_RD:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_cc_db_en_od(GPO_LOW);
+
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 0);
+ break;
+ case TYPEC_CC_RA_RD:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_GPO, 0);
+
+ /*
+ * RA is connected to VCONN
+ * RD is connected to CC
+ */
+ vpd_cc_db_en_od(GPO_HZ);
+ break;
+ case TYPEC_CC_OPEN:
+ vpd_cc_rpusb_odh(GPO_HZ);
+ vpd_config_cc_rp3a0_rd_l(PIN_CMP, 0);
+ vpd_cc_db_en_od(GPO_LOW);
+
+ /*
+ * Do nothing. CC is open on entry to this function
+ */
+ break;
+ }
+}
+
+void vpd_host_get_cc(int *cc)
+{
+ int v;
+
+ if (host_cc_pull == TYPEC_CC_OPEN) {
+ *cc = 0;
+ return;
+ } else if (host_cc_pull == TYPEC_CC_RP) {
+ v = mock_cc_vpdmcu.value;
+ } else {
+ v = mock_cc_vpdmcu.value2;
+ }
+
+ *cc = vpd_cc_voltage_to_status(v, host_cc_pull);
+}
+
+void vpd_rx_enable(int en)
+{
+ if (en) {
+ mock_ct_cl_sel = 0;
+ mock_mcu_cc_en = 1;
+ }
+
+ tcpm_set_polarity(0, 0);
+ tcpm_set_rx_enable(0, en);
+}
+
+/*
+ * PA1: Configure as ADC, CMP, or GPO
+ */
+void vpd_config_cc_vpdmcu(enum vpd_pin cfg, int en)
+{
+ mock_cc_vpdmcu.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc_vpdmcu.value = en ? 1 : 0;
+}
+
+/*
+ * PA2: Configure as COMP2_INM6 or GPO
+ */
+void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ mock_cc_rp3a0_rd_l.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc_rp3a0_rd_l.value = en ? 1 : 0;
+}
+
+/*
+ * PA4: Configure as ADC, CMP, or GPO
+ */
+void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ mock_cc1_rp3a0_rd_l.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc1_rp3a0_rd_l.value = en ? 1 : 0;
+}
+
+/*
+ * PA5: Configure as ADC, COMP, or GPO
+ */
+void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en)
+{
+ mock_cc2_rp3a0_rd_l.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc2_rp3a0_rd_l.value = en ? 1 : 0;
+}
+
+/*
+ * PB0: Configure as ADC or GPO
+ */
+void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en)
+{
+ mock_cc1_rpusb_odh.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc1_rpusb_odh.value = en ? 1 : 0;
+}
+
+/*
+ * PB1: Configure as ADC or GPO
+ */
+void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en)
+{
+ mock_cc2_rpusb_odh.cfg = cfg;
+
+ if (cfg == PIN_GPO)
+ mock_cc2_rpusb_odh.value = en ? 1 : 0;
+}
+
+int vpd_read_host_vbus(void)
+{
+ return mock_read_host_vbus;
+}
+
+int vpd_read_ct_vbus(void)
+{
+ return mock_read_ct_vbus;
+}
+
+int vpd_read_vconn(void)
+{
+ return mock_read_vconn;
+}
+
+int vpd_is_host_vbus_present(void)
+{
+ return (vpd_read_host_vbus() >= PD_SNK_VA);
+}
+
+int vpd_is_ct_vbus_present(void)
+{
+ return (vpd_read_ct_vbus() >= PD_SNK_VA);
+}
+
+int vpd_is_vconn_present(void)
+{
+ return (vpd_read_vconn() >= PD_SNK_VA);
+}
+
+int vpd_read_rdconnect_ref(void)
+{
+ return 200; /* 200 mV */
+}
+
+void vpd_red_led(int on)
+{
+ mock_red_led = on ? 0 : 1;
+}
+
+void vpd_green_led(int on)
+{
+ mock_green_led = on ? 0 : 1;
+}
+
+void vpd_vbus_pass_en(int en)
+{
+ mock_vbus_pass_en = en ? 1 : 0;
+}
+
+void vpd_present_billboard(enum vpd_billboard bb)
+{
+ mock_present_billboard = bb;
+}
+
+void vpd_mcu_cc_en(int en)
+{
+ mock_mcu_cc_en = en ? 1 : 0;
+}
+
+void vpd_ct_cc_sel(enum vpd_cc sel)
+{
+ mock_ct_cl_sel = sel;
+}
+
+/* Set as GPO High, GPO Low, or High-Z */
+void vpd_cc_db_en_od(enum vpd_gpo val)
+{
+ mock_cc_db_en_od = val;
+}
+
+void vpd_cc_rpusb_odh(enum vpd_gpo val)
+{
+ mock_cc_rpusb_odh = val;
+}
+
+void vpd_cc1_cc2_db_en_l(enum vpd_gpo val)
+{
+ mock_cc1_cc2_rd_l = val;
+}
+
+void vpd_vconn_pwr_sel_odl(enum vpd_pwr en)
+{
+ mock_vconn_pwr_sel_odl = en;
+}
diff --git a/test/vpd_api.h b/test/vpd_api.h
new file mode 100644
index 0000000000..3db4803288
--- /dev/null
+++ b/test/vpd_api.h
@@ -0,0 +1,333 @@
+/* Copyright 2019 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Vconn Power Device API module */
+
+#ifndef __CROS_EC_VPD_API_H
+#define __CROS_EC_VPD_API_H
+
+#include "adc.h"
+#include "gpio.h"
+#include "usb_pd.h"
+
+/*
+ * Type C power source charge current limits are identified by their cc
+ * voltage (set by selecting the proper Rd resistor). Any voltage below
+ * TYPE_C_SRC_DEFAULT_THRESHOLD will not be identified as a type C charger.
+ */
+#define TYPE_C_SRC_DEFAULT_THRESHOLD 200 /* mV */
+#define TYPE_C_SRC_1500_THRESHOLD 660 /* mV */
+#define TYPE_C_SRC_3000_THRESHOLD 1230 /* mV */
+
+
+enum vpd_pin {
+ PIN_ADC,
+ PIN_CMP,
+ PIN_GPO
+};
+
+enum vpd_gpo {
+ GPO_HZ,
+ GPO_HIGH,
+ GPO_LOW
+};
+
+enum vpd_pwr {
+ PWR_VCONN,
+ PWR_VBUS,
+};
+
+enum vpd_cc {
+ CT_OPEN,
+ CT_CC1,
+ CT_CC2
+};
+
+enum vpd_billboard {
+ BB_NONE,
+ BB_SRC,
+ BB_SNK
+};
+
+struct mock_pin {
+ enum vpd_pin cfg;
+ int value;
+ int value2;
+};
+
+enum vpd_pwr mock_get_vconn_pwr_source(void);
+enum vpd_gpo mock_get_ct_rd(void);
+enum vpd_gpo mock_get_cc_rp1a5_odh(void);
+enum vpd_gpo mock_get_cc_rpusb_odh(void);
+enum vpd_gpo mock_get_cc_db_en_od(void);
+enum vpd_cc moch_get_ct_cl_sel(void);
+int mock_get_mcu_cc_en(void);
+enum vpd_billboard mock_get_present_billboard(void);
+int mock_get_red_led(void);
+int mock_get_green_led(void);
+int mock_get_vbus_pass_en(void);
+int mock_set_cc_vpdmcu(int v);
+void mock_set_host_vbus(int v);
+void mock_set_ct_vbus(int v);
+void mock_set_vconn(int v);
+int mock_get_cfg_cc2_rpusb_odh(void);
+int mock_set_cc2_rpusb_odh(int v);
+int mock_get_cfg_cc2_rp3a0_rd_l(void);
+int mock_set_cc2_rp3a0_rd_l(int v);
+int mock_get_cfg_cc1_rpusb_odh(void);
+int mock_set_cc1_rpusb_odh(int v);
+int mock_get_cfg_cc_vpdmcu(void);
+int mock_get_cc_vpdmcu(int v);
+enum vpd_pin mock_get_cfg_cc_rp3a0_rd_l(void);
+int mock_get_cc_rp3a0_rd_l(void);
+int mock_get_cfg_cc1_rp3a0_rd_l(void);
+int mock_set_cc1_rp3a0_rd_l(int v);
+void mock_set_host_cc_sink_voltage(int v);
+void mock_set_host_cc_source_voltage(int v);
+int mock_get_ct_cc1_rpusb(void);
+int mock_get_ct_cc2_rpusb(void);
+
+/**
+ * Set Charge-Through Rp or Rd on CC lines
+ *
+ * @param pull Either TYPEC_CC_RP or TYPEC_CC_RD
+ * @param rp_value When pull is RP, set this to
+ * TYPEC_RP_USB or TYPEC_RP_1A5. Ignored
+ * for TYPEC_CC_RD
+ */
+void vpd_ct_set_pull(int pull, int rp_value);
+
+/**
+ * Get the status of the Charge-Through CC lines
+ *
+ * @param cc1 Either TYPEC_CC_VOLT_OPEN,
+ * TYPEC_CC_VOLT_RA,
+ * TYPEC_CC_VOLT_RD,
+ * any other value is considered RP
+ * @param cc2 Either TYPEC_CC_VOLT_OPEN,
+ * TYPEC_CC_VOLT_RA,
+ * TYPEC_CC_VOLT_RD,
+ * any other value is considered RP
+ */
+void vpd_ct_get_cc(int *cc1, int *cc2);
+
+/**
+ * Set Host Rp or Rd on CC lines
+ *
+ * @param pull Either TYPEC_CC_RP or TYPEC_CC_RD
+ * @param rp_value When pull is RP, set this to
+ * TYPEC_RP_USB or TYPEC_RP_1A5. Ignored
+ * for TYPEC_CC_RD
+ */
+void vpd_host_set_pull(int pull, int rp_value);
+
+/**
+ * Get the status of the Host CC line
+ *
+ * @param cc Either TYPEC_CC_VOLT_SNK_DEF, TYPEC_CC_VOLT_SNK_1_5,
+ * TYPEC_CC_VOLT_SNK_3_0, or TYPEC_CC_RD
+ */
+void vpd_host_get_cc(int *cc);
+
+/**
+ * Set RX Enable flag
+ *
+ * @param en 1 for enable, 0 for disable
+ */
+void vpd_rx_enable(int en);
+
+/**
+ * Configure the cc_vpdmcu pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc_vpdmcu(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc1_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc1_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc2_rp3a0_rd_l pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc2_rp3a0_rd_l(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc1_rpusb_odh pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc1_rpusb_odh(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc2_rpusb_odh pin as ADC, CMP, or GPO
+ *
+ * @param cfg PIN_ADC, PIN_CMP, or PIN_GPO
+ * @param en When cfg is PIN_GPO, 1 sets pin high
+ * and 0 sets pin low. Else ignored
+ */
+void vpd_config_cc2_rpusb_odh(enum vpd_pin cfg, int en);
+
+/**
+ * Configure the cc_db_en_od pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_db_en_od(enum vpd_gpo val);
+
+/**
+ * Configure the cc_rpusb_odh pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_rpusb_odh(enum vpd_gpo val);
+
+/**
+ * Configure the cc_rp1a5_odh pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc_rp1a5_odh(enum vpd_gpo val);
+
+/**
+ * Configure the cc1_cc2_db_en_l pin to High-Impedance, low, or high
+ *
+ * @param val GPO_HZ, GPO_HIGH, GPO_LOW
+ */
+void vpd_cc1_cc2_db_en_l(enum vpd_gpo val);
+
+/**
+ * Get status of host vbus
+ *
+ * @return 1 if host vbus is present, else 0
+ */
+int vpd_is_host_vbus_present(void);
+
+/**
+ * Get status of charge-through vbus
+ *
+ * @return 1 if charge-through vbus is present, else 0
+ */
+int vpd_is_ct_vbus_present(void);
+
+/**
+ * Get status of vconn
+ *
+ * @return 1 if vconn is present, else 0
+ */
+int vpd_is_vconn_present(void);
+
+/**
+ * Read Host VBUS voltage. Range from 22000mV to 3000mV
+ *
+ * @return vbus voltage
+ */
+int vpd_read_host_vbus(void);
+
+/**
+ * Read Host CC voltage.
+ *
+ * @return cc voltage
+ */
+int vpd_read_cc_host(void);
+
+/**
+ * Read voltage on cc_vpdmcu pin
+ *
+ * @return cc_vpdmcu voltage
+ */
+int vpd_read_cc_vpdmcu(void);
+
+/**
+ * Read charge-through VBUS voltage. Range from 22000mV to 3000mV
+ *
+ * @return charge-through vbus voltage
+ */
+int vpd_read_ct_vbus(void);
+
+/**
+ * Read VCONN Voltage. Range from 5500mV to 3000mV
+ *
+ * @return vconn voltage
+ */
+int vpd_read_vconn(void);
+
+/**
+ * Turn ON/OFF Red LED. Should be off when performing power
+ * measurements.
+ *
+ * @param on 0 turns LED off, any other value turns it ON
+ */
+void vpd_red_led(int on);
+
+/**
+ * Turn ON/OFF Green LED. Should be off when performing power
+ * measurements.
+ *
+ * @param on 0 turns LED off, any other value turns it ON
+ */
+void vpd_green_led(int on);
+
+/**
+ * Connects/Disconnects the Host VBUS to the Charge-Through VBUS.
+ *
+ * @param en 0 disconnectes the VBUS, any other value connects VBUS.
+ */
+void vpd_vbus_pass_en(int en);
+
+/**
+ * Preset Billboard device
+ *
+ * @param bb BB_NONE no billboard presented,
+ * BB_SRC source connected but not in charge-through
+ * BB_SNK sink connected
+ */
+void vpd_present_billboard(enum vpd_billboard bb);
+
+/**
+ * Enables the MCU to host cc communication
+ *
+ * @param en 1 enabled, 0 disabled
+ */
+void vpd_mcu_cc_en(int en);
+
+/**
+ * Selects which supply to power the VPD from
+ *
+ * @param en PWR_VCONN or PWR_VBUS
+ */
+void vpd_vconn_pwr_sel_odl(enum vpd_pwr en);
+
+/**
+ * Controls if the Charge-Through's CC1, CC2, or neither is
+ * connected to Host CC
+ *
+ * @param sel CT_OPEN neither, CT_CC1 cc1, CT_CC2 cc2
+ */
+void vpd_ct_cc_sel(enum vpd_cc sel);
+
+#endif /* __CROS_EC_VPD_API_H */
diff --git a/util/build.mk b/util/build.mk
index fef646f2de..7699ef4572 100644
--- a/util/build.mk
+++ b/util/build.mk
@@ -7,8 +7,8 @@
#
host-util-bin=ectool lbplay stm32mon ec_sb_firmware_update lbcc \
- ec_parse_panicinfo cbi-util
-build-util-bin=ec_uartd iteflash
+ ec_parse_panicinfo cbi-util iteflash
+build-util-bin=ec_uartd
build-util-art+=util/export_taskinfo.so
ifeq ($(CHIP),npcx)
build-util-bin+=ecst
diff --git a/util/ecst.c b/util/ecst.c
index 328587e8d9..0680f8b1e8 100755
--- a/util/ecst.c
+++ b/util/ecst.c
@@ -45,7 +45,7 @@ struct chip_info chip_info[] = {{NPCX5M5G_RAM_ADDR, NPCX5M5G_RAM_SIZE},
/* Support chips name strings */
const char *supported_chips = "npcx5m5g, npcx5m6g, npcx7m5g, npcx7m6g, "
- "npcx7m6f, npcx7m6fb, npcx7m6fc, or npcx7m7wb";
+ "npcx7m6f, npcx7m6fb, npcx7m6fc, npcx7m7wb, or npcx7m7wc";
static unsigned int calc_api_csum_bin(void);
static unsigned int initialize_crc_32(void);
@@ -239,8 +239,10 @@ int main(int argc, char *argv[])
supported_chips);
main_status = FALSE;
} else {
- if (str_cmp_no_case(main_str_temp,
- "npcx7m7wb") == 0) {
+ if ((str_cmp_no_case(main_str_temp,
+ "npcx7m7wb") == 0) ||
+ (str_cmp_no_case(main_str_temp,
+ "npcx7m7wc") == 0)) {
if ((bin_params.bin_params
& BIN_FW_LOAD_START_ADDR) ==
0x00000000)
diff --git a/util/ectool.c b/util/ectool.c
index 8c3e971afd..02af5f42cd 100644
--- a/util/ectool.c
+++ b/util/ectool.c
@@ -333,7 +333,7 @@ int parse_bool(const char *s, int *dest)
void print_help(const char *prog, int print_cmds)
{
printf("Usage: %s [--dev=n] [--interface=dev|i2c|lpc] ", prog);
- printf("[--name=cros_ec|cros_fp|cros_pd|cros_scp|cros_sh] [--ascii] ");
+ printf("[--name=cros_ec|cros_fp|cros_pd|cros_scp|cros_ish] [--ascii] ");
printf("<command> [params]\n\n");
if (print_cmds)
puts(help_str);
@@ -843,33 +843,14 @@ static const char *reset_cause_to_str(uint16_t cause)
int cmd_uptimeinfo(int argc, char *argv[])
{
- static const char * const reset_flag_strings[] = {
- "other",
- "reset-pin",
- "brownout",
- "power-on",
- "watchdog",
- "soft",
- "hibernate",
- "rtc-alarm",
- "wake-pin",
- "low-battery",
- "sysjump",
- "hard",
- "ap-off",
- "preserved",
- "usb-resume",
- "rdd",
- "rbox",
- "security",
- "ap-watchdog"
- };
-
struct ec_response_uptime_info r;
int rv;
int i;
int flag_count;
uint32_t flag;
+ static const char * const reset_flag_descs[] = {
+ #include "reset_flag_desc.inc"
+ };
if (argc != 1) {
fprintf(stderr, "uptimeinfo takes no arguments");
@@ -901,15 +882,27 @@ int cmd_uptimeinfo(int argc, char *argv[])
}
printf("EC reset flags at last EC boot: ");
+
+ if (!r.ec_reset_flags) {
+ printf("unknown\n");
+ return 0;
+ }
+
flag_count = 0;
- for (flag = 0; flag != ARRAY_SIZE(reset_flag_strings); ++flag) {
+ for (flag = 0; flag < ARRAY_SIZE(reset_flag_descs); ++flag) {
if ((r.ec_reset_flags & BIT(flag)) != 0) {
if (flag_count)
printf(" | ");
- printf(reset_flag_strings[flag]);
+ printf(reset_flag_descs[flag]);
flag_count++;
}
}
+
+ if (r.ec_reset_flags >= BIT(flag)) {
+ if (flag_count)
+ printf(" | ");
+ printf("no-desc");
+ }
printf("\n");
return 0;
}
@@ -1563,13 +1556,13 @@ int cmd_fp_mode(int argc, char *argv[])
int cmd_fp_seed(int argc, char *argv[])
{
struct ec_params_fp_seed p;
- const char *seed = argv[1];
- int rv;
+ char *seed;
- if (argc == 1) {
- printf("Missing seed argument.\n");
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <seed>\n", argv[0]);
return 1;
}
+ seed = argv[1];
if (strlen(seed) != FP_CONTEXT_TPM_BYTES) {
printf("Invalid seed '%s' is %zd bytes long instead of %d.\n",
seed, strlen(seed), FP_CONTEXT_TPM_BYTES);
@@ -1579,8 +1572,7 @@ int cmd_fp_seed(int argc, char *argv[])
p.struct_version = FP_TEMPLATE_FORMAT_VERSION;
memcpy(p.seed, seed, FP_CONTEXT_TPM_BYTES);
- rv = ec_command(EC_CMD_FP_SEED, 0, &p, sizeof(p), NULL, 0);
- return rv;
+ return ec_command(EC_CMD_FP_SEED, 0, &p, sizeof(p), NULL, 0);
}
int cmd_fp_stats(int argc, char *argv[])
diff --git a/util/flash_ec b/util/flash_ec
index 8456da6932..3b0dac40aa 100755
--- a/util/flash_ec
+++ b/util/flash_ec
@@ -1039,9 +1039,10 @@ function flash_stm32_dfu() {
$DFU_UTIL -a 0 -s ${ADDR}:${SIZE} -D "${IMG}"
}
+# TODO(b/130165933): Implement a dut-control command to look up the correct
+# I2C adapter number, and use that here in place of the hack of looking at
+# I2C adapter names.
function dut_i2c_dev() {
- # TODO(b/79684405): Drop support for $DUT_I2C_DEV env var override
- # as soon as dut-control dut_i2c_dev command is implemented.
if [ -n "$DUT_I2C_DEV" ]; then
[ -e "$DUT_I2C_DEV" ] ||
die "\$DUT_I2C_DEV is a non-existent path: $DUT_I2C_DEV"
@@ -1049,9 +1050,7 @@ function dut_i2c_dev() {
return
fi
- # TODO(b/79684405): Drop this hack as soon as dut-control dut_i2c_dev
- # command is implemented.
- if (
+ if ! (
set -e
cd /sys/class/i2c-dev
# Sorting in reverse numerical order generally picks the correct
@@ -1065,18 +1064,9 @@ function dut_i2c_dev() {
done
false
); then
- return
+ die "Could not find servo I2C adapter. This could be because "\
+"the i2c-pseudo module was not loaded when servod was started."
fi
-
- # TODO(b/79684405): This should just work once a dut-control dut_i2c_dev
- # command is implemented.
- local dut_i2c_dev="$("${DUT_CONTROL_CMD[@]}" dut_i2c_dev)" ||
- die "dut-control dut_i2c_dev exited $? (non-zero)"
- dut_i2c_dev="$(echo "$dut_i2c_dev" | sed -e 's/^dut_i2c_dev://')"
- [ -e "$dut_i2c_dev" ] ||
- die "dut-control dut_i2c_dev returned non-existent path: "\
-"$dut_i2c_dev"
- echo "$dut_i2c_dev"
}
function flash_it83xx() {
diff --git a/util/flash_fp_mcu_common.sh b/util/flash_fp_mcu_common.sh
index 31c264b617..38528382eb 100644
--- a/util/flash_fp_mcu_common.sh
+++ b/util/flash_fp_mcu_common.sh
@@ -18,6 +18,10 @@ if [[ "$#" -eq 0 ]]; then
exit 1
fi
+# print out canonical path to differentiate between /usr/local/bin and
+# /usr/bin installs
+echo "$(readlink -f "$0")"
+
check_hardware_write_protect_disabled() {
if ectool gpioget EC_WP_L | grep -q '= 0'; then
echo "Please make sure WP is deasserted."
diff --git a/util/getversion.sh b/util/getversion.sh
index 552f2d9bf0..765571492e 100755
--- a/util/getversion.sh
+++ b/util/getversion.sh
@@ -98,6 +98,9 @@ main() {
(cr50)
dir_list+=( ../../third_party/tpm2 ../../third_party/cryptoc )
;;
+ (*_fp)
+ dir_list+=( ./private )
+ ;;
esac
# Create a combined version string for all component directories.
diff --git a/util/iteflash.md b/util/iteflash.md
new file mode 100644
index 0000000000..df0e8db1a2
--- /dev/null
+++ b/util/iteflash.md
@@ -0,0 +1,119 @@
+# Reflashing an ITE EC
+
+This doc: [http://go/cros-ite-ec-reflash](https://goto.google.com/cros-ite-ec-reflash)
+<br>
+First written: 2019-04-02
+<br>
+Last updated: 2019-04-03
+
+Familiarity with [Chromium OS](https://www.chromium.org/chromium-os) [Embedded Controller (EC) development](https://www.chromium.org/chromium-os/ec-development) is assumed.
+
+[TOC]
+
+## Background
+
+### Terminology
+
+**ITE EC** refers to the [ITE](http://www.ite.com.tw/) [IT8320](http://www.ite.com.tw/en/product/view?mid=96) [Embedded Controller (EC)](https://en.wikipedia.org/wiki/Embedded_controller) microcontroller when used as a Chromium OS / Chrome OS EC.
+
+**CrOS** refers to Chromium OS, Chrome OS, or both, depending on the context. The distinction between Chromium OS and Chrome OS is largely immaterial to this document.
+
+**Servo** refers to a debug board providing direct debug access to various circuits on a Chrome OS device motherboard. As of this writing, the most common [servos](https://www.chromium.org/chromium-os/servo) used by CrOS developers are [CR50 (CCD)](https://www.chromium.org/chromium-os/ccd), [Servo Micro](https://www.chromium.org/chromium-os/servo/servomicro), and [Servo v2](https://www.chromium.org/chromium-os/servo/servo-v2). (Note that [Servo v4](https://www.chromium.org/chromium-os/servo/servov4) is **not** a Servo in this sense. It is a USB hub with a microcontroller that proxies Servo functionality from either CR50 or Servo Micro.) See also [Case-Closed Debug in Chromebooks and Servo Micro](https://chromium.googlesource.com/chromiumos/platform/ec/+/master/board/servo_micro/ccd.md).
+
+### How ITE EC reflashing works
+
+An ITE EC is reflashed using a Servo by:
+
+1. Sending special non-I2C waveforms over its I2C clock and data lines, to enable a debug mode / direct firmware update (DFU) mode.
+
+1. Communicating with it using I2C, including transferring the actual EC image over I2C.  The ITE EC will only respond over I2C after receiving the special waveforms.
+
+### Further reading
+
+Googlers, and Partners involved in ITE EC projects, see [The State of ITE CrOS EC Reflashing](https://docs.google.com/document/d/1fs29eBvwKrOWYozLZXTg7ObwAO5dyM4Js2Vq301EwAU/preview). That document is not public, do not request access if you lack it.
+
+## How to reflash
+
+### Prerequisites for CR50 CCD
+
+This section applies whether using CR50 CCD via [Servo v4](https://www.chromium.org/chromium-os/servo/servov4) or [SuzyQ aka SuzyQable](https://www.sparkfun.com/products/14746).
+
+CR50 MP minimum firmware version: `0.3.15`
+<br>
+CR50 pre-PVT minimum firmware version: `0.4.15`
+
+Googlers, to upgrade CR50 firmware if needed see [How to use CCD on CR50](https://docs.google.com/document/d/1MqDAoBsmGTmrFi-WNOoC5R-UFeuQK37_9kaEdCFU8QE/preview). That document is not public, do not request access if you lack it.
+
+The CR50 CCD capabilities must be set to `always`. To achieve this:
+
+1. Open CCD.
+ * root shell: `$ gsctool -o`
+ * CR50 console: `ccd open`
+1. Reset CCD to `factory` mode.
+ * CR50 console: `ccd reset factory`
+
+Reflashing with CR50 also requires the [i2c-pseudo kernel module](#i2c-pseudo), unless using the [CR50 CCD sans servod](#ccd-sans-servod) alternative method.
+
+### Prerequisites for Servo Micro
+
+This section applies whether the [Servo Micro](https://www.chromium.org/chromium-os/servo/servomicro) is connected directly to your development host, or through a [Servo v4](https://www.chromium.org/chromium-os/servo/servov4).
+
+Servo Micro minimum firmware version: `servo_micro_v2.3.5`
+
+To upgrade Servo Micro firmware if needed:
+
+1. Enter the chroot.
+ * `$ cros_sdk`
+1. Run servo_updater.
+ * `$ sudo servo_updater --board=servo_micro`
+
+If that still results in too old of a firmware version, use `repo sync` and `update_chroot` to update your CrOS development environment, then try again.
+
+Reflashing with Servo Micro also requires the [i2c-pseudo kernel module](#i2c-pseudo).
+
+### Installing i2c-pseudo {#i2c-pseudo}
+
+1. Install the `i2c-pseudo` Linux kernel module. (Do this **outside** of the chroot!)
+ * `$ cd src/platform/ec/extra/i2c_pseudo`
+ * `$ ./install`
+
+If the above fails, your system may be missing packages necessary for building kernel modules. Consult your Linux distribution's documentation and support forums. After installing any packages that might be missing, simply try the install script again.
+
+You will need to reinstall `i2c-pseudo` after each kernel upgrade.
+
+There is an intention to [upstream i2c-pseudo](https://issuetracker.google.com/129565355), though even if accepted upstream, it may or may not become included with common Linux distribution kernels.
+
+### Common reflash instructions
+
+These instructions apply when using any kind of Servo, including those with no special prerequisites (such as [Servo v2](https://www.chromium.org/chromium-os/servo/servo-v2) with its Yoshi flex cable connected to the DUT).
+
+1. Enter the chroot (for servod).
+ * `$ cros_sdk --no-ns-pid`
+1. Start servod.
+ * `$ sudo servod --board=<servod_board_name>`
+ * For some boards the servod board name is different than the EC codebase board name used below!
+1. Enter the chroot (for flash_ec).
+ * `$ cros_sdk`
+1. Build the EC image for your board.
+ * `$ cd ~/trunk/src/platform/ec`
+ * `$ board=<board_name>`
+ * `$ make -j BOARD="$board"`
+1. Run flash_ec from the util directory.
+ * `$ util/flash_ec --board="$board" --image=build/"$board"/ec.bin`
+
+## CR50 CCD sans servod alternative {#ccd-sans-servod}
+
+This section applies whether using CR50 CCD via [Servo v4](https://www.chromium.org/chromium-os/servo/servov4) or [SuzyQ aka SuzyQable](https://www.sparkfun.com/products/14746).
+
+When using CR50 CCD, it is possible to reflash without servod, which _must not_ be running when using this method.
+
+1. Enter the chroot.
+ * `$ cros_sdk`
+1. Build the EC image for your board.
+ * `$ cd ~/trunk/src/platform/ec`
+ * `$ board=<board_name>`
+ * `$ make -j BOARD="$board"`
+1. Run iteflash from the build/\<board\>/util directory.
+ * `$ build/"$board"/util/iteflash --i2c-interface=ccd --i2c-mux --send-waveform=1 --erase --write=build/"$board"/ec.bin`
+
+WARNING: The `--i2c-mux` flag is only required for some ITE EC boards. For boards without an I2C mux between CR50 and the EC, that flag _must not_ be specified. (This is handled for you when using `flash_ec` + `servod` because the latter has knowledge of which boards are expected to have the I2C mux.)
diff --git a/util/signer/bs b/util/signer/bs
index c06d611ef0..8c6b3a3822 100755
--- a/util/signer/bs
+++ b/util/signer/bs
@@ -99,8 +99,15 @@ tweak_manifest () {
exit 1
fi
- # Convert board RLZ code from ASCII to hex
- rlz="0x$(echo -n ${bid_params[0]} | hexdump -ve '/1 "%02x"')"
+ if [[ "${bid_params[0]}" == "0" ]] ; then
+ rlz="0"
+ elif [[ ${#bid_params[0]} == 4 ]] ; then
+ # Convert 4 char board RLZ code from ASCII to hex
+ rlz="0x$(echo -n ${bid_params[0]} | hexdump -ve '/1 "%02x"')"
+ else
+ echo "Invalid RLZ ${bid_params[0]}"
+ exit 1
+ fi
# Prepare text of all three board ID related nodes
sub="$(printf "\\\n\"board_id\": %s,\\\n" "${rlz}")"
@@ -291,8 +298,8 @@ tweak_manifest
count=0
for elf in ${elves[@]}; do
if [[ -n "${do_prod}" ]]; then
- if strings "${elf}" | grep -q "DBG/cr50"; then
- echo "Will not sign debug image with prod keys" >&2
+ if strings "${elf}" | egrep -q "(DBG|SQA)/cr50"; then
+ echo "Will not sign debug or SQA image with prod keys" >&2
exit 1
fi
fi
diff --git a/util/stm32mon.c b/util/stm32mon.c
index fc0a727644..05788b9189 100644
--- a/util/stm32mon.c
+++ b/util/stm32mon.c
@@ -20,9 +20,11 @@
#define _BSD_SOURCE /* Older glibc */
#include <arpa/inet.h>
+#include <compile_time_macros.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -35,6 +37,8 @@
#include <time.h>
#include <unistd.h>
+#define KBYTES_TO_BYTES 1024
+
/*
* Some Ubuntu versions do not export SPI_IOC_WR_MODE32 even though
* the kernel shipped on those supports it.
@@ -60,9 +64,10 @@
#define CMD_RP 0x82 /* Enables the read protection */
#define CMD_RU 0x92 /* Disables the read protection */
-#define RESP_NACK 0x1f
-#define RESP_ACK 0x79
-#define RESP_BUSY 0x76
+#define RESP_NACK 0x1f
+#define RESP_ACK 0x79 /* 0b 0111 1001 */
+#define RESP_BUSY 0x76
+#define RESP_DAMAGED_ACK 0xBC /* 0b 1011 1100, 1 bit shifted REST_ACK */
/* SPI Start of Frame */
#define SOF 0x5A
@@ -75,6 +80,56 @@
/* Upper bound of rebooting the monitor */
#define MAX_DELAY_REBOOT 100000 /* us */
+/* Standard addresses common across various ST chips */
+#define STM32_MAIN_MEMORY_ADDR 0x08000000
+#define STM32_SYSTEM_MEMORY_ADDR 0x1FFF0000
+
+#define STM32_UNIQUE_ID_SIZE_BYTES 12
+
+/*
+ * Device electronic signature contains factory-programmed identification
+ * and calibration data to automatically match the characteristics of the
+ * microcontroller.
+ */
+struct stm32_device_signature {
+ /*
+ * Address of the Unique Device ID register. This register contains a
+ * 96-bit value that is unique across all chips.
+ * Zero means ignore/unknown.
+ */
+ uint32_t unique_device_id_addr;
+ /*
+ * Address of the Flash Size register. This 16-bit register contains the
+ * flash size in KB.
+ * Zero means ignore/unknown.
+ */
+ uint32_t flash_size_addr;
+ /*
+ * Address of the Package Data register. This 16-bit register contains a
+ * value that differentiates between package types of a given chip.
+ * Zero means ignore/unknown.
+ */
+ uint32_t package_data_addr;
+};
+
+struct memory_info {
+ /* Zero means ignore/unknown/not-applicable */
+ uint32_t addr;
+ /* If addr is non-zero
+ * - zero here means value is dynamic and will be read from bootloader.
+ * If addr is zero,
+ * - zero here means ignore/unknown/not-applicable.
+ */
+ uint32_t size_bytes;
+};
+
+struct memory_layout {
+ struct memory_info main_memory;
+ struct memory_info system_memory;
+ struct memory_info otp_area;
+ struct memory_info option_bytes;
+};
+
/* known STM32 SoC parameters */
struct stm32_def {
uint16_t id;
@@ -82,22 +137,67 @@ struct stm32_def {
uint32_t flash_size;
uint32_t page_size;
uint32_t cmds_len[2];
+ const struct memory_layout memory_layout;
+ const struct stm32_device_signature device_signature;
} chip_defs[] = {
- {0x416, "STM32L15xxB", 0x20000, 256, {13, 13} },
- {0x429, "STM32L15xxB-A", 0x20000, 256, {13, 13} },
- {0x427, "STM32L15xxC", 0x40000, 256, {13, 13} },
- {0x435, "STM32L44xx", 0x40000, 2048, {13, 13} },
- {0x420, "STM32F100xx", 0x20000, 1024, {13, 13} },
- {0x410, "STM32F102R8", 0x10000, 1024, {13, 13} },
- {0x440, "STM32F05x", 0x10000, 1024, {13, 13} },
- {0x444, "STM32F03x", 0x08000, 1024, {13, 13} },
- {0x448, "STM32F07xB", 0x20000, 2048, {13, 13} },
- {0x432, "STM32F37xx", 0x40000, 2048, {13, 13} },
- {0x442, "STM32F09x", 0x40000, 2048, {13, 13} },
- {0x431, "STM32F411", 0x80000, 16384, {13, 19} },
- {0x441, "STM32F412", 0x80000, 16384, {13, 19} },
- {0x450, "STM32H74x", 0x200000, 131768, {13, 19} },
- {0x451, "STM32F76x", 0x200000, 32768, {13, 19} },
+ {0x416, "STM32L15xxB", 0x20000, 256, {13, 13}, { { 0 } }, { 0 } },
+ {0x429, "STM32L15xxB-A", 0x20000, 256, {13, 13}, { { 0 } }, { 0 } },
+ {0x427, "STM32L15xxC", 0x40000, 256, {13, 13}, { { 0 } }, { 0 } },
+ {0x435, "STM32L44xx", 0x40000, 2048, {13, 13}, { { 0 } }, { 0 } },
+ {0x420, "STM32F100xx", 0x20000, 1024, {13, 13}, { { 0 } }, { 0 } },
+ {0x410, "STM32F102R8", 0x10000, 1024, {13, 13}, { { 0 } }, { 0 } },
+ {0x440, "STM32F05x", 0x10000, 1024, {13, 13}, { { 0 } }, { 0 } },
+ {0x444, "STM32F03x", 0x08000, 1024, {13, 13}, { { 0 } }, { 0 } },
+ {0x448, "STM32F07xB", 0x20000, 2048, {13, 13}, { { 0 } }, { 0 } },
+ {0x432, "STM32F37xx", 0x40000, 2048, {13, 13}, { { 0 } }, { 0 } },
+ {0x442, "STM32F09x", 0x40000, 2048, {13, 13}, { { 0 } }, { 0 } },
+ {0x431, "STM32F411", 0x80000, 16384, {13, 19}, { { 0 } }, { 0 } },
+ {
+ .id = 0x441,
+ .name = "STM32F412",
+ .flash_size = 0x100000,
+ .page_size = 16384,
+ .cmds_len = {13, 19},
+ /*
+ * STM32F412:
+ * See https://www.st.com/resource/en/reference_manual/dm00180369.pdf
+ * Section 3.3 Table 5 Flash module organization
+ */
+ .memory_layout = {
+ .main_memory = {
+ .addr = STM32_MAIN_MEMORY_ADDR,
+ .size_bytes = 0, /* set by flash reg read */
+ },
+ .system_memory = {
+ .addr = STM32_SYSTEM_MEMORY_ADDR,
+ .size_bytes = 30 * KBYTES_TO_BYTES,
+ },
+ .otp_area = {
+ .addr = 0x1FFF7800,
+ .size_bytes = 528,
+ },
+ .option_bytes = {
+ .addr = 0x1FFFC000,
+ .size_bytes = 16,
+ }
+ },
+ /*
+ * STM32F412:
+ * See https://www.st.com/resource/en/reference_manual/dm00180369.pdf
+ * Section 31 Device electronic signature
+ */
+ .device_signature = {
+ .unique_device_id_addr = 0x1FFF7A10,
+ .flash_size_addr = 0x1FFF7A22,
+ /*
+ * Out of range for bootloader on this chip, so we don't
+ * attempt to read.
+ */
+ .package_data_addr = 0, /* 0x1FFF7BF0 */
+ }
+ },
+ {0x450, "STM32H74x", 0x200000, 131768, {13, 19}, { { 0 } }, { 0 } },
+ {0x451, "STM32F76x", 0x200000, 32768, {13, 19}, { { 0 } }, { 0 } },
{ 0 }
};
@@ -106,6 +206,8 @@ struct stm32_def {
#define DEFAULT_BAUDRATE B38400
#define PAGE_SIZE 256
#define INVALID_I2C_ADAPTER -1
+#define MAX_ACK_RETRY_COUNT (EXT_ERASE_TIMEOUT / DEFAULT_TIMEOUT)
+#define MAX_RETRY_COUNT 3
enum interface_mode {
MODE_SERIAL,
@@ -129,6 +231,20 @@ const char *serial_port = "/dev/ttyUSB1";
const char *input_filename;
const char *output_filename;
uint32_t offset = 0x08000000, length = 0;
+int retry_on_damaged_ack;
+
+/* STM32MON function return values */
+enum {
+ STM32_SUCCESS = 0,
+ STM32_EIO = -1, /* IO error */
+ STM32_EINVAL = -2, /* Got a faulty response from device */
+ STM32_ETIMEDOUT = -3, /* Device didn't respond in a time window. */
+ STM32_ENOMEM = -4, /* Failed to allocate memory. */
+ STM32_ENACK = -5, /* Got NACK. */
+ STM32_EDACK = -6, /* Got a damanged ACK. */
+};
+BUILD_ASSERT(STM32_SUCCESS == 0);
+#define IS_STM32_ERROR(res) ((res) < STM32_SUCCESS)
/* optional command flags */
enum {
@@ -159,6 +275,29 @@ static void discard_input(int);
/* On user request save all data exchange with the target in this log file. */
static FILE *log_file;
+/* Statistic data structure for response kind. */
+struct {
+ const char * const event_name;
+ uint32_t event_count;
+} stat_resp[] = {
+ { "RESP_ACK", 0 },
+ { "RESP_NACK", 0 },
+ { "RESP_BUSY", 0 },
+ { "RESP_DAMAGED_ACK", 0 },
+ { "JUNK", 0 },
+};
+
+enum {
+ RESP_ACK_IDX = 0,
+ RESP_NACK_IDX,
+ RESP_BUSY_IDX,
+ RESP_DAMAGED_ACK_IDX,
+ JUNK_IDX,
+ MAX_EVENT_IDX
+};
+
+BUILD_ASSERT(ARRAY_SIZE(stat_resp) == MAX_EVENT_IDX);
+
/*
* Print data into the log file, in hex, 16 bytes per line, prefix the first
* line with the value supplied by the caller (usually 'r' or 'w' for
@@ -207,6 +346,7 @@ static ssize_t write_wrapper(int fd, const void *buf, size_t count)
return rv;
}
+
int open_serial(const char *port, int cr50_mode)
{
int fd, res;
@@ -374,39 +514,62 @@ int wait_for_ack(int fd)
uint8_t resp;
int res;
time_t deadline = time(NULL) + DEFAULT_TIMEOUT;
- uint8_t ack = RESP_ACK;
+ const uint8_t ack = RESP_ACK;
while (time(NULL) < deadline) {
res = read_wrapper(fd, &resp, 1);
if ((res < 0) && (errno != EAGAIN)) {
perror("Failed to read answer");
- return -EIO;
+ return STM32_EIO;
}
- if (res == 1) {
- if (resp == RESP_ACK) {
- if (mode == MODE_SPI) /* Ack the ACK */
- if (write_wrapper(fd, &ack, 1) != 1)
- return -EIO;
- return 0;
- } else if (resp == RESP_NACK) {
- fprintf(stderr, "NACK\n");
- if (mode == MODE_SPI) /* Ack the NACK */
- if (write_wrapper(fd, &ack, 1) != 1)
- return -EIO;
- discard_input(fd);
- return -EINVAL;
- } else if (resp == RESP_BUSY) {
- /* I2C Boot protocol 1.1 */
- deadline = time(NULL) + DEFAULT_TIMEOUT;
- } else {
- if (mode == MODE_SERIAL)
- fprintf(stderr, "Receive junk: %02x\n",
- resp);
+
+ if (res != 1)
+ continue;
+
+ switch (resp) {
+ case RESP_ACK:
+ stat_resp[RESP_ACK_IDX].event_count++;
+ if (mode == MODE_SPI) /* Ack the ACK */
+ if (write_wrapper(fd, &ack, 1) != 1)
+ return STM32_EIO;
+ return STM32_SUCCESS;
+
+ case RESP_NACK:
+ stat_resp[RESP_NACK_IDX].event_count++;
+ fprintf(stderr, "NACK\n");
+ if (mode == MODE_SPI) /* Ack the NACK */
+ if (write_wrapper(fd, &ack, 1) != 1)
+ return STM32_EIO;
+ discard_input(fd);
+ return STM32_ENACK;
+
+ case RESP_BUSY:
+ stat_resp[RESP_BUSY_IDX].event_count++;
+ /* I2C Boot protocol 1.1 */
+ deadline = time(NULL) + DEFAULT_TIMEOUT;
+ break;
+
+ case RESP_DAMAGED_ACK:
+ if (retry_on_damaged_ack) {
+ /* It is a damaged ACK. However, device is
+ * likely to believe it sent ACK, so let's not
+ * treat it as junk.
+ */
+ stat_resp[RESP_DAMAGED_ACK_IDX].event_count++;
+ fprintf(stderr, "DAMAGED_ACK\n");
+ return STM32_EDACK;
}
+
+ /* Do not break so that it can be handled as junk */
+ default:
+ stat_resp[JUNK_IDX].event_count++;
+ if (mode == MODE_SERIAL)
+ fprintf(stderr, "Receive junk: %02x\n", resp);
+ break;
}
}
fprintf(stderr, "Timeout\n");
- return -ETIMEDOUT;
+ return STM32_ETIMEDOUT;
}
int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
@@ -418,19 +581,23 @@ int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
uint8_t cmd_frame[] = { SOF, cmd, 0xff ^ cmd }; /* XOR checksum */
/* only the SPI mode needs the Start Of Frame byte */
int cmd_off = mode == MODE_SPI ? 0 : 1;
+ int count_damaged_ack = 0;
/* Send the command index */
res = write_wrapper(fd, cmd_frame + cmd_off,
sizeof(cmd_frame) - cmd_off);
if (res <= 0) {
perror("Failed to write command frame");
- return -1;
+ return STM32_EIO;
}
/* Wait for the ACK */
- if (wait_for_ack(fd) < 0) {
+ res = wait_for_ack(fd);
+ if (res == STM32_EDACK) {
+ ++count_damaged_ack;
+ } else if (IS_STM32_ERROR(res)) {
fprintf(stderr, "Failed to get command 0x%02x ACK\n", cmd);
- return -1;
+ return res;
}
/* Send the command payloads */
@@ -442,7 +609,7 @@ int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
if (data == NULL) {
fprintf(stderr,
"Failed to allocate memory for load %d\n", c);
- return -ENOMEM;
+ return STM32_ENOMEM;
}
memcpy(data, p->data, size);
for (i = 0; i < size; i++)
@@ -457,35 +624,36 @@ int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
if (res < 0) {
perror("Failed to write command payload");
free(data);
- return -1;
+ return STM32_EIO;
}
size -= res;
data_ptr += res;
}
+ free(data);
/* Wait for the ACK */
res = wait_for_ack(fd);
- if (res < 0) {
- if (res != -ETIMEDOUT)
+ if (res == STM32_EDACK) {
+ ++count_damaged_ack;
+ } else if (IS_STM32_ERROR(res)) {
+ if (res != STM32_ETIMEDOUT)
fprintf(stderr,
"payload %d ACK failed for CMD%02x\n",
c, cmd);
- free(data);
return res;
}
- free(data);
}
/* Read the answer payload */
if (resp) {
if (mode == MODE_SPI) /* ignore dummy byte */
if (read_wrapper(fd, resp, 1) < 0)
- return -1;
+ return STM32_EIO;
while ((resp_size > 0) &&
(res = read_wrapper(fd, resp, resp_size))) {
if (res < 0) {
perror("Failed to read payload");
- return -1;
+ return STM32_EIO;
}
readcnt += res;
resp += res;
@@ -494,17 +662,48 @@ int send_command(int fd, uint8_t cmd, payload_t *loads, int cnt,
/* Wait for the ACK */
if (ack_requested) {
- if (wait_for_ack(fd) < 0) {
+ res = wait_for_ack(fd);
+ if (res == STM32_EDACK) {
+ ++count_damaged_ack;
+ } else if (IS_STM32_ERROR(res)) {
fprintf(stderr,
- "Failed to get response to command 0x%02x ACK\n",
- cmd);
- return -1;
+ "Failed to get response to command"
+ " 0x%02x ACK\n", cmd);
+ return res;
}
}
}
+
+ if (count_damaged_ack)
+ return STM32_EDACK;
+
return readcnt;
}
+int send_command_retry(int fd, uint8_t cmd, payload_t *loads,
+ int cnt, uint8_t *resp, int resp_size, int ack_requested)
+{
+ int res;
+ int retries = MAX_RETRY_COUNT;
+
+ do {
+ int ack_tries = MAX_ACK_RETRY_COUNT;
+
+ res = send_command(fd, cmd, loads, cnt, resp, resp_size,
+ ack_requested);
+
+ while (res == STM32_ETIMEDOUT && ack_tries--) {
+ if (cmd == CMD_WRITEMEM) {
+ /* send garbage byte */
+ write_wrapper(fd, loads->data, 1);
+ }
+ res = wait_for_ack(fd);
+ }
+ } while ((res == STM32_ENACK || res == STM32_EDACK) && retries--);
+
+ return res;
+}
+
struct stm32_def *command_get_id(int fd)
{
int res;
@@ -539,7 +738,7 @@ int init_monitor(int fd)
/* Skip in i2c mode */
if (mode == MODE_I2C)
- return 0;
+ return STM32_SUCCESS;
printf("Waiting for the monitor startup ...");
fflush(stdout);
@@ -549,23 +748,23 @@ int init_monitor(int fd)
res = write_wrapper(fd, &init, 1);
if (res <= 0) {
perror("Failed to write command");
- return -1;
+ return STM32_EIO;
}
/* Wait for the ACK */
res = wait_for_ack(fd);
- if (res == 0)
+ if (res == STM32_SUCCESS)
break;
- if (res == -EINVAL) {
+ if (res == STM32_ENACK) {
/* 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;
+ return STM32_SUCCESS;
}
}
- if (res < 0 && res != -ETIMEDOUT)
- return -1;
+ if (IS_STM32_ERROR(res) && res != STM32_ETIMEDOUT)
+ return res;
fflush(stdout);
}
printf("Done.\n");
@@ -573,7 +772,7 @@ int init_monitor(int fd)
/* read trailing chars */
discard_input(fd);
- return 0;
+ return STM32_SUCCESS;
}
int command_get_commands(int fd, struct stm32_def *chip)
@@ -590,7 +789,7 @@ int command_get_commands(int fd, struct stm32_def *chip)
if (cmds[0] > sizeof(cmds) - 2) {
fprintf(stderr, "invalid GET answer (%02x...)\n",
cmds[0]);
- return -1;
+ return STM32_EINVAL;
}
printf("Bootloader v%d.%d, commands : ",
cmds[1] >> 4, cmds[1] & 0xf);
@@ -607,11 +806,11 @@ int command_get_commands(int fd, struct stm32_def *chip)
erase = command_erase_i2c;
printf("\n");
- return 0;
+ return STM32_SUCCESS;
}
fprintf(stderr, "Cannot get bootloader command list.\n");
- return -1;
+ return STM32_EINVAL;
}
static int use_progressbar;
@@ -631,6 +830,7 @@ static void draw_spinner(uint32_t remaining, uint32_t size)
printf("\r%c%3d%%", wheel[windex++], percent);
windex %= sizeof(wheel);
}
+ fflush(stdout);
}
int command_read_mem(int fd, uint32_t address, uint32_t size, uint8_t *buffer)
@@ -645,18 +845,21 @@ int command_read_mem(int fd, uint32_t address, uint32_t size, uint8_t *buffer)
};
while (remaining) {
- cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE - 1 : remaining - 1;
+ uint32_t bytes = MIN(remaining, PAGE_SIZE);
+
+ cnt = (uint8_t) (bytes - 1);
addr_be = htonl(address);
draw_spinner(remaining, size);
- fflush(stdout);
- res = send_command(fd, CMD_READMEM, loads, 2, buffer, cnt + 1,
- 0);
- if (res < 0)
- return -EIO;
- buffer += cnt + 1;
- address += cnt + 1;
- remaining -= cnt + 1;
+
+ res = send_command_retry(fd, CMD_READMEM, loads, 2, buffer,
+ bytes, 0);
+ if (IS_STM32_ERROR(res))
+ return STM32_EIO;
+
+ buffer += bytes;
+ address += bytes;
+ remaining -= bytes;
}
return size;
@@ -676,7 +879,7 @@ int command_write_mem(int fd, uint32_t address, uint32_t size, uint8_t *buffer)
};
while (remaining) {
- cnt = (remaining > PAGE_SIZE) ? PAGE_SIZE : remaining;
+ cnt = MIN(remaining, PAGE_SIZE);
/* skip empty blocks to save time */
for (i = 0; i < cnt && buffer[i] == 0xff; i++)
;
@@ -687,11 +890,11 @@ int command_write_mem(int fd, uint32_t address, uint32_t size, uint8_t *buffer)
memcpy(outbuf + 1, buffer, cnt);
draw_spinner(remaining, size);
- fflush(stdout);
- res = send_command(fd, CMD_WRITEMEM, loads, 2,
+
+ res = send_command_retry(fd, CMD_WRITEMEM, loads, 2,
NULL, 0, 1);
- if (res < 0)
- return -EIO;
+ if (IS_STM32_ERROR(res))
+ return STM32_EIO;
}
buffer += cnt;
address += cnt;
@@ -707,7 +910,6 @@ int command_ext_erase(int fd, uint16_t count, uint16_t start)
uint16_t count_be = htons(count);
payload_t load = { 2, (uint8_t *)&count_be };
uint16_t *pages = NULL;
- int retries = EXT_ERASE_TIMEOUT / DEFAULT_TIMEOUT;
if (count < 0xfff0) {
int i;
@@ -715,7 +917,7 @@ int command_ext_erase(int fd, uint16_t count, uint16_t start)
load.size = 2 * (count + 1);
pages = malloc(load.size);
if (!pages)
- return -ENOMEM;
+ return STM32_ENOMEM;
load.data = (uint8_t *)pages;
pages[0] = htons(count - 1);
for (i = 0; i < count; i++)
@@ -723,11 +925,8 @@ int command_ext_erase(int fd, uint16_t count, uint16_t start)
}
printf("Erasing...\n");
- res = send_command(fd, CMD_EXTERASE, &load, 1, NULL, 0, 1);
- /* Erase can take long time (e.g. 13s+ on STM32H7) */
- while ((res == -ETIMEDOUT) && --retries)
- res = wait_for_ack(fd);
- if (res >= 0)
+ res = send_command_retry(fd, CMD_EXTERASE, &load, 1, NULL, 0, 1);
+ if (!IS_STM32_ERROR(res))
printf("Flash erased.\n");
if (pages)
@@ -759,20 +958,19 @@ int command_erase_i2c(int fd, uint16_t count, uint16_t start)
load[1].size = 2 * count;
pages = malloc(load[1].size);
if (!pages)
- return -ENOMEM;
+ return STM32_ENOMEM;
load[1].data = (uint8_t *)pages;
count_be = htons(count - 1);
for (i = 0; i < count; i++)
pages[i] = htons(start + i);
- } else {
- load_cnt = 1;
}
- erase_cmd = (boot_loader_version == 0x10 ? CMD_EXTERASE :
- CMD_NO_STRETCH_ERASE);
- res = send_command(fd, erase_cmd, load, load_cnt,
- NULL, 0, 1);
- if (res >= 0)
+ erase_cmd = (boot_loader_version == 0x10) ? CMD_EXTERASE :
+ CMD_NO_STRETCH_ERASE;
+
+ printf("Erasing...\n");
+ res = send_command(fd, erase_cmd, load, load_cnt, NULL, 0, 1);
+ if (!IS_STM32_ERROR(res))
printf("Flash erased.\n");
if (pages)
@@ -794,15 +992,16 @@ int command_erase(int fd, uint16_t count, uint16_t start)
load.size = count + 1;
pages = malloc(load.size);
if (!pages)
- return -ENOMEM;
+ return STM32_ENOMEM;
load.data = (uint8_t *)pages;
pages[0] = count - 1;
for (i = 0; i < count; i++)
pages[i+1] = start + i;
}
+ printf("Erasing...\n");
res = send_command(fd, CMD_ERASE, &load, 1, NULL, 0, 1);
- if (res >= 0)
+ if (!IS_STM32_ERROR(res))
printf("Flash erased.\n");
if (pages)
@@ -813,7 +1012,7 @@ int command_erase(int fd, uint16_t count, uint16_t start)
int command_read_unprotect(int fd)
{
int res;
- int retries = EXT_ERASE_TIMEOUT / DEFAULT_TIMEOUT;
+ int retries = MAX_RETRY_COUNT;
printf("Unprotecting flash read...\n");
@@ -824,9 +1023,9 @@ int command_read_unprotect(int fd)
*/
do {
res = wait_for_ack(fd);
- } while ((res == -ETIMEDOUT) && --retries);
+ } while ((res == STM32_ETIMEDOUT) && --retries);
- if (res < 0) {
+ if (IS_STM32_ERROR(res)) {
fprintf(stderr, "Failed to get read-protect ACK\n");
return res;
}
@@ -841,10 +1040,10 @@ int command_read_unprotect(int fd)
usleep(MAX_DELAY_REBOOT);
if (init_monitor(fd) < 0) {
fprintf(stderr, "Cannot recover after RU reset\n");
- return -EIO;
+ return STM32_EIO;
}
- return 0;
+ return STM32_SUCCESS;
}
int command_write_unprotect(int fd)
@@ -852,13 +1051,13 @@ int command_write_unprotect(int fd)
int res;
res = send_command(fd, CMD_WU, NULL, 0, NULL, 0, 1);
- if (res < 0)
- return -EIO;
+ if (IS_STM32_ERROR(res))
+ return STM32_EIO;
/* Wait for the ACK */
if (wait_for_ack(fd) < 0) {
fprintf(stderr, "Failed to get write-protect ACK\n");
- return -EINVAL;
+ return STM32_EINVAL;
}
printf("Flash write unprotected.\n");
@@ -871,10 +1070,10 @@ int command_write_unprotect(int fd)
usleep(MAX_DELAY_REBOOT);
if (init_monitor(fd) < 0) {
fprintf(stderr, "Cannot recover after WP reset\n");
- return -EIO;
+ return STM32_EIO;
}
- return 0;
+ return STM32_SUCCESS;
}
int command_go(int fd, uint32_t address)
@@ -884,8 +1083,8 @@ int command_go(int fd, uint32_t address)
payload_t load = { 4, (uint8_t *)&addr_be };
res = send_command(fd, CMD_GO, &load, 1, NULL, 0, 1);
- if (res < 0)
- return -EIO;
+ if (IS_STM32_ERROR(res))
+ return STM32_EIO;
#if 0 /* this ACK should exist according to the documentation ... */
/* Wait for the ACK */
@@ -896,7 +1095,162 @@ int command_go(int fd, uint32_t address)
#endif
printf("Program started at 0x%08x.\n", address);
- return 0;
+ return STM32_SUCCESS;
+}
+
+/*
+ * The bootloader does not allow reading directly from the "device signature"
+ * registers. However, it does allow reading the OTP region, so this function
+ * starts a read from the last byte in that region and reads an additional
+ * number of bytes to read the requested register.
+ *
+ * Example:
+ *
+ * Given a chip with OTP region starting at address 0x1FFF7800 with a size of
+ * 528 bytes and a register that we want to read at address 0x1FFF7A10 with a
+ * size of 12 bytes:
+ *
+ * We start the read at the last byte in the OTP region:
+ *
+ * 0x1FFF7800 + 528 - 1 = 0x1FFF7A0F
+ *
+ * From 0x1FFF7A0F we perform a read of (12 + 1) = 13 bytes in order to read the
+ * 12 bytes starting at 0x1FFF7A10 (the actual register we care about).
+ *
+ * Returns zero on success, negative on failure.
+ */
+int read_device_signature_register(int fd, const struct stm32_def *chip,
+ uint32_t addr, uint32_t size_bytes,
+ uint8_t *out_buffer)
+{
+ int res;
+ uint8_t *buffer;
+ struct memory_info otp = chip->memory_layout.otp_area;
+ uint32_t otp_end_addr = otp.addr + otp.size_bytes - 1;
+ uint32_t offset = addr - otp_end_addr;
+ uint32_t read_size_bytes = offset + size_bytes;
+
+ if (!otp.addr) {
+ fprintf(stderr, "No otp_area.addr specified for given chip.\n");
+ return STM32_EINVAL;
+ }
+
+ if (addr <= otp_end_addr) {
+ fprintf(stderr, "Attempting to read from invalid address: "
+ "%08X\n", addr);
+ return STM32_EINVAL;
+ }
+
+ /*
+ * The USART/SPI/I2C bootloader can only read at most 256 bytes in a
+ * single read command (see AN4286 section 2.5 or AN3155 section 3.4).
+ *
+ * command_read_mem will correctly chunk larger requests, but the
+ * subsequent reads will fail because the bootloader won't allow reads
+ * from a starting address that is beyond the OTP region.
+ */
+ if (read_size_bytes > PAGE_SIZE) {
+ fprintf(stderr,
+ "Requested register 0x%08X is outside read range.\n",
+ addr);
+ return STM32_EINVAL;
+ }
+
+ buffer = malloc(read_size_bytes);
+ if (!buffer) {
+ fprintf(stderr, "Cannot allocate %" PRIu32 " bytes\n",
+ read_size_bytes);
+ return STM32_ENOMEM;
+ }
+
+ res = command_read_mem(fd, otp_end_addr, read_size_bytes, buffer);
+ if (res == read_size_bytes)
+ memcpy(out_buffer, buffer + offset, size_bytes);
+ else
+ fprintf(stderr,
+ "Cannot read %" PRIu32 " bytes from address 0x%08X",
+ read_size_bytes, otp_end_addr);
+
+ free(buffer);
+ return IS_STM32_ERROR(res) ? res : STM32_SUCCESS;
+}
+
+/* Return zero on success, a negative error value on failures. */
+int read_flash_size_register(int fd, struct stm32_def *chip,
+ uint16_t *flash_size_kbytes)
+{
+ int res;
+ uint32_t flash_size_addr = chip->device_signature.flash_size_addr;
+
+ if (!flash_size_addr)
+ return STM32_EINVAL;
+
+ res = read_device_signature_register(fd, chip,
+ flash_size_addr, sizeof(*flash_size_kbytes),
+ (uint8_t *)flash_size_kbytes);
+
+ if (!IS_STM32_ERROR(res))
+ printf("Flash size: %" PRIu16 " KB\n", *flash_size_kbytes);
+ else
+ fprintf(stderr,
+ "Unable to read flash size register (0x%08X).\n",
+ flash_size_addr);
+
+ return res;
+}
+
+/* Return zero on success, a negative error value on failures. */
+int read_unique_device_id_register(int fd, struct stm32_def *chip,
+ uint8_t device_id[STM32_UNIQUE_ID_SIZE_BYTES])
+{
+ int i;
+ int res;
+ uint32_t unique_device_id_addr =
+ chip->device_signature.unique_device_id_addr;
+
+ if (!unique_device_id_addr)
+ return STM32_EINVAL;
+
+ res = read_device_signature_register(fd, chip, unique_device_id_addr,
+ STM32_UNIQUE_ID_SIZE_BYTES, device_id);
+
+ if (!IS_STM32_ERROR(res)) {
+ printf("Unique Device ID: 0x");
+ for (i = STM32_UNIQUE_ID_SIZE_BYTES - 1; i >= 0; i--)
+ printf("%02X", device_id[i]);
+ printf("\n");
+ } else {
+ fprintf(stderr,
+ "Unable to read unique device ID register (0x%08X). "
+ "Ignoring non-critical failure.\n",
+ unique_device_id_addr);
+ }
+
+ return res;
+}
+
+/* Return zero on success, a negative error value on failures. */
+int read_package_data_register(int fd, struct stm32_def *chip,
+ uint16_t *package_data)
+{
+ int res;
+ uint32_t package_data_addr = chip->device_signature.package_data_addr;
+
+ if (!package_data_addr)
+ return STM32_EINVAL;
+
+ res = read_device_signature_register(fd, chip, package_data_addr,
+ sizeof(*package_data),
+ (uint8_t *)package_data);
+
+ if (!IS_STM32_ERROR(res))
+ printf("Package data register: %04X\n", *package_data);
+ else
+ fprintf(stderr,
+ "Failed to read package data register (0x%08X). "
+ "Ignoring non-critical failure.\n", package_data_addr);
+
+ return res;
}
/* Return zero on success, a negative error value on failures. */
@@ -912,14 +1266,14 @@ int read_flash(int fd, struct stm32_def *chip, const char *filename,
buffer = malloc(size);
if (!buffer) {
fprintf(stderr, "Cannot allocate %d bytes\n", size);
- return -ENOMEM;
+ return STM32_ENOMEM;
}
hnd = fopen(filename, "w");
if (!hnd) {
fprintf(stderr, "Cannot open file %s for writing\n", filename);
free(buffer);
- return -EIO;
+ return STM32_EIO;
}
printf("Reading %d bytes at 0x%08x\n", size, offset);
@@ -932,7 +1286,7 @@ int read_flash(int fd, struct stm32_def *chip, const char *filename,
fclose(hnd);
free(buffer);
- return (res < 0) ? res : 0;
+ return IS_STM32_ERROR(res) ? res : STM32_SUCCESS;
}
/* Return zero on success, a negative error value on failures. */
@@ -946,7 +1300,7 @@ int write_flash(int fd, struct stm32_def *chip, const char *filename,
if (!buffer) {
fprintf(stderr, "Cannot allocate %d bytes\n", size);
- return -ENOMEM;
+ return STM32_ENOMEM;
}
if (!strncmp(filename, "-", sizeof("-")))
@@ -956,16 +1310,15 @@ int write_flash(int fd, struct stm32_def *chip, const char *filename,
if (!hnd) {
fprintf(stderr, "Cannot open file %s for reading\n", filename);
free(buffer);
- return -EIO;
+ return STM32_EIO;
}
res = fread(buffer, 1, size, hnd);
+ fclose(hnd);
if (res <= 0) {
fprintf(stderr, "Cannot read %s\n", filename);
free(buffer);
- fclose(hnd);
- return -EIO;
+ return STM32_EIO;
}
- fclose(hnd);
/* faster write: skip empty trailing space */
while (res && buffer[res - 1] == 0xff)
@@ -978,12 +1331,12 @@ int write_flash(int fd, struct stm32_def *chip, const char *filename,
if (written != res) {
fprintf(stderr, "Error writing to flash\n");
free(buffer);
- return -EIO;
+ return STM32_EIO;
}
- printf("\rDone.\n");
+ printf("\r %d bytes written.\n", written);
free(buffer);
- return 0;
+ return STM32_SUCCESS;
}
static const struct option longopts[] = {
@@ -1144,16 +1497,35 @@ int parse_parameters(int argc, char **argv)
return flags;
}
+static void display_stat_response(void)
+{
+ uint32_t total_events = MAX_EVENT_IDX;
+ uint32_t idx;
+
+ printf("--\n");
+ for (idx = 0; idx < total_events; ++idx) {
+ printf("%-18s %d\n", stat_resp[idx].event_name,
+ stat_resp[idx].event_count);
+ }
+ printf("--\n");
+}
+
int main(int argc, char **argv)
{
int ser;
struct stm32_def *chip;
- int ret = 1;
+ int ret = STM32_EIO;
+ int res;
int flags;
+ uint16_t flash_size_kbytes = 0;
+ uint8_t unique_device_id[STM32_UNIQUE_ID_SIZE_BYTES] = { 0 };
+ uint16_t package_data_reg = 0;
/* Parse command line options */
flags = parse_parameters(argc, argv);
+ retry_on_damaged_ack = !!(flags & FLAG_CR50_MODE);
+
switch (mode) {
case MODE_SPI:
ser = open_spi(spi_adapter);
@@ -1176,6 +1548,27 @@ int main(int argc, char **argv)
if (!chip)
goto terminate;
+ /*
+ * Use the actual size if we were able to read it since some chips
+ * have the same chip ID, but different flash sizes based on the
+ * package.
+ */
+ res = read_flash_size_register(ser, chip, &flash_size_kbytes);
+ if (!IS_STM32_ERROR(res))
+ chip->flash_size = flash_size_kbytes * KBYTES_TO_BYTES;
+
+ /*
+ * This is simply informative at the moment, so we don't care about the
+ * return value.
+ */
+ (void)read_unique_device_id_register(ser, chip, unique_device_id);
+
+ /*
+ * This is simply informative at the moment, so we don't care about the
+ * return value.
+ */
+ (void)read_package_data_register(ser, chip, &package_data_reg);
+
if (command_get_commands(ser, chip) < 0)
goto terminate;
@@ -1186,31 +1579,31 @@ int main(int argc, char **argv)
if (flags & FLAG_ERASE || output_filename) {
if ((!strncmp("STM32L15", chip->name, 8)) ||
- (!strncmp("STM32F41", chip->name, 8))) {
+ (!strncmp("STM32F411", chip->name, 9))) {
/* Mass erase is not supported on these chips*/
int i, page_count = chip->flash_size / chip->page_size;
for (i = 0; i < page_count; i += 128) {
int count = MIN(128, page_count - i);
ret = erase(ser, count, i);
- if (ret)
+ if (IS_STM32_ERROR(ret))
goto terminate;
}
} else {
ret = erase(ser, 0xFFFF, 0);
- if (ret)
+ if (IS_STM32_ERROR(ret))
goto terminate;
}
}
if (input_filename) {
ret = read_flash(ser, chip, input_filename, offset, length);
- if (ret)
+ if (IS_STM32_ERROR(ret))
goto terminate;
}
if (output_filename) {
ret = write_flash(ser, chip, output_filename, offset);
- if (ret)
+ if (IS_STM32_ERROR(ret))
goto terminate;
}
@@ -1219,12 +1612,22 @@ int main(int argc, char **argv)
command_go(ser, offset);
/* Normal exit */
- ret = 0;
+ ret = STM32_SUCCESS;
terminate:
if (log_file)
fclose(log_file);
/* Close serial port */
close(ser);
- return ret;
+
+ if (retry_on_damaged_ack)
+ display_stat_response();
+
+ if (IS_STM32_ERROR(ret)) {
+ fprintf(stderr, "Failed: %d\n", ret);
+ return 1;
+ }
+
+ printf("Done.\n");
+ return 0;
}
diff --git a/util/usb_if.c b/util/usb_if.c
index 744faf2b16..1f0adfecda 100644
--- a/util/usb_if.c
+++ b/util/usb_if.c
@@ -76,7 +76,7 @@ int usb_findit(uint16_t vid, uint16_t pid, uint16_t subclass,
r = libusb_init(NULL);
if (r < 0) {
USB_ERROR("libusb_init", r);
- return -1;
+ goto terminate_usb_findit;
}
printf("open_device %04x:%04x\n", vid, pid);
@@ -84,17 +84,17 @@ int usb_findit(uint16_t vid, uint16_t pid, uint16_t subclass,
uep->devh = libusb_open_device_with_vid_pid(NULL, vid, pid);
if (!uep->devh) {
fprintf(stderr, "Can't find device\n");
- return -1;
+ goto terminate_usb_findit;
}
iface_num = find_interface(subclass, protocol, uep);
if (iface_num < 0) {
- fprintf(stderr, "USB FW update not supported by that device\n");
- usb_shut_down(uep);
+ fprintf(stderr, "USB interface %d is not found\n", uep->ep_num);
+ goto terminate_usb_findit;
}
if (!uep->chunk_len) {
fprintf(stderr, "wMaxPacketSize isn't valid\n");
- usb_shut_down(uep);
+ goto terminate_usb_findit;
}
printf("found interface %d endpoint %d, chunk_len %d\n",
@@ -104,11 +104,16 @@ int usb_findit(uint16_t vid, uint16_t pid, uint16_t subclass,
r = libusb_claim_interface(uep->devh, iface_num);
if (r < 0) {
USB_ERROR("libusb_claim_interface", r);
- usb_shut_down(uep);
+ goto terminate_usb_findit;
}
printf("READY\n-------\n");
return 0;
+
+terminate_usb_findit:
+ if (uep->devh)
+ usb_shut_down(uep);
+ return -1;
}
int usb_trx(struct usb_endpoint *uep, void *outbuf, int outlen,